Cindy Lin | 6ec3c2b | 2024-05-16 07:39:23 +0000 | [diff] [blame] | 1 | // Copyright 2024, The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | //! Implements mls_rs_core's CryptoProvider and CipherSuiteProvider backed by BoringSSL. |
| 16 | |
| 17 | pub mod aead; |
| 18 | pub mod ecdh; |
| 19 | pub mod eddsa; |
| 20 | pub mod hash; |
| 21 | pub mod hpke; |
| 22 | pub mod kdf; |
| 23 | |
| 24 | #[cfg(test)] |
| 25 | mod test_helpers; |
| 26 | |
| 27 | use mls_rs_core::crypto::{ |
| 28 | CipherSuite, CipherSuiteProvider, CryptoProvider, HpkeCiphertext, HpkePublicKey, HpkeSecretKey, |
| 29 | SignaturePublicKey, SignatureSecretKey, |
| 30 | }; |
| 31 | use mls_rs_core::error::{AnyError, IntoAnyError}; |
| 32 | use mls_rs_crypto_traits::{AeadType, KdfType, KemType}; |
| 33 | use thiserror::Error; |
| 34 | use zeroize::Zeroizing; |
| 35 | |
| 36 | use aead::AeadWrapper; |
| 37 | use ecdh::Ecdh; |
| 38 | use eddsa::{EdDsa, EdDsaError}; |
| 39 | use hash::{Hash, HashError}; |
| 40 | use hpke::{ContextR, ContextS, DhKem, Hpke, HpkeError}; |
| 41 | use kdf::Kdf; |
| 42 | |
| 43 | /// Errors returned from BoringsslCryptoProvider. |
| 44 | #[derive(Debug, Error)] |
| 45 | pub enum BoringsslCryptoError { |
| 46 | /// Error returned from hash functions and HMACs. |
| 47 | #[error(transparent)] |
| 48 | HashError(#[from] HashError), |
| 49 | /// Error returned from KEMs. |
| 50 | #[error(transparent)] |
| 51 | KemError(AnyError), |
| 52 | /// Error returned from KDFs. |
| 53 | #[error(transparent)] |
| 54 | KdfError(AnyError), |
| 55 | /// Error returned from AEADs. |
| 56 | #[error(transparent)] |
| 57 | AeadError(AnyError), |
| 58 | /// Error returned from HPKE. |
| 59 | #[error(transparent)] |
| 60 | HpkeError(#[from] HpkeError), |
| 61 | /// Error returned from EdDSA. |
| 62 | #[error(transparent)] |
| 63 | EdDsaError(#[from] EdDsaError), |
| 64 | } |
| 65 | |
| 66 | impl IntoAnyError for BoringsslCryptoError { |
| 67 | fn into_dyn_error(self) -> Result<Box<dyn std::error::Error + Send + Sync>, Self> { |
| 68 | Ok(self.into()) |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | /// CryptoProvider trait implementation backed by BoringSSL. |
| 73 | #[derive(Debug, Clone)] |
| 74 | #[non_exhaustive] |
| 75 | pub struct BoringsslCryptoProvider { |
| 76 | /// Available cipher suites. |
| 77 | pub enabled_cipher_suites: Vec<CipherSuite>, |
| 78 | } |
| 79 | |
| 80 | impl BoringsslCryptoProvider { |
| 81 | /// Creates a new BoringsslCryptoProvider. |
| 82 | pub fn new() -> Self { |
| 83 | Default::default() |
| 84 | } |
| 85 | |
| 86 | /// Sets the enabled cipher suites. |
| 87 | pub fn with_enabled_cipher_suites(enabled_cipher_suites: Vec<CipherSuite>) -> Self { |
| 88 | Self { enabled_cipher_suites } |
| 89 | } |
| 90 | |
| 91 | /// Returns all available cipher suites. |
| 92 | pub fn all_supported_cipher_suites() -> Vec<CipherSuite> { |
| 93 | vec![CipherSuite::CURVE25519_AES128, CipherSuite::CURVE25519_CHACHA] |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | impl Default for BoringsslCryptoProvider { |
| 98 | fn default() -> Self { |
| 99 | Self { enabled_cipher_suites: Self::all_supported_cipher_suites() } |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | impl CryptoProvider for BoringsslCryptoProvider { |
| 104 | type CipherSuiteProvider = BoringsslCipherSuite<DhKem<Ecdh, Kdf>, Kdf, AeadWrapper>; |
| 105 | |
| 106 | fn supported_cipher_suites(&self) -> Vec<CipherSuite> { |
| 107 | self.enabled_cipher_suites.clone() |
| 108 | } |
| 109 | |
| 110 | fn cipher_suite_provider( |
| 111 | &self, |
| 112 | cipher_suite: CipherSuite, |
| 113 | ) -> Option<Self::CipherSuiteProvider> { |
| 114 | if !self.enabled_cipher_suites.contains(&cipher_suite) { |
| 115 | return None; |
| 116 | } |
| 117 | |
| 118 | let ecdh = Ecdh::new(cipher_suite)?; |
| 119 | let kdf = Kdf::new(cipher_suite)?; |
| 120 | let kem = DhKem::new(cipher_suite, ecdh, kdf.clone())?; |
| 121 | let aead = AeadWrapper::new(cipher_suite)?; |
| 122 | |
| 123 | BoringsslCipherSuite::new(cipher_suite, kem, kdf, aead) |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | /// CipherSuiteProvider trait implementation backed by BoringSSL. |
| 128 | #[derive(Clone)] |
| 129 | pub struct BoringsslCipherSuite<KEM, KDF, AEAD> |
| 130 | where |
| 131 | KEM: KemType + Clone, |
| 132 | KDF: KdfType + Clone, |
| 133 | AEAD: AeadType + Clone, |
| 134 | { |
| 135 | cipher_suite: CipherSuite, |
| 136 | hash: Hash, |
| 137 | kem: KEM, |
| 138 | kdf: KDF, |
| 139 | aead: AEAD, |
| 140 | hpke: Hpke, |
| 141 | eddsa: EdDsa, |
| 142 | } |
| 143 | |
| 144 | impl<KEM, KDF, AEAD> BoringsslCipherSuite<KEM, KDF, AEAD> |
| 145 | where |
| 146 | KEM: KemType + Clone, |
| 147 | KDF: KdfType + Clone, |
| 148 | AEAD: AeadType + Clone, |
| 149 | { |
| 150 | /// Creates a new BoringsslCipherSuite. |
| 151 | pub fn new(cipher_suite: CipherSuite, kem: KEM, kdf: KDF, aead: AEAD) -> Option<Self> { |
| 152 | Some(Self { |
| 153 | cipher_suite, |
| 154 | hash: Hash::new(cipher_suite).ok()?, |
| 155 | kem, |
| 156 | kdf, |
| 157 | aead, |
| 158 | hpke: Hpke::new(cipher_suite), |
| 159 | eddsa: EdDsa::new(cipher_suite)?, |
| 160 | }) |
| 161 | } |
| 162 | |
| 163 | /// Returns random bytes generated via BoringSSL. |
| 164 | pub fn random_bytes(&self, out: &mut [u8]) -> Result<(), BoringsslCryptoError> { |
| 165 | bssl_crypto::rand_bytes(out); |
| 166 | Ok(()) |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] |
| 171 | #[cfg_attr(all(target_arch = "wasm32", mls_build_async), maybe_async::must_be_async(?Send))] |
| 172 | #[cfg_attr(all(not(target_arch = "wasm32"), mls_build_async), maybe_async::must_be_async)] |
| 173 | impl<KEM, KDF, AEAD> CipherSuiteProvider for BoringsslCipherSuite<KEM, KDF, AEAD> |
| 174 | where |
| 175 | KEM: KemType + Clone + Send + Sync, |
| 176 | KDF: KdfType + Clone + Send + Sync, |
| 177 | AEAD: AeadType + Clone + Send + Sync, |
| 178 | { |
| 179 | type Error = BoringsslCryptoError; |
| 180 | type HpkeContextS = ContextS; |
| 181 | type HpkeContextR = ContextR; |
| 182 | |
| 183 | fn cipher_suite(&self) -> CipherSuite { |
| 184 | self.cipher_suite |
| 185 | } |
| 186 | |
| 187 | fn random_bytes(&self, out: &mut [u8]) -> Result<(), Self::Error> { |
| 188 | self.random_bytes(out) |
| 189 | } |
| 190 | |
| 191 | async fn hash(&self, data: &[u8]) -> Result<Vec<u8>, Self::Error> { |
| 192 | Ok(self.hash.hash(data)) |
| 193 | } |
| 194 | |
| 195 | async fn mac(&self, key: &[u8], data: &[u8]) -> Result<Vec<u8>, Self::Error> { |
| 196 | Ok(self.hash.mac(key, data)?) |
| 197 | } |
| 198 | |
| 199 | async fn kem_generate(&self) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error> { |
| 200 | self.kem.generate().await.map_err(|e| BoringsslCryptoError::KemError(e.into_any_error())) |
| 201 | } |
| 202 | |
| 203 | async fn kem_derive(&self, ikm: &[u8]) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error> { |
| 204 | self.kem.derive(ikm).await.map_err(|e| BoringsslCryptoError::KemError(e.into_any_error())) |
| 205 | } |
| 206 | |
| 207 | fn kem_public_key_validate(&self, key: &HpkePublicKey) -> Result<(), Self::Error> { |
| 208 | self.kem |
| 209 | .public_key_validate(key) |
| 210 | .map_err(|e| BoringsslCryptoError::KemError(e.into_any_error())) |
| 211 | } |
| 212 | |
| 213 | async fn kdf_extract( |
| 214 | &self, |
| 215 | salt: &[u8], |
| 216 | ikm: &[u8], |
| 217 | ) -> Result<Zeroizing<Vec<u8>>, Self::Error> { |
| 218 | self.kdf |
| 219 | .extract(salt, ikm) |
| 220 | .await |
| 221 | .map_err(|e| BoringsslCryptoError::KdfError(e.into_any_error())) |
| 222 | .map(Zeroizing::new) |
| 223 | } |
| 224 | |
| 225 | async fn kdf_expand( |
| 226 | &self, |
| 227 | prk: &[u8], |
| 228 | info: &[u8], |
| 229 | len: usize, |
| 230 | ) -> Result<Zeroizing<Vec<u8>>, Self::Error> { |
| 231 | self.kdf |
| 232 | .expand(prk, info, len) |
| 233 | .await |
| 234 | .map_err(|e| BoringsslCryptoError::KdfError(e.into_any_error())) |
| 235 | .map(Zeroizing::new) |
| 236 | } |
| 237 | |
| 238 | fn kdf_extract_size(&self) -> usize { |
| 239 | self.kdf.extract_size() |
| 240 | } |
| 241 | |
| 242 | async fn aead_seal( |
| 243 | &self, |
| 244 | key: &[u8], |
| 245 | data: &[u8], |
| 246 | aad: Option<&[u8]>, |
| 247 | nonce: &[u8], |
| 248 | ) -> Result<Vec<u8>, Self::Error> { |
| 249 | self.aead |
| 250 | .seal(key, data, aad, nonce) |
| 251 | .await |
| 252 | .map_err(|e| BoringsslCryptoError::AeadError(e.into_any_error())) |
| 253 | } |
| 254 | |
| 255 | async fn aead_open( |
| 256 | &self, |
| 257 | key: &[u8], |
| 258 | cipher_text: &[u8], |
| 259 | aad: Option<&[u8]>, |
| 260 | nonce: &[u8], |
| 261 | ) -> Result<Zeroizing<Vec<u8>>, Self::Error> { |
| 262 | self.aead |
| 263 | .open(key, cipher_text, aad, nonce) |
| 264 | .await |
| 265 | .map_err(|e| BoringsslCryptoError::AeadError(e.into_any_error())) |
| 266 | .map(Zeroizing::new) |
| 267 | } |
| 268 | |
| 269 | fn aead_key_size(&self) -> usize { |
| 270 | self.aead.key_size() |
| 271 | } |
| 272 | |
| 273 | fn aead_nonce_size(&self) -> usize { |
| 274 | self.aead.nonce_size() |
| 275 | } |
| 276 | |
| 277 | async fn hpke_setup_s( |
| 278 | &self, |
| 279 | remote_key: &HpkePublicKey, |
| 280 | info: &[u8], |
| 281 | ) -> Result<(Vec<u8>, Self::HpkeContextS), Self::Error> { |
| 282 | Ok(self.hpke.setup_sender(remote_key, info).await?) |
| 283 | } |
| 284 | |
| 285 | async fn hpke_seal( |
| 286 | &self, |
| 287 | remote_key: &HpkePublicKey, |
| 288 | info: &[u8], |
| 289 | aad: Option<&[u8]>, |
| 290 | pt: &[u8], |
| 291 | ) -> Result<HpkeCiphertext, Self::Error> { |
| 292 | Ok(self.hpke.seal(remote_key, info, aad, pt).await?) |
| 293 | } |
| 294 | |
| 295 | async fn hpke_setup_r( |
| 296 | &self, |
| 297 | enc: &[u8], |
| 298 | local_secret: &HpkeSecretKey, |
| 299 | // Other implementations use `_local_public` to skip derivation of the public from the |
| 300 | // private key for the KEM decapsulation step, but BoringSSL's API does not accept a public |
| 301 | // key and instead derives it under the hood. |
| 302 | _local_public: &HpkePublicKey, |
| 303 | info: &[u8], |
| 304 | ) -> Result<Self::HpkeContextR, Self::Error> { |
| 305 | Ok(self.hpke.setup_receiver(enc, local_secret, info).await?) |
| 306 | } |
| 307 | |
| 308 | async fn hpke_open( |
| 309 | &self, |
| 310 | ciphertext: &HpkeCiphertext, |
| 311 | local_secret: &HpkeSecretKey, |
| 312 | // Other implementations use `_local_public` to skip derivation of the public from the |
| 313 | // private key for hpke_setup_r()'s KEM decapsulation step, but BoringSSL's API does not |
| 314 | // accept a public key and instead derives it under the hood. |
| 315 | _local_public: &HpkePublicKey, |
| 316 | info: &[u8], |
| 317 | aad: Option<&[u8]>, |
| 318 | ) -> Result<Vec<u8>, Self::Error> { |
| 319 | Ok(self.hpke.open(ciphertext, local_secret, info, aad).await?) |
| 320 | } |
| 321 | |
| 322 | async fn signature_key_generate( |
| 323 | &self, |
| 324 | ) -> Result<(SignatureSecretKey, SignaturePublicKey), Self::Error> { |
| 325 | Ok(self.eddsa.signature_key_generate()?) |
| 326 | } |
| 327 | |
| 328 | async fn signature_key_derive_public( |
| 329 | &self, |
| 330 | secret_key: &SignatureSecretKey, |
| 331 | ) -> Result<SignaturePublicKey, Self::Error> { |
| 332 | Ok(self.eddsa.signature_key_derive_public(secret_key)?) |
| 333 | } |
| 334 | |
| 335 | async fn sign( |
| 336 | &self, |
| 337 | secret_key: &SignatureSecretKey, |
| 338 | data: &[u8], |
| 339 | ) -> Result<Vec<u8>, Self::Error> { |
| 340 | Ok(self.eddsa.sign(secret_key, data)?) |
| 341 | } |
| 342 | |
| 343 | async fn verify( |
| 344 | &self, |
| 345 | public_key: &SignaturePublicKey, |
| 346 | signature: &[u8], |
| 347 | data: &[u8], |
| 348 | ) -> Result<(), Self::Error> { |
| 349 | Ok(self.eddsa.verify(public_key, signature, data)?) |
| 350 | } |
| 351 | } |
| 352 | |
| 353 | #[cfg(all(not(mls_build_async), test))] |
| 354 | mod test { |
| 355 | use super::BoringsslCryptoProvider; |
| 356 | use crate::test_helpers::decode_hex; |
| 357 | use mls_rs_core::crypto::{ |
| 358 | CipherSuite, CipherSuiteProvider, CryptoProvider, HpkeContextR, HpkeContextS, |
| 359 | HpkePublicKey, HpkeSecretKey, SignaturePublicKey, SignatureSecretKey, |
| 360 | }; |
| 361 | |
| 362 | fn get_cipher_suites() -> Vec<CipherSuite> { |
| 363 | vec![CipherSuite::CURVE25519_AES128, CipherSuite::CURVE25519_CHACHA] |
| 364 | } |
| 365 | |
| 366 | #[test] |
| 367 | fn supported_cipher_suites() { |
| 368 | let bssl = BoringsslCryptoProvider::new(); |
| 369 | assert_eq!(bssl.supported_cipher_suites().len(), 2); |
| 370 | } |
| 371 | |
| 372 | #[test] |
| 373 | fn unsupported_cipher_suites() { |
| 374 | let bssl = BoringsslCryptoProvider::new(); |
| 375 | for suite in vec![ |
| 376 | CipherSuite::P256_AES128, |
| 377 | CipherSuite::CURVE448_AES256, |
| 378 | CipherSuite::P521_AES256, |
| 379 | CipherSuite::CURVE448_CHACHA, |
| 380 | CipherSuite::P384_AES256, |
| 381 | ] { |
| 382 | assert!(bssl.cipher_suite_provider(suite).is_none()); |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | #[test] |
| 387 | fn cipher_suite() { |
| 388 | let bssl = BoringsslCryptoProvider::new(); |
| 389 | for suite in get_cipher_suites() { |
| 390 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 391 | assert_eq!(crypto.cipher_suite(), suite); |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | #[test] |
| 396 | fn random_bytes() { |
| 397 | let bssl = BoringsslCryptoProvider::new(); |
| 398 | for suite in get_cipher_suites() { |
| 399 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 400 | let mut buf = [0; 32]; |
| 401 | let _ = crypto.random_bytes(&mut buf); |
| 402 | } |
| 403 | } |
| 404 | |
| 405 | #[test] |
| 406 | fn hash() { |
| 407 | let bssl = BoringsslCryptoProvider::new(); |
| 408 | for suite in get_cipher_suites() { |
| 409 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 410 | assert_eq!( |
| 411 | crypto.hash(&decode_hex::<4>("74ba2521")).unwrap(), |
| 412 | // bssl_crypto::hmac test vector. |
| 413 | decode_hex::<32>( |
| 414 | "b16aa56be3880d18cd41e68384cf1ec8c17680c45a02b1575dc1518923ae8b0e" |
| 415 | ) |
| 416 | ); |
| 417 | } |
| 418 | } |
| 419 | |
| 420 | #[test] |
| 421 | fn mac() { |
| 422 | let bssl = BoringsslCryptoProvider::new(); |
| 423 | for suite in get_cipher_suites() { |
| 424 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 425 | // bssl_crypto::hmac test vector. |
| 426 | let expected = vec![ |
| 427 | 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, |
| 428 | 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, |
| 429 | 0x2e, 0x32, 0xcf, 0xf7, |
| 430 | ]; |
| 431 | let key: [u8; 20] = [0x0b; 20]; |
| 432 | let data = b"Hi There"; |
| 433 | |
| 434 | assert_eq!(crypto.mac(&key, data).unwrap(), expected); |
| 435 | } |
| 436 | } |
| 437 | |
| 438 | #[test] |
| 439 | fn kem_generate() { |
| 440 | let bssl = BoringsslCryptoProvider::new(); |
| 441 | for suite in get_cipher_suites() { |
| 442 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 443 | assert!(crypto.kem_generate().is_ok()); |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | #[test] |
| 448 | fn kem_derive() { |
| 449 | let bssl = BoringsslCryptoProvider::new(); |
| 450 | for suite in get_cipher_suites() { |
| 451 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 452 | // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1 |
| 453 | let ikm: [u8; 32] = |
| 454 | decode_hex("7268600d403fce431561aef583ee1613527cff655c1343f29812e66706df3234"); |
| 455 | let expected_sk = HpkeSecretKey::from( |
| 456 | decode_hex::<32>( |
| 457 | "52c4a758a802cd8b936eceea314432798d5baf2d7e9235dc084ab1b9cfa2f736", |
| 458 | ) |
| 459 | .to_vec(), |
| 460 | ); |
| 461 | let expected_pk = HpkePublicKey::from( |
| 462 | decode_hex::<32>( |
| 463 | "37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431", |
| 464 | ) |
| 465 | .to_vec(), |
| 466 | ); |
| 467 | |
| 468 | let (sk, pk) = crypto.kem_derive(&ikm).unwrap(); |
| 469 | assert_eq!(sk, expected_sk); |
| 470 | assert_eq!(pk, expected_pk); |
| 471 | } |
| 472 | } |
| 473 | |
| 474 | #[test] |
| 475 | fn kem_public_key_validate() { |
| 476 | let bssl = BoringsslCryptoProvider::new(); |
| 477 | for suite in get_cipher_suites() { |
| 478 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 479 | // https://www.rfc-editor.org/rfc/rfc7748.html#section-6.1 |
| 480 | let public_key = HpkePublicKey::from( |
| 481 | decode_hex::<32>( |
| 482 | "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a", |
| 483 | ) |
| 484 | .to_vec(), |
| 485 | ); |
| 486 | assert!(crypto.kem_public_key_validate(&public_key).is_ok()); |
| 487 | } |
| 488 | } |
| 489 | |
| 490 | #[test] |
| 491 | fn kdf_extract_and_expand() { |
| 492 | let bssl = BoringsslCryptoProvider::new(); |
| 493 | for suite in get_cipher_suites() { |
| 494 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 495 | // https://www.rfc-editor.org/rfc/rfc5869.html#appendix-A.1 |
| 496 | let ikm: [u8; 22] = decode_hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); |
| 497 | let salt: [u8; 13] = decode_hex("000102030405060708090a0b0c"); |
| 498 | let info: [u8; 10] = decode_hex("f0f1f2f3f4f5f6f7f8f9"); |
| 499 | let expected_prk: [u8; 32] = |
| 500 | decode_hex("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"); |
| 501 | let expected_okm : [u8; 42] = decode_hex( |
| 502 | "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865" |
| 503 | ); |
| 504 | |
| 505 | let prk = crypto.kdf_extract(&salt, &ikm).unwrap(); |
| 506 | assert_eq!(prk.as_ref(), expected_prk); |
| 507 | assert_eq!(crypto.kdf_expand(&prk.as_ref(), &info, 42).unwrap().as_ref(), expected_okm); |
| 508 | } |
| 509 | } |
| 510 | |
| 511 | #[test] |
| 512 | fn kdf_extract_size() { |
| 513 | let bssl = BoringsslCryptoProvider::new(); |
| 514 | for suite in get_cipher_suites() { |
| 515 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 516 | assert_eq!(crypto.kdf_extract_size(), 32); |
| 517 | } |
| 518 | } |
| 519 | |
| 520 | #[test] |
| 521 | fn aead() { |
| 522 | let bssl = BoringsslCryptoProvider::new(); |
| 523 | for suite in get_cipher_suites() { |
| 524 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 525 | let key = vec![42u8; crypto.aead_key_size()]; |
| 526 | let associated_data = vec![42u8, 12]; |
| 527 | let nonce = vec![42u8; crypto.aead_nonce_size()]; |
| 528 | let plaintext = b"message"; |
| 529 | |
| 530 | let ciphertext = |
| 531 | crypto.aead_seal(&key, plaintext, Some(&associated_data), &nonce).unwrap(); |
| 532 | assert_eq!( |
| 533 | plaintext, |
| 534 | crypto |
| 535 | .aead_open(&key, ciphertext.as_slice(), Some(&associated_data), &nonce) |
| 536 | .unwrap() |
| 537 | .as_slice() |
| 538 | ); |
| 539 | } |
| 540 | } |
| 541 | |
| 542 | #[test] |
| 543 | fn hpke_setup_seal_open_export() { |
| 544 | let bssl = BoringsslCryptoProvider::new(); |
| 545 | for suite in get_cipher_suites() { |
| 546 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 547 | // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1 |
| 548 | let receiver_pub_key = HpkePublicKey::from( |
| 549 | decode_hex::<32>( |
| 550 | "3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d", |
| 551 | ) |
| 552 | .to_vec(), |
| 553 | ); |
| 554 | let receiver_priv_key = HpkeSecretKey::from( |
| 555 | decode_hex::<32>( |
| 556 | "4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8", |
| 557 | ) |
| 558 | .to_vec(), |
| 559 | ); |
| 560 | |
| 561 | let info = b"some_info"; |
| 562 | let plaintext = b"plaintext"; |
| 563 | let associated_data = b"some_ad"; |
| 564 | let exporter_ctx = b"export_ctx"; |
| 565 | |
| 566 | let (enc, mut sender_ctx) = crypto.hpke_setup_s(&receiver_pub_key, info).unwrap(); |
| 567 | let mut receiver_ctx = |
| 568 | crypto.hpke_setup_r(&enc, &receiver_priv_key, &receiver_pub_key, info).unwrap(); |
| 569 | let ct = sender_ctx.seal(Some(associated_data), plaintext).unwrap(); |
| 570 | assert_eq!(plaintext.as_ref(), receiver_ctx.open(Some(associated_data), &ct).unwrap(),); |
| 571 | assert_eq!( |
| 572 | sender_ctx.export(exporter_ctx, 32).unwrap(), |
| 573 | receiver_ctx.export(exporter_ctx, 32).unwrap(), |
| 574 | ); |
| 575 | } |
| 576 | } |
| 577 | |
| 578 | #[test] |
| 579 | fn hpke_seal_open() { |
| 580 | let bssl = BoringsslCryptoProvider::new(); |
| 581 | for suite in get_cipher_suites() { |
| 582 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 583 | // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1 |
| 584 | let receiver_pub_key = HpkePublicKey::from( |
| 585 | decode_hex::<32>( |
| 586 | "3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d", |
| 587 | ) |
| 588 | .to_vec(), |
| 589 | ); |
| 590 | let receiver_priv_key = HpkeSecretKey::from( |
| 591 | decode_hex::<32>( |
| 592 | "4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8", |
| 593 | ) |
| 594 | .to_vec(), |
| 595 | ); |
| 596 | |
| 597 | let info = b"some_info"; |
| 598 | let plaintext = b"plaintext"; |
| 599 | let associated_data = b"some_ad"; |
| 600 | |
| 601 | let ct = crypto |
| 602 | .hpke_seal(&receiver_pub_key, info, Some(associated_data), plaintext) |
| 603 | .unwrap(); |
| 604 | assert_eq!( |
| 605 | plaintext.as_ref(), |
| 606 | crypto |
| 607 | .hpke_open( |
| 608 | &ct, |
| 609 | &receiver_priv_key, |
| 610 | &receiver_pub_key, |
| 611 | info, |
| 612 | Some(associated_data) |
| 613 | ) |
| 614 | .unwrap(), |
| 615 | ); |
| 616 | } |
| 617 | } |
| 618 | |
| 619 | #[test] |
| 620 | fn signature_key_generate() { |
| 621 | let bssl = BoringsslCryptoProvider::new(); |
| 622 | for suite in get_cipher_suites() { |
| 623 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 624 | assert!(crypto.signature_key_generate().is_ok()); |
| 625 | } |
| 626 | } |
| 627 | |
| 628 | #[test] |
| 629 | fn signature_key_derive_public() { |
| 630 | let bssl = BoringsslCryptoProvider::new(); |
| 631 | for suite in get_cipher_suites() { |
| 632 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 633 | // Test 1 from https://www.rfc-editor.org/rfc/rfc8032#section-7.1 |
| 634 | let private_key = SignatureSecretKey::from( |
| 635 | decode_hex::<32>( |
| 636 | "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", |
| 637 | ) |
| 638 | .to_vec(), |
| 639 | ); |
| 640 | let expected_public_key = SignaturePublicKey::from( |
| 641 | decode_hex::<32>( |
| 642 | "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a", |
| 643 | ) |
| 644 | .to_vec(), |
| 645 | ); |
| 646 | |
| 647 | assert_eq!( |
| 648 | crypto.signature_key_derive_public(&private_key).unwrap(), |
| 649 | expected_public_key |
| 650 | ); |
| 651 | } |
| 652 | } |
| 653 | |
| 654 | #[test] |
| 655 | fn sign_verify() { |
| 656 | let bssl = BoringsslCryptoProvider::new(); |
| 657 | for suite in get_cipher_suites() { |
| 658 | let crypto = bssl.cipher_suite_provider(suite).unwrap(); |
| 659 | // Test 3 from https://www.rfc-editor.org/rfc/rfc8032#section-7.1 |
| 660 | let private_key = SignatureSecretKey::from( |
| 661 | decode_hex::<32>( |
| 662 | "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7", |
| 663 | ) |
| 664 | .to_vec(), |
| 665 | ); |
| 666 | let data: [u8; 2] = decode_hex("af82"); |
| 667 | let expected_sig = decode_hex::<64>("6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a").to_vec(); |
| 668 | |
| 669 | let sig = crypto.sign(&private_key, &data).unwrap(); |
| 670 | assert_eq!(sig, expected_sig); |
| 671 | |
| 672 | let public_key = crypto.signature_key_derive_public(&private_key).unwrap(); |
| 673 | assert!(crypto.verify(&public_key, &sig, &data).is_ok()); |
| 674 | } |
| 675 | } |
| 676 | } |