From 8477d927a190aa3ad16d319a53c19741ac63c711 Mon Sep 17 00:00:00 2001 From: Rodolphe Breard Date: Sun, 20 Sep 2020 11:09:14 +0200 Subject: [PATCH] Add support for NIST P-521 curve and ES512 signatures --- CHANGELOG.md | 1 + README.md | 2 +- .../src/crypto/jws_signature_algorithm.rs | 3 ++ acme_common/src/crypto/key_type.rs | 9 ++++- acme_common/src/crypto/openssl_keys.rs | 35 +++++++++++++++++-- man/en/acmed.toml.5 | 6 ++++ 6 files changed, 51 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5047e8..cf36457 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Some subject attributes can now be specified. +- Support for NIST P-521 certificates and account keys. ## [0.11.0] - 2020-09-19 diff --git a/README.md b/README.md index 27905c6..42a324d 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The Automatic Certificate Management Environment (ACME), is an internet standard - http-01, dns-01 and [tls-alpn-01](https://tools.ietf.org/html/rfc8737) challenges - IP identifier validation extension [RFC 8738](https://tools.ietf.org/html/rfc8738) -- RSA 2048, RSA 4096, ECDSA P-256 and ECDSA P-384 certificates +- RSA 2048, RSA 4096, ECDSA P-256, ECDSA P-384 and ECDSA P-521 certificates - Internationalized domain names support - Fully customizable challenge validation action - Fully customizable archiving method (yes, you can use git or anything else) diff --git a/acme_common/src/crypto/jws_signature_algorithm.rs b/acme_common/src/crypto/jws_signature_algorithm.rs index b06beda..8b2403c 100644 --- a/acme_common/src/crypto/jws_signature_algorithm.rs +++ b/acme_common/src/crypto/jws_signature_algorithm.rs @@ -10,6 +10,7 @@ pub enum JwsSignatureAlgorithm { Rs256, Es256, Es384, + Es512, #[cfg(ed25519)] Ed25519, #[cfg(ed448)] @@ -27,6 +28,7 @@ impl FromStr for JwsSignatureAlgorithm { "rs256" => Ok(JwsSignatureAlgorithm::Rs256), "es256" => Ok(JwsSignatureAlgorithm::Es256), "es384" => Ok(JwsSignatureAlgorithm::Es384), + "es512" => Ok(JwsSignatureAlgorithm::Es512), #[cfg(ed25519)] "ed25519" => Ok(JwsSignatureAlgorithm::Ed25519), #[cfg(ed448)] @@ -45,6 +47,7 @@ impl fmt::Display for JwsSignatureAlgorithm { JwsSignatureAlgorithm::Rs256 => "RS256", JwsSignatureAlgorithm::Es256 => "ES256", JwsSignatureAlgorithm::Es384 => "ES384", + JwsSignatureAlgorithm::Es512 => "ES512", #[cfg(ed25519)] JwsSignatureAlgorithm::Ed25519 => "Ed25519", #[cfg(ed448)] diff --git a/acme_common/src/crypto/key_type.rs b/acme_common/src/crypto/key_type.rs index 5c066ab..eb08822 100644 --- a/acme_common/src/crypto/key_type.rs +++ b/acme_common/src/crypto/key_type.rs @@ -9,6 +9,7 @@ pub enum KeyType { Rsa4096, EcdsaP256, EcdsaP384, + EcdsaP521, #[cfg(ed25519)] Ed25519, #[cfg(ed448)] @@ -21,6 +22,7 @@ impl KeyType { KeyType::Rsa2048 | KeyType::Rsa4096 => JwsSignatureAlgorithm::Rs256, KeyType::EcdsaP256 => JwsSignatureAlgorithm::Es256, KeyType::EcdsaP384 => JwsSignatureAlgorithm::Es384, + KeyType::EcdsaP521 => JwsSignatureAlgorithm::Es512, #[cfg(ed25519)] KeyType::Ed25519 => JwsSignatureAlgorithm::Ed25519, #[cfg(ed448)] @@ -31,7 +33,9 @@ impl KeyType { pub fn check_alg_compatibility(&self, alg: &JwsSignatureAlgorithm) -> Result<(), Error> { let ok = match self { KeyType::Rsa2048 | KeyType::Rsa4096 => *alg == JwsSignatureAlgorithm::Rs256, - KeyType::EcdsaP256 | KeyType::EcdsaP384 => *alg == self.get_default_signature_alg(), + KeyType::EcdsaP256 | KeyType::EcdsaP384 | KeyType::EcdsaP521 => { + *alg == self.get_default_signature_alg() + } #[cfg(ed25519)] KeyType::Ed25519 => *alg == self.get_default_signature_alg(), #[cfg(ed448)] @@ -54,6 +58,7 @@ impl KeyType { "rsa4096", "ecdsa-p256", "ecdsa-p384", + "ecdsa-p521", #[cfg(ed25519)] "ed25519", #[cfg(ed448)] @@ -71,6 +76,7 @@ impl FromStr for KeyType { "rsa4096" => Ok(KeyType::Rsa4096), "ecdsa_p256" => Ok(KeyType::EcdsaP256), "ecdsa_p384" => Ok(KeyType::EcdsaP384), + "ecdsa_p521" => Ok(KeyType::EcdsaP521), #[cfg(ed25519)] "ed25519" => Ok(KeyType::Ed25519), #[cfg(ed448)] @@ -87,6 +93,7 @@ impl fmt::Display for KeyType { KeyType::Rsa4096 => "rsa4096", KeyType::EcdsaP256 => "ecdsa-p256", KeyType::EcdsaP384 => "ecdsa-p384", + KeyType::EcdsaP521 => "ecdsa-p521", #[cfg(ed25519)] KeyType::Ed25519 => "ed25519", #[cfg(ed448)] diff --git a/acme_common/src/crypto/openssl_keys.rs b/acme_common/src/crypto/openssl_keys.rs index f6aae3f..5f97f12 100644 --- a/acme_common/src/crypto/openssl_keys.rs +++ b/acme_common/src/crypto/openssl_keys.rs @@ -25,6 +25,7 @@ macro_rules! get_key_type { Id::EC => match $key.ec_key()?.group().curve_name() { Some(Nid::X9_62_PRIME256V1) => KeyType::EcdsaP256, Some(Nid::SECP384R1) => KeyType::EcdsaP384, + Some(Nid::SECP521R1) => KeyType::EcdsaP521, Some(nid) => { return Err(format!("{:?}: unsupported EC key", nid).into()); } @@ -43,6 +44,21 @@ macro_rules! get_key_type { }; } +macro_rules! get_ecdsa_sig_part { + ($part: expr, $size: ident) => {{ + let mut p = $part.to_vec(); + let length = p.len(); + if length != $size { + let mut s: Vec = Vec::with_capacity($size); + s.resize_with($size - length, || 0); + s.append(&mut p); + s + } else { + p + } + }}; +} + #[derive(Clone, Debug)] pub struct KeyPair { pub key_type: KeyType, @@ -95,6 +111,7 @@ impl KeyPair { JwsSignatureAlgorithm::Rs256 => self.sign_rsa(&MessageDigest::sha256(), data), JwsSignatureAlgorithm::Es256 => self.sign_ecdsa(&HashFunction::Sha256, data), JwsSignatureAlgorithm::Es384 => self.sign_ecdsa(&HashFunction::Sha384, data), + JwsSignatureAlgorithm::Es512 => self.sign_ecdsa(&HashFunction::Sha512, data), #[cfg(ed25519)] JwsSignatureAlgorithm::Ed25519 => self.sign_eddsa(data), #[cfg(ed448)] @@ -112,8 +129,16 @@ impl KeyPair { fn sign_ecdsa(&self, hash_func: &HashFunction, data: &[u8]) -> Result, Error> { let fingerprint = hash_func.hash(data); let signature = EcdsaSig::sign(&fingerprint, self.inner_key.ec_key()?.as_ref())?; - let r = signature.r().to_vec(); - let mut s = signature.s().to_vec(); + let sig_size = match self.key_type { + KeyType::EcdsaP256 => 32, + KeyType::EcdsaP384 => 48, + KeyType::EcdsaP521 => 66, + _ => { + return Err("not an ecdsa key".into()); + } + }; + let r = get_ecdsa_sig_part!(signature.r(), sig_size); + let mut s = get_ecdsa_sig_part!(signature.s(), sig_size); let mut signature = r; signature.append(&mut s); Ok(signature) @@ -137,7 +162,9 @@ impl KeyPair { fn get_jwk_public_key(&self, thumbprint: bool) -> Result { match self.key_type { KeyType::Rsa2048 | KeyType::Rsa4096 => self.get_rsa_jwk(thumbprint), - KeyType::EcdsaP256 | KeyType::EcdsaP384 => self.get_ecdsa_jwk(thumbprint), + KeyType::EcdsaP256 | KeyType::EcdsaP384 | KeyType::EcdsaP521 => { + self.get_ecdsa_jwk(thumbprint) + } #[cfg(ed25519)] KeyType::Ed25519 => self.get_eddsa_jwk(thumbprint), #[cfg(ed448)] @@ -173,6 +200,7 @@ impl KeyPair { let (crv, alg, curve) = match self.key_type { KeyType::EcdsaP256 => ("P-256", "ES256", Nid::X9_62_PRIME256V1), KeyType::EcdsaP384 => ("P-384", "ES384", Nid::SECP384R1), + KeyType::EcdsaP521 => ("P-521", "ES512", Nid::SECP521R1), _ => { return Err("not an ECDSA elliptic curve".into()); } @@ -275,6 +303,7 @@ pub fn gen_keypair(key_type: KeyType) -> Result { KeyType::Rsa4096 => gen_rsa_pair(4096), KeyType::EcdsaP256 => gen_ec_pair(Nid::X9_62_PRIME256V1), KeyType::EcdsaP384 => gen_ec_pair(Nid::SECP384R1), + KeyType::EcdsaP521 => gen_ec_pair(Nid::SECP521R1), #[cfg(ed25519)] KeyType::Ed25519 => gen_ed25519_pair(), #[cfg(ed448)] diff --git a/man/en/acmed.toml.5 b/man/en/acmed.toml.5 index b137357..e9f1dc0 100644 --- a/man/en/acmed.toml.5 +++ b/man/en/acmed.toml.5 @@ -183,6 +183,8 @@ ecdsa_p256 .Aq default .It ecdsa_p384 +.It +ecdsa_p521 .El .It Cm signature_algorithm Ar string Name of the signature algorithm used to sign the messages sent to the endpoint as defined in @@ -195,6 +197,8 @@ RS256 ES256 .It ES384 +.It +ES512 .El .It Ic env Ar table Table of environment variables that will be accessible from hooks. @@ -301,6 +305,8 @@ rsa4096 ecdsa_p256 .It ecdsa_p384 +.It +ecdsa_p521 .El .It Ic csr_digest Ar string Name of the certificate's signing request digest algorithm. Possible values are: