From 62924c37e47b4097b1e602d48289a86fb9548631 Mon Sep 17 00:00:00 2001 From: William Brown Date: Fri, 6 Feb 2026 12:13:17 +1000 Subject: [PATCH 1/3] Resolve Clippy bugs, test short HS256 tokens --- src/crypto/hs256.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++- src/jwe.rs | 16 ---------------- src/lib.rs | 3 ++- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/crypto/hs256.rs b/src/crypto/hs256.rs index 67b2ad3..cdbbdc0 100644 --- a/src/crypto/hs256.rs +++ b/src/crypto/hs256.rs @@ -213,8 +213,9 @@ fn kid(skey: &HmacSha256Key) -> String { #[cfg(test)] mod tests { - use super::JwsHs256Signer; + use super::{hmac_s256, JwsHs256Signer}; use crate::compact::JwsCompact; + use crate::jws::JwsBuilder; use crate::traits::*; use base64::{engine::general_purpose, Engine as _}; use std::convert::TryFrom; @@ -295,4 +296,47 @@ mod tests { let released = jws_signer.verify(&jwsc).expect("Unable to validate jws"); trace!("rel -> {:?}", released); } + + #[test] + fn hs256_shortform() { + use uuid::Uuid; + + /* + use serde::{Serialize, Deserialize}; + #[derive(Default, Debug, Serialize, Clone, Deserialize, PartialEq)] + struct Inner { + exp: i64, + id: Uuid, + } + */ + + let _ = tracing_subscriber::fmt::try_init(); + + let skey = hmac_s256::new_key(); + + let mut jws_signer = JwsHs256Signer::from(skey); + let mut kid = crate::traits::JwsSigner::get_kid(&jws_signer).to_string(); + kid.truncate(12); + jws_signer.set_kid(&kid); + + let id = Uuid::default(); + let payload = id.as_bytes().to_vec(); + + let jws = JwsBuilder::from(payload) + // .set_typ(Some("a1")) + .build(); + + /* + let inner = Inner { exp: i64::MAX, id }; + let jws = JwsBuilder::into_json(&inner).unwrap() + .build(); + */ + + let jwsc = jws_signer.sign(&jws).expect("Failed to sign"); + + warn!(?jwsc, length = %jwsc.to_string().len()); + warn!(%jwsc); + + assert!(jwsc.to_string().len() < 128); + } } diff --git a/src/jwe.rs b/src/jwe.rs index 3282576..203c97e 100644 --- a/src/jwe.rs +++ b/src/jwe.rs @@ -65,19 +65,3 @@ impl Jwe { serde_json::from_slice(self.payload()) } } - -mod tests { - use serde::{Deserialize, Serialize}; - - #[allow(dead_code)] - #[derive(Default, Debug, Serialize, Clone, Deserialize, PartialEq)] - struct CustomExtension { - my_exten: String, - } - - #[test] - fn test_encrypt_and_decrypt() { - - // TODO: work out if this is just not implemented yet, or if it's designed to trigger something weird? - } -} diff --git a/src/lib.rs b/src/lib.rs index 01583b0..20856dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,7 +121,8 @@ pub use crate::jwt::{Jwt, JwtSigned, JwtUnverified}; pub use crate::oidc::{OidcClaims, OidcSigned, OidcSubject, OidcToken, OidcUnverified}; pub use crate::traits::{JwsSigner, JwsSignerToVerifier, JwsVerifier}; -const KID_LEN: usize = 32; +// 96 bits is more than enough to uniquely ID a key +const KID_LEN: usize = 12; pub(crate) fn btreemap_empty( m: &std::collections::BTreeMap, From 73a147465481cfe70175a9e457a1ab0e4fe6ae08 Mon Sep 17 00:00:00 2001 From: William Brown Date: Sat, 7 Feb 2026 10:43:36 +1000 Subject: [PATCH 2/3] Review feedback --- src/crypto/hs256.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/crypto/hs256.rs b/src/crypto/hs256.rs index cdbbdc0..29fa227 100644 --- a/src/crypto/hs256.rs +++ b/src/crypto/hs256.rs @@ -301,15 +301,6 @@ mod tests { fn hs256_shortform() { use uuid::Uuid; - /* - use serde::{Serialize, Deserialize}; - #[derive(Default, Debug, Serialize, Clone, Deserialize, PartialEq)] - struct Inner { - exp: i64, - id: Uuid, - } - */ - let _ = tracing_subscriber::fmt::try_init(); let skey = hmac_s256::new_key(); @@ -326,12 +317,6 @@ mod tests { // .set_typ(Some("a1")) .build(); - /* - let inner = Inner { exp: i64::MAX, id }; - let jws = JwsBuilder::into_json(&inner).unwrap() - .build(); - */ - let jwsc = jws_signer.sign(&jws).expect("Failed to sign"); warn!(?jwsc, length = %jwsc.to_string().len()); From 88458c778d32979f661211b2eb298bf343203218 Mon Sep 17 00:00:00 2001 From: William Brown Date: Tue, 10 Feb 2026 13:56:26 +1000 Subject: [PATCH 3/3] Update X509 test --- Cargo.toml | 2 +- src/crypto/mod.rs | 2 +- src/crypto/x509.rs | 100 ++++++++++++++++++++++++++++++++++++++- src/jws.rs | 115 +++++++++++++++++++++++++++++++-------------- 4 files changed, 179 insertions(+), 40 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 56527da..f2ac9ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ all-features = true [dependencies] base64 = "^0.22.1" base64urlsafedata = "^0.5.1" -crypto-glue = "^0.1.7" +crypto-glue = "^0.1.12" hex = "0.4" kanidm-hsm-crypto = "^0.3.2" openssl = { version = "^0.10.38", optional = true } diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index f92f786..584e88b 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -33,7 +33,7 @@ mod ms_oapxbc; pub use es256::{JwsEs256Signer, JwsEs256Verifier}; pub use hs256::JwsHs256Signer; pub use rs256::{JwsRs256Signer, JwsRs256Verifier}; -pub use x509::{JwsX509Verifier, JwsX509VerifierBuilder}; +pub use x509::{JwsX509Signer, JwsX509Verifier, JwsX509VerifierBuilder}; pub use a128gcm::JweA128GCMEncipher; pub use a128kw::JweA128KWEncipher; diff --git a/src/crypto/x509.rs b/src/crypto/x509.rs index de8467e..081ef9f 100644 --- a/src/crypto/x509.rs +++ b/src/crypto/x509.rs @@ -2,12 +2,16 @@ // use crate::compact::JwaAlg; +use crate::compact::ProtectedHeader; use crate::error::JwtError; use crate::traits::*; -use crate::KID_LEN; +use crate::{JwsCompact, KID_LEN}; +use base64::engine::general_purpose; +use base64::Engine; use crypto_glue::{ + ecdsa_p384::{EcdsaP384Digest, EcdsaP384PrivateKey, EcdsaP384Signature, EcdsaP384SigningKey}, s256, - traits::Digest, + traits::{Digest, DigestSigner, EncodeDer, SignatureEncoding}, x509::{x509_verify_signature, Certificate, SubjectKeyIdentifier, X509Store}, }; use std::time::SystemTime; @@ -115,6 +119,98 @@ impl JwsVerifier for JwsX509Verifier { } } +/// A builder for a verifier that will be rooted in a trusted ca chain. +#[derive(Clone)] +pub struct JwsX509Signer { + kid: String, + signer: K, + leaf: Certificate, + chain: Vec, +} + +impl JwsX509Signer { + /// Create a new X509 JWS Signer using this Key and Certificate. + pub fn new(signer: K, leaf: &Certificate, chain: &[Certificate]) -> Self { + let kid = certificate_to_kid(leaf); + + Self { + kid, + signer, + leaf: leaf.clone(), + chain: chain.to_vec(), + } + } +} + +impl JwsSigner for JwsX509Signer { + fn get_kid(&self) -> &str { + self.kid.as_str() + } + + fn set_kid(&mut self, kid: &str) { + self.kid = kid.to_string(); + } + + fn update_header(&self, header: &mut ProtectedHeader) -> Result<(), JwtError> { + // Embed the x5c/leaf + let x5c = std::iter::once(&self.leaf) + .chain(self.chain.iter()) + .map(|cert| { + Certificate::to_der(cert) + .map(|bytes| general_purpose::STANDARD.encode(bytes)) + .map_err(|err| { + debug!(?err); + JwtError::CryptoError + }) + }) + .collect::, _>>()?; + + header.x5c = Some(x5c); + + Ok(()) + } + + fn sign(&self, jws: &V) -> Result { + let mut sign_data = jws.data()?; + + // Let the signer update the header as required. + self.update_header(&mut sign_data.header)?; + + let hdr_b64 = serde_json::to_vec(&sign_data.header) + .map_err(|e| { + debug!(?e); + JwtError::InvalidHeaderFormat + }) + .map(|bytes| general_purpose::URL_SAFE_NO_PAD.encode(bytes))?; + + let mut hasher = EcdsaP384Digest::new(); + + hasher.update(hdr_b64.as_bytes()); + hasher.update(".".as_bytes()); + hasher.update(sign_data.payload_b64.as_bytes()); + + let signer = EcdsaP384SigningKey::from(&self.signer); + + let signature: EcdsaP384Signature = signer.try_sign_digest(hasher).map_err(|err| { + debug!(?err); + JwtError::CryptoError + })?; + + let jwsc = JwsCompact { + header: sign_data.header, + hdr_b64, + payload_b64: sign_data.payload_b64, + signature: signature.to_der().to_vec(), + }; + + jws.post_process(jwsc) + } + + fn set_sign_option_embed_kid(&self, _value: bool) -> Self { + self.clone() + } +} + fn certificate_to_kid(cert: &Certificate) -> String { let maybe_subject_key_id = cert .tbs_certificate diff --git a/src/jws.rs b/src/jws.rs index 3c606ec..9d5fd6a 100644 --- a/src/jws.rs +++ b/src/jws.rs @@ -165,9 +165,11 @@ impl fmt::Display for JwsSigned { mod tests { use super::JwsBuilder; use crate::compact::JwaAlg; - use crate::crypto::{JwsEs256Signer, JwsEs256Verifier, JwsHs256Signer, JwsX509VerifierBuilder}; + use crate::crypto::{ + JwsEs256Signer, JwsEs256Verifier, JwsHs256Signer, JwsX509Signer, JwsX509VerifierBuilder, + }; use crate::traits::*; - use crypto_glue::{traits::DecodePem, x509::Certificate}; + use crypto_glue::{ecdsa_p384::EcdsaP384PrivateKey, traits::DecodePem, x509::Certificate}; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; use std::time::{Duration, SystemTime}; @@ -245,58 +247,99 @@ mod tests { #[test] fn test_verification_jws_x5c() { - use std::str::FromStr; + let current_time = SystemTime::UNIX_EPOCH + Duration::from_secs(1770691587); + + const ROOT_CERT: &str = r#"-----BEGIN CERTIFICATE----- +MIICLjCCAbSgAwIBAgIRAU8Kb4rRT0mUsQf9rVbLAhowCgYIKoZIzj0EAwMwQzEL +MAkGA1UEBhMCQVUxETAPBgNVBAoMCFBscyBIZWxwMSEwHwYDVQQDDBhPaCBubyBo +ZSBpcyB3cml0aW5nIGEgQ0EwHhcNMjYwMjEwMDI0NTQyWhcNMjYwMjEwMDM0NTQy +WjBDMQswCQYDVQQGEwJBVTERMA8GA1UECgwIUGxzIEhlbHAxITAfBgNVBAMMGE9o +IG5vIGhlIGlzIHdyaXRpbmcgYSBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABFPu +aQn2tzNSL6ooAmYXhcqHbY8pBNis0LckMMytB1bsCdbS9JWGvxZnSSaDVgENFNvf +64G5Sh+eicjrUcAJLGfmdc7YBdZ3o5NcCWh/C6XiBw3XgnGWw/ZDj4OchlFbNqNs +MGowHQYDVR0OBBYEFAXszmYwxKgrhDYVy/GVodMtQNGhMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgEGMCgGA1UdHwQhMB8wHaAboBmGF2h0dHBzOi8vZXhh +bXBsZS5jb20vY3JsMAoGCCqGSM49BAMDA2gAMGUCMHQ6Nef4ENwiudXQMcH4DzuB +0zzgF9RIH5GvhcYNZ+13pZtHbiAO8AnHuSYquLFvdgIxAMgLk9X9KxTPcym41pGt +L9Ytm2ZPCfNpWL9qBqy3qPyGtfxpMclA3ck3JKlkGepD/g== +-----END CERTIFICATE-----"#; + const INT_CERT: &str = r#"-----BEGIN CERTIFICATE----- +MIICZjCCAeugAwIBAgIRAQ1mUzLJHEbjtJrFkwzt2xMwCgYIKoZIzj0EAwMwQzEL +MAkGA1UEBhMCQVUxETAPBgNVBAoMCFBscyBIZWxwMSEwHwYDVQQDDBhPaCBubyBo +ZSBpcyB3cml0aW5nIGEgQ0EwHhcNMjYwMjEwMDI0NTQyWhcNMjYwMjEwMDM0NTQy +WjAxMQswCQYDVQQGEwJBVTEiMCAGA1UEAwwZT2ggbm8gaXRzIGFuIGludGVybWVk +aWF0ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABI198gNal5U9+eMmEs8dlUdURuJU +nn33eysYelGNuWYDYiuPjYlsucHTKkdsNZuY9FicElQewzK7Cr47WZwChoRpOS9B +2r8i10tI2/ipmPIAlqWshSIlPN2UZ+NZ7EDGqqOBtDCBsTAdBgNVHQ4EFgQU7gqP +RZ82etk0XzGfbLWnZa5NEIowHwYDVR0jBBgwFoAUBezOZjDEqCuENhXL8ZWh0y1A +0aEwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwLAYDVR0fBCUw +IzAhoB+gHYYbaHR0cHM6Ly9leGFtcGxlLmNvbS9pbnQvY3JsMB0GA1UdHgEB/wQT +MBGgDzANggtleGFtcGxlLmNvbTAKBggqhkjOPQQDAwNpADBmAjEA9VvAuOTw9192 +/Djj7/iOAHbGmjk6a2PUS8CDUwjxL4qBjyRB3dmZNnF2wHPLE/oKAjEAmqhFqxWb +wtlOkqPmniGR103gurd3/alkHcxkqyU0KeQLAJ6gjVtm9wD3qhbDlJpX +-----END CERTIFICATE-----"#; + const LEAF_CERT: &str = r#"-----BEGIN CERTIFICATE----- +MIICEjCCAZegAwIBAgIRAV1zkh2XSE0wiN+axloUuo0wCgYIKoZIzj0EAwMwMTEL +MAkGA1UEBhMCQVUxIjAgBgNVBAMMGU9oIG5vIGl0cyBhbiBpbnRlcm1lZGlhdGUw +HhcNMjYwMjEwMDI0NTQyWhcNMjYwMjEwMDM0NTQyWjAUMRIwEAYDVQQDDAlsb2Nh +bGhvc3QwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARpZJM5KB3fqb4q9wJLZWq+JH5i +JbM5yZMVKJ6GS7XZDw/cRizVtcDPYiAaiIk76Cg83IPeAZmdOruulkRb5QmMiBJW +PSS2jxMcdE5pwLVvL0p7CS/hOmUS/WHhuS5Pop+jgY8wgYwwHQYDVR0OBBYEFO5A +oPdFRbBrkkBZpzl1k1Aps6FLMB8GA1UdIwQYMBaAFO4Kj0WfNnrZNF8xn2y1p2Wu +TRCKMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgPoMBYGA1UdJQEB/wQMMAoG +CCsGAQUFBwMBMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDAKBggqhkjOPQQDAwNpADBm +AjEAy1u1GsYeRUnJxSbuUveiN0rubXgk/y0EnESIM8f2lXe84ofElUXTozNFdWm9 +Sr4mAjEAvklqw0mWwQKgr5ypJFj6duCe1QMO0ff1Jv+6Z3NpbFABNquuyi+3H6vV +xH0SEy1Z +-----END CERTIFICATE-----"#; + const LEAF_KEY: &str = r#"-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDA949NqEFqlXON4L0/8AQuILa53B2JOE14xkWsp3lg0ywWPJ0FzXA0O +UWwCFT564PmgBwYFK4EEACKhZANiAARpZJM5KB3fqb4q9wJLZWq+JH5iJbM5yZMV +KJ6GS7XZDw/cRizVtcDPYiAaiIk76Cg83IPeAZmdOruulkRb5QmMiBJWPSS2jxMc +dE5pwLVvL0p7CS/hOmUS/WHhuS5Pop8= +-----END EC PRIVATE KEY-----"#; + let _ = tracing_subscriber::fmt::try_init(); - let gsr1 = r#"-----BEGIN CERTIFICATE----- -MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG -A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv -b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw -MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i -YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT -aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ -jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp -xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp -1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG -snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ -U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 -9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B -AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz -yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE -38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP -AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad -DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME -HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== ------END CERTIFICATE----- -"#; - - let jwsu = super::JwsCompact::from_str(r#"eyJhbGciOiJSUzI1NiIsIng1YyI6WyJNSUlGYmpDQ0JGYWdBd0lCQWdJUUFhM09LT2RvVFk0UUFBQUFBQTNYWERBTkJna3Foa2lHOXcwQkFRc0ZBREJHTVFzd0NRWURWUVFHRXdKVlV6RWlNQ0FHQTFVRUNoTVpSMjl2WjJ4bElGUnlkWE4wSUZObGNuWnBZMlZ6SUV4TVF6RVRNQkVHQTFVRUF4TUtSMVJUSUVOQklERkVOREFlRncweU1qQXpNakF5TVRFMU1qRmFGdzB5TWpBMk1UZ3lNVEUxTWpCYU1CMHhHekFaQmdOVkJBTVRFbUYwZEdWemRDNWhibVJ5YjJsa0xtTnZiVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFMbXFMQlhWNENaQTVzVDVjVGZ1WGN3MTFXRDJZVVczZUFKdmRxK1hJYkhEZ01OTUJyc3gvWER4NkxtOU9tSkNVNHZDcFdJTjRXQ0gyMFQ5T2ZlNkhkeU52RWVpM3pobHpOMFovWVR5b1RlcFdwNUgvbXJuR29zU3NtcEp1NDV3OVJYbm5KbElrRzU5dDN0V1JoYXNZZW5GY0hlY0ZobG1odm5UQnRHa01Vb0VGREZnanltZ2twUUdkMmxoaU9YWGJwMzE1SXlGbEdUVFpvNERBYTZiMHp2VGZQOXV6R1FJZHhma3N5TUlGZmJDYVd6TjNPanB1bVIwMHg2SVZjZDdyOUxvOVBlVWw5a296cjhFaDRDWS9PQitEOVEvVjZ4RVpiVHNHeXc0aUFxQ0tvMTRDRXpDRVFIMEZWWTQ1cFg3b2IrbWhmL1pKbWNzL014blZGbkx6bDBDQXdFQUFhT0NBbjh3Z2dKN01BNEdBMVVkRHdFQi93UUVBd0lGb0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREFUQU1CZ05WSFJNQkFmOEVBakFBTUIwR0ExVWREZ1FXQkJUOVI3Z21PZUQxdlpRVkNIYzBUdGh2T1lpMzlEQWZCZ05WSFNNRUdEQVdnQlFsNGhnT3NsZVJsQ3JsMUYyR2tJUGVVN080a2pCN0JnZ3JCZ0VGQlFjQkFRUnZNRzB3T0FZSUt3WUJCUVVITUFHR0xHaDBkSEE2THk5dlkzTndMbkJyYVM1bmIyOW5MM012WjNSek1XUTBhVzUwTDNoT0xWOHdkRE4zV1Rrd01ERUdDQ3NHQVFVRkJ6QUNoaVZvZEhSd09pOHZjR3RwTG1kdmIyY3ZjbVZ3Ynk5alpYSjBjeTluZEhNeFpEUXVaR1Z5TUIwR0ExVWRFUVFXTUJTQ0VtRjBkR1Z6ZEM1aGJtUnliMmxrTG1OdmJUQWhCZ05WSFNBRUdqQVlNQWdHQm1lQkRBRUNBVEFNQmdvckJnRUVBZFo1QWdVRE1EOEdBMVVkSHdRNE1EWXdOS0F5b0RDR0xtaDBkSEE2THk5amNteHpMbkJyYVM1bmIyOW5MMmQwY3pGa05HbHVkQzlZTWtveVNISmZOMUJwVFM1amNtd3dnZ0VFQmdvckJnRUVBZFo1QWdRQ0JJSDFCSUh5QVBBQWRnQlJvN0QxL1FGNW5GWnR1RGQ0and5a2Vzd2JKOHYzbm9oQ21nMysxSXNGNVFBQUFYK3Baa0pyQUFBRUF3QkhNRVVDSVFDb2RWRnpPQ1VubHVRUzB0MG9HdUEzdlZFR0Zxb2I4SVJiQ3BZeTdVZmNBUUlnRi9NZVVSdG9EN1FraFhCTjB1cmlDdEwvTENsMW1zRE5oWjFtMUhKeEpRb0FkZ0FwZWI3d25qazVJZkJXYzU5anBYZmx2bGQ5bkdBSytQbE5YU1pjSlYzSGhBQUFBWCtwWmtKWUFBQUVBd0JITUVVQ0lRQ1pvRW1Bbzc0UitGT0pQeVJLYkkyRSs2S0NYNkF1WG1oZnNXa2h0aUFLYWdJZ1p1dmZIcUE2UE9sM0JkV3RlU1l4TzA2QmNwT3dUYTV6NjVqSkw0dExEckl3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQURJcC93blFsZnE3dVZ6dDU3MHlRRTJOQVA1ajh5OGFzWWhKTXcrUTBYZ3M2a3pqZnpGL2g3OVpmRlhLOTh3QVJhVnI2amVSQXo2Y3E4cUVIMU8yQkQ5eDVEQ09UZzJxclNnSldiTU5VWkR5TXV6RmVyQ2EyNzloQklQVXBqNzg0YUdsYWp4Y2M3VHRYSHpacnhmbGM0d1BzZ2JnQ2twd3VqNmowandDNjdRNGJrOVVYKzNxcGw3MmFKMnpWbzFmT2s3U0ZwSTU4RjNJL1c4bkkva2Nwb1BvcDJCNkoxR3RxTURIRnByc3RnZUpMbFkzQWVmZWoyeW9Fd3UyajIrYzEvSjZ3SDV4YWRES3hnM052aDIreGhaUkZab0FUYjJlNllzeDRSMEJ0eWVYNEhaTWc0OFFhQk40N2xBeEFjZzR1YVNqRy8vQkhXTjM0cE1FYWNJeEdOMD0iLCJNSUlGakRDQ0EzU2dBd0lCQWdJTkFnQ09zZ0l6Tm1XTFpNM2JtekFOQmdrcWhraUc5dzBCQVFzRkFEQkhNUXN3Q1FZRFZRUUdFd0pWVXpFaU1DQUdBMVVFQ2hNWlIyOXZaMnhsSUZSeWRYTjBJRk5sY25acFkyVnpJRXhNUXpFVU1CSUdBMVVFQXhNTFIxUlRJRkp2YjNRZ1VqRXdIaGNOTWpBd09ERXpNREF3TURReVdoY05NamN3T1RNd01EQXdNRFF5V2pCR01Rc3dDUVlEVlFRR0V3SlZVekVpTUNBR0ExVUVDaE1aUjI5dloyeGxJRlJ5ZFhOMElGTmxjblpwWTJWeklFeE1RekVUTUJFR0ExVUVBeE1LUjFSVElFTkJJREZFTkRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBS3ZBcXFQQ0UyN2wwdzl6QzhkVFBJRTg5YkEreFRtRGFHN3k3VmZRNGMrbU9XaGxVZWJVUXBLMHl2MnI2NzhSSkV4SzBIV0RqZXErbkxJSE4xRW01ajZyQVJaaXhteVJTamhJUjBLT1FQR0JNVWxkc2F6dElJSjdPMGcvODJxai92R0RsLy8zdDR0VHF4aVJoTFFuVExYSmRlQisyRGhrZFU2SUlneDZ3TjdFNU5jVUgzUmNzZWpjcWo4cDVTajE5dkJtNmkxRmhxTEd5bWhNRnJvV1ZVR08zeHRJSDkxZHNneTRlRktjZktWTFdLM28yMTkwUTBMbS9TaUttTGJSSjVBdTR5MWV1RkptMkpNOWVCODRGa3FhM2l2clhXVWVWdHllMENRZEt2c1kyRmthenZ4dHh2dXNMSnpMV1lIazU1emNSQWFjREEyU2VFdEJiUWZEMXFzQ0F3RUFBYU9DQVhZd2dnRnlNQTRHQTFVZER3RUIvd1FFQXdJQmhqQWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0RBUVlJS3dZQkJRVUhBd0l3RWdZRFZSMFRBUUgvQkFnd0JnRUIvd0lCQURBZEJnTlZIUTRFRmdRVUplSVlEckpYa1pRcTVkUmRocENEM2xPenVKSXdId1lEVlIwakJCZ3dGb0FVNUs4ckpuRWFLMGduaFM5U1ppenY4SWtUY1Q0d2FBWUlLd1lCQlFVSEFRRUVYREJhTUNZR0NDc0dBUVVGQnpBQmhocG9kSFJ3T2k4dmIyTnpjQzV3YTJrdVoyOXZaeTluZEhOeU1UQXdCZ2dyQmdFRkJRY3dBb1lrYUhSMGNEb3ZMM0JyYVM1bmIyOW5MM0psY0c4dlkyVnlkSE12WjNSemNqRXVaR1Z5TURRR0ExVWRId1F0TUNzd0thQW5vQ1dHSTJoMGRIQTZMeTlqY213dWNHdHBMbWR2YjJjdlozUnpjakV2WjNSemNqRXVZM0pzTUUwR0ExVWRJQVJHTUVRd0NBWUdaNEVNQVFJQk1EZ0dDaXNHQVFRQjFua0NCUU13S2pBb0JnZ3JCZ0VGQlFjQ0FSWWNhSFIwY0hNNkx5OXdhMmt1WjI5dlp5OXlaWEJ2YzJsMGIzSjVMekFOQmdrcWhraUc5dzBCQVFzRkFBT0NBZ0VBSVZUb3kyNGp3WFVyMHJBUGM5MjR2dVNWYktRdVl3M25MZmxMZkxoNUFZV0VlVmwvRHUxOFFBV1VNZGNKNm8vcUZaYmhYa0JIMFBOY3c5N3RoYWYyQmVvRFlZOUNrL2IrVUdsdWh4MDZ6ZDRFQmY3SDlQODRubnJ3cFIrNEdCRFpLK1hoM0kwdHFKeTJyZ09xTkRmbHI1SU1ROFpUV0EzeWx0YWt6U0JLWjZYcEYwUHBxeUNSdnAvTkNHdjJLWDJUdVBDSnZzY3AxL20ycFZUdHlCallQUlErUXVDUUdBSktqdE43UjVERnJmVHFNV3ZZZ1ZscENKQmt3bHU3KzdLWTNjVElmekU3Y21BTHNrTUtOTHVEeitSekNjc1lUc1ZhVTdWcDN4TDYwT1locUZrdUFPT3hEWjZwSE9qOStPSm1ZZ1BtT1Q0WDMrN0w1MWZYSnlSSDlLZkxSUDZuVDMxRDVubXNHQU9nWjI2LzhUOWhzQlcxdW85anU1ZlpMWlhWVlM1SDBIeUlCTUVLeUdNSVBoRldybHQvaEZTMjhOMXphS0kwWkJHRDNnWWdETGJpRFQ5ZkdYc3RwaytGbWM0b2xWbFdQelhlODF2ZG9FbkZicjVNMjcySGRnSldvK1doVDlCWU0wSmkrd2RWbW5SZmZYZ2xvRW9sdVROY1d6YzQxZEZwZ0p1OGZGM0xHMGdsMmliU1lpQ2k5YTZodlUwVHBwakp5SVdYaGtKVGNNSmxQcld4MVZ5dEVVR3JYMmwwSkR3UmpXLzY1NnIwS1ZCMDJ4SFJLdm0yWktJMDNUZ2xMSXBtVkNLM2tCS2tLTnBCTmtGdDhyaGFmY0NLT2I5SngvOXRwTkZsUVRsN0IzOXJKbEpXa1IxN1FuWnFWcHRGZVBGT1JvWm1Gek09IiwiTUlJRllqQ0NCRXFnQXdJQkFnSVFkNzBOYk5zMitScnFJUS9FOEZqVERUQU5CZ2txaGtpRzl3MEJBUXNGQURCWE1Rc3dDUVlEVlFRR0V3SkNSVEVaTUJjR0ExVUVDaE1RUjJ4dlltRnNVMmxuYmlCdWRpMXpZVEVRTUE0R0ExVUVDeE1IVW05dmRDQkRRVEViTUJrR0ExVUVBeE1TUjJ4dlltRnNVMmxuYmlCU2IyOTBJRU5CTUI0WERUSXdNRFl4T1RBd01EQTBNbG9YRFRJNE1ERXlPREF3TURBME1sb3dSekVMTUFrR0ExVUVCaE1DVlZNeElqQWdCZ05WQkFvVEdVZHZiMmRzWlNCVWNuVnpkQ0JUWlhKMmFXTmxjeUJNVEVNeEZEQVNCZ05WQkFNVEMwZFVVeUJTYjI5MElGSXhNSUlDSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQWc4QU1JSUNDZ0tDQWdFQXRoRUNpeDdqb1hlYk85eS9sRDYzbGFkQVBLSDlndmw5TWdhQ2NmYjJqSC83Nk51OGFpNlhsNk9NUy9rcjlySDV6b1Fkc2ZuRmw5N3Z1ZktqNmJ3U2lWNm5xbEtyK0NNbnk2U3huR1BiMTVsKzhBcGU2MmltOU1aYVJ3MU5FRFBqVHJFVG84Z1liRXZzL0FtUTM1MWtLU1VqQjZHMDBqMHVZT0RQMGdtSHU4MUk4RTNDd25xSWlydTZ6MWtaMXErUHNBZXduakh4Z3NIQTN5Nm1iV3daRHJYWWZpWWFSUU05c0hta2xDaXREMzhtNWFnSS9wYm9QR2lVVSs2RE9vZ3JGWllKc3VCNmpDNTExcHpycDFaa2o1WlBhSzQ5bDhLRWo4QzhRTUFMWEwzMmg3TTFiS3dZVUgrRTRFek5rdE1nNlRPOFVwbXZNclVwc3lVcXRFajVjdUhLWlBmbWdoQ042SjNDaW9qNk9HYUsvR1A1QWZsNC9YdGNkL3AyaC9yczM3RU9lWlZYdEwwbTc5WUIwZXNXQ3J1T0M3WEZ4WXBWcTlPczZwRkxLY3dacERJbFRpcnhaVVRRQXM2cXprbTA2cDk4ZzdCQWUrZERxNmRzbzQ5OWlZSDZUS1gvMVk3RHprdmd0ZGl6amtYUGRzRHRRQ3Y5VXcrd3A5VTdEYkdLb2dQZU1hM01kK3B2ZXo3VzM1RWlFdWErK3RneS9CQmpGRkZ5M2wzV0ZwTzlLV2d6N3pwbTdBZUtKdDhUMTFkbGVDZmVYa2tVQUtJQWY1cW9JYmFwc1pXd3Bia05GaEhheDJ4SVBFRGdmZzFhelZZODBaY0Z1Y3RMN1RsTG5NUS8wbFVUYmlTdzFuSDY5TUc2ek8wYjlmNkJRZGdBbUQwNnlLNTZtRGNZQlpVQ0F3RUFBYU9DQVRnd2dnRTBNQTRHQTFVZER3RUIvd1FFQXdJQmhqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01CMEdBMVVkRGdRV0JCVGtyeXNtY1JvclNDZUZMMUptTE8vd2lSTnhQakFmQmdOVkhTTUVHREFXZ0JSZ2UyWWFSUTJYeW9sUUwzMEV6VFNvLy96OVN6QmdCZ2dyQmdFRkJRY0JBUVJVTUZJd0pRWUlLd1lCQlFVSE1BR0dHV2gwZEhBNkx5OXZZM053TG5CcmFTNW5iMjluTDJkemNqRXdLUVlJS3dZQkJRVUhNQUtHSFdoMGRIQTZMeTl3YTJrdVoyOXZaeTluYzNJeEwyZHpjakV1WTNKME1ESUdBMVVkSHdRck1Da3dKNkFsb0NPR0lXaDBkSEE2THk5amNtd3VjR3RwTG1kdmIyY3ZaM055TVM5bmMzSXhMbU55YkRBN0JnTlZIU0FFTkRBeU1BZ0dCbWVCREFFQ0FUQUlCZ1puZ1F3QkFnSXdEUVlMS3dZQkJBSFdlUUlGQXdJd0RRWUxLd1lCQkFIV2VRSUZBd013RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQURTa0hyRW9vOUMwZGhlbU1Yb2g2ZEZTUHNqYmRCWkJpTGc5TlIzdDVQK1Q0VnhmcTd2cWZNL2I1QTNSaTFmeUptOWJ2aGRHYUpRM2IydDZ5TUFZTi9vbFVhenNhTCt5eUVuOVdwcktBU09zaElBckFveVpsK3RKYW94MTE4ZmVzc21YbjFoSVZ3NDFvZVFhMXYxdmc0RnY3NHpQbDYvQWhTcnc5VTVwQ1pFdDRXaTR3U3R6NmRUWi9DTEFOeDhMWmgxSjdRSlZqMmZoTXRmVEpyOXc0ejMwWjIwOWZPVTBpT015K3FkdUJtcHZ2WXVSN2haTDZEdXBzemZudzBTa2Z0aHMxOGRHOVpLYjU5VWh2bWFTR1pSVmJOUXBzZzNCWmx2aWQwbElLTzJkMXhvemNsT3pnalhQWW92SkpJdWx0emtNdTM0cVFiOVN6L3lpbHJiQ2dqOD0iXX0.eyJub25jZSI6IkprblpTb1p1TnE4K09ydFB4MERmc0xVWEZWckozQ0M5U216RURjbGJHbms9IiwidGltZXN0YW1wTXMiOjE2NTQ1MjkzNTYwMTcsImFwa1BhY2thZ2VOYW1lIjoiY29tLmdvb2dsZS5hbmRyb2lkLmdtcyIsImFwa0RpZ2VzdFNoYTI1NiI6IlJZdkx3Vm1nRjJYYXMxMUREOTI1SXVzc0p1eEtEL3dCN2pjT01qbU1UR0E9IiwiY3RzUHJvZmlsZU1hdGNoIjp0cnVlLCJhcGtDZXJ0aWZpY2F0ZURpZ2VzdFNoYTI1NiI6WyI4UDFzVzBFUEpjc2x3N1V6UnNpWEw2NHcrTzUwRWQrUkJJQ3RheTFnMjRNPSJdLCJiYXNpY0ludGVncml0eSI6dHJ1ZSwiZXZhbHVhdGlvblR5cGUiOiJCQVNJQyJ9.QGm9B8pw6wwy0Zyly_lkLPw_56y9vzFggS7z6J0u9nLglFBc-VnDUgeZEBzYiSrU5bXsKFn9lF6MbjvmpVgnYgBFLEYAlNFDe-2CPf0UdNR1wS-cMep1IKsdkhCQGL7LVzucLvSMPJt4QvqEScQrsjw9X-zCKiKuEsrDfrBoVhvYEJjSNzMtIG8k1gAtJJ-QcdcoLU1ImJEvmU-5VdYfoiOuxyGULaBbjIQ7o190FEXtQuyHIxUUknVsADvDK9loQA0lp38sl1Ec4ddbsyNMFCctnNFdCrosp9PSmQLNMv1_bhIgctdYVTkr9CR59LJur4PWGmOGS_3bjot5IB5Qrg"#) - .expect("Invalid jwsu"); + let root_cert = + Certificate::from_pem(ROOT_CERT.as_bytes()).expect("Failed to parse trust root"); + let int_cert = + Certificate::from_pem(INT_CERT.as_bytes()).expect("Failed to parse trust int root"); + let leaf_cert = Certificate::from_pem(LEAF_CERT.as_bytes()).expect("Failed to parse leaf"); + let leaf_key = + EcdsaP384PrivateKey::from_sec1_pem(LEAF_KEY).expect("Failed to parse leaf key"); + + let signer = JwsX509Signer::new(leaf_key, &leaf_cert, &[int_cert]); + + let claims_original: std::collections::BTreeMap = + std::collections::BTreeMap::from([("a".to_string(), serde_json::value::Value::Null)]); + + let jws = JwsBuilder::into_json(&claims_original) + .expect("Failed to serialise json") + .build(); + + let jwsu: crate::JwsCompact = signer.sign(&jws).expect("Failed to sign"); let (leaf, chain) = jwsu .get_x5c_chain() .expect("Failed to get x5c chain") .expect("x5c chain is empty"); - assert!(chain.len() == 2); - - let trust_root = - Certificate::from_pem(gsr1.as_bytes()).expect("Failed to parse trust root"); - - let current_time = SystemTime::UNIX_EPOCH + Duration::from_secs(1655586820); + assert!(chain.len() == 1); let jws_x509_verifier = JwsX509VerifierBuilder::new(&leaf, &chain) - .add_trust_root(trust_root) + .add_trust_root(root_cert) .build(current_time) .expect("Failed to construct verifier"); - let _claims: std::collections::BTreeMap = + let claims: std::collections::BTreeMap = jws_x509_verifier .verify(&jwsu) .expect("Failed to verify") .from_json() .expect("Failed to deserialise contents"); + + assert_eq!(claims_original, claims); } #[test]