char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
dbb5c40
main
1mod asymmetric; 2 3use base64:: Engine ; 4use base64:: engine:: general_purpose:: URL_SAFE_NO_PAD ; 5use std:: collections:: HashMap ; 6use std:: io; 7use std:: panic::{ AssertUnwindSafe , catch_unwind}; 8use std:: sync:: atomic::{ AtomicU64 , Ordering }; 9use std:: sync::{ Arc , Mutex , OnceLock }; 10 11use aes:: cipher::{ BlockCipherEncrypt , KeyInit }; 12use aes::{ Aes128 , Aes192 , Aes256 }; 13use aes_kw::{ KwAes128 , KwAes192 , KwAes256 }; 14use avril_core:: serde_v8::{ JsBuffer , ToJsBuffer }; 15use avril_core::{ Extension , Op , message}; 16use cbc:: cipher::{ BlockModeDecrypt , BlockModeEncrypt , KeyIvInit , block_padding:: Pkcs7 }; 17use ctr:: cipher:: StreamCipher ; 18use ghash::{ GHash , universal_hash:: UniversalHash }; 19use hmac::{ Hmac , Mac }; 20use sha1:: Digest as _; 21use sha2::{ Sha256 , Sha384 , Sha512 }; 22use subtle:: ConstantTimeEq ; 23use zeroize::{ Zeroize , Zeroizing }; 24 25type DeriveKeyArgs =( 26u64 , 27String , 28JsBuffer , 29u32 , 30JsBuffer , 31usize , 32String , 33String , 34Vec < String >, 35bool , 36u64 , 37); 38 39struct Key { 40bytes : Vec < u8 >, 41algorithm : String , 42hash : String , 43usages : Vec < String >, 44extractable : bool , 45key_type : String , 46length : usize , 47curve : String , 48public_exponent : Vec < u8 >, 49} 50 51impl Drop for Key { 52fn drop ( & mut self ) { 53self . bytes . zeroize (); 54} 55} 56 57# [ derive ( serde :: Serialize )] 58struct KeyMetadata { 59id : u64 , 60length : usize , 61exponent : Option < ToJsBuffer >, 62# [ serde ( rename = "type" )] 63key_type : String , 64} 65 66struct KeyStore { 67keys : Mutex < HashMap < u64 , Arc < Key >>>, 68next_id : AtomicU64 , 69} 70 71impl KeyStore { 72fn new () ->Self { 73Self { 74keys : Mutex :: new ( HashMap :: new ()), 75next_id : AtomicU64 :: new ( 0 ), 76} 77} 78 79fn next_id ( & self ) -> io:: Result < u64 > { 80self . next_id 81. fetch_update ( Ordering :: Relaxed , Ordering :: Relaxed , |id|{ 82( id <9_007_199_254_740_991 ). then_some ( id +1 ) 83}) 84. map ( |id| id +1 ) 85. map_err ( |_| io:: Error :: other ( "Crypto key handles exhausted" )) 86} 87 88fn metadata ( & self , id : u64 ) -> io:: Result < KeyMetadata > { 89let key =self . get ( id) ?; 90Ok ( KeyMetadata { 91 id, 92length : key. length , 93exponent : ( !key. public_exponent . is_empty ()) 94. then ( ||ToJsBuffer :: from ( key. public_exponent . clone ())), 95key_type : key. key_type . clone (), 96}) 97} 98 99fn insert ( & self , key : Key ) -> io:: Result < u64 > { 100self . insert_shared ( Arc :: new ( key)) 101} 102 103fn insert_shared ( & self , key : Arc < Key >) -> io:: Result < u64 > { 104let id =self . next_id () ?; 105self . keys 106. lock () 107. map_err ( |_| io:: Error :: other ( "Crypto key registry poisoned" )) ? 108. insert ( id, key); 109Ok ( id) 110} 111 112fn get ( & self , id : u64 ) -> io:: Result < Arc < Key >> { 113self . keys 114. lock () 115. map_err ( |_| io:: Error :: other ( "Crypto key registry poisoned" )) ? 116. get ( & id) 117. cloned () 118. ok_or_else ( || io:: Error :: other ( "CryptoKey is no longer available" )) 119} 120 121fn close ( & self , id : u64 ) { 122if let Ok ( mut keys) =self . keys . lock () { 123 keys. remove ( & id); 124} 125} 126 127fn clone_key ( & self , id : u64 ) -> io:: Result < u64 > { 128self . insert_shared ( self . get ( id) ?) 129} 130} 131 132fn allowed_usages ( algorithm : & str , key_type : & str ) -> io:: Result < &' static [ &' static str ]> { 133Ok ( match ( algorithm, key_type) { 134( "AES-CBC" |"AES-CTR" |"AES-GCM" , "secret" ) =>{ 135& [ "encrypt" , "decrypt" , "wrapKey" , "unwrapKey" ] 136} 137( "AES-KW" , "secret" ) =>& [ "wrapKey" , "unwrapKey" ], 138( "HMAC" , "secret" ) =>& [ "sign" , "verify" ], 139( "PBKDF2" |"HKDF" , "secret" ) =>& [ "deriveBits" , "deriveKey" ], 140( "RSA-OAEP" , "public" ) =>& [ "encrypt" , "wrapKey" ], 141( "RSA-OAEP" , "private" ) =>& [ "decrypt" , "unwrapKey" ], 142( "RSA-PSS" |"RSASSA-PKCS1-V1_5" , "public" ) =>& [ "verify" ], 143( "RSA-PSS" |"RSASSA-PKCS1-V1_5" , "private" ) =>& [ "sign" ], 144( "ECDSA" |"ED25519" , "public" ) =>& [ "verify" ], 145( "ECDSA" |"ED25519" , "private" ) =>& [ "sign" ], 146( "ECDH" |"X25519" , "public" ) =>& [], 147( "ECDH" |"X25519" , "private" ) =>& [ "deriveBits" , "deriveKey" ], 148 _ =>return Err ( io:: Error :: other ( "Unsupported CryptoKey algorithm or type" )), 149}) 150} 151 152fn apply_bit_length ( bytes : & mut [ u8 ], bits : usize ) { 153if !bits. is_multiple_of ( 8 ) && !bytes. is_empty () { 154let last = bytes. len () -1 ; 155 bytes[ last] &=0xff <<( 8 - bits %8 ); 156} 157} 158 159fn truncate_bits ( bytes : & mut Vec < u8 >, requested : usize ) -> io:: Result < usize > { 160let bits =if requested ==0 { 161 bytes. len () * 8 162} else { 163 requested 164}; 165if bits ==0 || bits > bytes. len () * 8 ||( requested !=0 && bits <= bytes. len () * 8 -8 ) { 166return Err ( io:: Error :: other ( "Invalid HMAC key length" )); 167} 168 bytes. truncate ( bits. div_ceil ( 8 )); 169apply_bit_length ( bytes, bits); 170Ok ( bits) 171} 172 173fn insert_key ( store : & KeyStore , key : Key ) -> io:: Result < u64 > { 174let asymmetric =matches! ( 175 key. algorithm . as_str (), 176"RSA-OAEP" |"RSA-PSS" |"RSASSA-PKCS1-V1_5" |"ECDSA" |"ECDH" |"ED25519" |"X25519" 177); 178let allowed =allowed_usages ( & key. algorithm , & key. key_type ) ?; 179if key 180. usages 181. iter () 182. any ( |usage| !allowed. contains ( & usage. as_str ())) 183{ 184return Err ( io:: Error :: other ( "Invalid CryptoKey usage" )); 185} 186if key. key_type =="secret" && key. usages . is_empty () { 187return Err ( io:: Error :: other ( 188"Secret CryptoKeys must have at least one usage" , 189)); 190} 191if matches! ( key. algorithm . as_str (), "PBKDF2" |"HKDF" ) && key. extractable { 192return Err ( io:: Error :: other ( 193"Key derivation keys must be non-extractable" , 194)); 195} 196if asymmetric{ 197if key. bytes . is_empty () ||( key. key_type =="private" && key. usages . is_empty ()) { 198return Err ( io:: Error :: other ( "Invalid asymmetric key" )); 199} 200if matches! ( 201 key. algorithm . as_str (), 202"RSA-OAEP" |"RSA-PSS" |"RSASSA-PKCS1-V1_5" 203) &&hash_len ( & key. hash ). is_err () 204{ 205return Err ( io:: Error :: other ( "Invalid CryptoKey hash" )); 206} 207} else { 208let valid_length =match key. algorithm . as_str () { 209"AES-CBC" |"AES-CTR" |"AES-GCM" |"AES-KW" =>matches! ( key. bytes . len (), 16 |24 |32 ), 210"HMAC" =>{ 211 key. length >0 212 && key. length <= key. bytes . len () * 8 213 && key. bytes . len () == key. length . div_ceil ( 8 ) 214} 215"PBKDF2" |"HKDF" =>true , 216 _ =>false , 217}; 218if !valid_length{ 219return Err ( io:: Error :: other ( "Invalid CryptoKey length" )); 220} 221if key. algorithm =="HMAC" &&hash_len ( & key. hash ). is_err () { 222return Err ( io:: Error :: other ( "Invalid CryptoKey hash" )); 223} 224} 225 store. insert ( key) 226} 227 228fn insert_key_pair ( 229store : & KeyStore , 230algorithm : String , 231hash : String , 232curve : String , 233usages : Vec < String >, 234extractable : bool , 235mut pair : asymmetric:: KeyPairDer , 236) -> io:: Result <( u64 , u64 , usize , Option < ToJsBuffer >)> { 237let ( public_usages, private_usages): ( Vec < String >, Vec < String >) =match algorithm. as_str () { 238"RSA-OAEP" =>( 239 usages 240. iter () 241. filter ( |u|matches! ( u. as_str (), "encrypt" |"wrapKey" )) 242. cloned () 243. collect (), 244 usages 245. iter () 246. filter ( |u|matches! ( u. as_str (), "decrypt" |"unwrapKey" )) 247. cloned () 248. collect (), 249), 250"RSA-PSS" |"RSASSA-PKCS1-V1_5" |"ECDSA" |"ED25519" =>( 251 usages 252. iter () 253. filter ( |u| u. as_str () =="verify" ) 254. cloned () 255. collect (), 256 usages 257. iter () 258. filter ( |u| u. as_str () =="sign" ) 259. cloned () 260. collect (), 261), 262"ECDH" |"X25519" =>( 263Vec :: new (), 264 usages 265. iter () 266. filter ( |u|matches! ( u. as_str (), "deriveBits" |"deriveKey" )) 267. cloned () 268. collect (), 269), 270 _ =>return Err ( io:: Error :: other ( "Unsupported asymmetric key algorithm" )), 271}; 272let public =insert_key ( 273 store, 274Key { 275bytes : pair. public , 276algorithm : algorithm. clone (), 277hash : hash. clone (), 278usages : public_usages, 279extractable : true , 280key_type : "public" . into (), 281length : pair. length , 282curve : curve. clone (), 283public_exponent : pair. exponent . clone (), 284}, 285) ?; 286let private =match insert_key ( 287 store, 288Key { 289bytes : std:: mem:: take ( & mut * pair. private ), 290 algorithm, 291 hash, 292usages : private_usages, 293 extractable, 294key_type : "private" . into (), 295length : pair. length , 296 curve, 297public_exponent : pair. exponent . clone (), 298}, 299) { 300Ok ( id) => id, 301Err ( error) =>{ 302 store. close ( public); 303return Err ( error); 304} 305}; 306let exponent =( !pair. exponent . is_empty ()). then ( ||ToJsBuffer :: from ( pair. exponent )); 307Ok (( public, private, pair. length , exponent)) 308} 309 310fn require ( key : & Key , usage : & str , algorithm : & str ) -> io:: Result <()> { 311if !key. usages . iter (). any ( |item| item == usage) { 312return Err ( io:: Error :: other ( "CryptoKey does not permit this usage" )); 313} 314if key. algorithm != algorithm{ 315return Err ( io:: Error :: other ( "CryptoKey algorithm mismatch" )); 316} 317Ok (()) 318} 319 320fn hash_len ( hash : & str ) -> io:: Result < usize > { 321match hash. to_ascii_uppercase (). as_str () { 322"SHA-1" =>Ok ( 20 ), 323"SHA-256" =>Ok ( 32 ), 324"SHA-384" =>Ok ( 48 ), 325"SHA-512" =>Ok ( 64 ), 326 _ =>Err ( io:: Error :: other ( "Unsupported hash algorithm" )), 327} 328} 329 330fn digest ( hash : & str , data : & [ u8 ]) -> io:: Result < Vec < u8 >> { 331match hash. to_ascii_uppercase (). as_str () { 332"SHA-1" =>Ok ( sha1:: Sha1 :: digest ( data). to_vec ()), 333"SHA-256" =>Ok ( Sha256 :: digest ( data). to_vec ()), 334"SHA-384" =>Ok ( Sha384 :: digest ( data). to_vec ()), 335"SHA-512" =>Ok ( Sha512 :: digest ( data). to_vec ()), 336 _ =>Err ( io:: Error :: other ( "Unsupported hash algorithm" )), 337} 338} 339 340fn hmac_sign ( hash : & str , key : & [ u8 ], input : & [ u8 ]) -> io:: Result < Vec < u8 >> { 341macro_rules ! sign{ 342( $digest: ty) =>{{ 343let mut mac = <Hmac < $digest> as Mac >:: new_from_slice ( key). map_err ( io:: Error :: other) ?; 344 mac. update ( input); 345Ok ( mac. finalize (). into_bytes (). to_vec ()) 346}}; 347} 348match hash. to_ascii_uppercase (). as_str () { 349"SHA-1" =>sign! ( sha1:: Sha1 ), 350"SHA-256" =>sign! ( Sha256 ), 351"SHA-384" =>sign! ( Sha384 ), 352"SHA-512" =>sign! ( Sha512 ), 353 _ =>Err ( io:: Error :: other ( "Unsupported HMAC hash" )), 354} 355} 356 357const AES_GCM_TAG_LENGTHS : [ usize ; 7 ] =[ 32 , 64 , 96 , 104 , 112 , 120 , 128 ]; 358 359fn validate_gcm_tag_length ( bits : usize ) -> io:: Result <()> { 360if AES_GCM_TAG_LENGTHS . contains ( & bits) { 361Ok (()) 362} else { 363Err ( io:: Error :: other ( "Invalid AES-GCM tag length" )) 364} 365} 366 367macro_rules ! with_aes_block_cipher{ 368( $key: expr, $encrypt_block: ident => $body: block) =>{{ 369match $key. len () { 37016 =>{ 371let cipher =Aes128 :: new_from_slice ( $key). map_err ( io:: Error :: other) ?; 372let mut $encrypt_block = |block : & mut ghash:: Block | cipher. encrypt_block ( block); 373 $body 374} 37524 =>{ 376let cipher =Aes192 :: new_from_slice ( $key). map_err ( io:: Error :: other) ?; 377let mut $encrypt_block = |block : & mut ghash:: Block | cipher. encrypt_block ( block); 378 $body 379} 38032 =>{ 381let cipher =Aes256 :: new_from_slice ( $key). map_err ( io:: Error :: other) ?; 382let mut $encrypt_block = |block : & mut ghash:: Block | cipher. encrypt_block ( block); 383 $body 384} 385 _ =>Err ( io:: Error :: other ( "Invalid AES-GCM key length" )), 386} 387}}; 388} 389 390fn gcm_counter_block ( counter : & mut ghash:: Block ) { 391let bytes = counter. as_mut_slice (); 392let value = u32:: from_be_bytes ( 393 bytes[ 12 ..16 ] 394. try_into () 395. expect ( "GCM counter block is 16 bytes" ), 396) 397. wrapping_add ( 1 ); 398 bytes[ 12 ..16 ]. copy_from_slice ( & value. to_be_bytes ()); 399} 400 401fn gcm_j0 ( authenticator : & GHash , iv : & [ u8 ]) -> ghash:: Block { 402if iv. len () ==12 { 403let mut block = ghash:: Block :: default (); 404let bytes = block. as_mut_slice (); 405 bytes[ ..12 ]. copy_from_slice ( iv); 406 bytes[ 15 ] =1 ; 407 block 408} else { 409let mut authenticator = authenticator. clone (); 410 authenticator. update_padded ( iv); 411let mut lengths = ghash:: Block :: default (); 412 lengths. as_mut_slice ()[ 8 ..] 413. copy_from_slice ( & (( iv. len () as u64 ). wrapping_mul ( 8 )). to_be_bytes ()); 414 authenticator. update ( & [ lengths]); 415 authenticator. finalize () 416} 417} 418 419fn gcm_tag ( 420authenticator : & GHash , 421mask : & ghash:: Block , 422aad : & [ u8 ], 423ciphertext : & [ u8 ], 424) -> ghash:: Block { 425let mut authenticator = authenticator. clone (); 426 authenticator. update_padded ( aad); 427 authenticator. update_padded ( ciphertext); 428let mut lengths = ghash:: Block :: default (); 429let bytes = lengths. as_mut_slice (); 430 bytes[ ..8 ]. copy_from_slice ( & (( aad. len () as u64 ). wrapping_mul ( 8 )). to_be_bytes ()); 431 bytes[ 8 ..]. copy_from_slice ( & (( ciphertext. len () as u64 ). wrapping_mul ( 8 )). to_be_bytes ()); 432 authenticator. update ( & [ lengths]); 433let mut tag = authenticator. finalize (); 434for ( tag_byte, mask_byte) in tag. as_mut_slice (). iter_mut (). zip ( mask. as_slice ()) { 435* tag_byte ^=* mask_byte; 436} 437 tag 438} 439 440fn gcm_keystream ( 441encrypt_block : & mut dyn FnMut ( & mut ghash:: Block ), 442j0 : & ghash:: Block , 443output : & mut [ u8 ], 444) { 445let mut counter =* j0; 446gcm_counter_block ( & mut counter); 447for chunkin output. chunks_mut ( 16 ) { 448let mut keystream = counter; 449encrypt_block ( & mut keystream); 450for ( byte, key) in chunk. iter_mut (). zip ( keystream. iter ()) { 451* byte ^=* key; 452} 453gcm_counter_block ( & mut counter); 454} 455} 456 457fn aes_gcm_encrypt ( 458key : & [ u8 ], 459iv : & [ u8 ], 460input : & [ u8 ], 461aad : & [ u8 ], 462bits : usize , 463) -> io:: Result < Vec < u8 >> { 464validate_gcm_tag_length ( bits) ?; 465if iv. is_empty () { 466return Err ( io:: Error :: other ( "Invalid AES-GCM IV" )); 467} 468let tag_length = bits /8 ; 469with_aes_block_cipher! ( key, encrypt_block =>{ 470let mut hash_key = ghash:: Block :: default (); 471encrypt_block ( & mut hash_key); 472let authenticator =GHash :: new ( & hash_key); 473 hash_key. as_mut_slice (). zeroize (); 474let j0 =gcm_j0 ( & authenticator, iv); 475let mut mask = j0; 476encrypt_block ( & mut mask); 477let mut output = input. to_vec (); 478gcm_keystream ( & mut encrypt_block, & j0, & mut output); 479let tag =gcm_tag ( & authenticator, & mask, aad, & output); 480 mask. as_mut_slice (). zeroize (); 481 output. extend_from_slice ( & tag. as_slice ()[ ..tag_length]); 482Ok ( output) 483}) 484} 485 486fn aes_gcm_decrypt ( 487key : & [ u8 ], 488iv : & [ u8 ], 489input : & [ u8 ], 490aad : & [ u8 ], 491bits : usize , 492) -> io:: Result < Vec < u8 >> { 493validate_gcm_tag_length ( bits) ?; 494if iv. is_empty () { 495return Err ( io:: Error :: other ( "Invalid AES-GCM IV" )); 496} 497let tag_length = bits /8 ; 498if input. len () < tag_length{ 499return Err ( io:: Error :: other ( "Invalid AES-GCM ciphertext" )); 500} 501let ( ciphertext, tag) = input. split_at ( input. len () - tag_length); 502with_aes_block_cipher! ( key, encrypt_block =>{ 503let mut hash_key = ghash:: Block :: default (); 504encrypt_block ( & mut hash_key); 505let authenticator =GHash :: new ( & hash_key); 506 hash_key. as_mut_slice (). zeroize (); 507let j0 =gcm_j0 ( & authenticator, iv); 508let mut mask = j0; 509encrypt_block ( & mut mask); 510let expected =gcm_tag ( & authenticator, & mask, aad, ciphertext); 511 mask. as_mut_slice (). zeroize (); 512if bool :: from ( expected. as_slice ()[ ..tag_length]. ct_eq ( tag)) { 513let mut output =Zeroizing :: new ( ciphertext. to_vec ()); 514gcm_keystream ( & mut encrypt_block, & j0, & mut output); 515Ok ( std:: mem:: take ( & mut * output)) 516} else { 517Err ( io:: Error :: other ( "Invalid AES-GCM ciphertext" )) 518} 519}) 520} 521 522fn aes_ctr ( key : & [ u8 ], iv : & [ u8 ], input : & [ u8 ], bits : usize ) -> io:: Result < Vec < u8 >> { 523if !( 1 ..=128 ). contains ( & bits) || iv. len () !=16 { 524return Err ( io:: Error :: other ( "Invalid AES-CTR counter" )); 525} 526let blocks = input. len (). div_ceil ( 16 ) as u128 ; 527if bits <128 && blocks >( 1u128 << bits) { 528return Err ( io:: Error :: other ( "AES-CTR input exceeds the counter space" )); 529} 530let counter = u128:: from_be_bytes ( iv. try_into (). map_err ( io:: Error :: other) ?); 531let mask =if bits ==128 { 532 u128:: MAX 533} else { 534( 1u128 << bits) -1 535}; 536let prefix =if bits <128 && mask -( counter& mask) +1 < blocks{ 537( mask -( counter& mask) +1 ) as usize * 16 538} else { 539 input. len () 540}; 541let mut output = input. to_vec (); 542macro_rules ! crypt{ 543( $aes: ty) =>{{ 544let mut cipher = 545 ctr:: Ctr128BE ::< $aes>:: new_from_slices ( key, iv). map_err ( io:: Error :: other) ?; 546 cipher. apply_keystream ( & mut output[ ..prefix]); 547if prefix < output. len () { 548// Wrap only the counter field; the nonce bits must not carry. 549let nonce =( counter& !mask). to_be_bytes (); 550let mut cipher = ctr:: Ctr128BE ::< $aes>:: new_from_slices ( key, & nonce) 551. map_err ( io:: Error :: other) ?; 552 cipher. apply_keystream ( & mut output[ prefix..]); 553} 554}}; 555} 556match key. len () { 55716 =>crypt! ( Aes128 ), 55824 =>crypt! ( Aes192 ), 55932 =>crypt! ( Aes256 ), 560 _ =>return Err ( io:: Error :: other ( "Invalid AES key length" )), 561} 562Ok ( output) 563} 564 565fn aes_encrypt ( 566key : & [ u8 ], 567mode : & str , 568iv : & [ u8 ], 569input : & [ u8 ], 570aad : & [ u8 ], 571tag_length : usize , 572) -> io:: Result < Vec < u8 >> { 573macro_rules ! cbc_encrypt{ 574( $aes: ty) =>{{ 575let cipher = 576 cbc:: Encryptor ::< $aes>:: new_from_slices ( key, iv). map_err ( io:: Error :: other) ?; 577Ok ( cipher. encrypt_padded_vec ::< Pkcs7 >( input)) 578}}; 579} 580match mode{ 581"AES-CBC" =>match key. len () { 58216 =>cbc_encrypt! ( Aes128 ), 58324 =>cbc_encrypt! ( Aes192 ), 58432 =>cbc_encrypt! ( Aes256 ), 585 _ =>Err ( io:: Error :: other ( "Invalid AES key length" )), 586}, 587"AES-CTR" =>aes_ctr ( key, iv, input, tag_length), 588"AES-GCM" =>aes_gcm_encrypt ( key, iv, input, aad, tag_length), 589 _ =>Err ( io:: Error :: other ( "Unsupported AES mode" )), 590} 591} 592 593fn aes_decrypt ( 594key : & [ u8 ], 595mode : & str , 596iv : & [ u8 ], 597input : & [ u8 ], 598aad : & [ u8 ], 599tag_length : usize , 600) -> io:: Result < Vec < u8 >> { 601macro_rules ! cbc_decrypt{ 602( $aes: ty) =>{{ 603let cipher = 604 cbc:: Decryptor ::< $aes>:: new_from_slices ( key, iv). map_err ( io:: Error :: other) ?; 605 cipher 606. decrypt_padded_vec ::< Pkcs7 >( input) 607. map_err ( io:: Error :: other) 608}}; 609} 610match mode{ 611"AES-CBC" =>match key. len () { 61216 =>cbc_decrypt! ( Aes128 ), 61324 =>cbc_decrypt! ( Aes192 ), 61432 =>cbc_decrypt! ( Aes256 ), 615 _ =>Err ( io:: Error :: other ( "Invalid AES key length" )), 616}, 617"AES-CTR" =>aes_ctr ( key, iv, input, tag_length), 618"AES-GCM" =>aes_gcm_decrypt ( key, iv, input, aad, tag_length), 619 _ =>Err ( io:: Error :: other ( "Unsupported AES mode" )), 620} 621} 622 623fn wrap_kw ( key : & [ u8 ], input : & [ u8 ]) -> io:: Result < Vec < u8 >> { 624macro_rules ! wrap{ 625( $kind: ty) =>{{ 626let cipher = <$kind>:: new_from_slice ( key). map_err ( io:: Error :: other) ?; 627let mut output =vec! [ 0 ; input. len () +8 ]; 628let output_len = cipher 629. wrap_key ( input, & mut output) 630. map_err ( io:: Error :: other) ? 631. len (); 632 output. truncate ( output_len); 633Ok ( output) 634}}; 635} 636match key. len () { 63716 =>wrap! ( KwAes128 ), 63824 =>wrap! ( KwAes192 ), 63932 =>wrap! ( KwAes256 ), 640 _ =>Err ( io:: Error :: other ( "Invalid AES-KW key length" )), 641} 642} 643 644fn unwrap_kw ( key : & [ u8 ], input : & [ u8 ]) -> io:: Result < Vec < u8 >> { 645macro_rules ! unwrap{ 646( $kind: ty) =>{{ 647let cipher = <$kind>:: new_from_slice ( key). map_err ( io:: Error :: other) ?; 648let mut output =vec! [ 0 ; input. len ()]; 649let output_len = cipher 650. unwrap_key ( input, & mut output) 651. map_err ( io:: Error :: other) ? 652. len (); 653 output. truncate ( output_len); 654Ok ( output) 655}}; 656} 657match key. len () { 65816 =>unwrap! ( KwAes128 ), 65924 =>unwrap! ( KwAes192 ), 66032 =>unwrap! ( KwAes256 ), 661 _ =>Err ( io:: Error :: other ( "Invalid AES-KW key length" )), 662} 663} 664 665fn jwk_algorithm ( key : & Key ) -> io:: Result < String > { 666if key. algorithm =="HMAC" { 667Ok ( format! ( "HS{}" , & key. hash [ 4 ..])) 668} else { 669let mode =match key. algorithm . as_str () { 670"AES-CBC" =>"CBC" , 671"AES-CTR" =>"CTR" , 672"AES-GCM" =>"GCM" , 673"AES-KW" =>"KW" , 674 _ =>return Err ( io:: Error :: other ( "Unsupported JWK algorithm" )), 675}; 676Ok ( format! ( "A{}{}" , key. bytes . len () * 8 , mode)) 677} 678} 679 680fn key_jwk ( key : & Key ) -> io:: Result < serde_json:: Value > { 681if key. key_type !="secret" { 682return asymmetric:: export_jwk ( 683& key. bytes , 684& key. algorithm , 685& key. hash , 686& key. key_type , 687& key. curve , 688& key. usages , 689 key. extractable , 690); 691} 692Ok ( serde_json:: json!({ 693"kty" : "oct" , 694"k" : URL_SAFE_NO_PAD . encode ( & key. bytes ), 695"alg" : jwk_algorithm ( key) ?, 696"key_ops" : key. usages , 697"ext" : key. extractable , 698})) 699} 700 701fn key_from_jwk ( 702mut jwk : SensitiveJwk , 703algorithm : String , 704hash : String , 705usages : Vec < String >, 706extractable : bool , 707requested_length : usize , 708) -> io:: Result < Key > { 709let value =& mut jwk. 0 ; 710if value. get ( "kty" ). and_then ( |item| item. as_str ()) !=Some ( "oct" ) { 711return Err ( io:: Error :: other ( "JWK kty does not match a symmetric key" )); 712} 713if let Some ( ext) = value. get ( "ext" ) 714 &&( !ext. is_boolean () ||( ext. as_bool () ==Some ( false ) && extractable)) 715{ 716return Err ( io:: Error :: other ( "JWK extractability does not match" )); 717} 718if let Some ( ops) = value. get ( "key_ops" ) { 719let ops = ops 720. as_array () 721. ok_or_else ( || io:: Error :: other ( "Invalid JWK key_ops" )) ?; 722let mut seen = std:: collections:: HashSet :: new (); 723let valid_ops =[ 724"encrypt" , 725"decrypt" , 726"sign" , 727"verify" , 728"deriveKey" , 729"deriveBits" , 730"wrapKey" , 731"unwrapKey" , 732]; 733let allowed =allowed_usages ( & algorithm, "secret" ) ?; 734if ops. iter (). any ( |item|{ 735 item. as_str (). is_none_or ( |usage|{ 736 !valid_ops. contains ( & usage) || !allowed. contains ( & usage) || !seen. insert ( usage) 737}) 738}) || usages 739. iter () 740. any ( |usage| !ops. iter (). any ( |item| item. as_str () ==Some ( usage))) 741{ 742return Err ( io:: Error :: other ( 743"JWK key_ops do not permit the requested usages" , 744)); 745} 746} 747let encoded = value 748. get ( "k" ) 749. and_then ( |item| item. as_str ()) 750. ok_or_else ( || io:: Error :: other ( "JWK is missing key data" )) ?; 751let mut bytes =URL_SAFE_NO_PAD . decode ( encoded). map_err ( io:: Error :: other) ?; 752if let Some ( serde_json:: Value :: String ( encoded)) = value. get_mut ( "k" ) { 753 encoded. zeroize (); 754} 755let length =if algorithm =="HMAC" { 756truncate_bits ( & mut bytes, requested_length) ? 757} else { 758 bytes. len () * 8 759}; 760let key =Key { 761 bytes, 762 algorithm, 763 hash, 764 usages, 765 extractable, 766key_type : "secret" . into (), 767 length, 768curve : String :: new (), 769public_exponent : Vec :: new (), 770}; 771if let Some ( alg) = value. get ( "alg" ) { 772let alg = alg 773. as_str () 774. ok_or_else ( || io:: Error :: other ( "Invalid JWK alg" )) ?; 775if jwk_algorithm ( & key) ? != alg{ 776return Err ( io:: Error :: other ( "JWK alg does not match the key algorithm" )); 777} 778} 779Ok ( key) 780} 781 782# [ derive ( serde :: Serialize , serde :: Deserialize )] 783# [ serde ( transparent )] 784struct SensitiveJwk ( serde_json:: Value ); 785 786impl Drop for SensitiveJwk { 787fn drop ( & mut self ) { 788for fieldin [ "d" , "p" , "q" , "dp" , "dq" , "qi" , "k" ] { 789if let Some ( serde_json:: Value :: String ( encoded)) =self . 0 . get_mut ( field) { 790 encoded. zeroize (); 791} 792} 793} 794} 795 796fn import_asymmetric_jwk ( 797mut sensitive : SensitiveJwk , 798algorithm : String , 799hash : String , 800curve : String , 801usages : Vec < String >, 802extractable : bool , 803) -> io:: Result < Key > { 804let value =& mut sensitive. 0 ; 805if let Some ( ext) = value. get ( "ext" ) 806 &&( !ext. is_boolean () ||( ext. as_bool () ==Some ( false ) && extractable)) 807{ 808return Err ( io:: Error :: other ( "JWK extractability does not match" )); 809} 810if let Some ( ops) = value. get ( "key_ops" ) { 811let ops = ops 812. as_array () 813. ok_or_else ( || io:: Error :: other ( "Invalid JWK key_ops" )) ?; 814let valid =[ 815"encrypt" , 816"decrypt" , 817"sign" , 818"verify" , 819"deriveKey" , 820"deriveBits" , 821"wrapKey" , 822"unwrapKey" , 823]; 824let mut seen = std:: collections:: HashSet :: new (); 825if ops. iter (). any ( |item|{ 826 item. as_str () 827. is_none_or ( |op| !valid. contains ( & op) || !seen. insert ( op)) 828}) || usages 829. iter () 830. any ( |usage| !ops. iter (). any ( |item| item. as_str () ==Some ( usage))) 831{ 832return Err ( io:: Error :: other ( 833"JWK key_ops do not permit the requested usages" , 834)); 835} 836} 837let ( imported, key_type) = asymmetric:: import_jwk ( value, & algorithm, & hash, & curve) ?; 838if let Some ( ops) = value. get ( "key_ops" ). and_then ( serde_json:: Value :: as_array) { 839let allowed =allowed_usages ( & algorithm, & key_type) ?; 840if ops 841. iter () 842. any ( |item| item. as_str (). is_none_or ( |usage| !allowed. contains ( & usage))) 843{ 844return Err ( io:: Error :: other ( 845"JWK key_ops are incompatible with the key type" , 846)); 847} 848} 849let key =Key { 850bytes : imported. bytes , 851 algorithm, 852 hash, 853 usages, 854 extractable, 855 key_type, 856length : imported. length , 857curve : imported. curve , 858public_exponent : imported. exponent , 859}; 860if let Some ( alg) = value. get ( "alg" ) { 861let alg = alg 862. as_str () 863. ok_or_else ( || io:: Error :: other ( "Invalid JWK alg" )) ?; 864let expected =SensitiveJwk ( key_jwk ( & key) ?); 865if expected. 0 . get ( "alg" ). and_then ( |v| v. as_str ()) !=Some ( alg) { 866return Err ( io:: Error :: other ( "JWK alg does not match the key algorithm" )); 867} 868} 869Ok ( key) 870} 871 872fn key_material ( key : & Key , format : & str ) -> io:: Result < Vec < u8 >> { 873match format{ 874"raw" if key. key_type =="secret" =>Ok ( key. bytes . clone ()), 875"raw" |"spki" |"pkcs8" if key. key_type !="secret" => asymmetric:: export_key ( 876 format, 877& key. bytes , 878& key. algorithm , 879& key. key_type , 880& key. curve , 881), 882"jwk" =>{ 883let jwk =SensitiveJwk ( key_jwk ( key) ?); 884 serde_json:: to_vec ( & jwk). map_err ( io:: Error :: other) 885} 886 _ =>Err ( io:: Error :: other ( "Unsupported key format" )), 887} 888} 889 890struct WrapKeyRequest { 891wrapping : Arc < Key >, 892target : Arc < Key >, 893format : String , 894algorithm : String , 895hash : String , 896iv : Vec < u8 >, 897counter : Vec < u8 >, 898additional : Vec < u8 >, 899tag_length : usize , 900} 901 902struct WrappingParams < ' a > { 903algorithm : &' a str , 904hash : &' a str , 905iv : &' a [ u8 ], 906counter : &' a [ u8 ], 907additional : &' a [ u8 ], 908tag_length : usize , 909encrypt : bool , 910} 911 912fn wrapping_transform ( key : & Key , params : WrappingParams < ' _ >, input : & [ u8 ]) -> io:: Result < Vec < u8 >> { 913let WrappingParams { 914 algorithm, 915 hash, 916 iv, 917 counter, 918 additional, 919 tag_length, 920 encrypt, 921} = params; 922match algorithm{ 923"AES-KW" =>{ 924if encrypt{ 925wrap_kw ( & key. bytes , input) 926} else { 927unwrap_kw ( & key. bytes , input) 928} 929} 930"AES-CBC" |"AES-CTR" |"AES-GCM" =>{ 931let iv =if algorithm =="AES-CTR" { counter} else { iv}; 932if encrypt{ 933aes_encrypt ( & key. bytes , algorithm, iv, input, additional, tag_length) 934} else { 935aes_decrypt ( & key. bytes , algorithm, iv, input, additional, tag_length) 936} 937} 938"RSA-OAEP" =>{ 939if encrypt{ 940 asymmetric:: rsa_encrypt ( algorithm, & key. bytes , hash, iv, input) 941} else { 942 asymmetric:: rsa_decrypt ( algorithm, & key. bytes , hash, iv, input) 943} 944} 945 _ =>Err ( io:: Error :: other ( "Unsupported wrapping algorithm" )), 946} 947} 948 949fn wrapping_material ( key : & Key , format : & str , algorithm : & str ) -> io:: Result < Zeroizing < Vec < u8 >>> { 950let mut material =Zeroizing :: new ( key_material ( key, format) ?); 951if format =="jwk" && algorithm =="AES-KW" { 952let padded_length = material. len (). div_ceil ( 8 ) * 8 ; 953 material. resize ( padded_length, b' ' ); 954} 955Ok ( material) 956} 957 958fn wrap_key ( request : WrapKeyRequest ) -> io:: Result < Vec < u8 >> { 959let WrapKeyRequest { 960 wrapping, 961 target, 962 format, 963 algorithm, 964 hash, 965 iv, 966 counter, 967 additional, 968 tag_length, 969} = request; 970require ( & wrapping, "wrapKey" , & wrapping. algorithm ) ?; 971if wrapping. algorithm != algorithm{ 972return Err ( io:: Error :: other ( 973"Wrapping algorithm does not match the key" , 974)); 975} 976if !target. extractable { 977return Err ( io:: Error :: other ( "CryptoKey is not extractable" )); 978} 979let material =wrapping_material ( & target, & format, & algorithm) ?; 980wrapping_transform ( 981& wrapping, 982WrappingParams { 983algorithm : & algorithm, 984hash : & hash, 985iv : & iv, 986counter : & counter, 987additional : & additional, 988 tag_length, 989encrypt : true , 990}, 991& material, 992) 993} 994 995type UnwrapKeyOpArgs =( 996u64 , 997String , 998JsBuffer , 999String , 1000String , 1001String , 1002Vec < String >, 1003bool , 1004JsBuffer , 1005JsBuffer , 1006JsBuffer , 1007usize , 1008usize , 1009); 1010 1011struct UnwrapKeyRequest { 1012wrapping : Arc < Key >, 1013format : String , 1014wrapped : Vec < u8 >, 1015algorithm : String , 1016hash : String , 1017curve : String , 1018usages : Vec < String >, 1019extractable : bool , 1020iv : Vec < u8 >, 1021counter : Vec < u8 >, 1022additional : Vec < u8 >, 1023tag_length : usize , 1024length : usize , 1025} 1026 1027fn import_unwrapped_raw ( 1028store : & KeyStore , 1029bytes : & [ u8 ], 1030algorithm : String , 1031hash : String , 1032usages : Vec < String >, 1033extractable : bool , 1034requested_bits : usize , 1035) -> io:: Result < u64 > { 1036let mut raw =Zeroizing :: new ( bytes. to_vec ()); 1037let length =if algorithm =="HMAC" { 1038truncate_bits ( & mut raw, requested_bits) ? 1039} else { 1040 raw. len () * 8 1041}; 1042insert_key ( 1043 store, 1044Key { 1045bytes : std:: mem:: take ( & mut * raw), 1046 algorithm, 1047 hash, 1048 usages, 1049 extractable, 1050key_type : "secret" . into (), 1051 length, 1052curve : String :: new (), 1053public_exponent : Vec :: new (), 1054}, 1055) 1056} 1057 1058fn unwrap_key ( store : & KeyStore , request : UnwrapKeyRequest ) -> io:: Result < u64 > { 1059let UnwrapKeyRequest { 1060 wrapping, 1061 format, 1062 wrapped, 1063 algorithm, 1064 hash, 1065 curve, 1066 usages, 1067 extractable, 1068 iv, 1069 counter, 1070 additional, 1071 tag_length, 1072 length, 1073} = request; 1074require ( & wrapping, "unwrapKey" , & wrapping. algorithm ) ?; 1075let bytes =Zeroizing :: new ( wrapping_transform ( 1076& wrapping, 1077WrappingParams { 1078algorithm : & wrapping. algorithm , 1079hash : & wrapping. hash , 1080iv : & iv, 1081counter : & counter, 1082additional : & additional, 1083 tag_length, 1084encrypt : false , 1085}, 1086& wrapped, 1087) ?); 1088if format =="jwk" { 1089let jwk: SensitiveJwk = 1090SensitiveJwk ( serde_json:: from_slice ( & bytes). map_err ( io:: Error :: other) ?); 1091let key =if matches! ( 1092 algorithm. as_str (), 1093"RSA-OAEP" |"RSA-PSS" |"RSASSA-PKCS1-V1_5" |"ECDSA" |"ECDH" |"ED25519" |"X25519" 1094) { 1095import_asymmetric_jwk ( jwk, algorithm, hash, curve, usages, extractable) ? 1096} else { 1097if jwk. 0 . get ( "kty" ). and_then ( |value| value. as_str ()) !=Some ( "oct" ) { 1098return Err ( io:: Error :: other ( "Invalid wrapped JWK" )); 1099} 1100key_from_jwk ( jwk, algorithm, hash, usages, extractable, length) ? 1101}; 1102return insert_key ( store, key); 1103} 1104if matches! ( format. as_str (), "spki" |"pkcs8" |"raw" ) 1105 &&matches! ( 1106 algorithm. as_str (), 1107"RSA-OAEP" |"RSA-PSS" |"RSASSA-PKCS1-V1_5" |"ECDSA" |"ECDH" |"ED25519" |"X25519" 1108) 1109{ 1110let imported = asymmetric:: import_key ( & format, & bytes, & algorithm, & curve) ?; 1111let key_type =if format =="pkcs8" { 1112"private" 1113} else { 1114"public" 1115} 1116. to_owned (); 1117return insert_key ( 1118 store, 1119Key { 1120bytes : imported. bytes , 1121 algorithm, 1122 hash, 1123 usages, 1124 extractable, 1125 key_type, 1126length : imported. length , 1127curve : imported. curve , 1128public_exponent : imported. exponent , 1129}, 1130); 1131} 1132if format !="raw" { 1133return Err ( io:: Error :: other ( "Unsupported key format" )); 1134} 1135import_unwrapped_raw ( store, & bytes, algorithm, hash, usages, extractable, length) 1136} 1137 1138struct CryptoOperation { 1139operation : String , 1140key : Arc < Key >, 1141peer : Option < Arc < Key >>, 1142hash : String , 1143iv : Vec < u8 >, 1144counter : Vec < u8 >, 1145additional : Vec < u8 >, 1146tag_length : usize , 1147iterations : u32 , 1148salt : Vec < u8 >, 1149info : Vec < u8 >, 1150input : Vec < u8 >, 1151} 1152 1153fn crypt ( 1154CryptoOperation { 1155 operation, 1156key : base_key, 1157peer : peer_key, 1158 hash, 1159 iv, 1160counter : _counter, 1161 additional, 1162 tag_length, 1163 iterations, 1164 salt, 1165 info, 1166 input, 1167}: CryptoOperation , 1168) -> io:: Result < Vec < u8 >> { 1169match operation. as_str () { 1170"encrypt" |"decrypt" =>{ 1171let usage =& operation; 1172require ( & base_key, usage, & base_key. algorithm ) ?; 1173if base_key. algorithm =="RSA-OAEP" { 1174return if operation =="encrypt" { 1175 asymmetric:: rsa_encrypt ( 1176& base_key. algorithm , 1177& base_key. bytes , 1178& base_key. hash , 1179& iv, 1180& input, 1181) 1182} else { 1183 asymmetric:: rsa_decrypt ( 1184& base_key. algorithm , 1185& base_key. bytes , 1186& base_key. hash , 1187& iv, 1188& input, 1189) 1190}; 1191} 1192if !base_key. algorithm . starts_with ( "AES-" ) { 1193return Err ( io:: Error :: other ( "Unsupported encryption key algorithm" )); 1194} 1195let nonce =if base_key. algorithm =="AES-CTR" { 1196& _counter 1197} else { 1198& iv 1199}; 1200if operation =="encrypt" { 1201aes_encrypt ( 1202& base_key. bytes , 1203& base_key. algorithm , 1204 nonce, 1205& input, 1206& additional, 1207 tag_length, 1208) 1209} else { 1210aes_decrypt ( 1211& base_key. bytes , 1212& base_key. algorithm , 1213 nonce, 1214& input, 1215& additional, 1216 tag_length, 1217) 1218} 1219} 1220"wrapKey" |"unwrapKey" =>{ 1221require ( & base_key, & operation, & base_key. algorithm ) ?; 1222if base_key. algorithm =="RSA-OAEP" { 1223return if operation =="wrapKey" { 1224 asymmetric:: rsa_encrypt ( 1225& base_key. algorithm , 1226& base_key. bytes , 1227& base_key. hash , 1228& iv, 1229& input, 1230) 1231} else { 1232 asymmetric:: rsa_decrypt ( 1233& base_key. algorithm , 1234& base_key. bytes , 1235& base_key. hash , 1236& iv, 1237& input, 1238) 1239}; 1240} 1241if base_key. algorithm !="AES-KW" { 1242return Err ( io:: Error :: other ( "Unsupported wrapping algorithm" )); 1243} 1244if operation =="wrapKey" { 1245wrap_kw ( & base_key. bytes , & input) 1246} else { 1247unwrap_kw ( & base_key. bytes , & input) 1248} 1249} 1250"sign" |"verify" =>{ 1251require ( & base_key, & operation, & base_key. algorithm ) ?; 1252if base_key. algorithm =="HMAC" { 1253let signature =hmac_sign ( & base_key. hash , & base_key. bytes , & input) ?; 1254return if operation =="sign" { 1255Ok ( signature) 1256} else if signature. len () == additional. len () 1257 && bool:: from ( signature. ct_eq ( & additional)) 1258{ 1259Ok ( vec! [ 1 ]) 1260} else { 1261Ok ( vec! [ 0 ]) 1262}; 1263} 1264let hash =if hash. is_empty () { 1265 base_key. hash . as_str () 1266} else { 1267 hash. as_str () 1268}; 1269if operation =="sign" { 1270if matches! ( base_key. algorithm . as_str (), "RSA-PSS" |"RSASSA-PKCS1-V1_5" ) { 1271 asymmetric:: rsa_sign ( 1272& base_key. algorithm , 1273& base_key. bytes , 1274 hash, 1275 tag_length, 1276& input, 1277) 1278} else { 1279 asymmetric:: sign ( 1280& base_key. algorithm , 1281& base_key. bytes , 1282 hash, 1283& base_key. curve , 1284& input, 1285) 1286} 1287} else { 1288let valid = 1289if matches! ( base_key. algorithm . as_str (), "RSA-PSS" |"RSASSA-PKCS1-V1_5" ) { 1290 asymmetric:: rsa_verify ( 1291& base_key. algorithm , 1292& base_key. bytes , 1293 hash, 1294 tag_length, 1295& input, 1296& additional, 1297) ? 1298} else { 1299 asymmetric:: verify ( 1300& base_key. algorithm , 1301& base_key. bytes , 1302 hash, 1303& base_key. curve , 1304& input, 1305& additional, 1306) ? 1307}; 1308Ok ( vec! [ u8 :: from ( valid)]) 1309} 1310} 1311"deriveBits" |"deriveKey" =>{ 1312require ( & base_key, & operation, & base_key. algorithm ) ?; 1313if matches! ( base_key. algorithm . as_str (), "HKDF" |"PBKDF2" ) 1314 && !tag_length. is_multiple_of ( 8 ) 1315{ 1316return Err ( io:: Error :: other ( "Invalid derived bit length" )); 1317} 1318if base_key. algorithm =="PBKDF2" && iterations ==0 { 1319return Err ( io:: Error :: other ( "Invalid PBKDF2 iteration count" )); 1320} 1321let length = tag_length. div_ceil ( 8 ); 1322let derive_hash =if hash. is_empty () { 1323 base_key. hash . as_str () 1324} else { 1325 hash. as_str () 1326}; 1327match base_key. algorithm . as_str () { 1328"PBKDF2" =>{ 1329let mut output =vec! [ 0 ; length]; 1330match derive_hash. to_ascii_uppercase (). as_str () { 1331"SHA-1" => pbkdf2:: pbkdf2_hmac ::< sha1:: Sha1 >( 1332& base_key. bytes , 1333& salt, 1334 iterations, 1335& mut output, 1336), 1337"SHA-256" => pbkdf2:: pbkdf2_hmac ::< Sha256 >( 1338& base_key. bytes , 1339& salt, 1340 iterations, 1341& mut output, 1342), 1343"SHA-384" => pbkdf2:: pbkdf2_hmac ::< Sha384 >( 1344& base_key. bytes , 1345& salt, 1346 iterations, 1347& mut output, 1348), 1349"SHA-512" => pbkdf2:: pbkdf2_hmac ::< Sha512 >( 1350& base_key. bytes , 1351& salt, 1352 iterations, 1353& mut output, 1354), 1355 _ =>return Err ( io:: Error :: other ( "Unsupported PBKDF2 hash" )), 1356} 1357if operation =="deriveKey" { 1358apply_bit_length ( & mut output, tag_length); 1359} 1360Ok ( output) 1361} 1362"HKDF" =>{ 1363macro_rules ! expand{ 1364( $digest: ty) =>{{ 1365let hk = hkdf:: Hkdf ::< $digest>:: new ( Some ( & salt), & base_key. bytes ); 1366let mut output =vec! [ 0 ; length]; 1367 hk. expand ( & info, & mut output) 1368. map_err ( |_| io:: Error :: other ( "HKDF output length is invalid" )) ?; 1369if operation =="deriveKey" { 1370apply_bit_length ( & mut output, tag_length); 1371} 1372Ok ( output) 1373}}; 1374} 1375match derive_hash. to_ascii_uppercase (). as_str () { 1376"SHA-1" =>expand! ( sha1:: Sha1 ), 1377"SHA-256" =>expand! ( Sha256 ), 1378"SHA-384" =>expand! ( Sha384 ), 1379"SHA-512" =>expand! ( Sha512 ), 1380 _ =>Err ( io:: Error :: other ( "Unsupported HKDF hash" )), 1381} 1382} 1383"ECDH" |"X25519" =>{ 1384let peer = peer_key. ok_or_else ( || io:: Error :: other ( "ECDH peer key missing" )) ?; 1385if peer. algorithm != base_key. algorithm 1386 || peer. key_type !="public" 1387 || peer. curve != base_key. curve 1388{ 1389return Err ( io:: Error :: other ( "ECDH peer key does not match" )); 1390} 1391let mut secret =if base_key. algorithm =="ECDH" { 1392 asymmetric:: ecdh ( 1393& base_key. algorithm , 1394& base_key. curve , 1395& base_key. bytes , 1396& peer. bytes , 1397) ? 1398} else { 1399 asymmetric:: x25519 ( & base_key. bytes , & peer. bytes ) ? 1400}; 1401let output_len = tag_length. div_ceil ( 8 ); 1402if output_len > secret. len () { 1403 secret. zeroize (); 1404return Err ( io:: Error :: other ( 1405"Requested secret length exceeds the shared secret" , 1406)); 1407} 1408 secret. truncate ( output_len); 1409apply_bit_length ( & mut secret, tag_length); 1410Ok ( secret) 1411} 1412 _ =>Err ( io:: Error :: other ( "CryptoKey cannot derive bits" )), 1413} 1414} 1415 _ =>Err ( io:: Error :: other ( "Crypto operation not yet implemented" )), 1416} 1417} 1418 1419macro_rules ! with_store{ 1420( $store: ident, $operation: expr) =>{{ 1421let $store = $store. clone (); 1422 $operation 1423}}; 1424} 1425 1426fn send_key_result < T >( 1427sender : futures:: channel:: oneshot:: Sender < io:: Result < T >>, 1428result : io:: Result < T >, 1429store : & KeyStore , 1430ids : impl FnOnce ( & T ) ->Vec < u64 >, 1431) { 1432if let Err ( Ok ( value)) = sender. send ( result) { 1433for idin ids ( & value) { 1434 store. close ( id); 1435} 1436} 1437} 1438 1439fn spawn_crypto ( job : impl FnOnce () +Send +' static ) { 1440crypto_pool (). spawn ( move ||{ 1441let _ =catch_unwind ( AssertUnwindSafe ( job)); 1442}); 1443} 1444 1445fn crypto_pool () ->&' static rayon:: ThreadPool { 1446static POOL : OnceLock < rayon:: ThreadPool > =OnceLock :: new (); 1447POOL . get_or_init ( ||{ 1448 rayon:: ThreadPoolBuilder :: new () 1449. num_threads ( 4 ) 1450. thread_name ( |index|format! ( "avril:crypto:{index}" )) 1451. build () 1452. expect ( "failed to initialize crypto worker pool" ) 1453}) 1454} 1455 1456pub fn init () ->Extension { 1457let store =Arc :: new ( KeyStore :: new ()); 1458Extension { 1459name : "ext:crypto" , 1460dependencies : & [ "ext:web" ], 1461ops : vec! [ 1462Op :: new ( "op_crypto_random" , |mut bytes : JsBuffer |{ 1463if bytes. len () >65536 { 1464return Err ( io:: Error :: other ( "Random destination exceeds 65536 bytes" )); 1465} 1466 getrandom:: fill ( & mut bytes). map_err ( io:: Error :: other) 1467}), 1468Op :: new ( "op_crypto_uuid" , |(): () |{ 1469let mut bytes =[ 0 ; 16 ]; 1470 getrandom:: fill ( & mut bytes). map_err ( io:: Error :: other) ?; 1471 bytes[ 6 ] =( bytes[ 6 ] & 0x0f ) |0x40 ; 1472 bytes[ 8 ] =( bytes[ 8 ] & 0x3f ) |0x80 ; 1473Ok ::< _ , io:: Error >( format! ( 1474"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}" , 1475 bytes[ 0 ], 1476 bytes[ 1 ], 1477 bytes[ 2 ], 1478 bytes[ 3 ], 1479 bytes[ 4 ], 1480 bytes[ 5 ], 1481 bytes[ 6 ], 1482 bytes[ 7 ], 1483 bytes[ 8 ], 1484 bytes[ 9 ], 1485 bytes[ 10 ], 1486 bytes[ 11 ], 1487 bytes[ 12 ], 1488 bytes[ 13 ], 1489 bytes[ 14 ], 1490 bytes[ 15 ] 1491)) 1492}), 1493with_store! ( 1494 store, 1495Op :: new ( "op_crypto_key_dispose" , move |id : u64 |{ 1496 store. close ( id); 1497Ok ::< _ , io:: Error >(()) 1498}) 1499), 1500with_store! ( 1501 store, 1502Op :: new ( "op_crypto_key_clone" , move |id| store. clone_key ( id)) 1503), 1504with_store! ( 1505 store, 1506Op :: with_scope ( "op_crypto_key_serialize" , move |scope, id|{ 1507 message:: serialize_host ( scope, store. get ( id) ?) 1508}) 1509), 1510with_store! ( 1511 store, 1512Op :: with_scope ( "op_crypto_key_deserialize" , move |scope, id : u32 |{ 1513 store. insert_shared ( message:: deserialize_host ::< Key >( scope, id) ?) 1514}) 1515), 1516with_store! ( 1517 store, 1518Op :: asynchronous ( 1519"op_crypto_key_pair_generate" , 1520move |( algorithm, hash, length, exponent, curve, usages, extractable): ( 1521String , 1522String , 1523usize , 1524JsBuffer , 1525String , 1526Vec < String >, 1527bool , 1528) |{ 1529let store = store. clone (); 1530let exponent = exponent. as_ref (). to_vec (); 1531async move { 1532let ( sender, receiver) = futures:: channel:: oneshot:: channel (); 1533spawn_crypto ( move ||{ 1534let result = 1535 asymmetric:: generate ( & algorithm, length, & exponent, & curve) 1536. and_then ( |pair|{ 1537insert_key_pair ( 1538& store, 1539 algorithm, 1540 hash, 1541 curve, 1542 usages, 1543 extractable, 1544 pair, 1545) 1546}); 1547send_key_result ( sender, result, & store, |pair|{ 1548vec! [ pair. 0 , pair. 1 ] 1549}); 1550}); 1551 receiver. await . map_err ( io:: Error :: other) ? 1552} 1553}, 1554) 1555), 1556with_store! ( 1557 store, 1558Op :: asynchronous ( 1559"op_crypto_key_generate" , 1560move |( algorithm, length, hash, usages, extractable): ( 1561String , 1562usize , 1563String , 1564Vec < String >, 1565bool , 1566) |{ 1567let store = store. clone (); 1568async move { 1569let ( sender, receiver) = futures:: channel:: oneshot:: channel (); 1570spawn_crypto ( move ||{ 1571let mut bytes =vec! [ 0 ; length. div_ceil ( 8 )]; 1572let result = getrandom:: fill ( & mut bytes) 1573. map_err ( io:: Error :: other) 1574. and_then ( |_|{ 1575if algorithm =="HMAC" { 1576apply_bit_length ( & mut bytes, length); 1577} 1578insert_key ( 1579& store, 1580Key { 1581 length, 1582 bytes, 1583 algorithm, 1584 hash, 1585 usages, 1586 extractable, 1587key_type : "secret" . into (), 1588curve : String :: new (), 1589public_exponent : Vec :: new (), 1590}, 1591) 1592}); 1593send_key_result ( sender, result, & store, |id|vec! [ * id]); 1594}); 1595 receiver. await . map_err ( io:: Error :: other) ? 1596} 1597}, 1598) 1599), 1600with_store! ( 1601 store, 1602Op :: asynchronous ( 1603"op_crypto_key_import" , 1604move |( algorithm, bytes, length, hash, usages, extractable): ( 1605String , 1606JsBuffer , 1607usize , 1608String , 1609Vec < String >, 1610bool , 1611) |{ 1612let store = store. clone (); 1613let mut bytes = bytes. as_ref (). to_vec (); 1614async move { 1615let length =if algorithm =="HMAC" { 1616truncate_bits ( & mut bytes, length) ? 1617} else { 1618 bytes. len () * 8 1619}; 1620insert_key ( 1621& store, 1622Key { 1623 bytes, 1624 algorithm, 1625 hash, 1626 usages, 1627 extractable, 1628key_type : "secret" . into (), 1629 length, 1630curve : String :: new (), 1631public_exponent : Vec :: new (), 1632}, 1633) 1634} 1635}, 1636) 1637), 1638with_store! ( 1639 store, 1640Op :: asynchronous ( 1641"op_crypto_key_import_asymmetric" , 1642move |( format, data, algorithm, hash, curve, usages, extractable): ( 1643String , 1644JsBuffer , 1645String , 1646String , 1647String , 1648Vec < String >, 1649bool , 1650) |{ 1651let store = store. clone (); 1652let data =Zeroizing :: new ( data. as_ref (). to_vec ()); 1653let key_type =if format =="pkcs8" { 1654"private" 1655} else { 1656"public" 1657} 1658. to_owned (); 1659let public_extractable = extractable; 1660async move { 1661let ( sender, receiver) = futures:: channel:: oneshot:: channel (); 1662spawn_crypto ( move ||{ 1663let result = 1664 asymmetric:: import_key ( & format, & data, & algorithm, & curve) 1665. and_then ( |imported|{ 1666insert_key ( 1667& store, 1668Key { 1669bytes : imported. bytes , 1670 algorithm, 1671 hash, 1672 usages, 1673extractable : public_extractable, 1674 key_type, 1675length : imported. length , 1676curve : imported. curve , 1677public_exponent : imported. exponent , 1678}, 1679) 1680}) 1681. and_then ( |id| store. metadata ( id)); 1682send_key_result ( sender, result, & store, |value|vec! [ value. id ]); 1683}); 1684 receiver. await . map_err ( io:: Error :: other) ? 1685} 1686}, 1687) 1688), 1689with_store! ( 1690 store, 1691Op :: asynchronous ( 1692"op_crypto_key_import_jwk" , 1693move |( jwk, algorithm, hash, curve, usages, extractable, length): ( 1694SensitiveJwk , 1695String , 1696String , 1697String , 1698Vec < String >, 1699bool , 1700usize , 1701) |{ 1702let store = store. clone (); 1703async move { 1704let ( sender, receiver) = futures:: channel:: oneshot:: channel (); 1705spawn_crypto ( move ||{ 1706let result =if matches! ( 1707 algorithm. as_str (), 1708"RSA-OAEP" 1709 |"RSA-PSS" 1710 |"RSASSA-PKCS1-V1_5" 1711 |"ECDSA" 1712 |"ECDH" 1713 |"ED25519" 1714 |"X25519" 1715) { 1716import_asymmetric_jwk ( 1717 jwk, 1718 algorithm, 1719 hash, 1720 curve, 1721 usages, 1722 extractable, 1723) 1724} else { 1725key_from_jwk ( jwk, algorithm, hash, usages, extractable, length) 1726} 1727. and_then ( |key|insert_key ( & store, key)); 1728let result = result. and_then ( |id| store. metadata ( id)); 1729send_key_result ( sender, result, & store, |value|vec! [ value. id ]); 1730}); 1731 receiver. await . map_err ( io:: Error :: other) ? 1732} 1733}, 1734) 1735), 1736with_store! ( 1737 store, 1738Op :: asynchronous ( 1739"op_crypto_key_export" , 1740move |( id, format): ( u64 , String ) |{ 1741let key = store. get ( id); 1742async move { 1743let key = key?; 1744if !key. extractable { 1745return Err ( io:: Error :: other ( "CryptoKey is not extractable" )); 1746} 1747let ( sender, receiver) = futures:: channel:: oneshot:: channel (); 1748spawn_crypto ( move ||{ 1749let bytes =if key. key_type =="secret" { 1750if format !="raw" { 1751Err ( io:: Error :: other ( "Unsupported symmetric key format" )) 1752} else { 1753Ok ( key. bytes . clone ()) 1754} 1755} else { 1756 asymmetric:: export_key ( 1757& format, 1758& key. bytes , 1759& key. algorithm , 1760& key. key_type , 1761& key. curve , 1762) 1763}; 1764let _ = sender. send ( bytes); 1765}); 1766let bytes = receiver. await . map_err ( io:: Error :: other) ??; 1767Ok ::< _ , io:: Error >( ToJsBuffer :: from ( bytes)) 1768} 1769}, 1770) 1771), 1772with_store! ( 1773 store, 1774Op :: asynchronous ( "op_crypto_key_export_jwk" , move |id : u64 |{ 1775let key = store. get ( id); 1776async move { 1777let key = key?; 1778if !key. extractable { 1779return Err ( io:: Error :: other ( "CryptoKey is not extractable" )); 1780} 1781let ( sender, receiver) = futures:: channel:: oneshot:: channel (); 1782spawn_crypto ( move ||{ 1783let _ = sender. send ( key_jwk ( & key). map ( SensitiveJwk )); 1784}); 1785 receiver. await . map_err ( io:: Error :: other) ? 1786} 1787}) 1788), 1789with_store! ( 1790 store, 1791Op :: asynchronous ( 1792"op_crypto_derive_key" , 1793move |( 1794 id, 1795 hash, 1796 salt, 1797 iterations, 1798 info, 1799 length, 1800 algorithm, 1801 key_hash, 1802 usages, 1803 extractable, 1804 peer_id, 1805): DeriveKeyArgs |{ 1806let store = store. clone (); 1807let salt = salt. as_ref (). to_vec (); 1808let info = info. as_ref (). to_vec (); 1809let base_key = store. get ( id); 1810let peer_key =if peer_id ==0 { 1811Ok ( None ) 1812} else { 1813 store. get ( peer_id). map ( Some ) 1814}; 1815async move { 1816let base_key = base_key?; 1817let peer_key = peer_key?; 1818let ( sender, receiver) = futures:: channel:: oneshot:: channel (); 1819spawn_crypto ( move ||{ 1820let result =crypt ( CryptoOperation { 1821operation : "deriveKey" . into (), 1822key : base_key, 1823peer : peer_key, 1824 hash, 1825iv : Vec :: new (), 1826counter : Vec :: new (), 1827additional : Vec :: new (), 1828tag_length : length, 1829 iterations, 1830 salt, 1831 info, 1832input : Vec :: new (), 1833}) 1834. and_then ( |bytes|{ 1835insert_key ( 1836& store, 1837Key { 1838 bytes, 1839 algorithm, 1840hash : key_hash, 1841 usages, 1842 extractable, 1843key_type : "secret" . into (), 1844 length, 1845curve : String :: new (), 1846public_exponent : Vec :: new (), 1847}, 1848) 1849}); 1850send_key_result ( sender, result, & store, |id|vec! [ * id]); 1851}); 1852 receiver. await . map_err ( io:: Error :: other) ? 1853} 1854}, 1855) 1856), 1857with_store! ( 1858 store, 1859Op :: asynchronous ( 1860"op_crypto_wrap_key" , 1861move |( 1862 wrapping_id, 1863 target_id, 1864 format, 1865 algorithm, 1866 hash, 1867 iv, 1868 counter, 1869 additional, 1870 tag_length, 1871): ( 1872u64 , 1873u64 , 1874String , 1875String , 1876String , 1877JsBuffer , 1878JsBuffer , 1879JsBuffer , 1880usize , 1881) |{ 1882let iv = iv. as_ref (). to_vec (); 1883let counter = counter. as_ref (). to_vec (); 1884let additional = additional. as_ref (). to_vec (); 1885let wrapping = store. get ( wrapping_id); 1886let target = store. get ( target_id); 1887async move { 1888let wrapping = wrapping?; 1889let target = target?; 1890let ( sender, receiver) = futures:: channel:: oneshot:: channel (); 1891spawn_crypto ( move ||{ 1892let _ = sender. send ( wrap_key ( WrapKeyRequest { 1893 wrapping, 1894 target, 1895 format, 1896 algorithm, 1897 hash, 1898 iv, 1899 counter, 1900 additional, 1901 tag_length, 1902})); 1903}); 1904let bytes = receiver. await . map_err ( io:: Error :: other) ??; 1905Ok ::< _ , io:: Error >( ToJsBuffer :: from ( bytes)) 1906} 1907}, 1908) 1909), 1910with_store! ( 1911 store, 1912Op :: asynchronous ( 1913"op_crypto_unwrap_key" , 1914move |( 1915 wrapping_id, 1916 format, 1917 wrapped, 1918 algorithm, 1919 hash, 1920 curve, 1921 usages, 1922 extractable, 1923 iv, 1924 counter, 1925 additional, 1926 tag_length, 1927 length, 1928): UnwrapKeyOpArgs |{ 1929let store = store. clone (); 1930let wrapped = wrapped. as_ref (). to_vec (); 1931let iv = iv. as_ref (). to_vec (); 1932let counter = counter. as_ref (). to_vec (); 1933let additional = additional. as_ref (). to_vec (); 1934let wrapping = store. get ( wrapping_id); 1935async move { 1936let wrapping = wrapping?; 1937let ( sender, receiver) = futures:: channel:: oneshot:: channel (); 1938spawn_crypto ( move ||{ 1939let result =unwrap_key ( 1940& store, 1941UnwrapKeyRequest { 1942 wrapping, 1943 format, 1944 wrapped, 1945 algorithm, 1946 hash, 1947 curve, 1948 usages, 1949 extractable, 1950 iv, 1951 counter, 1952 additional, 1953 tag_length, 1954 length, 1955}, 1956) 1957. and_then ( |id| store. metadata ( id)); 1958send_key_result ( sender, result, & store, |value|vec! [ value. id ]); 1959}); 1960 receiver. await . map_err ( io:: Error :: other) ? 1961} 1962}, 1963) 1964), 1965with_store! ( 1966 store, 1967Op :: asynchronous ( 1968"op_crypto_run" , 1969move |( 1970 operation, 1971 id, 1972 hash, 1973 iv, 1974 counter, 1975 additional, 1976 tag_length, 1977 iterations, 1978 salt, 1979 info, 1980 input, 1981 peer_id, 1982): ( 1983String , 1984u64 , 1985String , 1986JsBuffer , 1987JsBuffer , 1988JsBuffer , 1989usize , 1990u32 , 1991JsBuffer , 1992JsBuffer , 1993JsBuffer , 1994u64 , 1995) |{ 1996let input = input. as_ref (). to_vec (); 1997let iv = iv. as_ref (). to_vec (); 1998let counter = counter. as_ref (). to_vec (); 1999let additional = additional. as_ref (). to_vec (); 2000let salt = salt. as_ref (). to_vec (); 2001let info = info. as_ref (). to_vec (); 2002let key = store. get ( id); 2003let peer =match & key{ 2004Ok ( key) 2005if operation =="deriveBits" 2006 &&matches! ( key. algorithm . as_str (), "ECDH" |"X25519" ) => 2007{ 2008 store. get ( peer_id). map ( Some ) 2009} 2010 _ =>Ok ( None ), 2011}; 2012async move { 2013let key = key?; 2014let peer = peer?; 2015let ( sender, receiver) = futures:: channel:: oneshot:: channel (); 2016spawn_crypto ( move ||{ 2017let result =crypt ( CryptoOperation { 2018 operation, 2019 key, 2020 peer, 2021 hash, 2022 iv, 2023 counter, 2024 additional, 2025 tag_length, 2026 iterations, 2027 salt, 2028 info, 2029 input, 2030}); 2031let _ = sender. send ( result); 2032}); 2033let bytes = receiver. await . map_err ( io:: Error :: other) ??; 2034Ok ::< _ , io:: Error >( ToJsBuffer :: from ( bytes)) 2035} 2036}, 2037) 2038), 2039Op :: asynchronous ( "op_crypto_digest" , |( name, input): ( String , JsBuffer ) |{ 2040let name = name. to_ascii_uppercase (); 2041let input = input. as_ref (). to_vec (); 2042async move { 2043let ( sender, receiver) = futures:: channel:: oneshot:: channel (); 2044spawn_crypto ( move ||{ 2045let result =digest ( & name, & input); 2046let _ = sender. send ( result); 2047}); 2048let output = receiver. await . map_err ( io:: Error :: other) ??; 2049Ok ::< _ , io:: Error >( ToJsBuffer :: from ( output)) 2050} 2051}), 2052], 2053modules : & [( 2054"ext:crypto/main.js" , 2055include_str! ( concat! ( env! ( "OUT_DIR" ), "/main.js" )), 2056)], 2057entry_point : Some ( "ext:crypto/main.js" ), 2058} 2059} 2060 2061# [ cfg ( test )] 2062mod tests{ 2063use super :: * ; 2064 2065fn test_key ( bytes : Vec < u8 >) ->Key { 2066Key { 2067 bytes, 2068algorithm : "AES-GCM" . into (), 2069hash : String :: new (), 2070usages : vec! [ "encrypt" . into ()], 2071extractable : false , 2072key_type : "secret" . into (), 2073length : 128 , 2074curve : String :: new (), 2075public_exponent : Vec :: new (), 2076} 2077} 2078 2079# [ test ] 2080fn dropping_a_runtime_store_releases_its_native_keys () { 2081let store =Arc :: new ( KeyStore :: new ()); 2082let id = store. insert ( test_key ( vec! [ 7 ; 16 ])). unwrap (); 2083let key = store. get ( id). unwrap (); 2084let weak =Arc :: downgrade ( & key); 2085drop ( key); 2086drop ( store); 2087assert! ( weak. upgrade (). is_none ()); 2088} 2089 2090# [ test ] 2091fn stores_are_isolated_and_handles_are_safe_integers () { 2092let first =KeyStore :: new (); 2093let second =KeyStore :: new (); 2094let first_id = first. insert ( test_key ( vec! [ 1 ; 16 ])). unwrap (); 2095let second_id = second. insert ( test_key ( vec! [ 2 ; 16 ])). unwrap (); 2096assert_eq! ( first_id, second_id); 2097assert_eq! ( first. get ( first_id). unwrap (). bytes , vec! [ 1 ; 16 ]); 2098assert_eq! ( second. get ( second_id). unwrap (). bytes , vec! [ 2 ; 16 ]); 2099 first 2100. next_id 2101. store ( 9_007_199_254_740_991 , Ordering :: Relaxed ); 2102assert! ( first. next_id (). is_err ()); 2103} 2104 2105# [ test ] 2106fn imported_public_keys_preserve_nonextractability () { 2107let store =KeyStore :: new (); 2108let mut key =test_key ( vec! [ 4 ; 32 ]); 2109 key. algorithm ="ED25519" . into (); 2110 key. usages =vec! [ "verify" . into ()]; 2111 key. key_type ="public" . into (); 2112 key. length =256 ; 2113 key. extractable =false ; 2114let id =insert_key ( & store, key). unwrap (); 2115assert! ( !store. get ( id). unwrap (). extractable ); 2116} 2117 2118# [ test ] 2119fn invalid_secret_metadata_is_rejected_natively () { 2120let store =KeyStore :: new (); 2121let mut key =test_key ( vec! [ 4 ; 16 ]); 2122 key. usages . clear (); 2123assert! ( insert_key ( & store, key). is_err ()); 2124let mut key =test_key ( vec! [ 4 ; 16 ]); 2125 key. algorithm ="HKDF" . into (); 2126 key. usages =vec! [ "deriveBits" . into ()]; 2127 key. extractable =true ; 2128assert! ( insert_key ( & store, key). is_err ()); 2129} 2130 2131# [ test ] 2132fn native_gcm_accepts_only_webcrypto_tag_lengths () { 2133for bitsin [ 32 , 64 , 96 , 104 , 112 , 120 , 128 ] { 2134let encrypted =aes_gcm_encrypt ( & [ 0 ; 16 ], & [ 0 ; 12 ], & [], & [], bits). unwrap (); 2135assert! ( 2136aes_gcm_decrypt ( & [ 0 ; 16 ], & [ 0 ; 12 ], & encrypted, & [], bits) 2137. unwrap () 2138. is_empty () 2139); 2140} 2141for bitsin [ 0 , 31 , 33 , 48 , 129 ] { 2142assert! ( aes_gcm_encrypt ( & [ 0 ; 16 ], & [ 0 ; 12 ], & [], & [], bits). is_err ()); 2143assert! ( aes_gcm_decrypt ( & [ 0 ; 16 ], & [ 0 ; 12 ], & [], & [], bits). is_err ()); 2144} 2145} 2146 2147# [ test ] 2148fn raw_hmac_unwrap_enforces_and_applies_requested_bits () { 2149let store =KeyStore :: new (); 2150let id =import_unwrapped_raw ( 2151& store, 2152& [ 0xff , 0xff ], 2153"HMAC" . into (), 2154"SHA-256" . into (), 2155vec! [ "sign" . into ()], 2156false , 21579 , 2158) 2159. unwrap (); 2160let key = store. get ( id). unwrap (); 2161assert_eq! ( key. length , 9 ); 2162assert_eq! ( key. bytes , [ 0xff , 0x80 ]); 2163assert! ( 2164import_unwrapped_raw ( 2165& store, 2166& [ 0xff , 0xff ], 2167"HMAC" . into (), 2168"SHA-256" . into (), 2169vec! [ "sign" . into ()], 2170false , 21718 2172) 2173. is_err () 2174); 2175assert! ( 2176import_unwrapped_raw ( 2177& store, 2178& [ 0xff , 0xff ], 2179"HMAC" . into (), 2180"SHA-256" . into (), 2181vec! [ "sign" . into ()], 2182false , 218317 2184) 2185. is_err () 2186); 2187} 2188 2189# [ test ] 2190fn jwk_padding_is_only_applied_for_aes_kw () { 2191let mut key =test_key ( vec! [ 0x31 ; 16 ]); 2192 key. algorithm ="HMAC" . into (); 2193 key. hash ="SHA-256" . into (); 2194 key. extractable =true ; 2195 key. length =128 ; 2196let raw =key_material ( & key, "jwk" ). unwrap (); 2197assert_eq! ( raw . last (), Some ( & b'}' )); 2198let kw =wrapping_material ( & key, "jwk" , "AES-KW" ). unwrap (); 2199assert_eq! ( kw. len () %8 , 0 ); 2200assert! ( serde_json:: from_slice ::< serde_json:: Value >( & kw). is_ok ()); 2201let gcm =wrapping_material ( & key, "jwk" , "AES-GCM" ). unwrap (); 2202assert_eq! ( &* gcm, & raw ); 2203} 2204 2205# [ test ] 2206fn worker_panics_do_not_poison_the_crypto_pool () { 2207let ( sender, receiver) = std:: sync:: mpsc:: channel (); 2208spawn_crypto ( ||panic! ( "worker panic regression" )); 2209spawn_crypto ( move ||{ 2210let _ = sender. send ( digest ( "SHA-256" , b"abc" )); 2211}); 2212assert_eq! ( 2213 receiver 2214. recv_timeout ( std:: time:: Duration :: from_secs ( 2 )) 2215. unwrap () 2216. unwrap (), 2217Sha256 :: digest ( b"abc" ). to_vec () 2218); 2219} 2220 2221# [ test ] 2222fn gcm_ext_iv_matches_nist_vector () { 2223// From the NIST CAVS `gcmEncryptExtIV128.rsp` vectors (1-byte IV). 2224let key =[ 22250x15 , 0xb2 , 0xd4 , 0x14 , 0x82 , 0x64 , 0x53 , 0xf9 , 0xe1 , 0xc7 , 0xdd , 0x0b , 0x69 , 0xd8 , 22260xd1 , 0xeb , 2227]; 2228let iv =[ 0xb6 ]; 2229let plaintext =[ 22300x8c , 0xfa , 0x25 , 0x55 , 0x30 , 0xc6 , 0xfb , 0xc1 , 0x9d , 0x51 , 0xbd , 0x4a , 0xeb , 0x39 , 22310xc9 , 0x1b , 2232]; 2233let ciphertext =aes_gcm_encrypt ( & key, & iv, & plaintext, & [], 128 ). unwrap (); 2234assert_eq! ( 2235 ciphertext, 2236[ 22370x48 , 0x22 , 0xcb , 0x98 , 0xbd , 0x5f , 0x5d , 0x92 , 0x1e , 0xe1 , 0x92 , 0x85 , 0xc9 , 0x03 , 22380x23 , 0x75 , 0x8a , 0x40 , 0x67 , 0x0e , 0xba , 0xc9 , 0x8c , 0xf4 , 0xe9 , 0xcc , 0x1b , 0xf8 , 22390xf8 , 0x03 , 0x16 , 0x7d , 2240] 2241); 2242assert_eq! ( 2243aes_gcm_decrypt ( & key, & iv, & ciphertext, & [], 128 ). unwrap (), 2244 plaintext 2245); 2246} 2247 2248# [ test ] 2249fn long_gcm_ivs_round_trip () { 2250let iv =[ 7 ; 65 ]; 2251let ciphertext =aes_gcm_encrypt ( & [ 0 ; 16 ], & iv, b"abc" , & [], 128 ). unwrap (); 2252assert_eq! ( 2253aes_gcm_decrypt ( & [ 0 ; 16 ], & iv, & ciphertext, & [], 128 ). unwrap (), 2254b"abc" 2255); 2256} 2257 2258# [ test ] 2259fn pinned_operations_survive_finalizer_disposal_until_finished () { 2260let store =KeyStore :: new (); 2261let id = store. insert ( test_key ( vec! [ 3 ; 16 ])). unwrap (); 2262let pinned = store. get ( id). unwrap (); 2263let weak =Arc :: downgrade ( & pinned); 2264 store. close ( id); 2265assert! ( weak. upgrade (). is_some ()); 2266drop ( pinned); 2267assert! ( weak. upgrade (). is_none ()); 2268} 2269}