Browse Source

Refactor the JWS signature algorithm management

Being tied with the key type, the signature algorithm should therefore
be at the same place than the key type, hence `acme_common::crypto`.
This reorganization will allow to specify the account key type as well
as the signature algorithm in the configuration.
pull/39/head
Rodolphe Breard 4 years ago
parent
commit
636fbf9cf6
  1. 2
      acme_common/src/crypto.rs
  2. 60
      acme_common/src/crypto/jws_signature_algorithm.rs
  3. 30
      acme_common/src/crypto/key_type.rs
  4. 17
      acme_common/src/crypto/openssl_keys.rs
  5. 9
      acmed/src/acme_proto/account.rs
  6. 24
      acmed/src/jws.rs
  7. 104
      acmed/src/jws/algorithms.rs
  8. 2
      acmed/src/main.rs

2
acme_common/src/crypto.rs

@ -1,3 +1,4 @@
mod jws_signature_algorithm;
mod key_type; mod key_type;
mod openssl_certificate; mod openssl_certificate;
mod openssl_hash; mod openssl_hash;
@ -7,6 +8,7 @@ pub const DEFAULT_ALGO: &str = "rsa2048";
pub const TLS_LIB_NAME: &str = env!("ACMED_TLS_LIB_NAME"); pub const TLS_LIB_NAME: &str = env!("ACMED_TLS_LIB_NAME");
pub const TLS_LIB_VERSION: &str = env!("ACMED_TLS_LIB_VERSION"); pub const TLS_LIB_VERSION: &str = env!("ACMED_TLS_LIB_VERSION");
pub use jws_signature_algorithm::JwsSignatureAlgorithm;
pub use key_type::KeyType; pub use key_type::KeyType;
pub use openssl_certificate::{Csr, X509Certificate}; pub use openssl_certificate::{Csr, X509Certificate};
pub use openssl_hash::{sha256, sha384}; pub use openssl_hash::{sha256, sha384};

60
acme_common/src/crypto/jws_signature_algorithm.rs

@ -0,0 +1,60 @@
use crate::error::Error;
use std::fmt;
use std::str::FromStr;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum JwsSignatureAlgorithm {
Rs256,
Es256,
Es384,
Ed25519,
}
impl FromStr for JwsSignatureAlgorithm {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Error> {
match s.to_lowercase().as_str() {
"rs256" => Ok(JwsSignatureAlgorithm::Rs256),
"es256" => Ok(JwsSignatureAlgorithm::Es256),
"es384" => Ok(JwsSignatureAlgorithm::Es384),
"ed25519" => Ok(JwsSignatureAlgorithm::Ed25519),
_ => Err(format!("{}: unknown algorithm.", s).into()),
}
}
}
impl fmt::Display for JwsSignatureAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
JwsSignatureAlgorithm::Rs256 => "RS256",
JwsSignatureAlgorithm::Es256 => "ES256",
JwsSignatureAlgorithm::Es384 => "ES384",
JwsSignatureAlgorithm::Ed25519 => "Ed25519",
};
write!(f, "{}", s)
}
}
#[cfg(test)]
mod tests {
use super::JwsSignatureAlgorithm;
use std::str::FromStr;
#[test]
fn test_es256_from_str() {
let variants = ["ES256", "Es256", "es256"];
for v in variants.iter() {
let a = JwsSignatureAlgorithm::from_str(v);
assert!(a.is_ok());
let a = a.unwrap();
assert_eq!(a, JwsSignatureAlgorithm::Es256);
}
}
#[test]
fn test_es256_to_str() {
let a = JwsSignatureAlgorithm::Es256;
assert_eq!(a.to_string().as_str(), "ES256");
}
}

30
acme_common/src/crypto/key_type.rs

