Browse Source

Add support for Ed25519 and Ed448 account keys and certificates

Fixes #36
pull/53/head
Rodolphe Bréard 4 years ago
parent
commit
32830cbd04
  1. 1
      CHANGELOG.md
  2. 3
      README.md
  3. 4
      acme_common/build.rs
  4. 37
      acme_common/src/crypto/openssl_keys.rs
  5. 52
      acme_common/src/tests/crypto_keys.rs
  6. 8
      man/en/acmed.toml.5

1
CHANGELOG.md

@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Add support for Ed25519 and Ed448 account keys and certificates.
- In addition to `restart`, the Polkit rule also allows the `reload`, `try-restart`, `reload-or-restart` and `try-reload-or-restart` verbs.

3
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, ECDSA P-384 and ECDSA P-521 certificates
- RSA 2048, RSA 4096, ECDSA P-256, ECDSA P-384, ECDSA P-521, Ed25519 and Ed448 certificates and account keys
- Internationalized domain names support
- Fully customizable challenge validation action
- Fully customizable archiving method (yes, you can use git or anything else)
@ -38,7 +38,6 @@ 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
- HTTP/2 support

4
acme_common/build.rs

@ -8,9 +8,13 @@ macro_rules! set_rustc_env_var {
fn main() {
if env::var("DEP_OPENSSL_VERSION_NUMBER").is_ok() {
println!("cargo:rustc-cfg=ed25519");
println!("cargo:rustc-cfg=ed448");
set_rustc_env_var!("ACMED_TLS_LIB_NAME", "OpenSSL");
}
if env::var("DEP_OPENSSL_LIBRESSL_VERSION_NUMBER").is_ok() {
println!("cargo:rustc-cfg=ed25519");
println!("cargo:rustc-cfg=ed448");
set_rustc_env_var!("ACMED_TLS_LIB_NAME", "LibreSSL");
}
}

37
acme_common/src/crypto/openssl_keys.rs

@ -247,12 +247,40 @@ impl KeyPair {
return Err("not an EdDSA elliptic curve".into());
}
};
let x = "";
/*
* /!\ WARNING: HAZARDOUS AND UGLY CODE /!\
*
* I couldn't find a way to get the value of `x` using the OpenSSL
* interface, therefore I had to hack my way arround.
*
* The idea behind this hack is to export the public key in PEM, then
* get the PEM base64 part, convert it to base64url without padding
* and finally truncate the first part so only the value of `x`
* remains.
*/
// -----BEGIN UGLY-----
let mut x = String::new();
let public_pem = self.public_key_to_pem()?;
let public_pem = String::from_utf8(public_pem)?;
for pem_line in public_pem.lines() {
if !pem_line.is_empty() && !pem_line.starts_with("-----") {
x += &pem_line
.trim()
.trim_end_matches('=')
.replace("/", "_")
.replace("+", "-");
}
}
x.replace_range(..16, "");
// -----END UGLY-----
let jwk = if thumbprint {
json!({
"crv": crv,
"kty": "OKP",
"x": x,
"x": &x,
})
} else {
json!({
@ -260,11 +288,10 @@ impl KeyPair {
"crv": crv,
"kty": "OKP",
"use": "sig",
"x": x,
"x": &x,
})
};
//Ok(jwk)
Err("TODO: implement get_eddsa_jwk (require binding to EVP_PKEY_get1_ED25519, which requires OpenSSL 3.0)".into())
Ok(jwk)
}
}

52
acme_common/src/tests/crypto_keys.rs

@ -92,6 +92,10 @@ PO2/53dpt/yV5zOPrYPEoKs4t973nbt46IUN19lLF/s=
const KEY_ECDSA_ED25519_PEM: &str = r#"-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIJhpRNsiUzoWqNkpJKCtKV5++Tttz3locu1gQKkQnrOa
-----END PRIVATE KEY-----"#;
#[cfg(ed25519)]
const KEY_ECDSA_ED25519_PEM_BIS: &str = r#"-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIKa3WD0qeUToPQKSwa9cTsLPgCovqAtXMhlMX2KYBz0o
-----END PRIVATE KEY-----"#;
#[cfg(ed448)]
const KEY_ECDSA_ED448_PEM: &str = r#"-----BEGIN PRIVATE KEY-----
MEcCAQAwBQYDK2VxBDsEOcFBwsH4zU7u5RgFh48MgJPzXyjN5uXxDapZv4rG6opU
@ -318,6 +322,50 @@ fn test_ed25519_jwk_thumbprint() {
);
}
#[cfg(ed25519)]
#[test]
fn test_ed25519_jwk_bis() {
let k = KeyPair::from_pem(KEY_ECDSA_ED25519_PEM_BIS.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("crv"));
assert!(jwk.contains_key("x"));
assert!(jwk.contains_key("use"));
assert!(jwk.contains_key("alg"));
assert_eq!(jwk.get("kty").unwrap(), "OKP");
assert_eq!(jwk.get("crv").unwrap(), "Ed25519");
assert_eq!(
jwk.get("x").unwrap(),
"i9K0eV5qOJ_l_TWjWFLm8R-JbyGdlqFFeL_J0eEXFnc"
);
assert_eq!(jwk.get("use").unwrap(), "sig");
assert_eq!(jwk.get("alg").unwrap(), "EdDSA");
}
#[cfg(ed25519)]
#[test]
fn test_ed25519_jwk_thumbprint_bis() {
let k = KeyPair::from_pem(KEY_ECDSA_ED25519_PEM_BIS.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("crv"));
assert!(jwk.contains_key("x"));
assert!(!jwk.contains_key("use"));
assert!(!jwk.contains_key("alg"));
assert_eq!(jwk.get("kty").unwrap(), "OKP");
assert_eq!(jwk.get("crv").unwrap(), "Ed25519");
assert_eq!(
jwk.get("x").unwrap(),
"i9K0eV5qOJ_l_TWjWFLm8R-JbyGdlqFFeL_J0eEXFnc"
);
}
#[cfg(ed448)]
#[test]
fn test_ed448_jwk() {
@ -335,7 +383,7 @@ fn test_ed448_jwk() {
assert_eq!(jwk.get("crv").unwrap(), "Ed448");
assert_eq!(
jwk.get("x").unwrap(),
"ib9GZ8b1hip3UMzkkNBdMF4JWBTZojxsNHK-jQBH94SY3boVs4Oeo291E1dGXz7RUMqIXjkSbU4EA"
"b9GZ8b1hip3UMzkkNBdMF4JWBTZojxsNHK-jQBH94SY3boVs4Oeo291E1dGXz7RUMqIXjkSbU4EA"
);
assert_eq!(jwk.get("use").unwrap(), "sig");
assert_eq!(jwk.get("alg").unwrap(), "EdDSA");
@ -358,6 +406,6 @@ fn test_ed448_jwk_thumbprint() {
assert_eq!(jwk.get("crv").unwrap(), "Ed448");
assert_eq!(
jwk.get("x").unwrap(),
"ib9GZ8b1hip3UMzkkNBdMF4JWBTZojxsNHK-jQBH94SY3boVs4Oeo291E1dGXz7RUMqIXjkSbU4EA"
"b9GZ8b1hip3UMzkkNBdMF4JWBTZojxsNHK-jQBH94SY3boVs4Oeo291E1dGXz7RUMqIXjkSbU4EA"
);
}

8
man/en/acmed.toml.5

@ -64,6 +64,10 @@ Names of hooks that will be called during operations on the account storage file
Name of the asymmetric cryptography algorithm used to generate the key pair. Possible values are:
.Bl -dash -compact
.It
ed25519
.It
ed448
.It
ecdsa_p256
.Aq default
.It
@ -176,6 +180,10 @@ The IP address.
Name of the asymmetric cryptography algorithm used to generate the certificate's key pair. Possible values are:
.Bl -dash -compact
.It
ed25519
.It
ed448
.It
ecdsa_p256
.It
ecdsa_p384

Loading…
Cancel
Save