Browse Source

Add partial EdDSA support

Currently, OpenSSL does not have the required
`EVP_PKEY_get1_ED(25519|448)` functions, hence EdDSA has been partially
implemented and disabled. Once OpenSSL 3.0.0 is out and the `openssl`
crates implements the bindings to those functions, full EdDSA
implementation could be done and activated.
Conditional compilation has been implemented using `rustc-cfg`
instructions rather than features so it can be activated from the build
script depending on whether or not the cryptographic library supports
Ed25519 and Ed448.
7c664b1f1b
pull/39/head
Rodolphe Breard 4 years ago
parent
commit
4614d6c407
  1. 15
      CONTRIBUTING.md
  2. 1
      README.md
  3. 1
      acme_common/Cargo.toml
  4. 12
      acme_common/src/crypto/jws_signature_algorithm.rs
  5. 20
      acme_common/src/crypto/key_type.rs
  6. 51
      acme_common/src/crypto/openssl_certificate.rs
  7. 76
      acme_common/src/crypto/openssl_keys.rs
  8. 1
      acme_common/src/tests.rs
  9. 40
      acme_common/src/tests/certificate.rs
  10. 173
      acme_common/src/tests/crypto_keys.rs
  11. 62
      acme_common/src/tests/jws_signature_algorithm.rs
  12. 5
      tacd/src/main.rs

15
CONTRIBUTING.md