@ -1,3 +1,4 @@
use crate::crypto::JwsSignatureAlgorithm;
use crate::error::Error; use crate::error::Error;
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
@ -11,6 +12,35 @@ pub enum KeyType {
Rsa4096, Rsa4096,
} }
impl KeyType {
pub fn get_default_signature_alg(&self) -> JwsSignatureAlgorithm {
match self {
KeyType::Curve25519 => JwsSignatureAlgorithm::Ed25519,
KeyType::EcdsaP256 => JwsSignatureAlgorithm::Es256,
KeyType::EcdsaP384 => JwsSignatureAlgorithm::Es384,
KeyType::Rsa2048 | KeyType::Rsa4096 => JwsSignatureAlgorithm::Rs256,
}
}
pub fn check_alg_compatibility(&self, alg: &JwsSignatureAlgorithm) -> Result<(), Error> {
let ok = match self {
KeyType::Curve25519 | KeyType::EcdsaP256 | KeyType::EcdsaP384 => {
*alg == self.get_default_signature_alg()
}
KeyType::Rsa2048 | KeyType::Rsa4096 => *alg == JwsSignatureAlgorithm::Rs256,
};
if ok {
Ok(())
} else {
let err_msg = format!(
"Incompatible signature algorithm: {} cannot be used with an {} key.",
alg, self
);
Err(err_msg.into())
}
}
}
impl FromStr for KeyType { impl FromStr for KeyType {
type Err = Error; type Err = Error;

17
acme_common/src/crypto/openssl_keys.rs

@ -1,5 +1,5 @@
use crate::b64_encode; use crate::b64_encode;
use crate::crypto::KeyType;
use crate::crypto::{JwsSignatureAlgorithm, KeyType};
use crate::error::Error; use crate::error::Error;
use openssl::bn::{BigNum, BigNumContext}; use openssl::bn::{BigNum, BigNumContext};
use openssl::ec::{Asn1Flag, EcGroup, EcKey}; use openssl::ec::{Asn1Flag, EcGroup, EcKey};
@ -64,12 +64,15 @@ impl KeyPair {
self.inner_key.public_key_to_pem().map_err(Error::from) self.inner_key.public_key_to_pem().map_err(Error::from)
} }
pub fn sign(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
match self.key_type {
KeyType::Curve25519 => Err("Curve25519 signatures are not implemented yet".into()),
KeyType::EcdsaP256 => self.sign_ecdsa(&crate::crypto::sha256, data),
KeyType::EcdsaP384 => self.sign_ecdsa(&crate::crypto::sha384, data),
KeyType::Rsa2048 | KeyType::Rsa4096 => self.sign_rsa(&MessageDigest::sha256(), data),
pub fn sign(&self, alg: &JwsSignatureAlgorithm, data: &[u8]) -> Result<Vec<u8>, Error> {
let _ = self.key_type.check_alg_compatibility(alg)?;
match alg {
JwsSignatureAlgorithm::Rs256 => self.sign_rsa(&MessageDigest::sha256(), data),
JwsSignatureAlgorithm::Es256 => self.sign_ecdsa(&crate::crypto::sha256, data),
JwsSignatureAlgorithm::Es384 => self.sign_ecdsa(&crate::crypto::sha384, data),
JwsSignatureAlgorithm::Ed25519 => {
Err("Curve25519 signatures are not implemented yet".into())
}
} }
} }

9
acmed/src/acme_proto/account.rs

@ -2,12 +2,10 @@ use crate::acme_proto::http;
use crate::acme_proto::structs::Account; use crate::acme_proto::structs::Account;
use crate::certificate::Certificate; use crate::certificate::Certificate;
use crate::endpoint::Endpoint; use crate::endpoint::Endpoint;
use crate::jws::algorithms::SignatureAlgorithm;
use crate::jws::encode_jwk; use crate::jws::encode_jwk;
use crate::storage; use crate::storage;
use acme_common::crypto::KeyPair;
use acme_common::crypto::{gen_keypair, KeyPair};
use acme_common::error::Error; use acme_common::error::Error;
use std::str::FromStr;
pub struct AccountManager { pub struct AccountManager {
pub key_pair: KeyPair, pub key_pair: KeyPair,
@ -41,9 +39,8 @@ impl AccountManager {
pub fn init_account(cert: &Certificate) -> Result<(), Error> { pub fn init_account(cert: &Certificate) -> Result<(), Error> {
if !storage::account_files_exists(cert) { if !storage::account_files_exists(cert) {
// TODO: allow to change the signature algo
let sign_alg = SignatureAlgorithm::from_str(crate::DEFAULT_JWS_SIGN_ALGO)?;
let key_pair = sign_alg.gen_key_pair()?;
// TODO: allow to change the account key type
let key_pair = gen_keypair(crate::DEFAULT_ACCOUNT_KEY_TYPE)?;
storage::set_account_keypair(cert, &key_pair)?; storage::set_account_keypair(cert, &key_pair)?;
cert.info(&format!("Account {} created", &cert.account.name)); cert.info(&format!("Account {} created", &cert.account.name));
} else { } else {

24
acmed/src/jws.rs

@ -1,12 +1,9 @@
use crate::jws::algorithms::SignatureAlgorithm;
use acme_common::b64_encode; use acme_common::b64_encode;
use acme_common::crypto::KeyPair;
use acme_common::crypto::{JwsSignatureAlgorithm, KeyPair};
use acme_common::error::Error; use acme_common::error::Error;
use serde::Serialize; use serde::Serialize;
use serde_json::value::Value; use serde_json::value::Value;
pub mod algorithms;
#[derive(Serialize)] #[derive(Serialize)]
struct JwsData { struct JwsData {
protected: String, protected: String,
@ -30,11 +27,16 @@ struct JwsProtectedHeaderKid {
url: String, url: String,
} }
fn get_data(key_pair: &KeyPair, protected: &str, payload: &[u8]) -> Result<String, Error> {
fn get_data(
key_pair: &KeyPair,
sign_alg: &JwsSignatureAlgorithm,
protected: &str,
payload: &[u8],
) -> Result<String, Error> {
let protected = b64_encode(protected); let protected = b64_encode(protected);
let payload = b64_encode(payload); let payload = b64_encode(payload);
let signing_input = format!("{}.{}", protected, payload); let signing_input = format!("{}.{}", protected, payload);
let signature = key_pair.sign(signing_input.as_bytes())?;
let signature = key_pair.sign(sign_alg, signing_input.as_bytes())?;
let signature = b64_encode(&signature); let signature = b64_encode(&signature);
let data = JwsData { let data = JwsData {
protected, protected,
@ -51,7 +53,8 @@ pub fn encode_jwk(
url: &str, url: &str,
nonce: &str, nonce: &str,
) -> Result<String, Error> { ) -> Result<String, Error> {
let sign_alg = SignatureAlgorithm::from_pkey(key_pair)?;
// TODO: allow to change the signature algo
let sign_alg = key_pair.key_type.get_default_signature_alg();
let protected = JwsProtectedHeaderJwk { let protected = JwsProtectedHeaderJwk {
alg: sign_alg.to_string(), alg: sign_alg.to_string(),
jwk: key_pair.jwk_public_key()?, jwk: key_pair.jwk_public_key()?,
@ -59,7 +62,7 @@ pub fn encode_jwk(
url: url.into(), url: url.into(),
}; };
let protected = serde_json::to_string(&protected)?; let protected = serde_json::to_string(&protected)?;
get_data(key_pair, &protected, payload)
get_data(key_pair, &sign_alg, &protected, payload)
} }
pub fn encode_kid( pub fn encode_kid(
@ -69,7 +72,8 @@ pub fn encode_kid(
url: &str, url: &str,
nonce: &str, nonce: &str,
) -> Result<String, Error> { ) -> Result<String, Error> {
let sign_alg = SignatureAlgorithm::from_pkey(key_pair)?;
// TODO: allow to change the signature algo
let sign_alg = key_pair.key_type.get_default_signature_alg();
let protected = JwsProtectedHeaderKid { let protected = JwsProtectedHeaderKid {
alg: sign_alg.to_string(), alg: sign_alg.to_string(),
kid: key_id.to_string(), kid: key_id.to_string(),
@ -77,7 +81,7 @@ pub fn encode_kid(
url: url.into(), url: url.into(),
}; };
let protected = serde_json::to_string(&protected)?; let protected = serde_json::to_string(&protected)?;
get_data(key_pair, &protected, payload)
get_data(key_pair, &sign_alg, &protected, payload)
} }
#[cfg(test)] #[cfg(test)]

104
acmed/src/jws/algorithms.rs

@ -1,104 +0,0 @@
use acme_common::crypto::{gen_keypair, KeyPair, KeyType};
use acme_common::error::Error;
use std::fmt;
use std::str::FromStr;
#[derive(Debug, PartialEq, Eq)]
pub enum SignatureAlgorithm {
Rs256,
Es256,
Es384,
}
impl fmt::Display for SignatureAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
SignatureAlgorithm::Rs256 => "RS256",
SignatureAlgorithm::Es256 => "ES256",
SignatureAlgorithm::Es384 => "ES384",
};
write!(f, "{}", s)
}
}
impl FromStr for SignatureAlgorithm {
type Err = Error;
fn from_str(data: &str) -> Result<Self, Self::Err> {
match data.to_lowercase().as_str() {
"rs256" => Ok(SignatureAlgorithm::Rs256),
"es256" => Ok(SignatureAlgorithm::Es256),
"es384" => Ok(SignatureAlgorithm::Es384),
_ => Err(format!("{}: unknown signature algorithm", data).into()),
}
}
}
impl SignatureAlgorithm {
pub fn from_pkey(key_pair: &KeyPair) -> Result<Self, Error> {
match key_pair.key_type {
KeyType::Rsa2048 => Ok(SignatureAlgorithm::Rs256),
KeyType::Rsa4096 => Ok(SignatureAlgorithm::Rs256),
KeyType::EcdsaP256 => Ok(SignatureAlgorithm::Es256),
KeyType::EcdsaP384 => Ok(SignatureAlgorithm::Es384),
t => Err(format!("{}: unsupported key type", t).into()),
}
}
pub fn gen_key_pair(&self) -> Result<KeyPair, Error> {
match self {
SignatureAlgorithm::Rs256 => gen_keypair(KeyType::Rsa2048),
SignatureAlgorithm::Es256 => gen_keypair(KeyType::EcdsaP256),
SignatureAlgorithm::Es384 => gen_keypair(KeyType::EcdsaP384),
}
}
}
#[cfg(test)]
mod tests {
use super::SignatureAlgorithm;
use acme_common::crypto::KeyPair;
use std::str::FromStr;
#[test]
fn test_es256_from_str() {
let variants = ["ES256", "Es256", "es256"];
for v in variants.iter() {
let a = SignatureAlgorithm::from_str(v);
assert!(a.is_ok());
let a = a.unwrap();
assert_eq!(a, SignatureAlgorithm::Es256);
}
}
#[test]
fn test_es256_to_str() {
let a = SignatureAlgorithm::Es256;
assert_eq!(a.to_string().as_str(), "ES256");
}
#[test]
fn test_eddsa_ed25519_from_str() {
let variants = ["ES256", "Es256", "es256"];
for v in variants.iter() {
let a = SignatureAlgorithm::from_str(v);
assert!(a.is_ok());
let a = a.unwrap();
assert_eq!(a, SignatureAlgorithm::Es256);
}
}
#[test]
fn test_from_p256() {
let pem = b"-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg6To1BW8qTehGhPca
0eMcW8iQU4yA02dvtKkuqfny4HChRANCAAQwxx+j3wYGzD5LSFNBTLlT7J+7rWrq
4BGdR8705iwpBeOQgMpLj+9vuFutlVtmoYpJSYa9+49Hxz8aCe1AQeWt
-----END PRIVATE KEY-----";
let k = KeyPair::from_pem(pem).unwrap();
let s = SignatureAlgorithm::from_pkey(&k);
assert!(s.is_ok());
let s = s.unwrap();
assert_eq!(s, SignatureAlgorithm::Es256)
}
}

2
acmed/src/main.rs

@ -26,7 +26,7 @@ pub const DEFAULT_CERT_FILE_MODE: u32 = 0o644;
pub const DEFAULT_PK_FILE_MODE: u32 = 0o600; pub const DEFAULT_PK_FILE_MODE: u32 = 0o600;
pub const DEFAULT_ACCOUNT_FILE_MODE: u32 = 0o600; pub const DEFAULT_ACCOUNT_FILE_MODE: u32 = 0o600;
pub const DEFAULT_KP_REUSE: bool = false; pub const DEFAULT_KP_REUSE: bool = false;
pub const DEFAULT_JWS_SIGN_ALGO: &str = "ES256";
pub const DEFAULT_ACCOUNT_KEY_TYPE: crypto::KeyType = crypto::KeyType::EcdsaP256;
pub const DEFAULT_POOL_NB_TRIES: usize = 20; pub const DEFAULT_POOL_NB_TRIES: usize = 20;
pub const DEFAULT_POOL_WAIT_SEC: u64 = 5; pub const DEFAULT_POOL_WAIT_SEC: u64 = 5;
pub const DEFAULT_HTTP_FAIL_NB_RETRY: usize = 10; pub const DEFAULT_HTTP_FAIL_NB_RETRY: usize = 10;

Loading…
Cancel
Save