diff options
Diffstat (limited to 'ext/node/ops/crypto/mod.rs')
-rw-r--r-- | ext/node/ops/crypto/mod.rs | 882 |
1 files changed, 22 insertions, 860 deletions
diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs index 1b545e024..07d901cbc 100644 --- a/ext/node/ops/crypto/mod.rs +++ b/ext/node/ops/crypto/mod.rs @@ -3,7 +3,6 @@ use deno_core::error::generic_error; use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::op2; -use deno_core::serde_v8::BigInt as V8BigInt; use deno_core::unsync::spawn_blocking; use deno_core::JsBuffer; use deno_core::OpState; @@ -11,21 +10,12 @@ use deno_core::StringOrBuffer; use deno_core::ToJsBuffer; use elliptic_curve::sec1::ToEncodedPoint; use hkdf::Hkdf; +use keys::KeyObjectHandle; use num_bigint::BigInt; use num_bigint_dig::BigUint; -use num_traits::FromPrimitive; -use once_cell::sync::Lazy; use rand::distributions::Distribution; use rand::distributions::Uniform; -use rand::thread_rng; use rand::Rng; -use rsa::pkcs1::DecodeRsaPrivateKey; -use rsa::pkcs1::DecodeRsaPublicKey; -use rsa::pkcs8; -use rsa::pkcs8::der::asn1; -use rsa::pkcs8::der::Decode; -use rsa::pkcs8::der::Encode; -use rsa::pkcs8::der::Reader; use std::future::Future; use std::rc::Rc; @@ -34,24 +24,21 @@ use p256::NistP256; use p384::NistP384; use rsa::pkcs8::DecodePrivateKey; use rsa::pkcs8::DecodePublicKey; -use rsa::signature::hazmat::PrehashSigner; -use rsa::signature::hazmat::PrehashVerifier; -use rsa::signature::SignatureEncoding; use rsa::Oaep; use rsa::Pkcs1v15Encrypt; use rsa::RsaPrivateKey; use rsa::RsaPublicKey; -use spki::EncodePublicKey; mod cipher; mod dh; mod digest; +pub mod keys; mod md5_sha1; mod primes; +mod sign; pub mod x509; use self::digest::match_fixed_digest_with_eager_block_buffer; -use self::digest::match_fixed_digest_with_oid; #[op2(fast)] pub fn op_node_check_prime( @@ -339,156 +326,23 @@ pub fn op_node_decipheriv_final( } #[op2] -#[serde] +#[buffer] pub fn op_node_sign( + #[cppgc] handle: &KeyObjectHandle, #[buffer] digest: &[u8], #[string] digest_type: &str, - #[serde] key: StringOrBuffer, - #[string] _type: &str, - #[string] format: &str, -) -> Result<ToJsBuffer, AnyError> { - let (label, doc) = - pkcs8::SecretDocument::from_pem(std::str::from_utf8(&key).unwrap())?; - - let oid; - let pkey = match format { - "pem" => match label { - "PRIVATE KEY" => { - let pk_info = pkcs8::PrivateKeyInfo::try_from(doc.as_bytes())?; - oid = pk_info.algorithm.oid; - pk_info.private_key - } - "RSA PRIVATE KEY" => { - oid = RSA_ENCRYPTION_OID; - doc.as_bytes() - } - "EC PRIVATE KEY" => { - let ec_pk = sec1::EcPrivateKey::from_der(doc.as_bytes())?; - match ec_pk.parameters { - Some(sec1::EcParameters::NamedCurve(o)) => { - oid = o; - ec_pk.private_key - } - // https://datatracker.ietf.org/doc/html/rfc5915#section-3 - // - // Though the ASN.1 indicates that - // the parameters field is OPTIONAL, implementations that conform to - // this document MUST always include the parameters field. - _ => return Err(type_error("invalid ECPrivateKey params")), - } - } - _ => return Err(type_error("Invalid PEM label")), - }, - _ => return Err(type_error("Unsupported key format")), - }; - - match oid { - RSA_ENCRYPTION_OID => { - use rsa::pkcs1v15::SigningKey; - let key = RsaPrivateKey::from_pkcs1_der(pkey)?; - - // md5-sha1 is special, because it's not wrapped in an ASN.1 DigestInfo - // (so has no prefix). - // See https://github.com/openssl/openssl/blob/af82623d32962b3eff5b0f0b0dedec5eb730b231/crypto/rsa/rsa_sign.c#L285 - if digest_type == "md5-sha1" { - let signing_key = SigningKey::<md5_sha1::Md5Sha1>::new_unprefixed(key); - let signature = signing_key.sign_prehash(digest)?.to_vec(); - return Ok(signature.into()); - } - - let signature = match_fixed_digest_with_oid!( - digest_type, - fn <D>() { - let signing_key = SigningKey::<D>::new(key); - signing_key.sign_prehash(digest)?.to_vec() - }, - _ => { - return Err(type_error(format!( - "digest not allowed for RSA signature: {}", - digest_type - ))) - } - ); - Ok(signature.into()) - } - // signature structure encoding is DER by default for DSA and ECDSA. - // - // TODO(@littledivy): Validate public_key if present - ID_SECP256R1_OID => { - let key = p256::ecdsa::SigningKey::from_slice(pkey)?; - Ok( - key - .sign_prehash(digest) - .map(|sig: p256::ecdsa::Signature| sig.to_der().to_vec().into())?, - ) - } - ID_SECP384R1_OID => { - let key = p384::ecdsa::SigningKey::from_slice(pkey)?; - Ok( - key - .sign_prehash(digest) - .map(|sig: p384::ecdsa::Signature| sig.to_der().to_vec().into())?, - ) - } - _ => Err(type_error("Unsupported signing key")), - } +) -> Result<Box<[u8]>, AnyError> { + handle.sign_prehashed(digest_type, digest) } -#[op2] +#[op2(fast)] pub fn op_node_verify( + #[cppgc] handle: &KeyObjectHandle, #[buffer] digest: &[u8], #[string] digest_type: &str, - #[serde] key: StringOrBuffer, - #[string] key_type: &str, - #[string] key_format: &str, #[buffer] signature: &[u8], ) -> Result<bool, AnyError> { - match key_type { - "rsa" => { - use rsa::pkcs1v15::VerifyingKey; - let key = match key_format { - "pem" => RsaPublicKey::from_public_key_pem((&key).try_into()?) - .map_err(|_| type_error("Invalid RSA public key"))?, - // TODO(kt3k): Support der and jwk formats - _ => { - return Err(type_error(format!( - "Unsupported key format: {}", - key_format - ))) - } - }; - - // md5-sha1 is special, because it's not wrapped in an ASN.1 DigestInfo - // (so has no prefix). - // See https://github.com/openssl/openssl/blob/af82623d32962b3eff5b0f0b0dedec5eb730b231/crypto/rsa/rsa_sign.c#L285 - if digest_type == "md5-sha1" { - let verifying_key = - VerifyingKey::<md5_sha1::Md5Sha1>::new_unprefixed(key); - let verified = verifying_key - .verify_prehash(digest, &signature.try_into()?) - .is_ok(); - return Ok(verified); - } - - Ok(match_fixed_digest_with_oid!( - digest_type, - fn <D>() { - let verifying_key = VerifyingKey::<D>::new(key); - verifying_key.verify_prehash(digest, &signature.try_into()?).is_ok() - }, - _ => { - return Err(type_error(format!( - "digest not allowed for RSA signature: {}", - digest_type - ))) - } - )) - } - _ => Err(type_error(format!( - "Verifying with {} keys is not supported", - key_type - ))), - } + handle.verify_prehashed(digest_type, digest, signature) } fn pbkdf2_sync( @@ -542,13 +396,13 @@ pub async fn op_node_pbkdf2_async( } #[op2(fast)] -pub fn op_node_generate_secret(#[buffer] buf: &mut [u8]) { +pub fn op_node_fill_random(#[buffer] buf: &mut [u8]) { rand::thread_rng().fill(buf); } #[op2(async)] #[serde] -pub async fn op_node_generate_secret_async(#[smi] len: i32) -> ToJsBuffer { +pub async fn op_node_fill_random_async(#[smi] len: i32) -> ToJsBuffer { spawn_blocking(move || { let mut buf = vec![0u8; len as usize]; rand::thread_rng().fill(&mut buf[..]); @@ -560,11 +414,15 @@ pub async fn op_node_generate_secret_async(#[smi] len: i32) -> ToJsBuffer { fn hkdf_sync( digest_algorithm: &str, - ikm: &[u8], + handle: &KeyObjectHandle, salt: &[u8], info: &[u8], okm: &mut [u8], ) -> Result<(), AnyError> { + let Some(ikm) = handle.as_secret_key() else { + return Err(type_error("expected secret key")); + }; + match_fixed_digest_with_eager_block_buffer!( digest_algorithm, fn <D>() { @@ -581,328 +439,32 @@ fn hkdf_sync( #[op2(fast)] pub fn op_node_hkdf( #[string] digest_algorithm: &str, - #[buffer] ikm: &[u8], + #[cppgc] handle: &KeyObjectHandle, #[buffer] salt: &[u8], #[buffer] info: &[u8], #[buffer] okm: &mut [u8], ) -> Result<(), AnyError> { - hkdf_sync(digest_algorithm, ikm, salt, info, okm) + hkdf_sync(digest_algorithm, handle, salt, info, okm) } #[op2(async)] #[serde] pub async fn op_node_hkdf_async( #[string] digest_algorithm: String, - #[buffer] ikm: JsBuffer, + #[cppgc] handle: &KeyObjectHandle, #[buffer] salt: JsBuffer, #[buffer] info: JsBuffer, #[number] okm_len: usize, ) -> Result<ToJsBuffer, AnyError> { + let handle = handle.clone(); spawn_blocking(move || { let mut okm = vec![0u8; okm_len]; - hkdf_sync(&digest_algorithm, &ikm, &salt, &info, &mut okm)?; + hkdf_sync(&digest_algorithm, &handle, &salt, &info, &mut okm)?; Ok(okm.into()) }) .await? } -use rsa::pkcs1::EncodeRsaPrivateKey; -use rsa::pkcs1::EncodeRsaPublicKey; - -use self::primes::Prime; - -fn generate_rsa( - modulus_length: usize, - public_exponent: usize, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - let mut rng = rand::thread_rng(); - let private_key = RsaPrivateKey::new_with_exp( - &mut rng, - modulus_length, - &rsa::BigUint::from_usize(public_exponent).unwrap(), - )?; - let public_key = private_key.to_public_key(); - let private_key_der = private_key.to_pkcs1_der()?.as_bytes().to_vec(); - let public_key_der = public_key.to_pkcs1_der()?.to_vec(); - - Ok((private_key_der.into(), public_key_der.into())) -} - -#[op2] -#[serde] -pub fn op_node_generate_rsa( - #[number] modulus_length: usize, - #[number] public_exponent: usize, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - generate_rsa(modulus_length, public_exponent) -} - -#[op2(async)] -#[serde] -pub async fn op_node_generate_rsa_async( - #[number] modulus_length: usize, - #[number] public_exponent: usize, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - spawn_blocking(move || generate_rsa(modulus_length, public_exponent)).await? -} - -#[op2] -#[string] -pub fn op_node_export_rsa_public_pem( - #[buffer] pkcs1_der: &[u8], -) -> Result<String, AnyError> { - let public_key = RsaPublicKey::from_pkcs1_der(pkcs1_der)?; - let export = public_key.to_public_key_pem(Default::default())?; - Ok(export) -} - -#[op2] -#[serde] -pub fn op_node_export_rsa_spki_der( - #[buffer] pkcs1_der: &[u8], -) -> Result<ToJsBuffer, AnyError> { - let public_key = RsaPublicKey::from_pkcs1_der(pkcs1_der)?; - let export = public_key.to_public_key_der()?.to_vec(); - Ok(export.into()) -} - -fn dsa_generate( - modulus_length: usize, - divisor_length: usize, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - let mut rng = rand::thread_rng(); - use dsa::pkcs8::EncodePrivateKey; - use dsa::Components; - use dsa::KeySize; - use dsa::SigningKey; - - let key_size = match (modulus_length, divisor_length) { - #[allow(deprecated)] - (1024, 160) => KeySize::DSA_1024_160, - (2048, 224) => KeySize::DSA_2048_224, - (2048, 256) => KeySize::DSA_2048_256, - (3072, 256) => KeySize::DSA_3072_256, - _ => return Err(type_error("Invalid modulus_length or divisor_length")), - }; - let components = Components::generate(&mut rng, key_size); - let signing_key = SigningKey::generate(&mut rng, components); - let verifying_key = signing_key.verifying_key(); - - Ok(( - signing_key - .to_pkcs8_der() - .map_err(|_| type_error("Not valid pkcs8"))? - .as_bytes() - .to_vec() - .into(), - verifying_key - .to_public_key_der() - .map_err(|_| type_error("Not valid spki"))? - .to_vec() - .into(), - )) -} - -#[op2] -#[serde] -pub fn op_node_dsa_generate( - #[number] modulus_length: usize, - #[number] divisor_length: usize, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - dsa_generate(modulus_length, divisor_length) -} - -#[op2(async)] -#[serde] -pub async fn op_node_dsa_generate_async( - #[number] modulus_length: usize, - #[number] divisor_length: usize, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - spawn_blocking(move || dsa_generate(modulus_length, divisor_length)).await? -} - -fn ec_generate( - named_curve: &str, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - let mut rng = rand::thread_rng(); - // TODO(@littledivy): Support public key point encoding. - // Default is uncompressed. - match named_curve { - "P-256" | "prime256v1" | "secp256r1" => { - let key = p256::SecretKey::random(&mut rng); - let public_key = key.public_key(); - - Ok(( - key.to_bytes().to_vec().into(), - public_key.to_encoded_point(false).as_ref().to_vec().into(), - )) - } - "P-384" | "prime384v1" | "secp384r1" => { - let key = p384::SecretKey::random(&mut rng); - let public_key = key.public_key(); - - Ok(( - key.to_bytes().to_vec().into(), - public_key.to_encoded_point(false).as_ref().to_vec().into(), - )) - } - _ => Err(type_error("Unsupported named curve")), - } -} - -#[op2] -#[serde] -pub fn op_node_ec_generate( - #[string] named_curve: &str, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - ec_generate(named_curve) -} - -#[op2(async)] -#[serde] -pub async fn op_node_ec_generate_async( - #[string] named_curve: String, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - spawn_blocking(move || ec_generate(&named_curve)).await? -} - -fn ed25519_generate() -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - use ring::signature::Ed25519KeyPair; - use ring::signature::KeyPair; - - let mut rng = thread_rng(); - let mut seed = vec![0u8; 32]; - rng.fill(seed.as_mut_slice()); - - let pair = Ed25519KeyPair::from_seed_unchecked(&seed) - .map_err(|_| type_error("Failed to generate Ed25519 key"))?; - - let public_key = pair.public_key().as_ref().to_vec(); - Ok((seed.into(), public_key.into())) -} - -#[op2] -#[serde] -pub fn op_node_ed25519_generate() -> Result<(ToJsBuffer, ToJsBuffer), AnyError> -{ - ed25519_generate() -} - -#[op2(async)] -#[serde] -pub async fn op_node_ed25519_generate_async( -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - spawn_blocking(ed25519_generate).await? -} - -fn x25519_generate() -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - // u-coordinate of the base point. - const X25519_BASEPOINT_BYTES: [u8; 32] = [ - 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - ]; - - let mut pkey = [0; 32]; - - let mut rng = thread_rng(); - rng.fill(pkey.as_mut_slice()); - - let pkey_copy = pkey.to_vec(); - // https://www.rfc-editor.org/rfc/rfc7748#section-6.1 - // pubkey = x25519(a, 9) which is constant-time Montgomery ladder. - // https://eprint.iacr.org/2014/140.pdf page 4 - // https://eprint.iacr.org/2017/212.pdf algorithm 8 - // pubkey is in LE order. - let pubkey = x25519_dalek::x25519(pkey, X25519_BASEPOINT_BYTES); - - Ok((pkey_copy.into(), pubkey.to_vec().into())) -} - -#[op2] -#[serde] -pub fn op_node_x25519_generate() -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - x25519_generate() -} - -#[op2(async)] -#[serde] -pub async fn op_node_x25519_generate_async( -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - spawn_blocking(x25519_generate).await? -} - -fn dh_generate_group( - group_name: &str, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - let dh = match group_name { - "modp5" => dh::DiffieHellman::group::<dh::Modp1536>(), - "modp14" => dh::DiffieHellman::group::<dh::Modp2048>(), - "modp15" => dh::DiffieHellman::group::<dh::Modp3072>(), - "modp16" => dh::DiffieHellman::group::<dh::Modp4096>(), - "modp17" => dh::DiffieHellman::group::<dh::Modp6144>(), - "modp18" => dh::DiffieHellman::group::<dh::Modp8192>(), - _ => return Err(type_error("Unsupported group name")), - }; - - Ok(( - dh.private_key.into_vec().into(), - dh.public_key.into_vec().into(), - )) -} - -#[op2] -#[serde] -pub fn op_node_dh_generate_group( - #[string] group_name: &str, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - dh_generate_group(group_name) -} - -#[op2(async)] -#[serde] -pub async fn op_node_dh_generate_group_async( - #[string] group_name: String, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - spawn_blocking(move || dh_generate_group(&group_name)).await? -} - -fn dh_generate( - prime: Option<&[u8]>, - prime_len: usize, - generator: usize, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - let prime = prime - .map(|p| p.into()) - .unwrap_or_else(|| Prime::generate(prime_len)); - let dh = dh::DiffieHellman::new(prime, generator); - - Ok(( - dh.private_key.into_vec().into(), - dh.public_key.into_vec().into(), - )) -} - -#[op2] -#[serde] -pub fn op_node_dh_generate( - #[serde] prime: Option<&[u8]>, - #[number] prime_len: usize, - #[number] generator: usize, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - dh_generate(prime, prime_len, generator) -} - -// TODO(lev): This duplication should be avoided. -#[op2] -#[serde] -pub fn op_node_dh_generate2( - #[buffer] prime: JsBuffer, - #[number] prime_len: usize, - #[number] generator: usize, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - dh_generate(Some(prime).as_deref(), prime_len, generator) -} - #[op2] #[serde] pub fn op_node_dh_compute_secret( @@ -918,17 +480,6 @@ pub fn op_node_dh_compute_secret( Ok(shared_secret.to_bytes_be().into()) } -#[op2(async)] -#[serde] -pub async fn op_node_dh_generate_async( - #[buffer] prime: Option<JsBuffer>, - #[number] prime_len: usize, - #[number] generator: usize, -) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> { - spawn_blocking(move || dh_generate(prime.as_deref(), prime_len, generator)) - .await? -} - #[op2(fast)] #[smi] pub fn op_node_random_int( @@ -1288,392 +839,3 @@ pub async fn op_node_gen_prime_async( ) -> Result<ToJsBuffer, AnyError> { Ok(spawn_blocking(move || gen_prime(size)).await?) } - -#[derive(serde::Serialize)] -#[serde(tag = "type")] -pub enum AsymmetricKeyDetails { - #[serde(rename = "rsa")] - #[serde(rename_all = "camelCase")] - Rsa { - modulus_length: usize, - public_exponent: V8BigInt, - }, - #[serde(rename = "rsa-pss")] - #[serde(rename_all = "camelCase")] - RsaPss { - modulus_length: usize, - public_exponent: V8BigInt, - hash_algorithm: String, - salt_length: u32, - }, - #[serde(rename = "ec")] - #[serde(rename_all = "camelCase")] - Ec { named_curve: String }, - #[serde(rename = "dh")] - Dh, -} - -// https://oidref.com/ -const ID_SHA1_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new_unwrap("1.3.14.3.2.26"); -const ID_SHA256_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1"); -const ID_SHA384_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2"); -const ID_SHA512_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3"); -const ID_MFG1: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.8"); -pub const ID_SECP256R1_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7"); -pub const ID_SECP384R1_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.3.132.0.34"); -pub const ID_SECP521R1_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.3.132.0.35"); - -// Default HashAlgorithm for RSASSA-PSS-params (sha1) -// -// sha1 HashAlgorithm ::= { -// algorithm id-sha1, -// parameters SHA1Parameters : NULL -// } -// -// SHA1Parameters ::= NULL -static SHA1_HASH_ALGORITHM: Lazy<rsa::pkcs8::AlgorithmIdentifierRef<'static>> = - Lazy::new(|| rsa::pkcs8::AlgorithmIdentifierRef { - // id-sha1 - oid: ID_SHA1_OID, - // NULL - parameters: Some(asn1::AnyRef::from(asn1::Null)), - }); - -// TODO(@littledivy): `pkcs8` should provide AlgorithmIdentifier to Any conversion. -static ENCODED_SHA1_HASH_ALGORITHM: Lazy<Vec<u8>> = - Lazy::new(|| SHA1_HASH_ALGORITHM.to_der().unwrap()); - -// Default MaskGenAlgrithm for RSASSA-PSS-params (mgf1SHA1) -// -// mgf1SHA1 MaskGenAlgorithm ::= { -// algorithm id-mgf1, -// parameters HashAlgorithm : sha1 -// } -static MGF1_SHA1_MASK_ALGORITHM: Lazy< - rsa::pkcs8::AlgorithmIdentifierRef<'static>, -> = Lazy::new(|| rsa::pkcs8::AlgorithmIdentifierRef { - // id-mgf1 - oid: ID_MFG1, - // sha1 - parameters: Some( - asn1::AnyRef::from_der(&ENCODED_SHA1_HASH_ALGORITHM).unwrap(), - ), -}); - -pub const RSA_ENCRYPTION_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1"); -pub const DH_KEY_AGREEMENT_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.3.1"); -pub const RSASSA_PSS_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10"); -pub const EC_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.2.840.10045.2.1"); - -// The parameters field associated with OID id-RSASSA-PSS -// Defined in RFC 3447, section A.2.3 -// -// RSASSA-PSS-params ::= SEQUENCE { -// hashAlgorithm [0] HashAlgorithm DEFAULT sha1, -// maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1, -// saltLength [2] INTEGER DEFAULT 20, -// trailerField [3] TrailerField DEFAULT trailerFieldBC -// } -pub struct PssPrivateKeyParameters<'a> { - pub hash_algorithm: rsa::pkcs8::AlgorithmIdentifierRef<'a>, - #[allow(dead_code)] - pub mask_gen_algorithm: rsa::pkcs8::AlgorithmIdentifierRef<'a>, - pub salt_length: u32, -} - -// Context-specific tag number for hashAlgorithm. -const HASH_ALGORITHM_TAG: rsa::pkcs8::der::TagNumber = - rsa::pkcs8::der::TagNumber::new(0); - -// Context-specific tag number for maskGenAlgorithm. -const MASK_GEN_ALGORITHM_TAG: rsa::pkcs8::der::TagNumber = - rsa::pkcs8::der::TagNumber::new(1); - -// Context-specific tag number for saltLength. -const SALT_LENGTH_TAG: rsa::pkcs8::der::TagNumber = - rsa::pkcs8::der::TagNumber::new(2); - -impl<'a> TryFrom<rsa::pkcs8::der::asn1::AnyRef<'a>> - for PssPrivateKeyParameters<'a> -{ - type Error = rsa::pkcs8::der::Error; - - fn try_from( - any: rsa::pkcs8::der::asn1::AnyRef<'a>, - ) -> rsa::pkcs8::der::Result<PssPrivateKeyParameters> { - any.sequence(|decoder| { - let hash_algorithm = decoder - .context_specific::<rsa::pkcs8::AlgorithmIdentifierRef>( - HASH_ALGORITHM_TAG, - pkcs8::der::TagMode::Explicit, - )? - .map(TryInto::try_into) - .transpose()? - .unwrap_or(*SHA1_HASH_ALGORITHM); - - let mask_gen_algorithm = decoder - .context_specific::<rsa::pkcs8::AlgorithmIdentifierRef>( - MASK_GEN_ALGORITHM_TAG, - pkcs8::der::TagMode::Explicit, - )? - .map(TryInto::try_into) - .transpose()? - .unwrap_or(*MGF1_SHA1_MASK_ALGORITHM); - - let salt_length = decoder - .context_specific::<u32>( - SALT_LENGTH_TAG, - pkcs8::der::TagMode::Explicit, - )? - .map(TryInto::try_into) - .transpose()? - .unwrap_or(20); - - Ok(Self { - hash_algorithm, - mask_gen_algorithm, - salt_length, - }) - }) - } -} - -fn parse_private_key( - key: &[u8], - format: &str, - type_: &str, -) -> Result<pkcs8::SecretDocument, AnyError> { - match format { - "pem" => { - let pem = std::str::from_utf8(key).map_err(|err| { - type_error(format!( - "Invalid PEM private key: not valid utf8 starting at byte {}", - err.valid_up_to() - )) - })?; - let (_, doc) = pkcs8::SecretDocument::from_pem(pem)?; - Ok(doc) - } - "der" => { - match type_ { - "pkcs8" => pkcs8::SecretDocument::from_pkcs8_der(key) - .map_err(|_| type_error("Invalid PKCS8 private key")), - "pkcs1" => pkcs8::SecretDocument::from_pkcs1_der(key) - .map_err(|_| type_error("Invalid PKCS1 private key")), - // TODO(@littledivy): sec1 type - _ => Err(type_error(format!("Unsupported key type: {}", type_))), - } - } - _ => Err(type_error(format!("Unsupported key format: {}", format))), - } -} - -#[op2] -#[serde] -pub fn op_node_create_private_key( - #[buffer] key: &[u8], - #[string] format: &str, - #[string] type_: &str, -) -> Result<AsymmetricKeyDetails, AnyError> { - use rsa::pkcs1::der::Decode; - - let doc = parse_private_key(key, format, type_)?; - let pk_info = pkcs8::PrivateKeyInfo::try_from(doc.as_bytes())?; - - let alg = pk_info.algorithm.oid; - - match alg { - RSA_ENCRYPTION_OID => { - let private_key = - rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key)?; - let modulus_length = private_key.modulus.as_bytes().len() * 8; - - Ok(AsymmetricKeyDetails::Rsa { - modulus_length, - public_exponent: BigInt::from_bytes_be( - num_bigint::Sign::Plus, - private_key.public_exponent.as_bytes(), - ) - .into(), - }) - } - DH_KEY_AGREEMENT_OID => Ok(AsymmetricKeyDetails::Dh), - RSASSA_PSS_OID => { - let params = PssPrivateKeyParameters::try_from( - pk_info - .algorithm - .parameters - .ok_or_else(|| type_error("Malformed parameters".to_string()))?, - ) - .map_err(|_| type_error("Malformed parameters".to_string()))?; - - let hash_alg = params.hash_algorithm; - let hash_algorithm = match hash_alg.oid { - ID_SHA1_OID => "sha1", - ID_SHA256_OID => "sha256", - ID_SHA384_OID => "sha384", - ID_SHA512_OID => "sha512", - _ => return Err(type_error("Unsupported hash algorithm")), - }; - - let private_key = - rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key)?; - let modulus_length = private_key.modulus.as_bytes().len() * 8; - Ok(AsymmetricKeyDetails::RsaPss { - modulus_length, - public_exponent: BigInt::from_bytes_be( - num_bigint::Sign::Plus, - private_key.public_exponent.as_bytes(), - ) - .into(), - hash_algorithm: hash_algorithm.to_string(), - salt_length: params.salt_length, - }) - } - EC_OID => { - let named_curve = pk_info - .algorithm - .parameters_oid() - .map_err(|_| type_error("malformed parameters"))?; - let named_curve = match named_curve { - ID_SECP256R1_OID => "p256", - ID_SECP384R1_OID => "p384", - ID_SECP521R1_OID => "p521", - _ => return Err(type_error("Unsupported named curve")), - }; - - Ok(AsymmetricKeyDetails::Ec { - named_curve: named_curve.to_string(), - }) - } - _ => Err(type_error("Unsupported algorithm")), - } -} - -fn parse_public_key( - key: &[u8], - format: &str, - type_: &str, -) -> Result<pkcs8::Document, AnyError> { - match format { - "pem" => { - let pem = std::str::from_utf8(key).map_err(|err| { - type_error(format!( - "Invalid PEM private key: not valid utf8 starting at byte {}", - err.valid_up_to() - )) - })?; - let (label, doc) = pkcs8::Document::from_pem(pem)?; - if label != "PUBLIC KEY" { - return Err(type_error("Invalid PEM label")); - } - Ok(doc) - } - "der" => match type_ { - "pkcs1" => pkcs8::Document::from_pkcs1_der(key) - .map_err(|_| type_error("Invalid PKCS1 public key")), - _ => Err(type_error(format!("Unsupported key type: {}", type_))), - }, - _ => Err(type_error(format!("Unsupported key format: {}", format))), - } -} - -#[op2] -#[serde] -pub fn op_node_create_public_key( - #[buffer] key: &[u8], - #[string] format: &str, - #[string] type_: &str, -) -> Result<AsymmetricKeyDetails, AnyError> { - let mut doc = None; - - let pk_info = if type_ != "spki" { - doc.replace(parse_public_key(key, format, type_)?); - spki::SubjectPublicKeyInfoRef::try_from(doc.as_ref().unwrap().as_bytes())? - } else { - spki::SubjectPublicKeyInfoRef::try_from(key)? - }; - - let alg = pk_info.algorithm.oid; - - match alg { - RSA_ENCRYPTION_OID => { - let public_key = rsa::pkcs1::RsaPublicKey::from_der( - pk_info.subject_public_key.raw_bytes(), - )?; - let modulus_length = public_key.modulus.as_bytes().len() * 8; - - Ok(AsymmetricKeyDetails::Rsa { - modulus_length, - public_exponent: BigInt::from_bytes_be( - num_bigint::Sign::Plus, - public_key.public_exponent.as_bytes(), - ) - .into(), - }) - } - RSASSA_PSS_OID => { - let params = PssPrivateKeyParameters::try_from( - pk_info - .algorithm - .parameters - .ok_or_else(|| type_error("Malformed parameters".to_string()))?, - ) - .map_err(|_| type_error("Malformed parameters".to_string()))?; - - let hash_alg = params.hash_algorithm; - let hash_algorithm = match hash_alg.oid { - ID_SHA1_OID => "sha1", - ID_SHA256_OID => "sha256", - ID_SHA384_OID => "sha384", - ID_SHA512_OID => "sha512", - _ => return Err(type_error("Unsupported hash algorithm")), - }; - - let public_key = rsa::pkcs1::RsaPublicKey::from_der( - pk_info.subject_public_key.raw_bytes(), - )?; - let modulus_length = public_key.modulus.as_bytes().len() * 8; - Ok(AsymmetricKeyDetails::RsaPss { - modulus_length, - public_exponent: BigInt::from_bytes_be( - num_bigint::Sign::Plus, - public_key.public_exponent.as_bytes(), - ) - .into(), - hash_algorithm: hash_algorithm.to_string(), - salt_length: params.salt_length, - }) - } - EC_OID => { - let named_curve = pk_info - .algorithm - .parameters_oid() - .map_err(|_| type_error("malformed parameters"))?; - let named_curve = match named_curve { - ID_SECP256R1_OID => "p256", - ID_SECP384R1_OID => "p384", - ID_SECP521R1_OID => "p521", - _ => return Err(type_error("Unsupported named curve")), - }; - - Ok(AsymmetricKeyDetails::Ec { - named_curve: named_curve.to_string(), - }) - } - DH_KEY_AGREEMENT_OID => Ok(AsymmetricKeyDetails::Dh), - _ => Err(type_error("Unsupported algorithm")), - } -} |