@ -13,6 +13,13 @@ Since the author is not a native English speaker, some of the texts used in this
## Work on dependencies
### rust-openssl
Although OpenSSL supports (Ed|X)(25519|448) since version 1.1.1, the `EVP_PKEY_get1_(ED|X)(25519|448)` functions, which are required for ACMEd, will only land in version 3.0.0. Being new function, bindings have to be written in the `openssl` crate. At first sight, such bindings should be added in the `openssl::pkey::PKey` struct along with the other `EVP_PKEY_get1_*` bindings.
- https://github.com/sfackler/rust-openssl/issues/1263
- https://github.com/openssl/openssl/commit/7c664b1f1b5f60bf896f5fdea5c08c401c541dfe
### botan and botan-sys
Although Botan isn't a dependency, it is considered for the replacement of OpenSSL as the default cryptographic API (although OpenSSL will be kept as an alternative). But before this can be done, the Botan crate need to support a few features:
@ -22,18 +29,10 @@ Although Botan isn't a dependency, it is considered for the replacement of OpenS
- Self-signed certificate generation (via `botan_sys::botan_x509_cert_gen_selfsigned`).
- CSR (requires to add bindings to [create_cert_req](https://botan.randombit.net/handbook/api_ref/x509.html#creating-pkcs-10-requests)) with DER export.
### attohttpc
Add an optional Botan support as the cryptographic library.
### rust-openssl
An improvement that would be appreciable is to add Curve 25519 support to the [openssl](https://crates.io/crates/openssl) crate.
- https://github.com/sfackler/rust-openssl/issues/947
- https://github.com/sfackler/rust-openssl/pull/1275
### Find or create a good template engine
As reported in [issue #8](https://github.com/breard-r/acmed/issues/8), there is currently no perfect template engine. A good way to help improve ACMEd would be to find or create one that supports all the listed requirements.

1
README.md

@ -37,6 +37,7 @@ The Automatic Certificate Management Environment (ACME), is an internet standard
## Planned features
- STAR certificates [RFC 8739](https://tools.ietf.org/html/rfc8739)
- EdDSA support: Ed25519 and Ed448 account keys and certificates
- Daemon and certificates management via the `acmectl` tool
- Nonce scoping configuration
- HTTP/2 support

1
acme_common/Cargo.toml

@ -15,7 +15,6 @@ name = "acme_common"
[features]
default = []
openssl_dyn = ["openssl", "openssl-sys"]
ed25519 = []
[dependencies]
attohttpc = { version = "0.15", default-features = false }

12
acme_common/src/crypto/jws_signature_algorithm.rs

@ -7,8 +7,10 @@ pub enum JwsSignatureAlgorithm {
Rs256,
Es256,
Es384,
#[cfg(feature = "ed25519")]
#[cfg(ed25519)]
Ed25519,
#[cfg(ed448)]
Ed448,
}
impl FromStr for JwsSignatureAlgorithm {
@ -19,8 +21,10 @@ impl FromStr for JwsSignatureAlgorithm {
"rs256" => Ok(JwsSignatureAlgorithm::Rs256),
"es256" => Ok(JwsSignatureAlgorithm::Es256),
"es384" => Ok(JwsSignatureAlgorithm::Es384),
#[cfg(feature = "ed25519")]
#[cfg(ed25519)]
"ed25519" => Ok(JwsSignatureAlgorithm::Ed25519),
#[cfg(ed448)]
"ed448" => Ok(JwsSignatureAlgorithm::Ed448),
_ => Err(format!("{}: unknown algorithm.", s).into()),
}
}
@ -32,8 +36,10 @@ impl fmt::Display for JwsSignatureAlgorithm {
JwsSignatureAlgorithm::Rs256 => "RS256",
JwsSignatureAlgorithm::Es256 => "ES256",
JwsSignatureAlgorithm::Es384 => "ES384",
#[cfg(feature = "ed25519")]
#[cfg(ed25519)]
JwsSignatureAlgorithm::Ed25519 => "Ed25519",
#[cfg(ed448)]
JwsSignatureAlgorithm::Ed448 => "Ed448",
};
write!(f, "{}", s)
}

20
acme_common/src/crypto/key_type.rs

@ -9,8 +9,10 @@ pub enum KeyType {
Rsa4096,
EcdsaP256,
EcdsaP384,
#[cfg(feature = "ed25519")]
#[cfg(ed25519)]
Ed25519,
#[cfg(ed448)]
Ed448,
}
impl KeyType {
@ -19,8 +21,10 @@ impl KeyType {
KeyType::Rsa2048 | KeyType::Rsa4096 => JwsSignatureAlgorithm::Rs256,
KeyType::EcdsaP256 => JwsSignatureAlgorithm::Es256,
KeyType::EcdsaP384 => JwsSignatureAlgorithm::Es384,
#[cfg(feature = "ed25519")]
#[cfg(ed25519)]
KeyType::Ed25519 => JwsSignatureAlgorithm::Ed25519,
#[cfg(ed448)]
KeyType::Ed448 => JwsSignatureAlgorithm::Ed448,
}
}
@ -28,8 +32,10 @@ impl KeyType {
let ok = match self {
KeyType::Rsa2048 | KeyType::Rsa4096 => *alg == JwsSignatureAlgorithm::Rs256,
KeyType::EcdsaP256 | KeyType::EcdsaP384 => *alg == self.get_default_signature_alg(),
#[cfg(feature = "ed25519")]
#[cfg(ed25519)]
KeyType::Ed25519 => *alg == self.get_default_signature_alg(),
#[cfg(ed448)]
KeyType::Ed448 => *alg == self.get_default_signature_alg(),
};
if ok {
Ok(())
@ -52,8 +58,10 @@ impl FromStr for KeyType {
"rsa4096" => Ok(KeyType::Rsa4096),
"ecdsa_p256" => Ok(KeyType::EcdsaP256),
"ecdsa_p384" => Ok(KeyType::EcdsaP384),
#[cfg(feature = "ed25519")]
#[cfg(ed25519)]
"ed25519" => Ok(KeyType::Ed25519),
#[cfg(ed448)]
"ed448" => Ok(KeyType::Ed448),
_ => Err(format!("{}: unknown algorithm.", s).into()),
}
}
@ -66,8 +74,10 @@ impl fmt::Display for KeyType {
KeyType::Rsa4096 => "rsa4096",
KeyType::EcdsaP256 => "ecdsa-p256",
KeyType::EcdsaP384 => "ecdsa-p384",
#[cfg(feature = "ed25519")]
#[cfg(ed25519)]
KeyType::Ed25519 => "ed25519",
#[cfg(ed448)]
KeyType::Ed448 => "ed448",
};
write!(f, "{}", s)
}

51
acme_common/src/crypto/openssl_certificate.rs

@ -66,9 +66,20 @@ impl X509Certificate {
Ok(native_tls::Certificate::from_pem(pem_data)?)
}
pub fn from_acme_ext(domain: &str, acme_ext: &str) -> Result<(KeyPair, Self), Error> {
let key_pair = gen_keypair(KeyType::EcdsaP256)?;
let inner_cert = gen_certificate(domain, &key_pair, acme_ext)?;
pub fn from_acme_ext(
domain: &str,
acme_ext: &str,
key_type: KeyType,
) -> Result<(KeyPair, Self), Error> {
let key_pair = gen_keypair(key_type)?;
#[cfg(not(any(ed25519, ed448)))]
let digest = MessageDigest::sha256();
#[cfg(any(ed25519, ed448))]
let digest = match key_pair.key_type {
KeyType::Ed25519 | KeyType::Ed448 => MessageDigest::null(),
_ => MessageDigest::sha256(),
};
let inner_cert = gen_certificate(domain, &key_pair, &digest, acme_ext)?;
let cert = X509Certificate { inner_cert };
Ok((key_pair, cert))
}
@ -114,7 +125,12 @@ impl X509Certificate {
}
}
fn gen_certificate(domain: &str, key_pair: &KeyPair, acme_ext: &str) -> Result<X509, Error> {
fn gen_certificate(
domain: &str,
key_pair: &KeyPair,
digest: &MessageDigest,
acme_ext: &str,
) -> Result<X509, Error> {
let mut x509_name = X509NameBuilder::new()?;
x509_name.append_entry_by_text("O", APP_ORG)?;
let ca_name = format!("{} TLS-ALPN-01 Authority", APP_NAME);
@ -142,19 +158,22 @@ fn gen_certificate(domain: &str, key_pair: &KeyPair, acme_ext: &str) -> Result<X
let san_ext = SubjectAlternativeName::new().dns(domain).build(&ctx)?;
builder.append_extension(san_ext)?;
let ctx = builder.x509v3_context(None, None);
let mut v: Vec<&str> = acme_ext.split('=').collect();
let value = v.pop().ok_or_else(|| Error::from(INVALID_EXT_MSG))?;
let acme_ext_name = v.pop().ok_or_else(|| Error::from(INVALID_EXT_MSG))?;
if !v.is_empty() {
return Err(Error::from(INVALID_EXT_MSG));
if !acme_ext.is_empty() {
let ctx = builder.x509v3_context(None, None);
let mut v: Vec<&str> = acme_ext.split('=').collect();
let value = v.pop().ok_or_else(|| Error::from(INVALID_EXT_MSG))?;
let acme_ext_name = v.pop().ok_or_else(|| Error::from(INVALID_EXT_MSG))?;
if !v.is_empty() {
return Err(Error::from(INVALID_EXT_MSG));
}
let acme_ext = X509Extension::new(None, Some(&ctx), &acme_ext_name, &value)
.map_err(|_| Error::from(INVALID_EXT_MSG))?;
builder
.append_extension(acme_ext)
.map_err(|_| Error::from(INVALID_EXT_MSG))?;
}
let acme_ext = X509Extension::new(None, Some(&ctx), &acme_ext_name, &value)
.map_err(|_| Error::from(INVALID_EXT_MSG))?;
builder
.append_extension(acme_ext)
.map_err(|_| Error::from(INVALID_EXT_MSG))?;
builder.sign(&key_pair.inner_key, MessageDigest::sha256())?;
builder.sign(&key_pair.inner_key, *digest)?;
let cert = builder.build();
Ok(cert)
}

76
acme_common/src/crypto/openssl_keys.rs

@ -32,6 +32,10 @@ macro_rules! get_key_type {
return Err("None: Unsupported EC key".into());
}
},
#[cfg(ed25519)]
Id::ED25519 => KeyType::Ed25519,
#[cfg(ed448)]
Id::ED448 => KeyType::Ed448,
_ => {
return Err("Unsupported key type".into());
}
@ -70,6 +74,10 @@ impl KeyPair {
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),
#[cfg(ed25519)]
JwsSignatureAlgorithm::Ed25519 => self.sign_eddsa(data),
#[cfg(ed448)]
JwsSignatureAlgorithm::Ed448 => self.sign_eddsa(data),
}
}
@ -94,6 +102,13 @@ impl KeyPair {
Ok(signature)
}
#[cfg(any(ed25519, ed448))]
fn sign_eddsa(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
let mut signer = Signer::new_without_digest(&self.inner_key)?;
let signature = signer.sign_oneshot_to_vec(data)?;
Ok(signature)
}
pub fn jwk_public_key(&self) -> Result<Value, Error> {
self.get_jwk_public_key(false)
}
@ -104,8 +119,12 @@ impl KeyPair {
fn get_jwk_public_key(&self, thumbprint: bool) -> Result<Value, Error> {
match self.key_type {
KeyType::EcdsaP256 | KeyType::EcdsaP384 => self.get_nist_ec_jwk(thumbprint),
KeyType::Rsa2048 | KeyType::Rsa4096 => self.get_rsa_jwk(thumbprint),
KeyType::EcdsaP256 | KeyType::EcdsaP384 => self.get_ecdsa_jwk(thumbprint),
#[cfg(ed25519)]
KeyType::Ed25519 => self.get_eddsa_jwk(thumbprint),
#[cfg(ed448)]
KeyType::Ed448 => self.get_eddsa_jwk(thumbprint),
}
}
@ -133,12 +152,12 @@ impl KeyPair {
Ok(jwk)
}
fn get_nist_ec_jwk(&self, thumbprint: bool) -> Result<Value, Error> {
fn get_ecdsa_jwk(&self, thumbprint: bool) -> Result<Value, Error> {
let (crv, alg, curve) = match self.key_type {
KeyType::EcdsaP256 => ("P-256", "ES256", Nid::X9_62_PRIME256V1),
KeyType::EcdsaP384 => ("P-384", "ES384", Nid::SECP384R1),
_ => {
return Err("Not a NIST elliptic curve.".into());
return Err("Not an ECDSA elliptic curve.".into());
}
};
let group = EcGroup::from_curve_name(curve).unwrap();
@ -171,6 +190,37 @@ impl KeyPair {
};
Ok(jwk)
}
#[cfg(any(ed25519, ed448))]
fn get_eddsa_jwk(&self, thumbprint: bool) -> Result<Value, Error> {
let crv = match self.key_type {
#[cfg(ed25519)]
KeyType::Ed25519 => "Ed25519",
#[cfg(ed448)]
KeyType::Ed448 => "Ed448",
_ => {
return Err("Not an EdDSA elliptic curve.".into());
}
};
let x = "";
let jwk = if thumbprint {
json!({
"crv": crv,
"kty": "OKP",
"x": x,
})
} else {
json!({
"alg": "EdDSA",
"crv": crv,
"kty": "OKP",
"use": "sig",
"x": x,
})
};
//Ok(jwk)
Err("TODO: implement get_eddsa_jwk (require binding to EVP_PKEY_get1_ED25519, which requires OpenSSL 3.0)".into())
}
}
fn gen_rsa_pair(nb_bits: u32) -> Result<PKey<Private>, Error> {
@ -190,12 +240,28 @@ fn gen_ec_pair(nid: Nid) -> Result<PKey<Private>, Error> {
Ok(pk)
}
#[cfg(ed25519)]
fn gen_ed25519_pair() -> Result<PKey<Private>, Error> {
let pk = PKey::generate_ed25519().map_err(|_| Error::from(""))?;
Ok(pk)
}
#[cfg(ed448)]
fn gen_ed448_pair() -> Result<PKey<Private>, Error> {
let pk = PKey::generate_ed448().map_err(|_| Error::from(""))?;
Ok(pk)
}
pub fn gen_keypair(key_type: KeyType) -> Result<KeyPair, Error> {
let priv_key = match key_type {
KeyType::EcdsaP256 => gen_ec_pair(Nid::X9_62_PRIME256V1),
KeyType::EcdsaP384 => gen_ec_pair(Nid::SECP384R1),
KeyType::Rsa2048 => gen_rsa_pair(2048),
KeyType::Rsa4096 => gen_rsa_pair(4096),
KeyType::EcdsaP256 => gen_ec_pair(Nid::X9_62_PRIME256V1),
KeyType::EcdsaP384 => gen_ec_pair(Nid::SECP384R1),
#[cfg(ed25519)]
KeyType::Ed25519 => gen_ed25519_pair(),
#[cfg(ed448)]
KeyType::Ed448 => gen_ed448_pair(),
}
.map_err(|_| Error::from(format!("Unable to generate a {} key pair.", key_type)))?;
let key_pair = KeyPair {

1
acme_common/src/tests.rs

@ -1,3 +1,4 @@
mod certificate;
mod crypto_keys;
mod idna;
mod jws_signature_algorithm;

40
acme_common/src/tests/certificate.rs

@ -1,4 +1,4 @@
use crate::crypto::X509Certificate;
use crate::crypto::{KeyType, X509Certificate};
use std::collections::HashSet;
use std::iter::FromIterator;
@ -82,3 +82,41 @@ fn test_san_domains_and_ip() {
let crt = X509Certificate::from_pem(CERTIFICATE_P256_DOMAINS_IP_PEM.as_bytes()).unwrap();
assert_eq!(crt.subject_alt_names(), san);
}
#[test]
fn generate_rsa2048_certificate() {
let (kp, _) = X509Certificate::from_acme_ext("example.org", "", KeyType::Rsa2048).unwrap();
assert_eq!(kp.key_type, KeyType::Rsa2048);
}
#[test]
fn generate_rsa4096_certificate() {
let (kp, _) = X509Certificate::from_acme_ext("example.org", "", KeyType::Rsa4096).unwrap();
assert_eq!(kp.key_type, KeyType::Rsa4096);
}
#[test]
fn generate_ecdsa_p256_certificate() {
let (kp, _) = X509Certificate::from_acme_ext("example.org", "", KeyType::EcdsaP256).unwrap();
assert_eq!(kp.key_type, KeyType::EcdsaP256);
}
#[test]
fn generate_ecdsa_p384_certificate() {
let (kp, _) = X509Certificate::from_acme_ext("example.org", "", KeyType::EcdsaP384).unwrap();
assert_eq!(kp.key_type, KeyType::EcdsaP384);
}
#[cfg(ed25519)]
#[test]
fn generate_ed25519_certificate() {
let (kp, _) = X509Certificate::from_acme_ext("example.org", "", KeyType::Ed25519).unwrap();
assert_eq!(kp.key_type, KeyType::Ed25519);
}
#[cfg(ed448)]
#[test]
fn generate_ed448_certificate() {
let (kp, _) = X509Certificate::from_acme_ext("example.org", "", KeyType::Ed448).unwrap();
assert_eq!(kp.key_type, KeyType::Ed448);
}

173
acme_common/src/tests/crypto_keys.rs

@ -1,13 +1,5 @@
use crate::crypto::KeyPair;
const KEY_ECDSA_P256_PEM: &str = r#"-----BEGIN PRIVATE KEY-----
MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCQc9OXwvygYqOFT4fN
NpXynr1lu+1sSplFdYoWu7hE4g==
-----END PRIVATE KEY-----"#;
const KEY_ECDSA_P384_PEM: &str = r#"-----BEGIN PRIVATE KEY-----
ME4CAQAwEAYHKoZIzj0CAQYFK4EEACIENzA1AgEBBDCMsN9kHPueLABk+0PKi7WO
PO2/53dpt/yV5zOPrYPEoKs4t973nbt46IUN19lLF/s=
-----END PRIVATE KEY-----"#;
const KEY_RSA_2048_PEM: &str = r#"-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzfwZGF8zKNAg2
9mdZ9ieE7V2clY3oeI+2V7eV5kUwOGqhhpDaDyDmju+l0dKFwF8xeDeeGmTSED10
@ -88,6 +80,95 @@ Q2ZTyps7X64dx6yOIRv6pPd3qZGRz2VoKW2x/sLoeErPsVtUW0u+NSKgR6O5sh7v
Mc5vg/2W9HWaAXdjyrXIJyypitp0Q9M1cSowzt/BaWNvb3i/En8uEXR5zZjl/CFG
yr9E4nQyE5YlYlPUK6iIRBu9j1N2MhY=
-----END PRIVATE KEY-----"#;
const KEY_ECDSA_P256_PEM: &str = r#"-----BEGIN PRIVATE KEY-----
MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCQc9OXwvygYqOFT4fN
NpXynr1lu+1sSplFdYoWu7hE4g==
-----END PRIVATE KEY-----"#;
const KEY_ECDSA_P384_PEM: &str = r#"-----BEGIN PRIVATE KEY-----
ME4CAQAwEAYHKoZIzj0CAQYFK4EEACIENzA1AgEBBDCMsN9kHPueLABk+0PKi7WO
PO2/53dpt/yV5zOPrYPEoKs4t973nbt46IUN19lLF/s=
-----END PRIVATE KEY-----"#;
#[cfg(ed25519)]
const KEY_ECDSA_ED25519_PEM: &str = r#"-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIJhpRNsiUzoWqNkpJKCtKV5++Tttz3locu1gQKkQnrOa
-----END PRIVATE KEY-----"#;
#[cfg(ed448)]
const KEY_ECDSA_ED448_PEM: &str = r#"-----BEGIN PRIVATE KEY-----
MEcCAQAwBQYDK2VxBDsEOcFBwsH4zU7u5RgFh48MgJPzXyjN5uXxDapZv4rG6opU
uMXco2JR1CSjKWgqgu1CAKadJIYiv2EgIw==
-----END PRIVATE KEY-----"#;
#[test]
fn test_rsa_2048_jwk() {
let k = KeyPair::from_pem(KEY_RSA_2048_PEM.as_bytes()).unwrap();
let jwk = k.jwk_public_key().unwrap();
assert!(jwk.is_object());
let jwk = jwk.as_object().unwrap();
assert_eq!(jwk.len(), 5);
assert!(jwk.contains_key("kty"));
assert!(jwk.contains_key("e"));
assert!(jwk.contains_key("n"));
assert!(jwk.contains_key("use"));
assert!(jwk.contains_key("alg"));
assert_eq!(jwk.get("kty").unwrap(), "RSA");
assert_eq!(jwk.get("e").unwrap(), "AQAB");
assert_eq!(jwk.get("n").unwrap(), "s38GRhfMyjQINvZnWfYnhO1dnJWN6HiPtle3leZFMDhqoYaQ2g8g5o7vpdHShcBfMXg3nhpk0hA9dHt_GbB6iRdHGaig6wd4TngwLJ-2erLR3_0WaM0DubAJmaTe4ND9JYVyZ8gK_li-fF-NZFrrn4j1W71EUL_7St8jdivqwujHWdpS7C3piosAJW8hqz31M7lXOnV61PCb15JMLiKQMhBCezk13QWk-FQBx7ZtmA1iMFvt-Drcqdhb20iWLCMCYwtNLez4ZmofWzI4sqQmQejpJ2Ve1gGeeY2hf68qQEQf8804nksp-EIv1Y4qVhO5zvxo7m8s6ybUJqvqOz5u9Q");
assert_eq!(jwk.get("use").unwrap(), "sig");
assert_eq!(jwk.get("alg").unwrap(), "RS256");
}
#[test]
fn test_rsa_2048_jwk_thumbprint() {
let k = KeyPair::from_pem(KEY_RSA_2048_PEM.as_bytes()).unwrap();
let jwk = k.jwk_public_key_thumbprint().unwrap();
assert!(jwk.is_object());
let jwk = jwk.as_object().unwrap();
assert_eq!(jwk.len(), 3);
assert!(jwk.contains_key("kty"));
assert!(jwk.contains_key("e"));
assert!(jwk.contains_key("n"));
assert!(!jwk.contains_key("use"));
assert!(!jwk.contains_key("alg"));
assert_eq!(jwk.get("kty").unwrap(), "RSA");
assert_eq!(jwk.get("e").unwrap(), "AQAB");
assert_eq!(jwk.get("n").unwrap(), "s38GRhfMyjQINvZnWfYnhO1dnJWN6HiPtle3leZFMDhqoYaQ2g8g5o7vpdHShcBfMXg3nhpk0hA9dHt_GbB6iRdHGaig6wd4TngwLJ-2erLR3_0WaM0DubAJmaTe4ND9JYVyZ8gK_li-fF-NZFrrn4j1W71EUL_7St8jdivqwujHWdpS7C3piosAJW8hqz31M7lXOnV61PCb15JMLiKQMhBCezk13QWk-FQBx7ZtmA1iMFvt-Drcqdhb20iWLCMCYwtNLez4ZmofWzI4sqQmQejpJ2Ve1gGeeY2hf68qQEQf8804nksp-EIv1Y4qVhO5zvxo7m8s6ybUJqvqOz5u9Q");
}
#[test]
fn test_rsa_4096_jwk() {
let k = KeyPair::from_pem(KEY_RSA_4096_PEM.as_bytes()).unwrap();
let jwk = k.jwk_public_key().unwrap();
assert!(jwk.is_object());
let jwk = jwk.as_object().unwrap();
assert_eq!(jwk.len(), 5);
assert!(jwk.contains_key("kty"));
assert!(jwk.contains_key("e"));
assert!(jwk.contains_key("n"));
assert!(jwk.contains_key("use"));
assert!(jwk.contains_key("alg"));
assert_eq!(jwk.get("kty").unwrap(), "RSA");
assert_eq!(jwk.get("e").unwrap(), "AQAB");
assert_eq!(jwk.get("n").unwrap(), "jm0Jh8Zk-aRqUxME0yLeHhBtdDFq6IvVesr_ffFwGqtlI4tIa6MqYuuOE5qlqoBrxt1fpccqGF82j47JqeoWerkNdZxYh1uKGQa9G2vB4OBOFHxYujT-NNPPlwnVPrp1_pjxK0hiOGzZRliNhO65lMbEueQCaLbQ6wstr4aVsWKiw8O4MG8Cc2ZegkWlYg0ZHOAn-uJlAt17NJfX8x576XRwX2KCssPYeyiwBukRne3ahVVqrc35EmV9JH3CxDaG9MeBLo7FUV5P1GNOIs313cbAgdMYV6Ahr2LvsaLucFvhSwmHh42--meDui-0wdFILjrz7MsITZy4xXjl8_zqMZ2dBaO38wP5o9VCfCXaVlD3IYB9A4-Y9IQ6wEVeodzGloPQyddUZ70kZ7O3E1kYu0tBKhoAL1VcKiUvOj1ABGooqf8QIAWCiAWqDqkZJ5HiR61NC9lJ3MH-aqYXzeHUuOxdlfr3oWPQKlRyYoUW93xD4dXjHQyHsIS4gWYE9ZJ9aIomNHj93UPNfR8ScxQTQBJV9Ttgl_vOUdmlasTmo0OXbTLMexzWCVISvUuoBoTcTY4DXCFeYHz3EE3ijJ9fnDkX3SsvV43x8X58py9rEyAQnRxfeahpWEoxW6TA5qBxg1GMBQu6CY2MupqsmI0XHTXzy8xCt4Pqd_a5zP6Wh_E");
assert_eq!(jwk.get("use").unwrap(), "sig");
assert_eq!(jwk.get("alg").unwrap(), "RS256");
}
#[test]
fn test_rsa_4096_jwk_thumbprint() {
let k = KeyPair::from_pem(KEY_RSA_4096_PEM.as_bytes()).unwrap();
let jwk = k.jwk_public_key_thumbprint().unwrap();
assert!(jwk.is_object());
let jwk = jwk.as_object().unwrap();
assert_eq!(jwk.len(), 3);
assert!(jwk.contains_key("kty"));
assert!(jwk.contains_key("e"));
assert!(jwk.contains_key("n"));
assert!(!jwk.contains_key("use"));
assert!(!jwk.contains_key("alg"));
assert_eq!(jwk.get("kty").unwrap(), "RSA");
assert_eq!(jwk.get("e").unwrap(), "AQAB");
assert_eq!(jwk.get("n").unwrap(), "jm0Jh8Zk-aRqUxME0yLeHhBtdDFq6IvVesr_ffFwGqtlI4tIa6MqYuuOE5qlqoBrxt1fpccqGF82j47JqeoWerkNdZxYh1uKGQa9G2vB4OBOFHxYujT-NNPPlwnVPrp1_pjxK0hiOGzZRliNhO65lMbEueQCaLbQ6wstr4aVsWKiw8O4MG8Cc2ZegkWlYg0ZHOAn-uJlAt17NJfX8x576XRwX2KCssPYeyiwBukRne3ahVVqrc35EmV9JH3CxDaG9MeBLo7FUV5P1GNOIs313cbAgdMYV6Ahr2LvsaLucFvhSwmHh42--meDui-0wdFILjrz7MsITZy4xXjl8_zqMZ2dBaO38wP5o9VCfCXaVlD3IYB9A4-Y9IQ6wEVeodzGloPQyddUZ70kZ7O3E1kYu0tBKhoAL1VcKiUvOj1ABGooqf8QIAWCiAWqDqkZJ5HiR61NC9lJ3MH-aqYXzeHUuOxdlfr3oWPQKlRyYoUW93xD4dXjHQyHsIS4gWYE9ZJ9aIomNHj93UPNfR8ScxQTQBJV9Ttgl_vOUdmlasTmo0OXbTLMexzWCVISvUuoBoTcTY4DXCFeYHz3EE3ijJ9fnDkX3SsvV43x8X58py9rEyAQnRxfeahpWEoxW6TA5qBxg1GMBQu6CY2MupqsmI0XHTXzy8xCt4Pqd_a5zP6Wh_E");
}
#[test]
fn test_ecdsa_p256_jwk() {
@ -193,74 +274,90 @@ fn test_ecdsa_p384_jwk_thumbprint() {
);
}
#[cfg(ed25519)]
#[test]
fn test_rsa_2048_jwk() {
let k = KeyPair::from_pem(KEY_RSA_2048_PEM.as_bytes()).unwrap();
fn test_ed25519_jwk() {
let k = KeyPair::from_pem(KEY_ECDSA_ED25519_PEM.as_bytes()).unwrap();
let jwk = k.jwk_public_key().unwrap();
assert!(jwk.is_object());
let jwk = jwk.as_object().unwrap();
assert_eq!(jwk.len(), 5);
assert!(jwk.contains_key("kty"));
assert!(jwk.contains_key("e"));
assert!(jwk.contains_key("n"));
assert!(jwk.contains_key("crv"));
assert!(jwk.contains_key("x"));
assert!(jwk.contains_key("use"));
assert!(jwk.contains_key("alg"));
assert_eq!(jwk.get("kty").unwrap(), "RSA");
assert_eq!(jwk.get("e").unwrap(), "AQAB");
assert_eq!(jwk.get("n").unwrap(), "s38GRhfMyjQINvZnWfYnhO1dnJWN6HiPtle3leZFMDhqoYaQ2g8g5o7vpdHShcBfMXg3nhpk0hA9dHt_GbB6iRdHGaig6wd4TngwLJ-2erLR3_0WaM0DubAJmaTe4ND9JYVyZ8gK_li-fF-NZFrrn4j1W71EUL_7St8jdivqwujHWdpS7C3piosAJW8hqz31M7lXOnV61PCb15JMLiKQMhBCezk13QWk-FQBx7ZtmA1iMFvt-Drcqdhb20iWLCMCYwtNLez4ZmofWzI4sqQmQejpJ2Ve1gGeeY2hf68qQEQf8804nksp-EIv1Y4qVhO5zvxo7m8s6ybUJqvqOz5u9Q");
assert_eq!(jwk.get("kty").unwrap(), "OKP");
assert_eq!(jwk.get("crv").unwrap(), "Ed25519");
assert_eq!(
jwk.get("x").unwrap(),
"DUX9ja8pq2wfkxuIaHzmhkdcVXMav_3rk5Y5ozOcp4o"
);
assert_eq!(jwk.get("use").unwrap(), "sig");
assert_eq!(jwk.get("alg").unwrap(), "RS256");
assert_eq!(jwk.get("alg").unwrap(), "EdDSA");
}
#[cfg(ed25519)]
#[test]
fn test_rsa_2048_jwk_thumbprint() {
let k = KeyPair::from_pem(KEY_RSA_2048_PEM.as_bytes()).unwrap();
fn test_ed25519_jwk_thumbprint() {
let k = KeyPair::from_pem(KEY_ECDSA_ED25519_PEM.as_bytes()).unwrap();
let jwk = k.jwk_public_key_thumbprint().unwrap();
assert!(jwk.is_object());
let jwk = jwk.as_object().unwrap();
assert_eq!(jwk.len(), 3);
assert!(jwk.contains_key("kty"));
assert!(jwk.contains_key("e"));
assert!(jwk.contains_key("n"));
assert!(jwk.contains_key("crv"));
assert!(jwk.contains_key("x"));
assert!(!jwk.contains_key("use"));
assert!(!jwk.contains_key("alg"));
assert_eq!(jwk.get("kty").unwrap(), "RSA");
assert_eq!(jwk.get("e").unwrap(), "AQAB");
assert_eq!(jwk.get("n").unwrap(), "s38GRhfMyjQINvZnWfYnhO1dnJWN6HiPtle3leZFMDhqoYaQ2g8g5o7vpdHShcBfMXg3nhpk0hA9dHt_GbB6iRdHGaig6wd4TngwLJ-2erLR3_0WaM0DubAJmaTe4ND9JYVyZ8gK_li-fF-NZFrrn4j1W71EUL_7St8jdivqwujHWdpS7C3piosAJW8hqz31M7lXOnV61PCb15JMLiKQMhBCezk13QWk-FQBx7ZtmA1iMFvt-Drcqdhb20iWLCMCYwtNLez4ZmofWzI4sqQmQejpJ2Ve1gGeeY2hf68qQEQf8804nksp-EIv1Y4qVhO5zvxo7m8s6ybUJqvqOz5u9Q");
assert_eq!(jwk.get("kty").unwrap(), "OKP");
assert_eq!(jwk.get("crv").unwrap(), "Ed25519");
assert_eq!(
jwk.get("x").unwrap(),
"DUX9ja8pq2wfkxuIaHzmhkdcVXMav_3rk5Y5ozOcp4o"
);
}
#[cfg(ed448)]
#[test]
fn test_rsa_4096_jwk() {
let k = KeyPair::from_pem(KEY_RSA_4096_PEM.as_bytes()).unwrap();
fn test_ed448_jwk() {
let k = KeyPair::from_pem(KEY_ECDSA_ED448_PEM.as_bytes()).unwrap();
let jwk = k.jwk_public_key().unwrap();
assert!(jwk.is_object());
let jwk = jwk.as_object().unwrap();
assert_eq!(jwk.len(), 5);
assert!(jwk.contains_key("kty"));
assert!(jwk.contains_key("e"));
assert!(jwk.contains_key("n"));
assert!(jwk.contains_key("crv"));
assert!(jwk.contains_key("x"));
assert!(jwk.contains_key("use"));
assert!(jwk.contains_key("alg"));
assert_eq!(jwk.get("kty").unwrap(), "RSA");
assert_eq!(jwk.get("e").unwrap(), "AQAB");
assert_eq!(jwk.get("n").unwrap(), "jm0Jh8Zk-aRqUxME0yLeHhBtdDFq6IvVesr_ffFwGqtlI4tIa6MqYuuOE5qlqoBrxt1fpccqGF82j47JqeoWerkNdZxYh1uKGQa9G2vB4OBOFHxYujT-NNPPlwnVPrp1_pjxK0hiOGzZRliNhO65lMbEueQCaLbQ6wstr4aVsWKiw8O4MG8Cc2ZegkWlYg0ZHOAn-uJlAt17NJfX8x576XRwX2KCssPYeyiwBukRne3ahVVqrc35EmV9JH3CxDaG9MeBLo7FUV5P1GNOIs313cbAgdMYV6Ahr2LvsaLucFvhSwmHh42--meDui-0wdFILjrz7MsITZy4xXjl8_zqMZ2dBaO38wP5o9VCfCXaVlD3IYB9A4-Y9IQ6wEVeodzGloPQyddUZ70kZ7O3E1kYu0tBKhoAL1VcKiUvOj1ABGooqf8QIAWCiAWqDqkZJ5HiR61NC9lJ3MH-aqYXzeHUuOxdlfr3oWPQKlRyYoUW93xD4dXjHQyHsIS4gWYE9ZJ9aIomNHj93UPNfR8ScxQTQBJV9Ttgl_vOUdmlasTmo0OXbTLMexzWCVISvUuoBoTcTY4DXCFeYHz3EE3ijJ9fnDkX3SsvV43x8X58py9rEyAQnRxfeahpWEoxW6TA5qBxg1GMBQu6CY2MupqsmI0XHTXzy8xCt4Pqd_a5zP6Wh_E");
assert_eq!(jwk.get("kty").unwrap(), "OKP");
assert_eq!(jwk.get("crv").unwrap(), "Ed448");
assert_eq!(
jwk.get("x").unwrap(),
"ib9GZ8b1hip3UMzkkNBdMF4JWBTZojxsNHK-jQBH94SY3boVs4Oeo291E1dGXz7RUMqIXjkSbU4EA"
);
assert_eq!(jwk.get("use").unwrap(), "sig");
assert_eq!(jwk.get("alg").unwrap(), "RS256");
assert_eq!(jwk.get("alg").unwrap(), "EdDSA");
}
#[cfg(ed448)]
#[test]
fn test_rsa_4096_jwk_thumbprint() {
let k = KeyPair::from_pem(KEY_RSA_4096_PEM.as_bytes()).unwrap();
fn test_ed448_jwk_thumbprint() {
let k = KeyPair::from_pem(KEY_ECDSA_ED448_PEM.as_bytes()).unwrap();
let jwk = k.jwk_public_key_thumbprint().unwrap();
assert!(jwk.is_object());
let jwk = jwk.as_object().unwrap();
assert_eq!(jwk.len(), 3);
assert!(jwk.contains_key("kty"));
assert!(jwk.contains_key("e"));
assert!(jwk.contains_key("n"));
assert!(jwk.contains_key("crv"));
assert!(jwk.contains_key("x"));
assert!(!jwk.contains_key("use"));
assert!(!jwk.contains_key("alg"));
assert_eq!(jwk.get("kty").unwrap(), "RSA");
assert_eq!(jwk.get("e").unwrap(), "AQAB");
assert_eq!(jwk.get("n").unwrap(), "jm0Jh8Zk-aRqUxME0yLeHhBtdDFq6IvVesr_ffFwGqtlI4tIa6MqYuuOE5qlqoBrxt1fpccqGF82j47JqeoWerkNdZxYh1uKGQa9G2vB4OBOFHxYujT-NNPPlwnVPrp1_pjxK0hiOGzZRliNhO65lMbEueQCaLbQ6wstr4aVsWKiw8O4MG8Cc2ZegkWlYg0ZHOAn-uJlAt17NJfX8x576XRwX2KCssPYeyiwBukRne3ahVVqrc35EmV9JH3CxDaG9MeBLo7FUV5P1GNOIs313cbAgdMYV6Ahr2LvsaLucFvhSwmHh42--meDui-0wdFILjrz7MsITZy4xXjl8_zqMZ2dBaO38wP5o9VCfCXaVlD3IYB9A4-Y9IQ6wEVeodzGloPQyddUZ70kZ7O3E1kYu0tBKhoAL1VcKiUvOj1ABGooqf8QIAWCiAWqDqkZJ5HiR61NC9lJ3MH-aqYXzeHUuOxdlfr3oWPQKlRyYoUW93xD4dXjHQyHsIS4gWYE9ZJ9aIomNHj93UPNfR8ScxQTQBJV9Ttgl_vOUdmlasTmo0OXbTLMexzWCVISvUuoBoTcTY4DXCFeYHz3EE3ijJ9fnDkX3SsvV43x8X58py9rEyAQnRxfeahpWEoxW6TA5qBxg1GMBQu6CY2MupqsmI0XHTXzy8xCt4Pqd_a5zP6Wh_E");
assert_eq!(jwk.get("kty").unwrap(), "OKP");
assert_eq!(jwk.get("crv").unwrap(), "Ed448");
assert_eq!(
jwk.get("x").unwrap(),
"ib9GZ8b1hip3UMzkkNBdMF4JWBTZojxsNHK-jQBH94SY3boVs4Oeo291E1dGXz7RUMqIXjkSbU4EA"
);
}

62
acme_common/src/tests/jws_signature_algorithm.rs

@ -0,0 +1,62 @@
use crate::crypto::{gen_keypair, JwsSignatureAlgorithm, KeyType};
const TEST_DATA: &'static [u8] = &[72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33];
#[test]
fn test_rs256_sign_rsa2048() {
let k = gen_keypair(KeyType::Rsa2048).unwrap();
let _ = k.sign(&JwsSignatureAlgorithm::Rs256, TEST_DATA).unwrap();
}
#[test]
fn test_rs256_sign_rsa4096() {
let k = gen_keypair(KeyType::Rsa4096).unwrap();
let _ = k.sign(&JwsSignatureAlgorithm::Rs256, TEST_DATA).unwrap();
}
#[test]
fn test_rs256_sign_ecdsa() {
let k = gen_keypair(KeyType::EcdsaP256).unwrap();
let res = k.sign(&JwsSignatureAlgorithm::Rs256, TEST_DATA);
assert!(res.is_err());
}
#[test]
fn test_es256_sign_p256() {
let k = gen_keypair(KeyType::EcdsaP256).unwrap();
let _ = k.sign(&JwsSignatureAlgorithm::Es256, TEST_DATA).unwrap();
}
#[test]
fn test_es256_sign_p384() {
let k = gen_keypair(KeyType::EcdsaP384).unwrap();
let res = k.sign(&JwsSignatureAlgorithm::Es256, TEST_DATA);
assert!(res.is_err());
}
#[test]
fn test_es384_sign_p384() {
let k = gen_keypair(KeyType::EcdsaP384).unwrap();
let _ = k.sign(&JwsSignatureAlgorithm::Es384, TEST_DATA).unwrap();
}
#[test]
fn test_es384_sign_p256() {
let k = gen_keypair(KeyType::EcdsaP256).unwrap();
let res = k.sign(&JwsSignatureAlgorithm::Es384, TEST_DATA);
assert!(res.is_err());
}
#[cfg(ed25519)]
#[test]
fn test_ed25519_sign() {
let k = gen_keypair(KeyType::Ed25519).unwrap();
let _ = k.sign(&JwsSignatureAlgorithm::Ed25519, TEST_DATA).unwrap();
}
#[cfg(ed448)]
#[test]
fn test_ed448_sign() {
let k = gen_keypair(KeyType::Ed448).unwrap();
let _ = k.sign(&JwsSignatureAlgorithm::Ed448, TEST_DATA).unwrap();
}

5
tacd/src/main.rs

@ -1,7 +1,7 @@
mod openssl_server;
use crate::openssl_server::start as server_start;
use acme_common::crypto::X509Certificate;
use acme_common::crypto::{KeyType, X509Certificate};
use acme_common::error::Error;
use acme_common::{clean_pid_file, to_idna};
use clap::{App, Arg, ArgMatches};
@ -13,6 +13,7 @@ const APP_NAME: &str = env!("CARGO_PKG_NAME");
const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
const DEFAULT_PID_FILE: &str = "/var/run/tacd.pid";
const DEFAULT_LISTEN_ADDR: &str = "127.0.0.1:5001";
const DEFAULT_CRT_KEY_TYPE: KeyType = KeyType::EcdsaP256;
const ALPN_ACME_PROTO_NAME: &[u8] = b"\x0aacme-tls/1";
fn read_line(path: Option<&str>) -> Result<String, Error> {
@ -49,7 +50,7 @@ fn init(cnf: &ArgMatches) -> Result<(), Error> {
let domain = to_idna(&domain)?;
let ext = get_acme_value(cnf, "acme-ext", "acme-ext-file")?;
let listen_addr = cnf.value_of("listen").unwrap_or(DEFAULT_LISTEN_ADDR);
let (pk, cert) = X509Certificate::from_acme_ext(&domain, &ext)?;
let (pk, cert) = X509Certificate::from_acme_ext(&domain, &ext, DEFAULT_CRT_KEY_TYPE)?;
info!("Starting {} on {} for {}", APP_NAME, listen_addr, domain);
server_start(listen_addr, &cert, &pk)?;
Ok(())

Loading…
Cancel
Save