/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/crypto/lib.rs
980 строк
33 KB
Nathan Whitaker
perf(ext/crypto): batch randomUUID generation (#35953)
13 июл 2026, 20:41
Не верифицирован
13 июл 2026, 20:41
2c7aa39
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. use std::num::NonZeroU32; use aws_lc_rs::hkdf; use aws_lc_rs::hmac::Algorithm as HmacAlgorithm; use aws_lc_rs::hmac::Key as HmacKey; use aws_lc_rs::pbkdf2; use deno_core::OpState; use deno_core::convert::Uint8Array; use deno_core::op2; use deno_error::JsErrorBox; use p256::ecdsa::Signature as P256Signature; use p256::ecdsa::SigningKey as P256SigningKey; use p256::ecdsa::VerifyingKey as P256VerifyingKey; use p256::elliptic_curve::sec1::FromEncodedPoint; use p256::pkcs8::DecodePrivateKey; use p384::ecdsa::Signature as P384Signature; use p384::ecdsa::SigningKey as P384SigningKey; use p384::ecdsa::VerifyingKey as P384VerifyingKey; use p521::ecdsa::Signature as P521Signature; use p521::ecdsa::SigningKey as P521SigningKey; use p521::ecdsa::VerifyingKey as P521VerifyingKey; pub use rand; use rand::SeedableRng; use rand::rngs::OsRng; use rand::rngs::StdRng; use rsa::Pss; use rsa::RsaPrivateKey; use rsa::RsaPublicKey; use rsa::pkcs1::DecodeRsaPrivateKey; use rsa::pkcs1::DecodeRsaPublicKey; use rsa::signature::SignatureEncoding; use rsa::signature::Signer; use rsa::signature::Verifier; use rsa::traits::SignatureScheme; use serde::Deserialize; use sha1::Sha1; use sha2::Digest; use sha2::Sha256; use sha2::Sha384; use sha2::Sha512; use sha3::Sha3_256; use sha3::Sha3_384; use sha3::Sha3_512; use signature::hazmat::PrehashSigner; use signature::hazmat::PrehashVerifier; mod algorithm; mod crypto; mod crypto_key; mod decrypt; mod digest; mod ed25519; mod encrypt; mod export_key; mod generate_key; mod import_key; mod key; mod key_store; mod make_key; mod mldsa; mod mlkem; mod node_interop; mod shared; mod slhdsa; mod subtle_crypto; mod subtle_decrypt; mod subtle_derive_bits; mod subtle_derive_key; mod subtle_encapsulate; mod subtle_encapsulate_key; mod subtle_encrypt; mod subtle_export_key; mod subtle_generate_key; mod subtle_get_public_key; mod subtle_import_key; mod subtle_key; mod subtle_sign; mod subtle_verify; mod subtle_wrap_key; mod x25519; mod x448; pub use crate::decrypt::DecryptError; pub use crate::ed25519::Ed25519Error; pub use crate::encrypt::EncryptError; pub use crate::export_key::ExportKeyError; pub use crate::generate_key::GenerateKeyError; pub use crate::import_key::ImportKeyError; use crate::key::Algorithm; use crate::key::CryptoHash; use crate::key::CryptoNamedCurve; use crate::key::HkdfOutput; pub use crate::mldsa::MlDsaError; pub use crate::mlkem::MlKemError; pub use crate::shared::RawKeyData; pub use crate::shared::SharedError; pub use crate::x448::X448Error; pub use crate::x25519::X25519Error; #[op2(fast)] fn op_crypto_is_seeded(state: &OpState) -> bool { state.try_borrow::<StdRng>().is_some() } // WebCrypto entry points are implemented as methods on the // `Crypto`/`SubtleCrypto`/`CryptoKey` cppgc classes registered below under // `objects`. The standalone ops select the seeded `randomUUID` path once when // the runtime mints its Crypto singleton and refill the normal path's UUID // batch. The per-algorithm helpers // (`subtle_*::run`, `import_key_inner`, `export_key_inner`, etc.) are // pure-Rust and called directly from those method bodies. deno_core::extension!(deno_crypto, deps = [ deno_webidl, deno_web ], ops = [ crypto::op_crypto_random_uuid_batch, op_crypto_is_seeded, ], objects = [ crypto::Crypto, subtle_crypto::SubtleCrypto, crypto_key::CryptoKey, ], lazy_loaded_js = [ "00_crypto.js" ], options = { maybe_seed: Option<u64>, }, state = |state, options| { if let Some(seed) = options.maybe_seed { state.put(StdRng::seed_from_u64(seed)); } }, ); #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CryptoError { #[class(inherit)] #[error(transparent)] General( #[from] #[inherit] SharedError, ), #[class(inherit)] #[error(transparent)] JoinError( #[from] #[inherit] tokio::task::JoinError, ), #[class(generic)] #[error(transparent)] Der(#[from] rsa::pkcs1::der::Error), #[class(type)] #[error("Missing argument hash")] MissingArgumentHash, #[class(type)] #[error("Missing argument saltLength")] MissingArgumentSaltLength, #[class(type)] #[error("unsupported algorithm")] UnsupportedAlgorithm, #[class(generic)] #[error(transparent)] KeyRejected(#[from] aws_lc_rs::error::KeyRejected), #[class(generic)] #[error(transparent)] RSA(#[from] rsa::Error), #[class(generic)] #[error(transparent)] Pkcs1(#[from] rsa::pkcs1::Error), #[class(generic)] #[error(transparent)] Unspecified(#[from] aws_lc_rs::error::Unspecified), #[class(type)] #[error("Invalid key format")] InvalidKeyFormat, #[class(generic)] #[error(transparent)] P256Ecdsa(#[from] p256::ecdsa::Error), #[class(type)] #[error("Unexpected error decoding private key")] DecodePrivateKey, #[class(type)] #[error("Missing argument publicKey")] MissingArgumentPublicKey, #[class(type)] #[error("Missing argument namedCurve")] MissingArgumentNamedCurve, #[class(type)] #[error("Missing argument info")] MissingArgumentInfo, #[class("DOMExceptionOperationError")] #[error("The length provided for HKDF is too large")] HKDFLengthTooLarge, #[class(generic)] #[error(transparent)] Base64Decode(#[from] base64::DecodeError), #[class(type)] #[error("Data must be multiple of 8 bytes")] DataInvalidSize, #[class(type)] #[error("Invalid key length")] InvalidKeyLength, #[class("DOMExceptionOperationError")] #[error("encryption error")] EncryptionError, #[class("DOMExceptionOperationError")] #[error("decryption error - integrity check failed")] DecryptionError, #[class("DOMExceptionOperationError")] #[error("Invalid XOF parameters")] InvalidXofParameters, #[class("DOMExceptionQuotaExceededError")] #[error( "The ArrayBufferView's byte length ({0}) exceeds the number of bytes of entropy available via this API (65536)" )] ArrayBufferViewLengthExceeded(usize), #[class("DOMExceptionTypeMismatchError")] #[error("The provided value is not an integer-type TypedArray")] TypedArrayNotInteger, #[class("DOMExceptionNotSupportedError")] #[error("Algorithm '{0}' is not supported")] UnsupportedDigestAlgorithm(String), #[class(generic)] #[error("failed to allocate UUID string")] UuidStringAllocation, #[class(inherit)] #[error(transparent)] Other( #[from] #[inherit] JsErrorBox, ), } #[derive(Deserialize)] #[serde(rename_all = "lowercase")] pub enum KeyFormat { Raw, Pkcs8, Spki, } #[derive(Deserialize)] #[serde(rename_all = "lowercase")] pub enum KeyType { Secret, Private, Public, } /// Owned key material handed to the sign/verify/derive ops after being looked /// up from the Rust-side [`KeyStore`] by handle. Previously the key bytes were /// serialized and passed from JavaScript on every operation. pub struct KeyData { r#type: KeyType, data: Box<[u8]>, } impl From<&RawKeyData> for KeyData { fn from(raw: &RawKeyData) -> Self { let (r#type, data) = match raw { RawKeyData::Secret(d) => (KeyType::Secret, d), RawKeyData::Private(d) => (KeyType::Private, d), RawKeyData::Public(d) => (KeyType::Public, d), RawKeyData::Raw(d) => (KeyType::Secret, d), // Composite (ML-KEM/ML-DSA) keys are never handed to these ops. RawKeyData::SeededPrivate { .. } => unreachable!(), }; KeyData { r#type, data: data.as_ref().into(), } } } #[derive(deno_core::FromV8)] pub struct SignArg { #[from_v8(serde)] algorithm: Algorithm, salt_length: Option<u32>, #[from_v8(serde)] hash: Option<CryptoHash>, #[from_v8(serde)] named_curve: Option<CryptoNamedCurve>, } impl SignArg { pub(crate) fn new( algorithm: Algorithm, salt_length: Option<u32>, hash: Option<CryptoHash>, named_curve: Option<CryptoNamedCurve>, ) -> Self { Self { algorithm, salt_length, hash, named_curve, } } } /// Synchronous per-algorithm sign dispatch. Called from /// [`crate::subtle_sign::run`] inside `spawn_blocking`; the op-layer /// wrapper that previously fronted this for the JS shim has been /// retired alongside the JS `SubtleCrypto.prototype.sign` body. pub(crate) fn sign_key_sync( key: KeyData, args: SignArg, data: &[u8], ) -> Result<Vec<u8>, CryptoError> { { let algorithm = args.algorithm; let signature = match algorithm { Algorithm::RsassaPkcs1v15 => { use rsa::pkcs1v15::SigningKey; let private_key = RsaPrivateKey::from_pkcs1_der(&key.data)?; match args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)? { CryptoHash::Sha1 => { let signing_key = SigningKey::<Sha1>::new(private_key); signing_key.sign(data) } CryptoHash::Sha256 => { let signing_key = SigningKey::<Sha256>::new(private_key); signing_key.sign(data) } CryptoHash::Sha384 => { let signing_key = SigningKey::<Sha384>::new(private_key); signing_key.sign(data) } CryptoHash::Sha512 => { let signing_key = SigningKey::<Sha512>::new(private_key); signing_key.sign(data) } _ => return Err(CryptoError::UnsupportedAlgorithm), } .to_vec() } Algorithm::RsaPss => { let private_key = RsaPrivateKey::from_pkcs1_der(&key.data)?; let salt_len = args .salt_length .ok_or_else(|| CryptoError::MissingArgumentSaltLength)? as usize; let mut rng = OsRng; match args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)? { CryptoHash::Sha1 => { let signing_key = Pss::new_with_salt::<Sha1>(salt_len); let hashed = Sha1::digest(data); signing_key.sign(Some(&mut rng), &private_key, &hashed)? } CryptoHash::Sha256 => { let signing_key = Pss::new_with_salt::<Sha256>(salt_len); let hashed = Sha256::digest(data); signing_key.sign(Some(&mut rng), &private_key, &hashed)? } CryptoHash::Sha384 => { let signing_key = Pss::new_with_salt::<Sha384>(salt_len); let hashed = Sha384::digest(data); signing_key.sign(Some(&mut rng), &private_key, &hashed)? } CryptoHash::Sha512 => { let signing_key = Pss::new_with_salt::<Sha512>(salt_len); let hashed = Sha512::digest(data); signing_key.sign(Some(&mut rng), &private_key, &hashed)? } _ => return Err(CryptoError::UnsupportedAlgorithm), } .to_vec() } Algorithm::Ecdsa => { let hash = args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)?; let named_curve = args.named_curve.ok_or_else(JsErrorBox::not_supported)?; match named_curve { CryptoNamedCurve::P256 => { // Decode PKCS#8 private key. let secret_key = p256::SecretKey::from_pkcs8_der(&key.data) .map_err(|_| CryptoError::InvalidKeyFormat)?; let signing_key = P256SigningKey::from(secret_key); let prehash = match hash { CryptoHash::Sha1 => sha1::Sha1::digest(data).to_vec(), CryptoHash::Sha256 => sha2::Sha256::digest(data).to_vec(), CryptoHash::Sha384 => sha2::Sha384::digest(data).to_vec(), CryptoHash::Sha512 => sha2::Sha512::digest(data).to_vec(), _ => return Err(CryptoError::UnsupportedAlgorithm), }; // Sign the prehashed message, producing a raw r||s signature. let signature: P256Signature = signing_key.sign_prehash(&prehash)?; signature.to_bytes().to_vec() } CryptoNamedCurve::P384 => { let secret_key = p384::SecretKey::from_pkcs8_der(&key.data) .map_err(|_| CryptoError::InvalidKeyFormat)?; let signing_key = P384SigningKey::from(secret_key); let prehash = match hash { CryptoHash::Sha1 => sha1::Sha1::digest(data).to_vec(), CryptoHash::Sha256 => sha2::Sha256::digest(data).to_vec(), CryptoHash::Sha384 => sha2::Sha384::digest(data).to_vec(), CryptoHash::Sha512 => sha2::Sha512::digest(data).to_vec(), _ => return Err(CryptoError::UnsupportedAlgorithm), }; let signature: P384Signature = signing_key.sign_prehash(&prehash)?; signature.to_bytes().to_vec() } CryptoNamedCurve::P521 => { let secret_key = p521::SecretKey::from_pkcs8_der(&key.data) .map_err(|_| CryptoError::InvalidKeyFormat)?; let signing_key = P521SigningKey::from_bytes(&secret_key.to_bytes()) .map_err(|_| CryptoError::InvalidKeyFormat)?; let prehash = match hash { CryptoHash::Sha1 => sha1::Sha1::digest(data).to_vec(), CryptoHash::Sha256 => sha2::Sha256::digest(data).to_vec(), CryptoHash::Sha384 => sha2::Sha384::digest(data).to_vec(), CryptoHash::Sha512 => sha2::Sha512::digest(data).to_vec(), _ => return Err(CryptoError::UnsupportedAlgorithm), }; // P-521 field size is 66 bytes; bits2field requires at least // half that (33 bytes). Left-pad shorter hashes to meet the // minimum. let prehash = if prehash.len() < 33 { let mut padded = vec![0u8; 33 - prehash.len()]; padded.extend_from_slice(&prehash); padded } else { prehash }; let signature: P521Signature = signing_key.sign_prehash(&prehash)?; signature.to_bytes().to_vec() } } } Algorithm::Hmac => { let hash = args.hash.ok_or_else(JsErrorBox::not_supported)?; match hash { CryptoHash::Sha3_256 => { hmac_sign::<hmac::Hmac<Sha3_256>>(&key.data, data)? } CryptoHash::Sha3_384 => { hmac_sign::<hmac::Hmac<Sha3_384>>(&key.data, data)? } CryptoHash::Sha3_512 => { hmac_sign::<hmac::Hmac<Sha3_512>>(&key.data, data)? } _ => { let hash: HmacAlgorithm = hash.into(); let key = HmacKey::new(hash, &key.data); let signature = aws_lc_rs::hmac::sign(&key, data); signature.as_ref().to_vec() } } } _ => return Err(CryptoError::UnsupportedAlgorithm), }; Ok(signature) } } fn hmac_sign<M: hmac::Mac + hmac::digest::KeyInit>( key: &[u8], data: &[u8], ) -> Result<Vec<u8>, CryptoError> { let mut mac = <M as hmac::Mac>::new_from_slice(key) .map_err(|_| CryptoError::InvalidKeyLength)?; mac.update(data); Ok(mac.finalize().into_bytes().to_vec()) } fn hmac_verify<M: hmac::Mac + hmac::digest::KeyInit>( key: &[u8], data: &[u8], signature: &[u8], ) -> Result<bool, CryptoError> { let mut mac = <M as hmac::Mac>::new_from_slice(key) .map_err(|_| CryptoError::InvalidKeyLength)?; mac.update(data); Ok(mac.verify_slice(signature).is_ok()) } #[derive(deno_core::FromV8)] pub struct VerifyArg { #[from_v8(serde)] algorithm: Algorithm, salt_length: Option<u32>, #[from_v8(serde)] hash: Option<CryptoHash>, signature: Uint8Array, #[from_v8(serde)] named_curve: Option<CryptoNamedCurve>, } impl VerifyArg { pub(crate) fn new( algorithm: Algorithm, salt_length: Option<u32>, hash: Option<CryptoHash>, signature: Vec<u8>, named_curve: Option<CryptoNamedCurve>, ) -> Self { Self { algorithm, salt_length, hash, signature: signature.into(), named_curve, } } } /// Synchronous per-algorithm verify dispatch. Called from /// [`crate::subtle_verify::run`] inside `spawn_blocking`; the op-layer /// wrapper that previously fronted this for the JS shim has been /// retired alongside the JS `SubtleCrypto.prototype.verify` body. pub(crate) fn verify_key_sync( key: KeyData, args: VerifyArg, data: &[u8], ) -> Result<bool, CryptoError> { { let algorithm = args.algorithm; let verification = match algorithm { Algorithm::RsassaPkcs1v15 => { use rsa::pkcs1v15::Signature; use rsa::pkcs1v15::VerifyingKey; let public_key = read_rsa_public_key(key)?; let signature: Signature = (&*args.signature).try_into()?; match args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)? { CryptoHash::Sha1 => { let verifying_key = VerifyingKey::<Sha1>::new(public_key); verifying_key.verify(data, &signature).is_ok() } CryptoHash::Sha256 => { let verifying_key = VerifyingKey::<Sha256>::new(public_key); verifying_key.verify(data, &signature).is_ok() } CryptoHash::Sha384 => { let verifying_key = VerifyingKey::<Sha384>::new(public_key); verifying_key.verify(data, &signature).is_ok() } CryptoHash::Sha512 => { let verifying_key = VerifyingKey::<Sha512>::new(public_key); verifying_key.verify(data, &signature).is_ok() } _ => return Err(CryptoError::UnsupportedAlgorithm), } } Algorithm::RsaPss => { let public_key = read_rsa_public_key(key)?; let signature = args.signature.as_ref(); let salt_len = args .salt_length .ok_or_else(|| CryptoError::MissingArgumentSaltLength)? as usize; match args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)? { CryptoHash::Sha1 => { let pss = Pss::new_with_salt::<Sha1>(salt_len); let hashed = Sha1::digest(data); pss.verify(&public_key, &hashed, signature).is_ok() } CryptoHash::Sha256 => { let pss = Pss::new_with_salt::<Sha256>(salt_len); let hashed = Sha256::digest(data); pss.verify(&public_key, &hashed, signature).is_ok() } CryptoHash::Sha384 => { let pss = Pss::new_with_salt::<Sha384>(salt_len); let hashed = Sha384::digest(data); pss.verify(&public_key, &hashed, signature).is_ok() } CryptoHash::Sha512 => { let pss = Pss::new_with_salt::<Sha512>(salt_len); let hashed = Sha512::digest(data); pss.verify(&public_key, &hashed, signature).is_ok() } _ => return Err(CryptoError::UnsupportedAlgorithm), } } Algorithm::Hmac => { let hash = args.hash.ok_or_else(JsErrorBox::not_supported)?; match hash { CryptoHash::Sha3_256 => hmac_verify::<hmac::Hmac<Sha3_256>>( &key.data, data, &args.signature, )?, CryptoHash::Sha3_384 => hmac_verify::<hmac::Hmac<Sha3_384>>( &key.data, data, &args.signature, )?, CryptoHash::Sha3_512 => hmac_verify::<hmac::Hmac<Sha3_512>>( &key.data, data, &args.signature, )?, _ => { let hash: HmacAlgorithm = hash.into(); let key = HmacKey::new(hash, &key.data); aws_lc_rs::hmac::verify(&key, data, &args.signature).is_ok() } } } Algorithm::Ecdsa => { let hash = args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)?; let named_curve = args.named_curve.ok_or_else(JsErrorBox::not_supported)?; match named_curve { CryptoNamedCurve::P256 => { let verifying_key = match key.r#type { KeyType::Public => P256VerifyingKey::from_sec1_bytes(&key.data) .map_err(|_| CryptoError::InvalidKeyFormat)?, KeyType::Private => { let secret_key = p256::SecretKey::from_pkcs8_der(&key.data) .map_err(|_| CryptoError::InvalidKeyFormat)?; let signing_key = P256SigningKey::from(secret_key); *signing_key.verifying_key() } _ => return Err(CryptoError::InvalidKeyFormat), }; match P256Signature::from_slice(&args.signature) { Ok(signature) => { let prehash = match hash { CryptoHash::Sha1 => sha1::Sha1::digest(data).to_vec(), CryptoHash::Sha256 => sha2::Sha256::digest(data).to_vec(), CryptoHash::Sha384 => sha2::Sha384::digest(data).to_vec(), CryptoHash::Sha512 => sha2::Sha512::digest(data).to_vec(), _ => return Err(CryptoError::UnsupportedAlgorithm), }; verifying_key.verify_prehash(&prehash, &signature).is_ok() } _ => false, } } CryptoNamedCurve::P384 => { let verifying_key = match key.r#type { KeyType::Public => P384VerifyingKey::from_sec1_bytes(&key.data) .map_err(|_| CryptoError::InvalidKeyFormat)?, KeyType::Private => { let secret_key = p384::SecretKey::from_pkcs8_der(&key.data) .map_err(|_| CryptoError::InvalidKeyFormat)?; let signing_key = P384SigningKey::from(secret_key); *signing_key.verifying_key() } _ => return Err(CryptoError::InvalidKeyFormat), }; match P384Signature::from_slice(&args.signature) { Ok(signature) => { let prehash = match hash { CryptoHash::Sha1 => sha1::Sha1::digest(data).to_vec(), CryptoHash::Sha256 => sha2::Sha256::digest(data).to_vec(), CryptoHash::Sha384 => sha2::Sha384::digest(data).to_vec(), CryptoHash::Sha512 => sha2::Sha512::digest(data).to_vec(), _ => return Err(CryptoError::UnsupportedAlgorithm), }; verifying_key.verify_prehash(&prehash, &signature).is_ok() } _ => false, } } CryptoNamedCurve::P521 => { let verifying_key = match key.r#type { KeyType::Public => P521VerifyingKey::from_sec1_bytes(&key.data) .map_err(|_| CryptoError::InvalidKeyFormat)?, KeyType::Private => { let secret_key = p521::SecretKey::from_pkcs8_der(&key.data) .map_err(|_| CryptoError::InvalidKeyFormat)?; // Construct inner ecdsa signing key to get verifying key let inner_signing_key = ecdsa::SigningKey::<p521::NistP521>::from(secret_key); P521VerifyingKey::from(*inner_signing_key.verifying_key()) } _ => return Err(CryptoError::InvalidKeyFormat), }; match P521Signature::from_slice(&args.signature) { Ok(signature) => { let prehash = match hash { CryptoHash::Sha1 => sha1::Sha1::digest(data).to_vec(), CryptoHash::Sha256 => sha2::Sha256::digest(data).to_vec(), CryptoHash::Sha384 => sha2::Sha384::digest(data).to_vec(), CryptoHash::Sha512 => sha2::Sha512::digest(data).to_vec(), _ => return Err(CryptoError::UnsupportedAlgorithm), }; // P-521 field size is 66 bytes; bits2field requires at least // half that (33 bytes). Left-pad shorter hashes to meet the // minimum. let prehash = if prehash.len() < 33 { let mut padded = vec![0u8; 33 - prehash.len()]; padded.extend_from_slice(&prehash); padded } else { prehash }; verifying_key.verify_prehash(&prehash, &signature).is_ok() } _ => false, } } } } _ => return Err(CryptoError::UnsupportedAlgorithm), }; Ok(verification) } } /// Synchronous body of the old `op_crypto_derive_bits`; called directly /// from [`crate::subtle_derive_bits::run`] inside `spawn_blocking`. The /// `salt` is `Some` for PBKDF2 / HKDF and `None` for ECDH. #[allow( clippy::too_many_arguments, reason = "internal sync dispatch lifted out of op_crypto_derive_bits; \ the per-algorithm DeriveKeyArg fields are unpacked rather than \ reintroduced as a struct just for this lint" )] pub(crate) fn derive_bits_sync( key: KeyData, public_key: Option<KeyData>, algorithm: Algorithm, hash: Option<CryptoHash>, length: usize, iterations: Option<u32>, named_curve: Option<CryptoNamedCurve>, info: Option<Vec<u8>>, salt: Option<Vec<u8>>, ) -> Result<Vec<u8>, CryptoError> { match algorithm { Algorithm::Pbkdf2 => { let salt = salt.ok_or_else(JsErrorBox::not_supported)?; assert!(length > 0); assert!(length.is_multiple_of(8)); let algorithm = match hash.ok_or_else(JsErrorBox::not_supported)? { CryptoHash::Sha1 => pbkdf2::PBKDF2_HMAC_SHA1, CryptoHash::Sha256 => pbkdf2::PBKDF2_HMAC_SHA256, CryptoHash::Sha384 => pbkdf2::PBKDF2_HMAC_SHA384, CryptoHash::Sha512 => pbkdf2::PBKDF2_HMAC_SHA512, _ => return Err(CryptoError::UnsupportedAlgorithm), }; let iterations = NonZeroU32::new(iterations.ok_or_else(JsErrorBox::not_supported)?) .unwrap(); let secret = key.data; let mut out = vec![0; length / 8]; pbkdf2::derive(algorithm, iterations, &salt, &secret, &mut out); Ok(out) } Algorithm::Ecdh => { let named_curve = named_curve.ok_or(CryptoError::MissingArgumentNamedCurve)?; let public_key = public_key.ok_or(CryptoError::MissingArgumentPublicKey)?; match named_curve { CryptoNamedCurve::P256 => { let secret_key = p256::SecretKey::from_pkcs8_der(&key.data) .map_err(|_| CryptoError::DecodePrivateKey)?; let public_key = match public_key.r#type { KeyType::Private => { p256::SecretKey::from_pkcs8_der(&public_key.data) .map_err(|_| CryptoError::DecodePrivateKey)? .public_key() } KeyType::Public => { let point = p256::EncodedPoint::from_bytes(public_key.data) .map_err(|_| CryptoError::DecodePrivateKey)?; let pk = p256::PublicKey::from_encoded_point(&point); // pk is a constant time Option. if pk.is_some().into() { pk.unwrap() } else { return Err(CryptoError::DecodePrivateKey); } } _ => unreachable!(), }; let shared_secret = p256::elliptic_curve::ecdh::diffie_hellman( secret_key.to_nonzero_scalar(), public_key.as_affine(), ); // raw serialized x-coordinate of the computed point Ok(shared_secret.raw_secret_bytes().to_vec()) } CryptoNamedCurve::P384 => { let secret_key = p384::SecretKey::from_pkcs8_der(&key.data) .map_err(|_| CryptoError::DecodePrivateKey)?; let public_key = match public_key.r#type { KeyType::Private => { p384::SecretKey::from_pkcs8_der(&public_key.data) .map_err(|_| CryptoError::DecodePrivateKey)? .public_key() } KeyType::Public => { let point = p384::EncodedPoint::from_bytes(public_key.data) .map_err(|_| CryptoError::DecodePrivateKey)?; let pk = p384::PublicKey::from_encoded_point(&point); // pk is a constant time Option. if pk.is_some().into() { pk.unwrap() } else { return Err(CryptoError::DecodePrivateKey); } } _ => unreachable!(), }; let shared_secret = p384::elliptic_curve::ecdh::diffie_hellman( secret_key.to_nonzero_scalar(), public_key.as_affine(), ); // raw serialized x-coordinate of the computed point Ok(shared_secret.raw_secret_bytes().to_vec()) } CryptoNamedCurve::P521 => { let secret_key = p521::SecretKey::from_pkcs8_der(&key.data) .map_err(|_| CryptoError::DecodePrivateKey)?; let public_key = match public_key.r#type { KeyType::Private => { p521::SecretKey::from_pkcs8_der(&public_key.data) .map_err(|_| CryptoError::DecodePrivateKey)? .public_key() } KeyType::Public => { let point = p521::EncodedPoint::from_bytes(public_key.data) .map_err(|_| CryptoError::DecodePrivateKey)?; let pk = p521::PublicKey::from_encoded_point(&point); // pk is a constant time Option. if pk.is_some().into() { pk.unwrap() } else { return Err(CryptoError::DecodePrivateKey); } } _ => unreachable!(), }; let shared_secret = p521::elliptic_curve::ecdh::diffie_hellman( secret_key.to_nonzero_scalar(), public_key.as_affine(), ); // raw serialized x-coordinate of the computed point Ok(shared_secret.raw_secret_bytes().to_vec()) } } } Algorithm::Hkdf => { let salt = salt.ok_or_else(JsErrorBox::not_supported)?; let algorithm = match hash.ok_or_else(JsErrorBox::not_supported)? { CryptoHash::Sha1 => hkdf::HKDF_SHA1_FOR_LEGACY_USE_ONLY, CryptoHash::Sha256 => hkdf::HKDF_SHA256, CryptoHash::Sha384 => hkdf::HKDF_SHA384, CryptoHash::Sha512 => hkdf::HKDF_SHA512, _ => return Err(CryptoError::UnsupportedAlgorithm), }; let info = info.ok_or(CryptoError::MissingArgumentInfo)?; let secret = key.data; let length = length / 8; let salt = hkdf::Salt::new(algorithm, &salt); let prk = salt.extract(&secret); let info_slice: &[&[u8]] = &[&info]; let okm = prk .expand(info_slice, HkdfOutput(length)) .map_err(|_e| CryptoError::HKDFLengthTooLarge)?; let mut r = vec![0u8; length]; okm.fill(&mut r)?; Ok(r) } _ => Err(CryptoError::UnsupportedAlgorithm), } } fn read_rsa_public_key(key_data: KeyData) -> Result<RsaPublicKey, CryptoError> { let public_key = match key_data.r#type { KeyType::Private => { RsaPrivateKey::from_pkcs1_der(&key_data.data)?.to_public_key() } KeyType::Public => RsaPublicKey::from_pkcs1_der(&key_data.data)?, KeyType::Secret => unreachable!("unexpected KeyType::Secret"), }; Ok(public_key) } const HEX_CHARS: &[u8; 16] = b"0123456789abcdef"; pub(crate) fn fast_uuid_v4_bytes(bytes: &mut [u8; 16]) -> [u8; 36] { // Set UUID version to 4 and variant to 1. bytes[6] = (bytes[6] & 0x0f) | 0x40; bytes[8] = (bytes[8] & 0x3f) | 0x80; [ HEX_CHARS[(bytes[0] >> 4) as usize], HEX_CHARS[(bytes[0] & 0x0f) as usize], HEX_CHARS[(bytes[1] >> 4) as usize], HEX_CHARS[(bytes[1] & 0x0f) as usize], HEX_CHARS[(bytes[2] >> 4) as usize], HEX_CHARS[(bytes[2] & 0x0f) as usize], HEX_CHARS[(bytes[3] >> 4) as usize], HEX_CHARS[(bytes[3] & 0x0f) as usize], b'-', HEX_CHARS[(bytes[4] >> 4) as usize], HEX_CHARS[(bytes[4] & 0x0f) as usize], HEX_CHARS[(bytes[5] >> 4) as usize], HEX_CHARS[(bytes[5] & 0x0f) as usize], b'-', HEX_CHARS[(bytes[6] >> 4) as usize], HEX_CHARS[(bytes[6] & 0x0f) as usize], HEX_CHARS[(bytes[7] >> 4) as usize], HEX_CHARS[(bytes[7] & 0x0f) as usize], b'-', HEX_CHARS[(bytes[8] >> 4) as usize], HEX_CHARS[(bytes[8] & 0x0f) as usize], HEX_CHARS[(bytes[9] >> 4) as usize], HEX_CHARS[(bytes[9] & 0x0f) as usize], b'-', HEX_CHARS[(bytes[10] >> 4) as usize], HEX_CHARS[(bytes[10] & 0x0f) as usize], HEX_CHARS[(bytes[11] >> 4) as usize], HEX_CHARS[(bytes[11] & 0x0f) as usize], HEX_CHARS[(bytes[12] >> 4) as usize], HEX_CHARS[(bytes[12] & 0x0f) as usize], HEX_CHARS[(bytes[13] >> 4) as usize], HEX_CHARS[(bytes[13] & 0x0f) as usize], HEX_CHARS[(bytes[14] >> 4) as usize], HEX_CHARS[(bytes[14] & 0x0f) as usize], HEX_CHARS[(bytes[15] >> 4) as usize], HEX_CHARS[(bytes[15] & 0x0f) as usize], ] } #[cfg(test)] fn fast_uuid_v4(bytes: &mut [u8; 16]) -> String { let buf = fast_uuid_v4_bytes(bytes); // Safety: the buffer is all valid UTF-8. unsafe { String::from_utf8_unchecked(buf.to_vec()) } } #[test] fn test_fast_uuid_v4_correctness() { use rand::Rng; let mut rng = rand::thread_rng(); let mut bytes = [0u8; 16]; rng.fill(&mut bytes); let uuid = fast_uuid_v4(&mut bytes.clone()); let uuid_lib = uuid::Builder::from_bytes(bytes) .set_variant(uuid::Variant::RFC4122) .set_version(uuid::Version::Random) .as_uuid() .to_string(); assert_eq!(uuid, uuid_lib); }