diff --git a/crates/caips/src/caip10/aleo.rs b/crates/caips/src/caip10/aleo.rs index acb450ce3..15a96936f 100644 --- a/crates/caips/src/caip10/aleo.rs +++ b/crates/caips/src/caip10/aleo.rs @@ -8,7 +8,7 @@ pub(crate) fn encode_aleo_address(jwk: &JWK, network_id: &str) -> Result params, + Params::OKP(ref params) if params.curve == ssi_jwk::okp::aleo::OKP_CURVE => params, _ => return Err("Invalid public key type for Aleo"), }; diff --git a/crates/claims/crates/data-integrity/suites/src/suites/unspecified/aleo_signature_2021.rs b/crates/claims/crates/data-integrity/suites/src/suites/unspecified/aleo_signature_2021.rs index e0b7f308c..52980e3e0 100644 --- a/crates/claims/crates/data-integrity/suites/src/suites/unspecified/aleo_signature_2021.rs +++ b/crates/claims/crates/data-integrity/suites/src/suites/unspecified/aleo_signature_2021.rs @@ -133,7 +133,7 @@ impl VerificationAlgorithm for AleoSignatureAlgorithm { return Err(ProofValidationError::InvalidKey); } - let result = ssi_jwk::aleo::verify( + let result = ssi_jwk::okp::aleo::verify( &prepared_claims, &account_id.account_address, &signature_bytes, @@ -141,7 +141,7 @@ impl VerificationAlgorithm for AleoSignatureAlgorithm { match result { Ok(()) => Ok(Ok(())), - Err(ssi_jwk::aleo::AleoVerifyError::InvalidSignature) => { + Err(ssi_jwk::okp::aleo::AleoVerifyError::InvalidSignature) => { Ok(Err(ssi_claims_core::InvalidProof::Signature)) } Err(_) => Err(ProofValidationError::InvalidSignature), diff --git a/crates/jwk/src/der.rs b/crates/jwk/src/der.rs index dfba16ba7..c526dc363 100644 --- a/crates/jwk/src/der.rs +++ b/crates/jwk/src/der.rs @@ -13,70 +13,10 @@ // ISO/IEC 8825-1:2015 (E) // https://tools.ietf.org/html/rfc8017#page-55 // https://tools.ietf.org/html/rfc8410 +use num_bigint::BigInt; +use simple_asn1::{ASN1Block, ASN1Class, ASN1EncodeErr, ToASN1}; -use num_bigint::{BigInt, Sign}; -use simple_asn1::{ - der_encode, ASN1Block, ASN1Class, ASN1DecodeErr, ASN1EncodeErr, FromASN1, ToASN1, -}; - -/// RSA private key for ASN.1 encoding, as specified in [RFC 8017]. -/// -/// [RFC 8017]: https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.1.2 "RFC 8017 PKCS #1 v2.2 - A.1.2. RSA Private Key Syntax" -#[derive(Debug, Clone)] -pub struct RSAPrivateKey { - pub modulus: Integer, - pub public_exponent: Integer, - pub private_exponent: Integer, - pub prime1: Integer, - pub prime2: Integer, - pub exponent1: Integer, - pub exponent2: Integer, - pub coefficient: Integer, - pub other_prime_infos: Option, -} - -/// RSA public key for ASN.1 encoding, as specified in [RFC 8017]. -/// -/// [RFC 8017]: https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.1.1 "RFC 8017 PKCS #1 v2.2 - A.1.1. RSA Public Key Syntax" -#[derive(Debug, Clone)] -// https://datatracker.ietf.org/doc/html/rfc3447#appendix-A.1.1 -pub struct RSAPublicKey { - pub modulus: Integer, - pub public_exponent: Integer, -} - -/// Ed25519 public key for ASN.1 encoding, as specified in [RFC 8410]. -/// -/// [RFC 8410]: https://datatracker.ietf.org/doc/html/rfc8410#section-10.1 "RFC 8410 Safe Curves for X.509 - 10.1. Example Ed25519 Public Key" -#[derive(Debug, Clone)] -pub struct Ed25519PublicKey { - pub public_key: BitString, -} - -/// Ed25519 private key for ASN.1 encoding, as specified in [RFC 8410]. -/// -/// [RFC 8410]: https://datatracker.ietf.org/doc/html/rfc8410#section-10.3 "RFC 8410 Safe Curves for X.509 - 10.3. Examples of Ed25519 Private Key" -#[derive(Debug, Clone)] -pub struct Ed25519PrivateKey { - pub public_key: BitString, - pub private_key: OctetString, -} - -/// Additional primes in a [RSA private key][RSAPrivateKey], as specified in [RFC 8017]. -/// -/// [RFC 8017]: https://datatracker.ietf.org/doc/html/rfc8017#page-56 "RFC 8017 PKCS #1 v2.2 - Page 56" -#[derive(Debug, Clone)] -pub struct OtherPrimeInfos(pub Vec); - -/// Additional prime in a [RSA private key][RSAPrivateKey], as specified in [RFC 8017]. -/// -/// [RFC 8017]: https://datatracker.ietf.org/doc/html/rfc8017#page-56 "RFC 8017 PKCS #1 v2.2 - Page 56" -#[derive(Debug, Clone)] -pub struct OtherPrimeInfo { - pub prime: Integer, - pub exponent: Integer, - pub coefficient: Integer, -} +use crate::{Error, Params, JWK}; #[derive(Debug, Clone)] /// An integer value, for encoding in [ASN.1][ITU X.690] @@ -99,116 +39,6 @@ pub struct OctetString(pub Vec); /// [ITU X.690]: https://www.itu.int/rec/T-REC-X.690-202102-I/en pub struct BitString(pub Vec); -impl ToASN1 for RSAPrivateKey { - type Error = ASN1EncodeErr; - fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { - let multiprime = self.other_prime_infos.is_some(); - let version = Integer(BigInt::new(Sign::Plus, vec![u32::from(multiprime)])); - Ok(vec![ASN1Block::Sequence( - 0, - [ - version.to_asn1_class(class)?, - self.modulus.to_asn1_class(class)?, - self.public_exponent.to_asn1_class(class)?, - self.private_exponent.to_asn1_class(class)?, - self.prime1.to_asn1_class(class)?, - self.prime2.to_asn1_class(class)?, - self.exponent1.to_asn1_class(class)?, - self.exponent2.to_asn1_class(class)?, - self.coefficient.to_asn1_class(class)?, - match self.other_prime_infos { - Some(ref infos) => infos.to_asn1_class(class)?, - None => Vec::new(), - }, - ] - .concat(), - )]) - } -} - -impl ToASN1 for RSAPublicKey { - type Error = ASN1EncodeErr; - fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { - Ok(vec![ASN1Block::Sequence( - 0, - [ - self.modulus.to_asn1_class(class)?, - self.public_exponent.to_asn1_class(class)?, - ] - .concat(), - )]) - } -} - -#[derive(thiserror::Error, Debug)] -pub enum RSAPublicKeyFromASN1Error { - #[error("Expected single sequence")] - ExpectedSingleSequence, - #[error("Expected two integers")] - ExpectedTwoIntegers, - #[error("ASN1 decoding error: {0:?}")] - ASN1Decode(#[from] ASN1DecodeErr), -} - -impl FromASN1 for RSAPublicKey { - type Error = RSAPublicKeyFromASN1Error; - fn from_asn1(v: &[ASN1Block]) -> Result<(Self, &[ASN1Block]), Self::Error> { - let vec = match v { - [ASN1Block::Sequence(_, vec)] => vec, - _ => return Err(RSAPublicKeyFromASN1Error::ExpectedSingleSequence), - }; - let (n, e) = match vec.as_slice() { - [ASN1Block::Integer(_, n), ASN1Block::Integer(_, e)] => (n, e), - _ => return Err(RSAPublicKeyFromASN1Error::ExpectedTwoIntegers), - }; - let pk = Self { - modulus: Integer(n.clone()), - public_exponent: Integer(e.clone()), - }; - Ok((pk, &[])) - } -} - -impl Ed25519PrivateKey { - fn oid() -> ASN1Block { - use simple_asn1::BigUint; - // id-Ed25519 1.3.101.112 - let oid = simple_asn1::OID::new(vec![ - BigUint::new(vec![1]), - BigUint::new(vec![3]), - BigUint::new(vec![101]), - BigUint::new(vec![112]), - ]); - ASN1Block::Sequence(0, vec![ASN1Block::ObjectIdentifier(0, oid)]) - } -} - -impl ToASN1 for Ed25519PrivateKey { - type Error = ASN1EncodeErr; - fn to_asn1_class(&self, _class: ASN1Class) -> Result, Self::Error> { - let version = 0; - // TODO: include public key - Ok(vec![ASN1Block::Sequence( - 0, - vec![ - ASN1Block::Integer(0, BigInt::new(Sign::NoSign, vec![version])), - Ed25519PrivateKey::oid(), - ASN1Block::OctetString(0, der_encode(&self.private_key)?), - ], - )]) - } -} - -impl ToASN1 for Ed25519PublicKey { - type Error = ASN1EncodeErr; - fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { - Ok(vec![ASN1Block::Sequence( - 0, - self.public_key.to_asn1_class(class)?, - )]) - } -} - impl ToASN1 for Integer { type Error = ASN1EncodeErr; fn to_asn1_class(&self, _: ASN1Class) -> Result, Self::Error> { @@ -230,49 +60,27 @@ impl ToASN1 for BitString { } } -impl ToASN1 for OtherPrimeInfos { - type Error = ASN1EncodeErr; - fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { - Ok(vec![ASN1Block::Sequence( - 0, - self.0 - .iter() - .map(|x| x.to_asn1_class(class)) - .collect::>, ASN1EncodeErr>>()? - .concat(), - )]) - } -} +impl ToASN1 for JWK { + type Error = Error; -impl ToASN1 for OtherPrimeInfo { - type Error = ASN1EncodeErr; fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { - Ok(vec![ASN1Block::Sequence( - 0, - [ - self.prime.to_asn1_class(class)?, - self.exponent.to_asn1_class(class)?, - self.coefficient.to_asn1_class(class)?, - ] - .concat(), - )]) + match &self.params { + // EC(params) => params.to_asn1_class(class), + Params::RSA(params) => params.to_asn1_class(class), + // Symmetric(params) => params.to_asn1_class(class), + Params::OKP(params) => params.to_asn1_class(class), + _ => Err(Error::KeyTypeNotImplemented(Box::new(self.to_public()))), + } } } #[cfg(test)] mod tests { - use base64::Engine; + use num_bigint::Sign; + use simple_asn1::der_encode; use super::*; - #[test] - fn encode_oid() { - let expected = vec![0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x70]; - let asn1 = Ed25519PrivateKey::oid(); - let der = simple_asn1::to_der(&asn1).unwrap(); - assert_eq!(der, expected); - } - #[test] fn encode_integer() { let integer = Integer(BigInt::new(Sign::Plus, vec![5])); @@ -283,22 +91,4 @@ mod tests { let der = der_encode(&integer).unwrap(); assert_eq!(der, expected); } - - #[test] - fn encode_ed25519_private_key() { - let key = Ed25519PrivateKey { - public_key: BitString(vec![]), - private_key: OctetString(vec![ - 0xD4, 0xEE, 0x72, 0xDB, 0xF9, 0x13, 0x58, 0x4A, 0xD5, 0xB6, 0xD8, 0xF1, 0xF7, 0x69, - 0xF8, 0xAD, 0x3A, 0xFE, 0x7C, 0x28, 0xCB, 0xF1, 0xD4, 0xFB, 0xE0, 0x97, 0xA8, 0x8F, - 0x44, 0x75, 0x58, 0x42, - ]), - }; - let expected_b64 = "MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC"; - let expected_key = base64::prelude::BASE64_STANDARD - .decode(expected_b64) - .unwrap(); - let key_der = der_encode(&key).unwrap(); - assert_eq!(key_der, expected_key); - } } diff --git a/crates/jwk/src/error.rs b/crates/jwk/src/error.rs index 57c115925..8bfee6ea2 100644 --- a/crates/jwk/src/error.rs +++ b/crates/jwk/src/error.rs @@ -1,6 +1,6 @@ //! Error types for `ssi-jwk` crate #[cfg(feature = "aleo")] -use crate::aleo::AleoGeneratePrivateKeyError; +use crate::okp::aleo::AleoGeneratePrivateKeyError; use crate::JWK; use base64::DecodeError as Base64Error; #[cfg(feature = "ring")] diff --git a/crates/jwk/src/blakesig.rs b/crates/jwk/src/hash/blakesig.rs similarity index 96% rename from crates/jwk/src/blakesig.rs rename to crates/jwk/src/hash/blakesig.rs index 42e8319ac..43edb0d04 100644 --- a/crates/jwk/src/blakesig.rs +++ b/crates/jwk/src/hash/blakesig.rs @@ -31,11 +31,11 @@ pub fn hash_public_key(jwk: &JWK) -> Result { let curve = params.curve.as_ref().ok_or(Error::MissingCurve)?; match &curve[..] { "secp256k1" => { - bytes = crate::serialize_secp256k1(params)?; + bytes = crate::ec::k256::serialize_secp256k1(params)?; (&TZ2_HASH, &bytes) } "P-256" => { - bytes = crate::serialize_p256(params)?; + bytes = crate::ec::p256::serialize_p256(params)?; (&TZ3_HASH, &bytes) } _ => return Err(Error::CurveNotImplemented(curve.to_string())), diff --git a/crates/jwk/src/eip155.rs b/crates/jwk/src/hash/eip155.rs similarity index 100% rename from crates/jwk/src/eip155.rs rename to crates/jwk/src/hash/eip155.rs diff --git a/crates/jwk/src/hash/mod.rs b/crates/jwk/src/hash/mod.rs new file mode 100644 index 000000000..44c939d18 --- /dev/null +++ b/crates/jwk/src/hash/mod.rs @@ -0,0 +1,8 @@ +#[cfg(feature = "ripemd-160")] +pub mod ripemd160; + +#[cfg(feature = "eip")] +pub mod eip155; + +#[cfg(feature = "tezos")] +pub mod blakesig; diff --git a/crates/jwk/src/ripemd160.rs b/crates/jwk/src/hash/ripemd160.rs similarity index 100% rename from crates/jwk/src/ripemd160.rs rename to crates/jwk/src/hash/ripemd160.rs diff --git a/crates/jwk/src/lib.rs b/crates/jwk/src/lib.rs index f92380431..3068272bf 100644 --- a/crates/jwk/src/lib.rs +++ b/crates/jwk/src/lib.rs @@ -1,69 +1,52 @@ +// RFC 7516 - JSON Web Encryption (JWE) +// RFC 7517 - JSON Web Key (JWK) +// RFC 7518 - JSON Web Algorithms (JWA) +// RFC 7638 - JSON Web Key (JWK) Thumbprint +// RFC 8037 - CFRG ECDH and Signatures in JOSE +// RFC 8812 - CBOR Object Signing and Encryption (COSE) and JSON Object Signing and Encryption +// (JOSE) Registrations for Web Authentication (WebAuthn) Algorithms #![cfg_attr(docsrs, feature(doc_auto_cfg))] - -use base64::Engine; use core::fmt; -use num_bigint::{BigInt, Sign}; -use simple_asn1::{ASN1Block, ASN1Class, ToASN1}; +use serde::{Deserialize, Serialize}; use std::result::Result; use std::{convert::TryFrom, str::FromStr}; -use zeroize::Zeroize; + +mod utils; +pub use utils::*; + +mod r#type; +pub use r#type::*; pub mod error; pub use error::Error; +mod hash; +pub use hash::*; + pub mod algorithm; pub use algorithm::Algorithm; mod resolver; pub use resolver::*; -#[cfg(feature = "ripemd-160")] -pub mod ripemd160; - -#[cfg(feature = "aleo")] -pub mod aleo; - -#[cfg(feature = "eip")] -pub mod eip155; - -#[cfg(feature = "tezos")] -pub mod blakesig; - -#[cfg(feature = "bbs")] -mod bbs; -#[cfg(feature = "bbs")] -pub use bbs::*; - mod multicodec; pub use multicodec::*; -pub mod der; - -use der::{ - BitString, Ed25519PrivateKey, Ed25519PublicKey, Integer, OctetString, RSAPrivateKey, - RSAPublicKey, RSAPublicKeyFromASN1Error, -}; - -use serde::{Deserialize, Serialize}; +pub(crate) mod der; -// RFC 7516 - JSON Web Encryption (JWE) -// RFC 7517 - JSON Web Key (JWK) -// RFC 7518 - JSON Web Algorithms (JWA) -// RFC 7638 - JSON Web Key (JWK) Thumbprint -// RFC 8037 - CFRG ECDH and Signatures in JOSE -// RFC 8812 - CBOR Object Signing and Encryption (COSE) and JSON Object Signing and Encryption -// (JOSE) Registrations for Web Authentication (WebAuthn) Algorithms - -/// Deprecated -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct JWTKeys { - #[serde(rename = "es256kPrivateKeyJwk")] - #[serde(skip_serializing_if = "Option::is_none")] - pub es256k_private_key: Option, - #[serde(rename = "rs256PrivateKeyJwk")] - #[serde(skip_serializing_if = "Option::is_none")] - pub rs256_private_key: Option, -} +// Re-export legacy de/serialization functions. +#[cfg(feature = "rsa")] +pub use crate::rsa::rsa_x509_pub_parse; +#[cfg(feature = "bbs")] +pub use ec::bbs::bls12381g2_parse; +#[cfg(feature = "secp256k1")] +pub use ec::k256::{secp256k1_parse, secp256k1_parse_private, serialize_secp256k1}; +#[cfg(feature = "secp256r1")] +pub use ec::p256::{p256_parse, p256_parse_private, serialize_p256}; +#[cfg(feature = "secp384r1")] +pub use ec::p384::{p384_parse, p384_parse_private, serialize_p384}; +#[cfg(feature = "ed25519")] +pub use okp::curve25519::{ed25519_parse, ed25519_parse_private}; #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq)] pub struct JWK { @@ -127,250 +110,7 @@ impl fmt::Display for JWK { linked_data::json_literal!(JWK); -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Zeroize)] -#[serde(tag = "kty")] -pub enum Params { - EC(ECParams), - RSA(RSAParams), - #[serde(rename = "oct")] - Symmetric(SymmetricParams), - OKP(OctetParams), -} - -impl Drop for ECParams { - fn drop(&mut self) { - // Zeroize private key - if let Some(ref mut d) = self.ecc_private_key { - d.zeroize(); - } - } -} - -impl Drop for RSAParams { - fn drop(&mut self) { - // Zeroize private key fields - if let Some(ref mut d) = self.private_exponent { - d.zeroize(); - } - if let Some(ref mut p) = self.first_prime_factor { - p.zeroize(); - } - if let Some(ref mut q) = self.second_prime_factor { - q.zeroize(); - } - if let Some(ref mut dp) = self.first_prime_factor_crt_exponent { - dp.zeroize(); - } - if let Some(ref mut dq) = self.second_prime_factor_crt_exponent { - dq.zeroize(); - } - if let Some(ref mut qi) = self.first_crt_coefficient { - qi.zeroize(); - } - if let Some(ref mut primes) = self.other_primes_info { - for prime in primes { - prime.zeroize(); - } - } - } -} - -impl Drop for SymmetricParams { - fn drop(&mut self) { - // Zeroize private/symmetric key - if let Some(ref mut k) = self.key_value { - k.zeroize(); - } - } -} - -impl Drop for OctetParams { - fn drop(&mut self) { - // Zeroize private key - if let Some(ref mut d) = self.private_key { - d.zeroize(); - } - } -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Zeroize)] -pub struct ECParams { - // Parameters for Elliptic Curve Public Keys - #[serde(rename = "crv")] - pub curve: Option, - #[serde(rename = "x")] - pub x_coordinate: Option, - #[serde(rename = "y")] - pub y_coordinate: Option, - - // Parameters for Elliptic Curve Private Keys - #[serde(rename = "d")] - #[serde(skip_serializing_if = "Option::is_none")] - pub ecc_private_key: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default, Hash, Eq, Zeroize)] -pub struct RSAParams { - // Parameters for RSA Public Keys - #[serde(rename = "n")] - pub modulus: Option, - #[serde(rename = "e")] - pub exponent: Option, - - // Parameters for RSA Private Keys - #[serde(rename = "d")] - #[serde(skip_serializing_if = "Option::is_none")] - pub private_exponent: Option, - #[serde(rename = "p")] - #[serde(skip_serializing_if = "Option::is_none")] - pub first_prime_factor: Option, - #[serde(rename = "q")] - #[serde(skip_serializing_if = "Option::is_none")] - pub second_prime_factor: Option, - #[serde(rename = "dp")] - #[serde(skip_serializing_if = "Option::is_none")] - pub first_prime_factor_crt_exponent: Option, - #[serde(rename = "dq")] - #[serde(skip_serializing_if = "Option::is_none")] - pub second_prime_factor_crt_exponent: Option, - #[serde(rename = "qi")] - #[serde(skip_serializing_if = "Option::is_none")] - pub first_crt_coefficient: Option, - #[serde(rename = "oth")] - #[serde(skip_serializing_if = "Option::is_none")] - pub other_primes_info: Option>, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Zeroize)] -pub struct SymmetricParams { - // Parameters for Symmetric Keys - #[serde(rename = "k")] - pub key_value: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Zeroize)] -pub struct OctetParams { - // Parameters for Octet Key Pair Public Keys - #[serde(rename = "crv")] - pub curve: String, - #[serde(rename = "x")] - pub public_key: Base64urlUInt, - - // Parameters for Octet Key Pair Private Keys - #[serde(rename = "d")] - #[serde(skip_serializing_if = "Option::is_none")] - pub private_key: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Zeroize)] -pub struct Prime { - #[serde(rename = "r")] - pub prime_factor: Base64urlUInt, - #[serde(rename = "d")] - pub factor_crt_exponent: Base64urlUInt, - #[serde(rename = "t")] - pub factor_crt_coefficient: Base64urlUInt, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Zeroize)] -#[serde(try_from = "String")] -#[serde(into = "Base64urlUIntString")] -pub struct Base64urlUInt(pub Vec); -type Base64urlUIntString = String; - impl JWK { - #[cfg(feature = "ed25519")] - pub fn generate_ed25519() -> Result { - #[cfg(feature = "ring")] - { - let rng = ring::rand::SystemRandom::new(); - let mut key_pkcs8 = ring::signature::Ed25519KeyPair::generate_pkcs8(&rng)? - .as_ref() - .to_vec(); - // reference: ring/src/ec/curve25519/ed25519/signing.rs - let private_key = key_pkcs8[0x10..0x30].to_vec(); - let public_key = key_pkcs8[0x35..0x55].to_vec(); - key_pkcs8.zeroize(); - Ok(JWK::from(Params::OKP(OctetParams { - curve: "Ed25519".to_string(), - public_key: Base64urlUInt(public_key), - private_key: Some(Base64urlUInt(private_key)), - }))) - } - #[cfg(not(feature = "ring"))] - { - let mut csprng = rand::rngs::OsRng {}; - let secret = ed25519_dalek::SigningKey::generate(&mut csprng); - let public = secret.verifying_key(); - Ok(JWK::from(Params::OKP(OctetParams { - curve: "Ed25519".to_string(), - public_key: Base64urlUInt(public.as_ref().to_vec()), - private_key: Some(Base64urlUInt(secret.to_bytes().to_vec())), - }))) - } - } - - #[cfg(feature = "ed25519")] - pub fn generate_ed25519_from( - rng: &mut (impl rand::CryptoRng + rand::RngCore), - ) -> Result { - let secret = ed25519_dalek::SigningKey::generate(rng); - let public = secret.verifying_key(); - Ok(JWK::from(Params::OKP(OctetParams { - curve: "Ed25519".to_string(), - public_key: Base64urlUInt(public.as_ref().to_vec()), - private_key: Some(Base64urlUInt(secret.to_bytes().to_vec())), - }))) - } - - #[cfg(feature = "secp256k1")] - pub fn generate_secp256k1() -> JWK { - let mut rng = rand::rngs::OsRng {}; - Self::generate_secp256k1_from(&mut rng) - } - - #[cfg(feature = "secp256k1")] - pub fn generate_secp256k1_from(rng: &mut (impl rand::CryptoRng + rand::RngCore)) -> JWK { - let secret_key = k256::SecretKey::random(rng); - let sk_bytes = zeroize::Zeroizing::new(secret_key.to_bytes().to_vec()); - let public_key = secret_key.public_key(); - let mut ec_params = ECParams::from(&public_key); - ec_params.ecc_private_key = Some(Base64urlUInt(sk_bytes.to_vec())); - JWK::from(Params::EC(ec_params)) - } - - #[cfg(feature = "secp256r1")] - pub fn generate_p256() -> JWK { - let mut rng = rand::rngs::OsRng {}; - Self::generate_p256_from(&mut rng) - } - - #[cfg(feature = "secp256r1")] - pub fn generate_p256_from(rng: &mut (impl rand::CryptoRng + rand::RngCore)) -> JWK { - let secret_key = p256::SecretKey::random(rng); - let sk_bytes = zeroize::Zeroizing::new(secret_key.to_bytes().to_vec()); - let public_key: p256::PublicKey = secret_key.public_key(); - let mut ec_params = ECParams::from(&public_key); - ec_params.ecc_private_key = Some(Base64urlUInt(sk_bytes.to_vec())); - JWK::from(Params::EC(ec_params)) - } - - #[cfg(feature = "secp384r1")] - pub fn generate_p384() -> JWK { - let mut rng = rand::rngs::OsRng {}; - let secret_key = p384::SecretKey::random(&mut rng); - let sk_bytes = zeroize::Zeroizing::new(secret_key.to_bytes().to_vec()); - let public_key: p384::PublicKey = secret_key.public_key(); - let mut ec_params = ECParams::from(&public_key); - ec_params.ecc_private_key = Some(Base64urlUInt(sk_bytes.to_vec())); - JWK::from(Params::EC(ec_params)) - } - - #[cfg(feature = "aleo")] - pub fn generate_aleo() -> Result { - crate::aleo::generate_private_key_jwk().map_err(Error::AleoGeneratePrivateKey) - } - pub fn get_algorithm(&self) -> Option { if let Some(algorithm) = self.algorithm { return Some(algorithm); @@ -383,7 +123,7 @@ impl JWK { return Some(Algorithm::EdDSA); } #[cfg(feature = "aleo")] - Params::OKP(okp_params) if okp_params.curve == crate::aleo::OKP_CURVE => { + Params::OKP(okp_params) if okp_params.curve == crate::okp::aleo::OKP_CURVE => { return Some(Algorithm::AleoTestnet1Signature); } Params::EC(ec_params) => { @@ -512,833 +252,12 @@ impl JWK { } } -impl From for JWK { - fn from(params: Params) -> Self { - Self { - params, - public_key_use: None, - key_operations: None, - algorithm: None, - key_id: None, - x509_url: None, - x509_certificate_chain: None, - x509_thumbprint_sha1: None, - x509_thumbprint_sha256: None, - } - } -} - -impl ToASN1 for JWK { - type Error = Error; - fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { - match &self.params { - // EC(params) => params.to_asn1_class(class), - Params::RSA(params) => params.to_asn1_class(class), - // Symmetric(params) => params.to_asn1_class(class), - Params::OKP(params) => params.to_asn1_class(class), - _ => Err(Error::KeyTypeNotImplemented(Box::new(self.to_public()))), - } - } -} - -impl Params { - pub fn is_public(&self) -> bool { - match self { - Self::EC(params) => params.is_public(), - Self::RSA(params) => params.is_public(), - Self::Symmetric(params) => params.is_public(), - Self::OKP(params) => params.is_public(), - } - } - - /// Strip private key material - pub fn to_public(&self) -> Self { - match self { - Self::EC(params) => Self::EC(params.to_public()), - Self::RSA(params) => Self::RSA(params.to_public()), - Self::Symmetric(params) => Self::Symmetric(params.to_public()), - Self::OKP(params) => Self::OKP(params.to_public()), - } - } -} - -impl ECParams { - pub fn is_public(&self) -> bool { - self.ecc_private_key.is_none() - } - - /// Strip private key material - pub fn to_public(&self) -> Self { - Self { - curve: self.curve.clone(), - x_coordinate: self.x_coordinate.clone(), - y_coordinate: self.y_coordinate.clone(), - ecc_private_key: None, - } - } -} - -impl RSAParams { - pub fn is_public(&self) -> bool { - self.private_exponent.is_none() - && self.first_prime_factor.is_none() - && self.second_prime_factor.is_none() - && self.first_prime_factor_crt_exponent.is_none() - && self.second_prime_factor_crt_exponent.is_none() - && self.first_crt_coefficient.is_none() - && self.other_primes_info.is_none() - } - - /// Strip private key material - pub fn to_public(&self) -> Self { - Self { - modulus: self.modulus.clone(), - exponent: self.exponent.clone(), - private_exponent: None, - first_prime_factor: None, - second_prime_factor: None, - first_prime_factor_crt_exponent: None, - second_prime_factor_crt_exponent: None, - first_crt_coefficient: None, - other_primes_info: None, - } - } - - /// Construct a RSA public key - pub fn new_public(exponent: &[u8], modulus: &[u8]) -> Self { - Self { - modulus: Some(Base64urlUInt(modulus.to_vec())), - exponent: Some(Base64urlUInt(exponent.to_vec())), - private_exponent: None, - first_prime_factor: None, - second_prime_factor: None, - first_prime_factor_crt_exponent: None, - second_prime_factor_crt_exponent: None, - first_crt_coefficient: None, - other_primes_info: None, - } - } - - /// Validate key size is at least 2048 bits, per [RFC 7518 section 3.3](https://www.rfc-editor.org/rfc/rfc7518#section-3.3). - pub fn validate_key_size(&self) -> Result<(), Error> { - let n = &self.modulus.as_ref().ok_or(Error::MissingModulus)?.0; - if n.len() < 256 { - return Err(Error::InvalidKeyLength(n.len())); - } - Ok(()) - } -} - -impl ToASN1 for RSAParams { - type Error = Error; - fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { - let modulus = match &self.modulus { - Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), - None => return Err(Error::MissingModulus), - }; - let public_exponent = match &self.exponent { - Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), - None => return Err(Error::MissingExponent), - }; - if let Some(ref private_exponent) = self.private_exponent { - let key = RSAPrivateKey { - modulus, - public_exponent, - private_exponent: Integer(BigInt::from_bytes_be(Sign::Plus, &private_exponent.0)), - prime1: match &self.first_prime_factor { - Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), - None => Integer(BigInt::new(Sign::NoSign, vec![])), - }, - prime2: match &self.second_prime_factor { - Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), - None => Integer(BigInt::new(Sign::NoSign, vec![])), - }, - exponent1: match &self.first_prime_factor_crt_exponent { - Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), - None => Integer(BigInt::new(Sign::NoSign, vec![])), - }, - exponent2: match &self.second_prime_factor_crt_exponent { - Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), - None => Integer(BigInt::new(Sign::NoSign, vec![])), - }, - coefficient: match &self.first_crt_coefficient { - Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), - None => Integer(BigInt::new(Sign::NoSign, vec![0])), - }, - other_prime_infos: None, - }; - Ok(key.to_asn1_class(class)?) - } else { - let key = RSAPublicKey { - modulus, - public_exponent, - }; - Ok(key.to_asn1_class(class)?) - } - } -} - -impl SymmetricParams { - pub fn is_public(&self) -> bool { - self.key_value.is_none() - } - - /// Strip private key material - pub fn to_public(&self) -> Self { - Self { key_value: None } - } -} - -impl OctetParams { - pub fn is_public(&self) -> bool { - self.private_key.is_none() - } - - /// Strip private key material - pub fn to_public(&self) -> Self { - Self { - curve: self.curve.clone(), - public_key: self.public_key.clone(), - private_key: None, - } - } -} - -impl ToASN1 for OctetParams { - type Error = Error; - fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { - if self.curve != *"Ed25519" { - return Err(Error::CurveNotImplemented(self.curve.to_string())); - } - let public_key = BitString(self.public_key.0.clone()); - if let Some(private_key) = self - .private_key - .as_ref() - .map(|private_key| OctetString(private_key.0.clone())) - { - let key = Ed25519PrivateKey { - public_key, - private_key, - }; - Ok(key.to_asn1_class(class)?) - } else { - let key = Ed25519PublicKey { public_key }; - Ok(key.to_asn1_class(class)?) - } - } -} - -#[cfg(feature = "rsa")] -impl From<&Base64urlUInt> for rsa::BigUint { - fn from(uint: &Base64urlUInt) -> Self { - Self::from_bytes_be(&uint.0) - } -} - -#[cfg(feature = "rsa")] -impl TryFrom<&RSAParams> for rsa::RsaPublicKey { - type Error = Error; - fn try_from(params: &RSAParams) -> Result { - let n = params.modulus.as_ref().ok_or(Error::MissingModulus)?; - let e = params.exponent.as_ref().ok_or(Error::MissingExponent)?; - Ok(Self::new(n.into(), e.into())?) - } -} - -#[cfg(feature = "rsa")] -impl TryFrom<&RSAParams> for rsa::RsaPrivateKey { - type Error = Error; - #[allow(clippy::many_single_char_names)] - fn try_from(params: &RSAParams) -> Result { - let n = params.modulus.as_ref().ok_or(Error::MissingModulus)?; - let e = params.exponent.as_ref().ok_or(Error::MissingExponent)?; - let d = params - .private_exponent - .as_ref() - .ok_or(Error::MissingExponent)?; - let p = params - .first_prime_factor - .as_ref() - .ok_or(Error::MissingPrime)?; - let q = params - .second_prime_factor - .as_ref() - .ok_or(Error::MissingPrime)?; - let mut primes = vec![p.into(), q.into()]; - for prime in params.other_primes_info.iter().flatten() { - primes.push((&prime.prime_factor).into()); - } - Ok(Self::from_components(n.into(), e.into(), d.into(), primes)) - } -} - -#[cfg(feature = "ring")] -impl<'a> TryFrom<&'a RSAParams> for ring::signature::RsaPublicKeyComponents<&'a [u8]> { - type Error = Error; - fn try_from(params: &'a RSAParams) -> Result { - fn trim_bytes(bytes: &[u8]) -> &[u8] { - const ZERO: [u8; 1] = [0]; - // Remove leading zeros - match bytes.iter().position(|&x| x != 0) { - Some(n) => &bytes[n..], - None => &ZERO, - } - } - let n = trim_bytes(¶ms.modulus.as_ref().ok_or(Error::MissingModulus)?.0); - let e = trim_bytes(¶ms.exponent.as_ref().ok_or(Error::MissingExponent)?.0); - Ok(Self { n, e }) - } -} - -#[cfg(feature = "ring")] -impl TryFrom<&RSAParams> for ring::signature::RsaKeyPair { - type Error = Error; - fn try_from(params: &RSAParams) -> Result { - let der = simple_asn1::der_encode(params)?; - let keypair = Self::from_der(&der)?; - Ok(keypair) - } -} - -#[cfg(feature = "ed25519")] -impl TryFrom<&OctetParams> for ed25519_dalek::VerifyingKey { - type Error = Error; - fn try_from(params: &OctetParams) -> Result { - if params.curve != *"Ed25519" { - return Err(Error::CurveNotImplemented(params.curve.to_string())); - } - Ok(params.public_key.0.as_slice().as_ref().try_into()?) - } -} - -#[cfg(feature = "ed25519")] -impl TryFrom<&OctetParams> for ed25519_dalek::SigningKey { - type Error = Error; - fn try_from(params: &OctetParams) -> Result { - if params.curve != *"Ed25519" { - return Err(Error::CurveNotImplemented(params.curve.to_string())); - } - let private_key = params - .private_key - .as_ref() - .ok_or(Error::MissingPrivateKey)?; - Ok(private_key.0.as_slice().as_ref().try_into()?) - } -} - -#[cfg(feature = "ring")] -impl TryFrom<&OctetParams> for &ring::signature::EdDSAParameters { - type Error = Error; - fn try_from(params: &OctetParams) -> Result { - if params.curve != *"Ed25519" { - return Err(Error::CurveNotImplemented(params.curve.to_string())); - } - Ok(&ring::signature::ED25519) - } -} - -#[cfg(feature = "ring")] -impl TryFrom<&OctetParams> for ring::signature::Ed25519KeyPair { - type Error = Error; - fn try_from(params: &OctetParams) -> Result { - if params.curve != *"Ed25519" { - return Err(Error::CurveNotImplemented(params.curve.to_string())); - } - params - .private_key - .as_ref() - .ok_or(Error::MissingPrivateKey)?; - let der = simple_asn1::der_encode(params)?; - let keypair = Self::from_pkcs8_maybe_unchecked(&der)?; - Ok(keypair) - } -} - -#[cfg(feature = "ed25519")] -pub fn ed25519_parse(data: &[u8]) -> Result { - let public_key = ed25519_dalek::VerifyingKey::try_from(data)?; - Ok(public_key.into()) -} - -#[cfg(feature = "ed25519")] -impl From for JWK { - fn from(value: ed25519_dalek::VerifyingKey) -> Self { - JWK::from(Params::OKP(OctetParams { - curve: "Ed25519".to_string(), - public_key: Base64urlUInt(value.to_bytes().to_vec()), - private_key: None, - })) - } -} - -#[cfg(feature = "ed25519")] -fn ed25519_parse_private(data: &[u8]) -> Result { - let key: ed25519_dalek::SigningKey = data.try_into()?; - Ok(JWK::from(Params::OKP(OctetParams { - curve: "Ed25519".to_string(), - public_key: Base64urlUInt(ed25519_dalek::VerifyingKey::from(&key).as_bytes().to_vec()), - private_key: Some(Base64urlUInt(data.to_owned())), - }))) -} - -#[cfg(feature = "secp256k1")] -pub fn secp256k1_parse(data: &[u8]) -> Result { - let pk = k256::PublicKey::from_sec1_bytes(data)?; - Ok(pk.into()) -} - -#[cfg(feature = "secp256k1")] -impl From for JWK { - fn from(value: k256::PublicKey) -> Self { - JWK { - params: Params::EC(ECParams::from(&value)), - public_key_use: None, - key_operations: None, - algorithm: None, - key_id: None, - x509_url: None, - x509_certificate_chain: None, - x509_thumbprint_sha1: None, - x509_thumbprint_sha256: None, - } - } -} - -#[cfg(feature = "secp256k1")] -pub fn secp256k1_parse_private(data: &[u8]) -> Result { - let k = k256::SecretKey::from_sec1_der(data)?; - let jwk = JWK { - params: Params::EC(ECParams::from(&k)), - public_key_use: None, - key_operations: None, - algorithm: None, - key_id: None, - x509_url: None, - x509_certificate_chain: None, - x509_thumbprint_sha1: None, - x509_thumbprint_sha256: None, - }; - Ok(jwk) -} - -#[cfg(feature = "secp256r1")] -pub fn p256_parse(pk_bytes: &[u8]) -> Result { - let pk = p256::PublicKey::from_sec1_bytes(pk_bytes)?; - Ok(pk.into()) -} - -#[cfg(feature = "secp256r1")] -impl From for JWK { - fn from(value: p256::PublicKey) -> Self { - JWK { - params: Params::EC(ECParams::from(&value)), - public_key_use: None, - key_operations: None, - algorithm: None, - key_id: None, - x509_url: None, - x509_certificate_chain: None, - x509_thumbprint_sha1: None, - x509_thumbprint_sha256: None, - } - } -} - -#[cfg(feature = "secp256r1")] -fn p256_parse_private(data: &[u8]) -> Result { - let k = p256::SecretKey::from_bytes(data.into())?; - let jwk = JWK { - params: Params::EC(ECParams::from(&k)), - public_key_use: None, - key_operations: None, - algorithm: None, - key_id: None, - x509_url: None, - x509_certificate_chain: None, - x509_thumbprint_sha1: None, - x509_thumbprint_sha256: None, - }; - Ok(jwk) -} - -#[cfg(feature = "secp384r1")] -pub fn p384_parse(pk_bytes: &[u8]) -> Result { - let pk = p384::PublicKey::from_sec1_bytes(pk_bytes)?; - let jwk = JWK { - params: Params::EC(ECParams::from(&pk)), - public_key_use: None, - key_operations: None, - algorithm: None, - key_id: None, - x509_url: None, - x509_certificate_chain: None, - x509_thumbprint_sha1: None, - x509_thumbprint_sha256: None, - }; - Ok(jwk) -} - -#[cfg(feature = "secp384r1")] -impl From for JWK { - fn from(value: p384::PublicKey) -> Self { - JWK { - params: Params::EC(ECParams::from(&value)), - public_key_use: None, - key_operations: None, - algorithm: None, - key_id: None, - x509_url: None, - x509_certificate_chain: None, - x509_thumbprint_sha1: None, - x509_thumbprint_sha256: None, - } - } -} - -#[cfg(feature = "secp384r1")] -fn p384_parse_private(data: &[u8]) -> Result { - let k = p384::SecretKey::from_bytes(data.into())?; - let jwk = JWK { - params: Params::EC(ECParams::from(&k)), - public_key_use: None, - key_operations: None, - algorithm: None, - key_id: None, - x509_url: None, - x509_certificate_chain: None, - x509_thumbprint_sha1: None, - x509_thumbprint_sha256: None, - }; - Ok(jwk) -} - -/// Serialize a secp256k1 public key as a 33-byte string with point compression. -#[cfg(feature = "secp256k1")] -pub fn serialize_secp256k1(params: &ECParams) -> Result, Error> { - use k256::elliptic_curve::sec1::ToEncodedPoint; - let pk = k256::PublicKey::try_from(params)?; - let pk_compressed_bytes = pk.to_encoded_point(true); - Ok(pk_compressed_bytes.as_bytes().to_vec()) -} - -/// Serialize a P-256 public key as a 33-byte string with point compression. -#[cfg(feature = "secp256r1")] -pub fn serialize_p256(params: &ECParams) -> Result, Error> { - // TODO: check that curve is P-256 - use p256::elliptic_curve::{sec1::EncodedPoint, FieldBytes}; - - let x = FieldBytes::::from_slice( - ¶ms.x_coordinate.as_ref().ok_or(Error::MissingPoint)?.0, - ); - let y = FieldBytes::::from_slice( - ¶ms.y_coordinate.as_ref().ok_or(Error::MissingPoint)?.0, - ); - let encoded_point = EncodedPoint::::from_affine_coordinates(x, y, true); - let pk_compressed_bytes = encoded_point.to_bytes(); - Ok(pk_compressed_bytes.to_vec()) -} - -/// Serialize a P-384 public key as a 49-byte string with point compression. -#[cfg(feature = "secp384r1")] -pub fn serialize_p384(params: &ECParams) -> Result, Error> { - // TODO: check that curve is P-384 - use p384::elliptic_curve::{sec1::EncodedPoint, FieldBytes}; - let x = FieldBytes::::from_slice( - ¶ms.x_coordinate.as_ref().ok_or(Error::MissingPoint)?.0, - ); - let y = FieldBytes::::from_slice( - ¶ms.y_coordinate.as_ref().ok_or(Error::MissingPoint)?.0, - ); - let encoded_point = EncodedPoint::::from_affine_coordinates(x, y, true); - let pk_compressed_bytes = encoded_point.to_bytes(); - Ok(pk_compressed_bytes.to_vec()) -} - -#[derive(thiserror::Error, Debug)] -pub enum RSAParamsFromPublicKeyError { - #[error("RSA Public Key from ASN1 error: {0:?}")] - RSAPublicKeyFromASN1(RSAPublicKeyFromASN1Error), - #[error("Expected positive integer in RSA key")] - ExpectedPlus, -} - -impl TryFrom<&RSAPublicKey> for RSAParams { - type Error = RSAParamsFromPublicKeyError; - fn try_from(pk: &RSAPublicKey) -> Result { - let (sign, n) = pk.modulus.0.to_bytes_be(); - if sign != Sign::Plus { - return Err(RSAParamsFromPublicKeyError::ExpectedPlus); - } - let (sign, e) = pk.public_exponent.0.to_bytes_be(); - if sign != Sign::Plus { - return Err(RSAParamsFromPublicKeyError::ExpectedPlus); - } - Ok(RSAParams { - modulus: Some(Base64urlUInt(n)), - exponent: Some(Base64urlUInt(e)), - private_exponent: None, - first_prime_factor: None, - second_prime_factor: None, - first_prime_factor_crt_exponent: None, - second_prime_factor_crt_exponent: None, - first_crt_coefficient: None, - other_primes_info: None, - }) - } -} - -#[derive(thiserror::Error, Debug)] -pub enum RsaX509PubParseError { - #[error("RSAPublicKey from ASN1: {0:?}")] - RSAPublicKeyFromASN1(#[from] RSAPublicKeyFromASN1Error), - #[error("RSA JWK params from RSAPublicKey: {0:?}")] - RSAParamsFromPublicKey(#[from] RSAParamsFromPublicKeyError), -} - -/// Parse a "RSA public key (X.509 encoded)" (multicodec) into a JWK. -pub fn rsa_x509_pub_parse(pk_bytes: &[u8]) -> Result { - let rsa_pk: RSAPublicKey = simple_asn1::der_decode(pk_bytes)?; - let rsa_params = RSAParams::try_from(&rsa_pk)?; - Ok(JWK::from(Params::RSA(rsa_params))) -} - -#[cfg(feature = "secp256k1")] -impl TryFrom<&ECParams> for k256::SecretKey { - type Error = Error; - fn try_from(params: &ECParams) -> Result { - let curve = params.curve.as_ref().ok_or(Error::MissingCurve)?; - if curve != "secp256k1" { - return Err(Error::CurveNotImplemented(curve.to_string())); - } - let private_key = params - .ecc_private_key - .as_ref() - .ok_or(Error::MissingPrivateKey)?; - let secret_key = k256::SecretKey::from_bytes(private_key.0.as_slice().into())?; - Ok(secret_key) - } -} - -#[cfg(feature = "secp256r1")] -impl TryFrom<&ECParams> for p256::SecretKey { - type Error = Error; - fn try_from(params: &ECParams) -> Result { - let curve = params.curve.as_ref().ok_or(Error::MissingCurve)?; - if curve != "P-256" { - return Err(Error::CurveNotImplemented(curve.to_string())); - } - let private_key = params - .ecc_private_key - .as_ref() - .ok_or(Error::MissingPrivateKey)?; - let secret_key = p256::SecretKey::from_bytes(private_key.0.as_slice().into())?; - Ok(secret_key) - } -} - -#[cfg(feature = "secp384r1")] -impl TryFrom<&ECParams> for p384::SecretKey { - type Error = Error; - fn try_from(params: &ECParams) -> Result { - let curve = params.curve.as_ref().ok_or(Error::MissingCurve)?; - if curve != "P-384" { - return Err(Error::CurveNotImplemented(curve.to_string())); - } - let private_key = params - .ecc_private_key - .as_ref() - .ok_or(Error::MissingPrivateKey)?; - let secret_key = p384::SecretKey::from_bytes(private_key.0.as_slice().into())?; - Ok(secret_key) - } -} - -#[cfg(feature = "secp256k1")] -impl TryFrom<&ECParams> for k256::PublicKey { - type Error = Error; - fn try_from(params: &ECParams) -> Result { - let curve = params.curve.as_ref().ok_or(Error::MissingCurve)?; - if curve != "secp256k1" { - return Err(Error::CurveNotImplemented(curve.to_string())); - } - const EC_UNCOMPRESSED_POINT_TAG: &[u8] = &[0x04]; - let x = ¶ms.x_coordinate.as_ref().ok_or(Error::MissingPoint)?.0; - let y = ¶ms.y_coordinate.as_ref().ok_or(Error::MissingPoint)?.0; - let pk_data = [EC_UNCOMPRESSED_POINT_TAG, x.as_slice(), y.as_slice()].concat(); - let public_key = k256::PublicKey::from_sec1_bytes(&pk_data)?; - Ok(public_key) - } -} - -#[cfg(feature = "secp256r1")] -impl TryFrom<&ECParams> for p256::PublicKey { - type Error = Error; - fn try_from(params: &ECParams) -> Result { - let curve = params.curve.as_ref().ok_or(Error::MissingCurve)?; - if curve != "P-256" { - return Err(Error::CurveNotImplemented(curve.to_string())); - } - const EC_UNCOMPRESSED_POINT_TAG: &[u8] = &[0x04]; - let x = ¶ms.x_coordinate.as_ref().ok_or(Error::MissingPoint)?.0; - let y = ¶ms.y_coordinate.as_ref().ok_or(Error::MissingPoint)?.0; - let pk_data = [EC_UNCOMPRESSED_POINT_TAG, x.as_slice(), y.as_slice()].concat(); - let public_key = p256::PublicKey::from_sec1_bytes(&pk_data)?; - Ok(public_key) - } -} - -#[cfg(feature = "secp384r1")] -impl TryFrom<&ECParams> for p384::PublicKey { - type Error = Error; - fn try_from(params: &ECParams) -> Result { - let curve = params.curve.as_ref().ok_or(Error::MissingCurve)?; - if curve != "P-384" { - return Err(Error::CurveNotImplemented(curve.to_string())); - } - const EC_UNCOMPRESSED_POINT_TAG: &[u8] = &[0x04]; - let x = ¶ms.x_coordinate.as_ref().ok_or(Error::MissingPoint)?.0; - let y = ¶ms.y_coordinate.as_ref().ok_or(Error::MissingPoint)?.0; - let pk_data = [EC_UNCOMPRESSED_POINT_TAG, x.as_slice(), y.as_slice()].concat(); - let public_key = p384::PublicKey::from_sec1_bytes(&pk_data)?; - Ok(public_key) - } -} - -#[cfg(feature = "secp256k1")] -impl From<&k256::PublicKey> for ECParams { - fn from(pk: &k256::PublicKey) -> Self { - use k256::elliptic_curve::sec1::ToEncodedPoint; - let ec_points = pk.to_encoded_point(false); - ECParams { - // TODO according to https://tools.ietf.org/id/draft-jones-webauthn-secp256k1-00.html#rfc.section.2 it should be P-256K? - curve: Some("secp256k1".to_string()), - x_coordinate: ec_points.x().map(|x| Base64urlUInt(x.to_vec())), - y_coordinate: ec_points.y().map(|y| Base64urlUInt(y.to_vec())), - ecc_private_key: None, - } - } -} - -#[cfg(feature = "secp256k1")] -impl From<&k256::SecretKey> for ECParams { - fn from(k: &k256::SecretKey) -> Self { - let pk = k.public_key(); - use k256::elliptic_curve::sec1::ToEncodedPoint; - let ec_points = pk.to_encoded_point(false); - ECParams { - // TODO according to https://tools.ietf.org/id/draft-jones-webauthn-secp256k1-00.html#rfc.section.2 it should be P-256K? - curve: Some("secp256k1".to_string()), - x_coordinate: ec_points.x().map(|x| Base64urlUInt(x.to_vec())), - y_coordinate: ec_points.y().map(|y| Base64urlUInt(y.to_vec())), - ecc_private_key: Some(Base64urlUInt(k.to_bytes().to_vec())), - } - } -} - -#[cfg(feature = "secp256r1")] -impl From<&p256::PublicKey> for ECParams { - fn from(pk: &p256::PublicKey) -> Self { - use p256::elliptic_curve::sec1::ToEncodedPoint; - let encoded_point = pk.to_encoded_point(false); - ECParams { - curve: Some("P-256".to_string()), - x_coordinate: encoded_point.x().map(|x| Base64urlUInt(x.to_vec())), - y_coordinate: encoded_point.y().map(|y| Base64urlUInt(y.to_vec())), - ecc_private_key: None, - } - } -} - -#[cfg(feature = "secp256r1")] -impl From<&p256::SecretKey> for ECParams { - fn from(k: &p256::SecretKey) -> Self { - let pk = k.public_key(); - use p256::elliptic_curve::sec1::ToEncodedPoint; - let encoded_point = pk.to_encoded_point(false); - Self { - curve: Some("P-256".to_string()), - x_coordinate: encoded_point.x().map(|x| Base64urlUInt(x.to_vec())), - y_coordinate: encoded_point.y().map(|y| Base64urlUInt(y.to_vec())), - ecc_private_key: Some(Base64urlUInt(k.to_bytes().to_vec())), - } - } -} - -#[cfg(feature = "secp384r1")] -impl From<&p384::PublicKey> for ECParams { - fn from(pk: &p384::PublicKey) -> Self { - use p384::elliptic_curve::sec1::ToEncodedPoint; - let encoded_point = pk.to_encoded_point(false); - ECParams { - curve: Some("P-384".to_string()), - x_coordinate: encoded_point.x().map(|x| Base64urlUInt(x.to_vec())), - y_coordinate: encoded_point.y().map(|y| Base64urlUInt(y.to_vec())), - ecc_private_key: None, - } - } -} - -#[cfg(feature = "secp384r1")] -impl From<&p384::SecretKey> for ECParams { - fn from(k: &p384::SecretKey) -> Self { - let pk = k.public_key(); - use p384::elliptic_curve::sec1::ToEncodedPoint; - let encoded_point = pk.to_encoded_point(false); - ECParams { - curve: Some("P-384".to_string()), - x_coordinate: encoded_point.x().map(|x| Base64urlUInt(x.to_vec())), - y_coordinate: encoded_point.y().map(|y| Base64urlUInt(y.to_vec())), - ecc_private_key: Some(Base64urlUInt(k.to_bytes().to_vec())), - } - } -} - -const BASE64_URL_SAFE_INDIFFERENT_PAD: base64::engine::GeneralPurpose = - base64::engine::GeneralPurpose::new( - &base64::alphabet::URL_SAFE, - base64::engine::GeneralPurposeConfig::new() - .with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent), - ); - -impl TryFrom for Base64urlUInt { - type Error = base64::DecodeError; - fn try_from(data: String) -> Result { - Ok(Base64urlUInt(BASE64_URL_SAFE_INDIFFERENT_PAD.decode(data)?)) - } -} - -impl From<&Base64urlUInt> for String { - fn from(data: &Base64urlUInt) -> String { - base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(&data.0) - } -} - -impl From for Base64urlUIntString { - fn from(data: Base64urlUInt) -> Base64urlUIntString { - String::from(&data) - } -} - #[cfg(test)] mod tests { use super::*; - const RSA_JSON: &str = include_str!("../../../tests/rsa2048-2020-08-25.json"); - const RSA_DER: &[u8] = include_bytes!("../../../tests/rsa2048-2020-08-25.der"); - const RSA_PK_DER: &[u8] = include_bytes!("../../../tests/rsa2048-2020-08-25-pk.der"); - const ED25519_JSON: &str = include_str!("../../../tests/ed25519-2020-10-18.json"); const JWK_JCS_JSON: &[u8] = include_bytes!("../../../tests/jwk_jcs-pub.json"); - #[test] - fn jwk_to_from_der_rsa() { - let key: JWK = serde_json::from_str(RSA_JSON).unwrap(); - let der = simple_asn1::der_encode(&key).unwrap(); - assert_eq!(der, RSA_DER); - let rsa_pk: RSAPublicKey = simple_asn1::der_decode(RSA_PK_DER).unwrap(); - let rsa_params = RSAParams::try_from(&rsa_pk).unwrap(); - assert_eq!(key.to_public().params, Params::RSA(rsa_params)); - } - #[test] fn jwk_try_from_bytes() { let actual_jwk: JWK = JWK::try_from(JWK_JCS_JSON).unwrap(); @@ -1350,40 +269,6 @@ mod tests { } } - #[test] - fn rsa_from_str() { - let _key: JWK = serde_json::from_str(RSA_JSON).unwrap(); - } - - #[test] - fn ed25519_from_str() { - let _jwk: JWK = serde_json::from_str(ED25519_JSON).unwrap(); - } - - #[test] - #[cfg(feature = "ed25519")] - fn generate_ed25519() { - let _key = JWK::generate_ed25519().unwrap(); - } - - #[test] - #[cfg(feature = "secp256k1")] - fn secp256k1_generate() { - let _jwk = JWK::generate_secp256k1(); - } - - #[test] - #[cfg(feature = "secp256r1")] - fn p256_generate() { - let _jwk = JWK::generate_p256(); - } - - #[test] - #[cfg(feature = "secp384r1")] - fn p384_generate() { - let _jwk = JWK::generate_p384(); - } - #[test] fn jwk_thumbprint() { // https://tools.ietf.org/html/rfc7638#section-3.1 diff --git a/crates/jwk/src/multicodec.rs b/crates/jwk/src/multicodec.rs index 1be18d848..03c427a0e 100644 --- a/crates/jwk/src/multicodec.rs +++ b/crates/jwk/src/multicodec.rs @@ -143,7 +143,7 @@ impl Codec for JWK { pub enum FromMulticodecError { #[cfg(feature = "rsa")] #[error(transparent)] - RsaPub(crate::RsaX509PubParseError), + RsaPub(crate::rsa::RsaX509PubParseError), #[cfg(feature = "ed25519")] #[error(transparent)] diff --git a/crates/jwk/src/bbs.rs b/crates/jwk/src/type/ec/curve/bbs.rs similarity index 100% rename from crates/jwk/src/bbs.rs rename to crates/jwk/src/type/ec/curve/bbs.rs diff --git a/crates/jwk/src/type/ec/curve/k256.rs b/crates/jwk/src/type/ec/curve/k256.rs new file mode 100644 index 000000000..ca41f82cc --- /dev/null +++ b/crates/jwk/src/type/ec/curve/k256.rs @@ -0,0 +1,133 @@ +use crate::{Base64urlUInt, ECParams, Error, Params, JWK}; + +pub fn secp256k1_parse(data: &[u8]) -> Result { + let pk = k256::PublicKey::from_sec1_bytes(data)?; + Ok(pk.into()) +} + +pub fn secp256k1_parse_private(data: &[u8]) -> Result { + let k = k256::SecretKey::from_sec1_der(data)?; + let jwk = JWK { + params: Params::EC(ECParams::from(&k)), + public_key_use: None, + key_operations: None, + algorithm: None, + key_id: None, + x509_url: None, + x509_certificate_chain: None, + x509_thumbprint_sha1: None, + x509_thumbprint_sha256: None, + }; + Ok(jwk) +} + +/// Serialize a secp256k1 public key as a 33-byte string with point compression. +pub fn serialize_secp256k1(params: &ECParams) -> Result, Error> { + use k256::elliptic_curve::sec1::ToEncodedPoint; + let pk = k256::PublicKey::try_from(params)?; + let pk_compressed_bytes = pk.to_encoded_point(true); + Ok(pk_compressed_bytes.as_bytes().to_vec()) +} + +impl JWK { + pub fn generate_secp256k1() -> JWK { + let mut rng = rand::rngs::OsRng {}; + Self::generate_secp256k1_from(&mut rng) + } + + pub fn generate_secp256k1_from(rng: &mut (impl rand::CryptoRng + rand::RngCore)) -> JWK { + let secret_key = k256::SecretKey::random(rng); + let sk_bytes = zeroize::Zeroizing::new(secret_key.to_bytes().to_vec()); + let public_key = secret_key.public_key(); + let mut ec_params = ECParams::from(&public_key); + ec_params.ecc_private_key = Some(Base64urlUInt(sk_bytes.to_vec())); + JWK::from(Params::EC(ec_params)) + } +} + +impl From for JWK { + fn from(value: k256::PublicKey) -> Self { + JWK { + params: Params::EC(ECParams::from(&value)), + public_key_use: None, + key_operations: None, + algorithm: None, + key_id: None, + x509_url: None, + x509_certificate_chain: None, + x509_thumbprint_sha1: None, + x509_thumbprint_sha256: None, + } + } +} + +impl TryFrom<&ECParams> for k256::SecretKey { + type Error = Error; + fn try_from(params: &ECParams) -> Result { + let curve = params.curve.as_ref().ok_or(Error::MissingCurve)?; + if curve != "secp256k1" { + return Err(Error::CurveNotImplemented(curve.to_string())); + } + let private_key = params + .ecc_private_key + .as_ref() + .ok_or(Error::MissingPrivateKey)?; + let secret_key = k256::SecretKey::from_bytes(private_key.0.as_slice().into())?; + Ok(secret_key) + } +} + +impl TryFrom<&ECParams> for k256::PublicKey { + type Error = Error; + fn try_from(params: &ECParams) -> Result { + let curve = params.curve.as_ref().ok_or(Error::MissingCurve)?; + if curve != "secp256k1" { + return Err(Error::CurveNotImplemented(curve.to_string())); + } + const EC_UNCOMPRESSED_POINT_TAG: &[u8] = &[0x04]; + let x = ¶ms.x_coordinate.as_ref().ok_or(Error::MissingPoint)?.0; + let y = ¶ms.y_coordinate.as_ref().ok_or(Error::MissingPoint)?.0; + let pk_data = [EC_UNCOMPRESSED_POINT_TAG, x.as_slice(), y.as_slice()].concat(); + let public_key = k256::PublicKey::from_sec1_bytes(&pk_data)?; + Ok(public_key) + } +} + +impl From<&k256::PublicKey> for ECParams { + fn from(pk: &k256::PublicKey) -> Self { + use k256::elliptic_curve::sec1::ToEncodedPoint; + let ec_points = pk.to_encoded_point(false); + ECParams { + // TODO according to https://tools.ietf.org/id/draft-jones-webauthn-secp256k1-00.html#rfc.section.2 it should be P-256K? + curve: Some("secp256k1".to_string()), + x_coordinate: ec_points.x().map(|x| Base64urlUInt(x.to_vec())), + y_coordinate: ec_points.y().map(|y| Base64urlUInt(y.to_vec())), + ecc_private_key: None, + } + } +} + +impl From<&k256::SecretKey> for ECParams { + fn from(k: &k256::SecretKey) -> Self { + let pk = k.public_key(); + use k256::elliptic_curve::sec1::ToEncodedPoint; + let ec_points = pk.to_encoded_point(false); + ECParams { + // TODO according to https://tools.ietf.org/id/draft-jones-webauthn-secp256k1-00.html#rfc.section.2 it should be P-256K? + curve: Some("secp256k1".to_string()), + x_coordinate: ec_points.x().map(|x| Base64urlUInt(x.to_vec())), + y_coordinate: ec_points.y().map(|y| Base64urlUInt(y.to_vec())), + ecc_private_key: Some(Base64urlUInt(k.to_bytes().to_vec())), + } + } +} + +#[cfg(test)] +mod tests { + use crate::JWK; + + #[test] + fn secp256k1_generate() { + let _jwk = JWK::generate_secp256k1(); + } +} diff --git a/crates/jwk/src/type/ec/curve/mod.rs b/crates/jwk/src/type/ec/curve/mod.rs new file mode 100644 index 000000000..dbd70a2a7 --- /dev/null +++ b/crates/jwk/src/type/ec/curve/mod.rs @@ -0,0 +1,11 @@ +#[cfg(feature = "secp256k1")] +pub mod k256; + +#[cfg(feature = "secp256r1")] +pub mod p256; + +#[cfg(feature = "secp384r1")] +pub mod p384; + +#[cfg(feature = "bbs")] +pub mod bbs; diff --git a/crates/jwk/src/type/ec/curve/p256.rs b/crates/jwk/src/type/ec/curve/p256.rs new file mode 100644 index 000000000..e72c68123 --- /dev/null +++ b/crates/jwk/src/type/ec/curve/p256.rs @@ -0,0 +1,139 @@ +use crate::{Base64urlUInt, ECParams, Error, Params, JWK}; + +pub fn p256_parse(pk_bytes: &[u8]) -> Result { + let pk = p256::PublicKey::from_sec1_bytes(pk_bytes)?; + Ok(pk.into()) +} + +/// Serialize a P-256 public key as a 33-byte string with point compression. +pub fn serialize_p256(params: &ECParams) -> Result, Error> { + // TODO: check that curve is P-256 + use p256::elliptic_curve::{sec1::EncodedPoint, FieldBytes}; + + let x = FieldBytes::::from_slice( + ¶ms.x_coordinate.as_ref().ok_or(Error::MissingPoint)?.0, + ); + let y = FieldBytes::::from_slice( + ¶ms.y_coordinate.as_ref().ok_or(Error::MissingPoint)?.0, + ); + let encoded_point = EncodedPoint::::from_affine_coordinates(x, y, true); + let pk_compressed_bytes = encoded_point.to_bytes(); + Ok(pk_compressed_bytes.to_vec()) +} + +pub fn p256_parse_private(data: &[u8]) -> Result { + let k = p256::SecretKey::from_bytes(data.into())?; + let jwk = JWK { + params: Params::EC(ECParams::from(&k)), + public_key_use: None, + key_operations: None, + algorithm: None, + key_id: None, + x509_url: None, + x509_certificate_chain: None, + x509_thumbprint_sha1: None, + x509_thumbprint_sha256: None, + }; + Ok(jwk) +} + +impl JWK { + pub fn generate_p256() -> JWK { + let mut rng = rand::rngs::OsRng {}; + Self::generate_p256_from(&mut rng) + } + + pub fn generate_p256_from(rng: &mut (impl rand::CryptoRng + rand::RngCore)) -> JWK { + let secret_key = p256::SecretKey::random(rng); + let sk_bytes = zeroize::Zeroizing::new(secret_key.to_bytes().to_vec()); + let public_key: p256::PublicKey = secret_key.public_key(); + let mut ec_params = ECParams::from(&public_key); + ec_params.ecc_private_key = Some(Base64urlUInt(sk_bytes.to_vec())); + JWK::from(Params::EC(ec_params)) + } +} + +impl From for JWK { + fn from(value: p256::PublicKey) -> Self { + JWK { + params: Params::EC(ECParams::from(&value)), + public_key_use: None, + key_operations: None, + algorithm: None, + key_id: None, + x509_url: None, + x509_certificate_chain: None, + x509_thumbprint_sha1: None, + x509_thumbprint_sha256: None, + } + } +} + +impl TryFrom<&ECParams> for p256::SecretKey { + type Error = Error; + fn try_from(params: &ECParams) -> Result { + let curve = params.curve.as_ref().ok_or(Error::MissingCurve)?; + if curve != "P-256" { + return Err(Error::CurveNotImplemented(curve.to_string())); + } + let private_key = params + .ecc_private_key + .as_ref() + .ok_or(Error::MissingPrivateKey)?; + let secret_key = p256::SecretKey::from_bytes(private_key.0.as_slice().into())?; + Ok(secret_key) + } +} + +impl TryFrom<&ECParams> for p256::PublicKey { + type Error = Error; + fn try_from(params: &ECParams) -> Result { + let curve = params.curve.as_ref().ok_or(Error::MissingCurve)?; + if curve != "P-256" { + return Err(Error::CurveNotImplemented(curve.to_string())); + } + const EC_UNCOMPRESSED_POINT_TAG: &[u8] = &[0x04]; + let x = ¶ms.x_coordinate.as_ref().ok_or(Error::MissingPoint)?.0; + let y = ¶ms.y_coordinate.as_ref().ok_or(Error::MissingPoint)?.0; + let pk_data = [EC_UNCOMPRESSED_POINT_TAG, x.as_slice(), y.as_slice()].concat(); + let public_key = p256::PublicKey::from_sec1_bytes(&pk_data)?; + Ok(public_key) + } +} + +impl From<&p256::PublicKey> for ECParams { + fn from(pk: &p256::PublicKey) -> Self { + use p256::elliptic_curve::sec1::ToEncodedPoint; + let encoded_point = pk.to_encoded_point(false); + ECParams { + curve: Some("P-256".to_string()), + x_coordinate: encoded_point.x().map(|x| Base64urlUInt(x.to_vec())), + y_coordinate: encoded_point.y().map(|y| Base64urlUInt(y.to_vec())), + ecc_private_key: None, + } + } +} + +impl From<&p256::SecretKey> for ECParams { + fn from(k: &p256::SecretKey) -> Self { + let pk = k.public_key(); + use p256::elliptic_curve::sec1::ToEncodedPoint; + let encoded_point = pk.to_encoded_point(false); + Self { + curve: Some("P-256".to_string()), + x_coordinate: encoded_point.x().map(|x| Base64urlUInt(x.to_vec())), + y_coordinate: encoded_point.y().map(|y| Base64urlUInt(y.to_vec())), + ecc_private_key: Some(Base64urlUInt(k.to_bytes().to_vec())), + } + } +} + +#[cfg(test)] +mod tests { + use crate::JWK; + + #[test] + fn p256_generate() { + let _jwk = JWK::generate_p256(); + } +} diff --git a/crates/jwk/src/type/ec/curve/p384.rs b/crates/jwk/src/type/ec/curve/p384.rs new file mode 100644 index 000000000..65a6af3c2 --- /dev/null +++ b/crates/jwk/src/type/ec/curve/p384.rs @@ -0,0 +1,145 @@ +use crate::{Base64urlUInt, ECParams, Error, Params, JWK}; + +pub fn p384_parse(pk_bytes: &[u8]) -> Result { + let pk = p384::PublicKey::from_sec1_bytes(pk_bytes)?; + let jwk = JWK { + params: Params::EC(ECParams::from(&pk)), + public_key_use: None, + key_operations: None, + algorithm: None, + key_id: None, + x509_url: None, + x509_certificate_chain: None, + x509_thumbprint_sha1: None, + x509_thumbprint_sha256: None, + }; + Ok(jwk) +} + +pub fn p384_parse_private(data: &[u8]) -> Result { + let k = p384::SecretKey::from_bytes(data.into())?; + let jwk = JWK { + params: Params::EC(ECParams::from(&k)), + public_key_use: None, + key_operations: None, + algorithm: None, + key_id: None, + x509_url: None, + x509_certificate_chain: None, + x509_thumbprint_sha1: None, + x509_thumbprint_sha256: None, + }; + Ok(jwk) +} + +/// Serialize a P-384 public key as a 49-byte string with point compression. +pub fn serialize_p384(params: &ECParams) -> Result, Error> { + // TODO: check that curve is P-384 + use p384::elliptic_curve::{sec1::EncodedPoint, FieldBytes}; + let x = FieldBytes::::from_slice( + ¶ms.x_coordinate.as_ref().ok_or(Error::MissingPoint)?.0, + ); + let y = FieldBytes::::from_slice( + ¶ms.y_coordinate.as_ref().ok_or(Error::MissingPoint)?.0, + ); + let encoded_point = EncodedPoint::::from_affine_coordinates(x, y, true); + let pk_compressed_bytes = encoded_point.to_bytes(); + Ok(pk_compressed_bytes.to_vec()) +} + +impl JWK { + pub fn generate_p384() -> JWK { + let mut rng = rand::rngs::OsRng {}; + let secret_key = p384::SecretKey::random(&mut rng); + let sk_bytes = zeroize::Zeroizing::new(secret_key.to_bytes().to_vec()); + let public_key: p384::PublicKey = secret_key.public_key(); + let mut ec_params = ECParams::from(&public_key); + ec_params.ecc_private_key = Some(Base64urlUInt(sk_bytes.to_vec())); + JWK::from(Params::EC(ec_params)) + } +} + +impl From for JWK { + fn from(value: p384::PublicKey) -> Self { + JWK { + params: Params::EC(ECParams::from(&value)), + public_key_use: None, + key_operations: None, + algorithm: None, + key_id: None, + x509_url: None, + x509_certificate_chain: None, + x509_thumbprint_sha1: None, + x509_thumbprint_sha256: None, + } + } +} + +impl TryFrom<&ECParams> for p384::SecretKey { + type Error = Error; + fn try_from(params: &ECParams) -> Result { + let curve = params.curve.as_ref().ok_or(Error::MissingCurve)?; + if curve != "P-384" { + return Err(Error::CurveNotImplemented(curve.to_string())); + } + let private_key = params + .ecc_private_key + .as_ref() + .ok_or(Error::MissingPrivateKey)?; + let secret_key = p384::SecretKey::from_bytes(private_key.0.as_slice().into())?; + Ok(secret_key) + } +} + +impl TryFrom<&ECParams> for p384::PublicKey { + type Error = Error; + fn try_from(params: &ECParams) -> Result { + let curve = params.curve.as_ref().ok_or(Error::MissingCurve)?; + if curve != "P-384" { + return Err(Error::CurveNotImplemented(curve.to_string())); + } + const EC_UNCOMPRESSED_POINT_TAG: &[u8] = &[0x04]; + let x = ¶ms.x_coordinate.as_ref().ok_or(Error::MissingPoint)?.0; + let y = ¶ms.y_coordinate.as_ref().ok_or(Error::MissingPoint)?.0; + let pk_data = [EC_UNCOMPRESSED_POINT_TAG, x.as_slice(), y.as_slice()].concat(); + let public_key = p384::PublicKey::from_sec1_bytes(&pk_data)?; + Ok(public_key) + } +} + +impl From<&p384::PublicKey> for ECParams { + fn from(pk: &p384::PublicKey) -> Self { + use p384::elliptic_curve::sec1::ToEncodedPoint; + let encoded_point = pk.to_encoded_point(false); + ECParams { + curve: Some("P-384".to_string()), + x_coordinate: encoded_point.x().map(|x| Base64urlUInt(x.to_vec())), + y_coordinate: encoded_point.y().map(|y| Base64urlUInt(y.to_vec())), + ecc_private_key: None, + } + } +} + +impl From<&p384::SecretKey> for ECParams { + fn from(k: &p384::SecretKey) -> Self { + let pk = k.public_key(); + use p384::elliptic_curve::sec1::ToEncodedPoint; + let encoded_point = pk.to_encoded_point(false); + ECParams { + curve: Some("P-384".to_string()), + x_coordinate: encoded_point.x().map(|x| Base64urlUInt(x.to_vec())), + y_coordinate: encoded_point.y().map(|y| Base64urlUInt(y.to_vec())), + ecc_private_key: Some(Base64urlUInt(k.to_bytes().to_vec())), + } + } +} + +#[cfg(test)] +mod tests { + use crate::JWK; + + #[test] + fn p384_generate() { + let _jwk = JWK::generate_p384(); + } +} diff --git a/crates/jwk/src/type/ec/mod.rs b/crates/jwk/src/type/ec/mod.rs new file mode 100644 index 000000000..469026ee6 --- /dev/null +++ b/crates/jwk/src/type/ec/mod.rs @@ -0,0 +1,48 @@ +use serde::{Deserialize, Serialize}; +use zeroize::Zeroize; + +use crate::Base64urlUInt; + +mod curve; +pub use curve::*; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Zeroize)] +pub struct ECParams { + // Parameters for Elliptic Curve Public Keys + #[serde(rename = "crv")] + pub curve: Option, + #[serde(rename = "x")] + pub x_coordinate: Option, + #[serde(rename = "y")] + pub y_coordinate: Option, + + // Parameters for Elliptic Curve Private Keys + #[serde(rename = "d")] + #[serde(skip_serializing_if = "Option::is_none")] + pub ecc_private_key: Option, +} + +impl ECParams { + pub fn is_public(&self) -> bool { + self.ecc_private_key.is_none() + } + + /// Strip private key material + pub fn to_public(&self) -> Self { + Self { + curve: self.curve.clone(), + x_coordinate: self.x_coordinate.clone(), + y_coordinate: self.y_coordinate.clone(), + ecc_private_key: None, + } + } +} + +impl Drop for ECParams { + fn drop(&mut self) { + // Zeroize private key + if let Some(ref mut d) = self.ecc_private_key { + d.zeroize(); + } + } +} diff --git a/crates/jwk/src/type/mod.rs b/crates/jwk/src/type/mod.rs new file mode 100644 index 000000000..76805a3fa --- /dev/null +++ b/crates/jwk/src/type/mod.rs @@ -0,0 +1,63 @@ +use serde::{Deserialize, Serialize}; +use zeroize::Zeroize; + +use crate::JWK; + +pub mod ec; +pub use ec::ECParams; + +pub mod rsa; +pub use rsa::RSAParams; + +pub mod okp; +pub use okp::OctetParams; + +mod oct; +pub use oct::SymmetricParams; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Zeroize)] +#[serde(tag = "kty")] +pub enum Params { + EC(ECParams), + RSA(RSAParams), + #[serde(rename = "oct")] + Symmetric(SymmetricParams), + OKP(OctetParams), +} + +impl Params { + pub fn is_public(&self) -> bool { + match self { + Self::EC(params) => params.is_public(), + Self::RSA(params) => params.is_public(), + Self::Symmetric(params) => params.is_public(), + Self::OKP(params) => params.is_public(), + } + } + + /// Strip private key material + pub fn to_public(&self) -> Self { + match self { + Self::EC(params) => Self::EC(params.to_public()), + Self::RSA(params) => Self::RSA(params.to_public()), + Self::Symmetric(params) => Self::Symmetric(params.to_public()), + Self::OKP(params) => Self::OKP(params.to_public()), + } + } +} + +impl From for JWK { + fn from(params: Params) -> Self { + Self { + params, + public_key_use: None, + key_operations: None, + algorithm: None, + key_id: None, + x509_url: None, + x509_certificate_chain: None, + x509_thumbprint_sha1: None, + x509_thumbprint_sha256: None, + } + } +} diff --git a/crates/jwk/src/type/oct.rs b/crates/jwk/src/type/oct.rs new file mode 100644 index 000000000..91fbf7fd5 --- /dev/null +++ b/crates/jwk/src/type/oct.rs @@ -0,0 +1,31 @@ +use serde::{Deserialize, Serialize}; +use zeroize::Zeroize; + +use crate::Base64urlUInt; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Zeroize)] +pub struct SymmetricParams { + // Parameters for Symmetric Keys + #[serde(rename = "k")] + pub key_value: Option, +} + +impl SymmetricParams { + pub fn is_public(&self) -> bool { + self.key_value.is_none() + } + + /// Strip private key material + pub fn to_public(&self) -> Self { + Self { key_value: None } + } +} + +impl Drop for SymmetricParams { + fn drop(&mut self) { + // Zeroize private/symmetric key + if let Some(ref mut k) = self.key_value { + k.zeroize(); + } + } +} diff --git a/crates/jwk/src/aleo.rs b/crates/jwk/src/type/okp/curve/aleo.rs similarity index 95% rename from crates/jwk/src/aleo.rs rename to crates/jwk/src/type/okp/curve/aleo.rs index f52baa2da..62fb3ec81 100644 --- a/crates/jwk/src/aleo.rs +++ b/crates/jwk/src/type/okp/curve/aleo.rs @@ -8,7 +8,7 @@ //! using static parameters ([struct@COM_PARAMS], [struct@ENC_PARAMS], [struct@SIG_PARAMS]) //! and a [JWK-based keypair representation](OKP_CURVE). -use crate::{Base64urlUInt, OctetParams, Params, JWK}; +use crate::{Base64urlUInt, Error, OctetParams, Params, JWK}; use thiserror::Error; use blake2::Blake2s; @@ -157,6 +157,12 @@ lazy_static::lazy_static! { /// [struct@ENC_PARAMS]. pub const OKP_CURVE: &str = "AleoTestnet1Key"; +impl JWK { + pub fn generate_aleo() -> Result { + generate_private_key_jwk().map_err(Error::AleoGeneratePrivateKey) + } +} + /// Generate an Aleo private key in [unofficial JWK format][OKP_CURVE]. **CPU-intensive (slow)**. /// /// Uses [struct@SIG_PARAMS], [struct@COM_PARAMS], and [struct@ENC_PARAMS]. @@ -287,9 +293,10 @@ mod tests { #[test] fn parse_private_key_jwk() { - let key: JWK = - serde_json::from_str(include_str!("../../../tests/aleotestnet1-2021-11-22.json")) - .unwrap(); + let key: JWK = serde_json::from_str(include_str!( + "../../../../../../tests/aleotestnet1-2021-11-22.json" + )) + .unwrap(); let private_key = aleo_jwk_to_private_key(&key).unwrap(); let private_key_str = private_key.to_string(); assert_eq!( @@ -311,9 +318,10 @@ mod tests { #[test] fn aleo_jwk_sign_verify() { - let private_key: JWK = - serde_json::from_str(include_str!("../../../tests/aleotestnet1-2021-11-22.json")) - .unwrap(); + let private_key: JWK = serde_json::from_str(include_str!( + "../../../../../../tests/aleotestnet1-2021-11-22.json" + )) + .unwrap(); let public_key = private_key.to_public(); let msg1 = b"asdf"; diff --git a/crates/jwk/src/type/okp/curve/curve25519.rs b/crates/jwk/src/type/okp/curve/curve25519.rs new file mode 100644 index 000000000..faba214b8 --- /dev/null +++ b/crates/jwk/src/type/okp/curve/curve25519.rs @@ -0,0 +1,128 @@ +use crate::{Base64urlUInt, Error, OctetParams, Params, JWK}; + +impl JWK { + pub fn generate_ed25519() -> Result { + #[cfg(feature = "ring")] + { + use zeroize::Zeroize; + let rng = ring::rand::SystemRandom::new(); + let mut key_pkcs8 = ring::signature::Ed25519KeyPair::generate_pkcs8(&rng)? + .as_ref() + .to_vec(); + // reference: ring/src/ec/curve25519/ed25519/signing.rs + let private_key = key_pkcs8[0x10..0x30].to_vec(); + let public_key = key_pkcs8[0x35..0x55].to_vec(); + key_pkcs8.zeroize(); + Ok(JWK::from(Params::OKP(OctetParams { + curve: "Ed25519".to_string(), + public_key: Base64urlUInt(public_key), + private_key: Some(Base64urlUInt(private_key)), + }))) + } + #[cfg(not(feature = "ring"))] + { + let mut csprng = rand::rngs::OsRng {}; + let secret = ed25519_dalek::SigningKey::generate(&mut csprng); + let public = secret.verifying_key(); + Ok(JWK::from(Params::OKP(OctetParams { + curve: "Ed25519".to_string(), + public_key: Base64urlUInt(public.as_ref().to_vec()), + private_key: Some(Base64urlUInt(secret.to_bytes().to_vec())), + }))) + } + } + + pub fn generate_ed25519_from( + rng: &mut (impl rand::CryptoRng + rand::RngCore), + ) -> Result { + let secret = ed25519_dalek::SigningKey::generate(rng); + let public = secret.verifying_key(); + Ok(JWK::from(Params::OKP(OctetParams { + curve: "Ed25519".to_string(), + public_key: Base64urlUInt(public.as_ref().to_vec()), + private_key: Some(Base64urlUInt(secret.to_bytes().to_vec())), + }))) + } +} + +impl TryFrom<&OctetParams> for ed25519_dalek::VerifyingKey { + type Error = Error; + fn try_from(params: &OctetParams) -> Result { + if params.curve != *"Ed25519" { + return Err(Error::CurveNotImplemented(params.curve.to_string())); + } + Ok(params.public_key.0.as_slice().as_ref().try_into()?) + } +} + +impl TryFrom<&OctetParams> for ed25519_dalek::SigningKey { + type Error = Error; + fn try_from(params: &OctetParams) -> Result { + if params.curve != *"Ed25519" { + return Err(Error::CurveNotImplemented(params.curve.to_string())); + } + let private_key = params + .private_key + .as_ref() + .ok_or(Error::MissingPrivateKey)?; + Ok(private_key.0.as_slice().as_ref().try_into()?) + } +} + +#[cfg(feature = "ring")] +impl TryFrom<&OctetParams> for ring::signature::Ed25519KeyPair { + type Error = Error; + fn try_from(params: &OctetParams) -> Result { + if params.curve != *"Ed25519" { + return Err(Error::CurveNotImplemented(params.curve.to_string())); + } + params + .private_key + .as_ref() + .ok_or(Error::MissingPrivateKey)?; + let der = simple_asn1::der_encode(params)?; + let keypair = Self::from_pkcs8_maybe_unchecked(&der)?; + Ok(keypair) + } +} + +pub fn ed25519_parse(data: &[u8]) -> Result { + let public_key = ed25519_dalek::VerifyingKey::try_from(data)?; + Ok(public_key.into()) +} + +impl From for JWK { + fn from(value: ed25519_dalek::VerifyingKey) -> Self { + JWK::from(Params::OKP(OctetParams { + curve: "Ed25519".to_string(), + public_key: Base64urlUInt(value.to_bytes().to_vec()), + private_key: None, + })) + } +} + +pub fn ed25519_parse_private(data: &[u8]) -> Result { + let key: ed25519_dalek::SigningKey = data.try_into()?; + Ok(JWK::from(Params::OKP(OctetParams { + curve: "Ed25519".to_string(), + public_key: Base64urlUInt(ed25519_dalek::VerifyingKey::from(&key).as_bytes().to_vec()), + private_key: Some(Base64urlUInt(data.to_owned())), + }))) +} + +#[cfg(test)] +mod tests { + use crate::JWK; + + const ED25519_JSON: &str = include_str!("../../../../../../tests/ed25519-2020-10-18.json"); + + #[test] + fn generate_ed25519() { + let _key = JWK::generate_ed25519().unwrap(); + } + + #[test] + fn ed25519_from_str() { + let _jwk: JWK = serde_json::from_str(ED25519_JSON).unwrap(); + } +} diff --git a/crates/jwk/src/type/okp/curve/mod.rs b/crates/jwk/src/type/okp/curve/mod.rs new file mode 100644 index 000000000..230e58962 --- /dev/null +++ b/crates/jwk/src/type/okp/curve/mod.rs @@ -0,0 +1,5 @@ +#[cfg(feature = "ed25519")] +pub mod curve25519; + +#[cfg(feature = "aleo")] +pub mod aleo; diff --git a/crates/jwk/src/type/okp/der.rs b/crates/jwk/src/type/okp/der.rs new file mode 100644 index 000000000..44d374b7f --- /dev/null +++ b/crates/jwk/src/type/okp/der.rs @@ -0,0 +1,120 @@ +use num_bigint::{BigInt, Sign}; +use simple_asn1::{der_encode, ASN1Block, ASN1Class, ASN1EncodeErr, ToASN1}; + +use crate::{ + der::{BitString, OctetString}, + Error, +}; + +use super::OctetParams; + +/// Ed25519 public key for ASN.1 encoding, as specified in [RFC 8410]. +/// +/// [RFC 8410]: https://datatracker.ietf.org/doc/html/rfc8410#section-10.1 "RFC 8410 Safe Curves for X.509 - 10.1. Example Ed25519 Public Key" +#[derive(Debug, Clone)] +pub struct Ed25519PublicKey { + pub public_key: BitString, +} + +/// Ed25519 private key for ASN.1 encoding, as specified in [RFC 8410]. +/// +/// [RFC 8410]: https://datatracker.ietf.org/doc/html/rfc8410#section-10.3 "RFC 8410 Safe Curves for X.509 - 10.3. Examples of Ed25519 Private Key" +#[derive(Debug, Clone)] +pub struct Ed25519PrivateKey { + pub private_key: OctetString, +} + +impl Ed25519PrivateKey { + fn oid() -> ASN1Block { + use simple_asn1::BigUint; + // id-Ed25519 1.3.101.112 + let oid = simple_asn1::OID::new(vec![ + BigUint::new(vec![1]), + BigUint::new(vec![3]), + BigUint::new(vec![101]), + BigUint::new(vec![112]), + ]); + ASN1Block::Sequence(0, vec![ASN1Block::ObjectIdentifier(0, oid)]) + } +} + +impl ToASN1 for Ed25519PrivateKey { + type Error = ASN1EncodeErr; + + fn to_asn1_class(&self, _class: ASN1Class) -> Result, Self::Error> { + let version = 0; + // TODO: include public key + Ok(vec![ASN1Block::Sequence( + 0, + vec![ + ASN1Block::Integer(0, BigInt::new(Sign::NoSign, vec![version])), + Ed25519PrivateKey::oid(), + ASN1Block::OctetString(0, der_encode(&self.private_key)?), + ], + )]) + } +} + +impl ToASN1 for Ed25519PublicKey { + type Error = ASN1EncodeErr; + + fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { + Ok(vec![ASN1Block::Sequence( + 0, + self.public_key.to_asn1_class(class)?, + )]) + } +} + +impl ToASN1 for OctetParams { + type Error = Error; + + fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { + if self.curve != *"Ed25519" { + return Err(Error::CurveNotImplemented(self.curve.to_string())); + } + let public_key = BitString(self.public_key.0.clone()); + if let Some(private_key) = self + .private_key + .as_ref() + .map(|private_key| OctetString(private_key.0.clone())) + { + let key = Ed25519PrivateKey { private_key }; + Ok(key.to_asn1_class(class)?) + } else { + let key = Ed25519PublicKey { public_key }; + Ok(key.to_asn1_class(class)?) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use base64::Engine; + + #[test] + fn encode_oid() { + let expected = vec![0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x70]; + let asn1 = Ed25519PrivateKey::oid(); + let der = simple_asn1::to_der(&asn1).unwrap(); + assert_eq!(der, expected); + } + + #[test] + fn encode_ed25519_private_key() { + let key = Ed25519PrivateKey { + private_key: OctetString(vec![ + 0xD4, 0xEE, 0x72, 0xDB, 0xF9, 0x13, 0x58, 0x4A, 0xD5, 0xB6, 0xD8, 0xF1, 0xF7, 0x69, + 0xF8, 0xAD, 0x3A, 0xFE, 0x7C, 0x28, 0xCB, 0xF1, 0xD4, 0xFB, 0xE0, 0x97, 0xA8, 0x8F, + 0x44, 0x75, 0x58, 0x42, + ]), + }; + let expected_b64 = "MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC"; + let expected_key = base64::prelude::BASE64_STANDARD + .decode(expected_b64) + .unwrap(); + let key_der = der_encode(&key).unwrap(); + assert_eq!(key_der, expected_key); + } +} diff --git a/crates/jwk/src/type/okp/mod.rs b/crates/jwk/src/type/okp/mod.rs new file mode 100644 index 000000000..2d37992fa --- /dev/null +++ b/crates/jwk/src/type/okp/mod.rs @@ -0,0 +1,61 @@ +use serde::{Deserialize, Serialize}; +use zeroize::Zeroize; + +use crate::Base64urlUInt; + +mod curve; +pub use curve::*; + +mod der; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Zeroize)] +pub struct OctetParams { + // Parameters for Octet Key Pair Public Keys + #[serde(rename = "crv")] + pub curve: String, + + #[serde(rename = "x")] + pub public_key: Base64urlUInt, + + // Parameters for Octet Key Pair Private Keys + #[serde(rename = "d")] + #[serde(skip_serializing_if = "Option::is_none")] + pub private_key: Option, +} + +impl OctetParams { + pub fn is_public(&self) -> bool { + self.private_key.is_none() + } + + /// Strip private key material + pub fn to_public(&self) -> Self { + Self { + curve: self.curve.clone(), + public_key: self.public_key.clone(), + private_key: None, + } + } +} + +impl Drop for OctetParams { + fn drop(&mut self) { + // Zeroize private key + if let Some(ref mut d) = self.private_key { + d.zeroize(); + } + } +} + +#[cfg(feature = "ring")] +impl TryFrom<&OctetParams> for &ring::signature::EdDSAParameters { + type Error = crate::Error; + + fn try_from(params: &OctetParams) -> Result { + if params.curve != *"Ed25519" { + return Err(crate::Error::CurveNotImplemented(params.curve.to_string())); + } + + Ok(&ring::signature::ED25519) + } +} diff --git a/crates/jwk/src/type/rsa/der.rs b/crates/jwk/src/type/rsa/der.rs new file mode 100644 index 000000000..e9b9b763c --- /dev/null +++ b/crates/jwk/src/type/rsa/der.rs @@ -0,0 +1,271 @@ +use num_bigint::{BigInt, Sign}; +use simple_asn1::{ASN1Block, ASN1Class, ASN1DecodeErr, ASN1EncodeErr, FromASN1, ToASN1}; + +use crate::{der::Integer, Base64urlUInt, Error, Params, JWK}; + +use super::RSAParams; + +/// RSA private key for ASN.1 encoding, as specified in [RFC 8017]. +/// +/// [RFC 8017]: https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.1.2 "RFC 8017 PKCS #1 v2.2 - A.1.2. RSA Private Key Syntax" +#[derive(Debug, Clone)] +struct RSAPrivateKey { + pub modulus: Integer, + pub public_exponent: Integer, + pub private_exponent: Integer, + pub prime1: Integer, + pub prime2: Integer, + pub exponent1: Integer, + pub exponent2: Integer, + pub coefficient: Integer, + pub other_prime_infos: Option, +} + +/// RSA public key for ASN.1 encoding, as specified in [RFC 8017]. +/// +/// [RFC 8017]: https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.1.1 "RFC 8017 PKCS #1 v2.2 - A.1.1. RSA Public Key Syntax" +#[derive(Debug, Clone)] +// https://datatracker.ietf.org/doc/html/rfc3447#appendix-A.1.1 +struct RSAPublicKey { + pub modulus: Integer, + pub public_exponent: Integer, +} + +/// Additional primes in a [RSA private key][RSAPrivateKey], as specified in [RFC 8017]. +/// +/// [RFC 8017]: https://datatracker.ietf.org/doc/html/rfc8017#page-56 "RFC 8017 PKCS #1 v2.2 - Page 56" +#[derive(Debug, Clone)] +struct OtherPrimeInfos(pub Vec); + +impl ToASN1 for OtherPrimeInfos { + type Error = ASN1EncodeErr; + + fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { + Ok(vec![ASN1Block::Sequence( + 0, + self.0 + .iter() + .map(|x| x.to_asn1_class(class)) + .collect::>, ASN1EncodeErr>>()? + .concat(), + )]) + } +} + +impl ToASN1 for OtherPrimeInfo { + type Error = ASN1EncodeErr; + fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { + Ok(vec![ASN1Block::Sequence( + 0, + [ + self.prime.to_asn1_class(class)?, + self.exponent.to_asn1_class(class)?, + self.coefficient.to_asn1_class(class)?, + ] + .concat(), + )]) + } +} + +/// Additional prime in a [RSA private key][RSAPrivateKey], as specified in [RFC 8017]. +/// +/// [RFC 8017]: https://datatracker.ietf.org/doc/html/rfc8017#page-56 "RFC 8017 PKCS #1 v2.2 - Page 56" +#[derive(Debug, Clone)] +struct OtherPrimeInfo { + pub prime: Integer, + pub exponent: Integer, + pub coefficient: Integer, +} + +impl ToASN1 for RSAPrivateKey { + type Error = ASN1EncodeErr; + + fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { + let multiprime = self.other_prime_infos.is_some(); + let version = Integer(BigInt::new(Sign::Plus, vec![u32::from(multiprime)])); + Ok(vec![ASN1Block::Sequence( + 0, + [ + version.to_asn1_class(class)?, + self.modulus.to_asn1_class(class)?, + self.public_exponent.to_asn1_class(class)?, + self.private_exponent.to_asn1_class(class)?, + self.prime1.to_asn1_class(class)?, + self.prime2.to_asn1_class(class)?, + self.exponent1.to_asn1_class(class)?, + self.exponent2.to_asn1_class(class)?, + self.coefficient.to_asn1_class(class)?, + match self.other_prime_infos { + Some(ref infos) => infos.to_asn1_class(class)?, + None => Vec::new(), + }, + ] + .concat(), + )]) + } +} + +impl ToASN1 for RSAPublicKey { + type Error = ASN1EncodeErr; + fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { + Ok(vec![ASN1Block::Sequence( + 0, + [ + self.modulus.to_asn1_class(class)?, + self.public_exponent.to_asn1_class(class)?, + ] + .concat(), + )]) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum RSAPublicKeyFromASN1Error { + #[error("Expected single sequence")] + ExpectedSingleSequence, + #[error("Expected two integers")] + ExpectedTwoIntegers, + #[error("ASN1 decoding error: {0:?}")] + ASN1Decode(#[from] ASN1DecodeErr), +} + +#[derive(thiserror::Error, Debug)] +pub enum RSAParamsFromPublicKeyError { + #[error("RSA Public Key from ASN1 error: {0:?}")] + RSAPublicKeyFromASN1(RSAPublicKeyFromASN1Error), + + #[error("Expected positive integer in RSA key")] + ExpectedPlus, +} + +#[derive(thiserror::Error, Debug)] +pub enum RsaX509PubParseError { + #[error("RSAPublicKey from ASN1: {0:?}")] + RSAPublicKeyFromASN1(#[from] RSAPublicKeyFromASN1Error), + + #[error("RSA JWK params from RSAPublicKey: {0:?}")] + RSAParamsFromPublicKey(#[from] RSAParamsFromPublicKeyError), +} + +impl FromASN1 for RSAPublicKey { + type Error = RSAPublicKeyFromASN1Error; + + fn from_asn1(v: &[ASN1Block]) -> Result<(Self, &[ASN1Block]), Self::Error> { + let vec = match v { + [ASN1Block::Sequence(_, vec)] => vec, + _ => return Err(RSAPublicKeyFromASN1Error::ExpectedSingleSequence), + }; + let (n, e) = match vec.as_slice() { + [ASN1Block::Integer(_, n), ASN1Block::Integer(_, e)] => (n, e), + _ => return Err(RSAPublicKeyFromASN1Error::ExpectedTwoIntegers), + }; + let pk = Self { + modulus: Integer(n.clone()), + public_exponent: Integer(e.clone()), + }; + Ok((pk, &[])) + } +} + +impl ToASN1 for RSAParams { + type Error = Error; + + fn to_asn1_class(&self, class: ASN1Class) -> Result, Self::Error> { + let modulus = match &self.modulus { + Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), + None => return Err(Error::MissingModulus), + }; + let public_exponent = match &self.exponent { + Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), + None => return Err(Error::MissingExponent), + }; + if let Some(ref private_exponent) = self.private_exponent { + let key = RSAPrivateKey { + modulus, + public_exponent, + private_exponent: Integer(BigInt::from_bytes_be(Sign::Plus, &private_exponent.0)), + prime1: match &self.first_prime_factor { + Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), + None => Integer(BigInt::new(Sign::NoSign, vec![])), + }, + prime2: match &self.second_prime_factor { + Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), + None => Integer(BigInt::new(Sign::NoSign, vec![])), + }, + exponent1: match &self.first_prime_factor_crt_exponent { + Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), + None => Integer(BigInt::new(Sign::NoSign, vec![])), + }, + exponent2: match &self.second_prime_factor_crt_exponent { + Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), + None => Integer(BigInt::new(Sign::NoSign, vec![])), + }, + coefficient: match &self.first_crt_coefficient { + Some(integer) => Integer(BigInt::from_bytes_be(Sign::Plus, &integer.0)), + None => Integer(BigInt::new(Sign::NoSign, vec![0])), + }, + other_prime_infos: None, + }; + Ok(key.to_asn1_class(class)?) + } else { + let key = RSAPublicKey { + modulus, + public_exponent, + }; + Ok(key.to_asn1_class(class)?) + } + } +} + +impl TryFrom<&RSAPublicKey> for RSAParams { + type Error = RSAParamsFromPublicKeyError; + + fn try_from(pk: &RSAPublicKey) -> Result { + let (sign, n) = pk.modulus.0.to_bytes_be(); + if sign != Sign::Plus { + return Err(RSAParamsFromPublicKeyError::ExpectedPlus); + } + let (sign, e) = pk.public_exponent.0.to_bytes_be(); + if sign != Sign::Plus { + return Err(RSAParamsFromPublicKeyError::ExpectedPlus); + } + Ok(RSAParams { + modulus: Some(Base64urlUInt(n)), + exponent: Some(Base64urlUInt(e)), + private_exponent: None, + first_prime_factor: None, + second_prime_factor: None, + first_prime_factor_crt_exponent: None, + second_prime_factor_crt_exponent: None, + first_crt_coefficient: None, + other_primes_info: None, + }) + } +} + +/// Parse a "RSA public key (X.509 encoded)" (multicodec) into a JWK. +pub fn rsa_x509_pub_parse(pk_bytes: &[u8]) -> Result { + let rsa_pk: RSAPublicKey = simple_asn1::der_decode(pk_bytes)?; + let rsa_params = RSAParams::try_from(&rsa_pk)?; + Ok(JWK::from(Params::RSA(rsa_params))) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::JWK; + + const RSA_JSON: &str = include_str!("../../../../../tests/rsa2048-2020-08-25.json"); + const RSA_DER: &[u8] = include_bytes!("../../../../../tests/rsa2048-2020-08-25.der"); + const RSA_PK_DER: &[u8] = include_bytes!("../../../../../tests/rsa2048-2020-08-25-pk.der"); + + #[test] + fn jwk_to_from_der_rsa() { + let key: JWK = serde_json::from_str(RSA_JSON).unwrap(); + let der = simple_asn1::der_encode(&key).unwrap(); + assert_eq!(der, RSA_DER); + let rsa_pk: RSAPublicKey = simple_asn1::der_decode(RSA_PK_DER).unwrap(); + let rsa_params = RSAParams::try_from(&rsa_pk).unwrap(); + assert_eq!(key.to_public().params, Params::RSA(rsa_params)); + } +} diff --git a/crates/jwk/src/type/rsa/mod.rs b/crates/jwk/src/type/rsa/mod.rs new file mode 100644 index 000000000..39ba63cce --- /dev/null +++ b/crates/jwk/src/type/rsa/mod.rs @@ -0,0 +1,216 @@ +use serde::{Deserialize, Serialize}; +use zeroize::Zeroize; + +use crate::{Base64urlUInt, Error}; + +mod der; +pub use der::*; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default, Hash, Eq, Zeroize)] +pub struct RSAParams { + // Parameters for RSA Public Keys + #[serde(rename = "n")] + pub modulus: Option, + #[serde(rename = "e")] + pub exponent: Option, + + // Parameters for RSA Private Keys + #[serde(rename = "d")] + #[serde(skip_serializing_if = "Option::is_none")] + pub private_exponent: Option, + #[serde(rename = "p")] + #[serde(skip_serializing_if = "Option::is_none")] + pub first_prime_factor: Option, + #[serde(rename = "q")] + #[serde(skip_serializing_if = "Option::is_none")] + pub second_prime_factor: Option, + #[serde(rename = "dp")] + #[serde(skip_serializing_if = "Option::is_none")] + pub first_prime_factor_crt_exponent: Option, + #[serde(rename = "dq")] + #[serde(skip_serializing_if = "Option::is_none")] + pub second_prime_factor_crt_exponent: Option, + #[serde(rename = "qi")] + #[serde(skip_serializing_if = "Option::is_none")] + pub first_crt_coefficient: Option, + #[serde(rename = "oth")] + #[serde(skip_serializing_if = "Option::is_none")] + pub other_primes_info: Option>, +} + +impl RSAParams { + pub fn is_public(&self) -> bool { + self.private_exponent.is_none() + && self.first_prime_factor.is_none() + && self.second_prime_factor.is_none() + && self.first_prime_factor_crt_exponent.is_none() + && self.second_prime_factor_crt_exponent.is_none() + && self.first_crt_coefficient.is_none() + && self.other_primes_info.is_none() + } + + /// Strip private key material + pub fn to_public(&self) -> Self { + Self { + modulus: self.modulus.clone(), + exponent: self.exponent.clone(), + private_exponent: None, + first_prime_factor: None, + second_prime_factor: None, + first_prime_factor_crt_exponent: None, + second_prime_factor_crt_exponent: None, + first_crt_coefficient: None, + other_primes_info: None, + } + } + + /// Construct a RSA public key + pub fn new_public(exponent: &[u8], modulus: &[u8]) -> Self { + Self { + modulus: Some(Base64urlUInt(modulus.to_vec())), + exponent: Some(Base64urlUInt(exponent.to_vec())), + private_exponent: None, + first_prime_factor: None, + second_prime_factor: None, + first_prime_factor_crt_exponent: None, + second_prime_factor_crt_exponent: None, + first_crt_coefficient: None, + other_primes_info: None, + } + } + + /// Validate key size is at least 2048 bits, per [RFC 7518 section 3.3](https://www.rfc-editor.org/rfc/rfc7518#section-3.3). + pub fn validate_key_size(&self) -> Result<(), Error> { + let n = &self.modulus.as_ref().ok_or(Error::MissingModulus)?.0; + if n.len() < 256 { + return Err(Error::InvalidKeyLength(n.len())); + } + Ok(()) + } +} + +impl Drop for RSAParams { + fn drop(&mut self) { + // Zeroize private key fields + if let Some(ref mut d) = self.private_exponent { + d.zeroize(); + } + if let Some(ref mut p) = self.first_prime_factor { + p.zeroize(); + } + if let Some(ref mut q) = self.second_prime_factor { + q.zeroize(); + } + if let Some(ref mut dp) = self.first_prime_factor_crt_exponent { + dp.zeroize(); + } + if let Some(ref mut dq) = self.second_prime_factor_crt_exponent { + dq.zeroize(); + } + if let Some(ref mut qi) = self.first_crt_coefficient { + qi.zeroize(); + } + if let Some(ref mut primes) = self.other_primes_info { + for prime in primes { + prime.zeroize(); + } + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Zeroize)] +pub struct Prime { + #[serde(rename = "r")] + pub prime_factor: Base64urlUInt, + #[serde(rename = "d")] + pub factor_crt_exponent: Base64urlUInt, + #[serde(rename = "t")] + pub factor_crt_coefficient: Base64urlUInt, +} + +#[cfg(feature = "rsa")] +impl From<&Base64urlUInt> for rsa::BigUint { + fn from(uint: &Base64urlUInt) -> Self { + Self::from_bytes_be(&uint.0) + } +} + +#[cfg(feature = "rsa")] +impl TryFrom<&RSAParams> for rsa::RsaPublicKey { + type Error = Error; + fn try_from(params: &RSAParams) -> Result { + let n = params.modulus.as_ref().ok_or(Error::MissingModulus)?; + let e = params.exponent.as_ref().ok_or(Error::MissingExponent)?; + Ok(Self::new(n.into(), e.into())?) + } +} + +#[cfg(feature = "rsa")] +impl TryFrom<&RSAParams> for rsa::RsaPrivateKey { + type Error = Error; + #[allow(clippy::many_single_char_names)] + fn try_from(params: &RSAParams) -> Result { + let n = params.modulus.as_ref().ok_or(Error::MissingModulus)?; + let e = params.exponent.as_ref().ok_or(Error::MissingExponent)?; + let d = params + .private_exponent + .as_ref() + .ok_or(Error::MissingExponent)?; + let p = params + .first_prime_factor + .as_ref() + .ok_or(Error::MissingPrime)?; + let q = params + .second_prime_factor + .as_ref() + .ok_or(Error::MissingPrime)?; + let mut primes = vec![p.into(), q.into()]; + for prime in params.other_primes_info.iter().flatten() { + primes.push((&prime.prime_factor).into()); + } + Self::from_components(n.into(), e.into(), d.into(), primes) + // NOTE it's not the correct error type, but it'll be replaced soon + // anyway. + .map_err(|_| Error::InvalidCoordinates) + } +} + +#[cfg(feature = "ring")] +impl<'a> TryFrom<&'a RSAParams> for ring::signature::RsaPublicKeyComponents<&'a [u8]> { + type Error = Error; + fn try_from(params: &'a RSAParams) -> Result { + fn trim_bytes(bytes: &[u8]) -> &[u8] { + const ZERO: [u8; 1] = [0]; + // Remove leading zeros + match bytes.iter().position(|&x| x != 0) { + Some(n) => &bytes[n..], + None => &ZERO, + } + } + let n = trim_bytes(¶ms.modulus.as_ref().ok_or(Error::MissingModulus)?.0); + let e = trim_bytes(¶ms.exponent.as_ref().ok_or(Error::MissingExponent)?.0); + Ok(Self { n, e }) + } +} + +#[cfg(feature = "ring")] +impl TryFrom<&RSAParams> for ring::signature::RsaKeyPair { + type Error = Error; + fn try_from(params: &RSAParams) -> Result { + let der = simple_asn1::der_encode(params)?; + let keypair = Self::from_der(&der)?; + Ok(keypair) + } +} + +#[cfg(test)] +mod tests { + use crate::JWK; + + const RSA_JSON: &str = include_str!("../../../../../tests/rsa2048-2020-08-25.json"); + + #[test] + fn rsa_from_str() { + let _key: JWK = serde_json::from_str(RSA_JSON).unwrap(); + } +} diff --git a/crates/jwk/src/utils.rs b/crates/jwk/src/utils.rs new file mode 100644 index 000000000..a641a38cb --- /dev/null +++ b/crates/jwk/src/utils.rs @@ -0,0 +1,36 @@ +use base64::Engine; +use serde::{Deserialize, Serialize}; +use zeroize::Zeroize; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Zeroize)] +#[serde(try_from = "String")] +#[serde(into = "Base64urlUIntString")] +pub struct Base64urlUInt(pub Vec); + +type Base64urlUIntString = String; + +const BASE64_URL_SAFE_INDIFFERENT_PAD: base64::engine::GeneralPurpose = + base64::engine::GeneralPurpose::new( + &base64::alphabet::URL_SAFE, + base64::engine::GeneralPurposeConfig::new() + .with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent), + ); + +impl TryFrom for Base64urlUInt { + type Error = base64::DecodeError; + fn try_from(data: String) -> Result { + Ok(Base64urlUInt(BASE64_URL_SAFE_INDIFFERENT_PAD.decode(data)?)) + } +} + +impl From<&Base64urlUInt> for String { + fn from(data: &Base64urlUInt) -> String { + base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(&data.0) + } +} + +impl From for Base64urlUIntString { + fn from(data: Base64urlUInt) -> Base64urlUIntString { + String::from(&data) + } +} diff --git a/crates/verification-methods/src/methods/unspecified/aleo_method_2021.rs b/crates/verification-methods/src/methods/unspecified/aleo_method_2021.rs index 07c32c273..7c378e9b2 100644 --- a/crates/verification-methods/src/methods/unspecified/aleo_method_2021.rs +++ b/crates/verification-methods/src/methods/unspecified/aleo_method_2021.rs @@ -66,7 +66,7 @@ impl AleoMethod2021 { key: &JWK, // FIXME: check key algorithm? bytes: &[u8], ) -> Result, MessageSignatureError> { - ssi_jwk::aleo::sign(bytes, key).map_err(|_| MessageSignatureError::InvalidSecretKey) + ssi_jwk::okp::aleo::sign(bytes, key).map_err(|_| MessageSignatureError::InvalidSecretKey) } pub fn verify_bytes( @@ -75,13 +75,13 @@ impl AleoMethod2021 { bytes: &[u8], signature: &[u8], ) -> Result { - match ssi_jwk::aleo::verify( + match ssi_jwk::okp::aleo::verify( bytes, &self.blockchain_account_id.account_address, signature, ) { Ok(()) => Ok(true), - Err(ssi_jwk::aleo::AleoVerifyError::InvalidSignature) => Ok(false), + Err(ssi_jwk::okp::aleo::AleoVerifyError::InvalidSignature) => Ok(false), Err(_) => Err(MessageSignatureError::InvalidSecretKey), } }