diff --git a/Cargo.toml b/Cargo.toml index 41f4be86..222eeff4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,8 +71,8 @@ webauthn-rs-device-catalog = { path = "./device-catalog" } async-std = { version = "1.6", features = ["attributes"] } base64 = "0.21" -clap = { version = "=4.4.18", features = ["derive", "env"] } -compact_jwt = "0.2.3" +clap = { version = "^4.5", features = ["derive", "env"] } +compact_jwt = "0.4.2" futures = "^0.3.25" hex = "0.4.3" http = "^0.2.9" @@ -89,7 +89,6 @@ rand_chacha = "0.3.1" serde = { version = "^1.0.141", features = ["derive"] } serde_cbor_2 = { version = "0.12.0-dev" } serde_json = "^1.0.79" -tide = "0.16" thiserror = "^1.0.37" tokio = { version = "1.22.0", features = [ "sync", diff --git a/compat_tester/webauthn-rs-demo/Cargo.toml b/compat_tester/webauthn-rs-demo/Cargo.toml index 99c5133b..9125ad69 100644 --- a/compat_tester/webauthn-rs-demo/Cargo.toml +++ b/compat_tester/webauthn-rs-demo/Cargo.toml @@ -15,7 +15,7 @@ webauthn-rs-demo-shared = { path = "../webauthn-rs-demo-shared", features = ["co webauthn-rs-core.workspace = true webauthn-rs = { workspace = true, features = ["conditional-ui", "attestation", "resident-key-support", "danger-allow-state-serialisation"] } -tide.workspace = true +tide = "0.16" tokio.workspace = true structopt = { version = "0.3", default-features = false } diff --git a/fido-mds/src/mds.rs b/fido-mds/src/mds.rs index 3ed369e0..f7bf1cea 100644 --- a/fido-mds/src/mds.rs +++ b/fido-mds/src/mds.rs @@ -4,15 +4,11 @@ //! This allows parsing the fido metadata blob and consuming it's content. See `FidoMds` //! for more. -// use base64urlsafedata::Base64UrlSafeData; -use compact_jwt::{Jws, JwsUnverified, JwtError}; -use openssl::stack; +use compact_jwt::{crypto::JwsX509VerifierBuilder, JwsCompact, JwsVerifier, JwtError}; use openssl::x509; -use openssl::x509::store; use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; -use tracing::{debug, error}; use std::collections::BTreeMap; use std::hash::{Hash, Hasher}; @@ -1089,67 +1085,24 @@ impl FromStr for FidoMds { let root_ca = x509::X509::from_pem(GLOBAL_SIGN_ROOT_CA_R3.as_bytes()) .map_err(|_| JwtError::OpenSSLError)?; - let mut ca_store = store::X509StoreBuilder::new().map_err(|_| JwtError::OpenSSLError)?; - ca_store - .add_cert(root_ca) - .map_err(|_| JwtError::OpenSSLError)?; - - let ca_store = ca_store.build(); - - let jws = JwsUnverified::from_str(s)?; + let jws = JwsCompact::from_str(s)?; let fullchain = jws .get_x5c_chain() .and_then(|chain| chain.ok_or(JwtError::InvalidHeaderFormat))?; - let (leaf, chain) = fullchain - .split_first() - .ok_or(JwtError::InvalidHeaderFormat)?; - - let mut chain_stack = stack::Stack::new().map_err(|_| JwtError::OpenSSLError)?; - - for crt in chain.iter() { - chain_stack - .push(crt.clone()) - .map_err(|_| JwtError::OpenSSLError)?; - } - - let mut ca_ctx = x509::X509StoreContext::new().map_err(|_| JwtError::OpenSSLError)?; - - // Given the ca_store, the leaf cert, and the chain between leaf to ca_store, verify - // the certificate chain. - let res: Result<_, _> = ca_ctx - .init(&ca_store, leaf, &chain_stack, |ca_ctx_ref| { - ca_ctx_ref.verify_cert().map(|_| { - let res = ca_ctx_ref.error(); - debug!("{:?}", res); - if res == x509::X509VerifyResult::OK { - Ok(()) - } else { - debug!( - "ca_ctx_ref verify cert - error depth={}, sn={:?}", - ca_ctx_ref.error_depth(), - ca_ctx_ref.current_cert().map(|crt| crt.subject_name()) - ); - Err(JwtError::X5cPublicKeyDenied) - } - }) - }) - .map_err(|e| { - // If an openssl error occured, dump it here. - error!(?e); - JwtError::OpenSSLError - })?; - - debug!(?res); - res?; + let verifier = JwsX509VerifierBuilder::new() + .add_fullchain(fullchain) + .add_trust_root(root_ca) + .build() + .map_err(|_| JwtError::OpenSSLError)?; // Now we can release the embedded cert, since we have asserted the trust in the chain // that has signed this metadata. + let released = verifier.verify(&jws)?; - let x: Jws = jws.validate_embeded()?; + let metadata: FidoMds = released.from_json().map_err(|_| JwtError::Serde)?; - let metadata = x.into_inner(); // trace!(?metadata); Ok(metadata) diff --git a/tutorial/server/tide/Cargo.toml b/tutorial/server/tide/Cargo.toml index bf1d6c82..312e6f6a 100644 --- a/tutorial/server/tide/Cargo.toml +++ b/tutorial/server/tide/Cargo.toml @@ -9,7 +9,7 @@ license = "MPL-2.0" [dependencies] webauthn-rs = { workspace = true, features = ["danger-allow-state-serialisation"] } -tide.workspace = true +tide = "0.16" async-std.workspace = true tracing.workspace = true tracing-subscriber.workspace = true diff --git a/webauthn-rs-core/src/attestation.rs b/webauthn-rs-core/src/attestation.rs index 35445700..125c779b 100644 --- a/webauthn-rs-core/src/attestation.rs +++ b/webauthn-rs-core/src/attestation.rs @@ -9,6 +9,7 @@ use crate::error::WebauthnError; use crate::internals::*; use crate::proto::*; use base64urlsafedata::HumanBinaryData; +use compact_jwt::{crypto::JwsX509Verifier, JwsCompact, JwsVerifier}; use openssl::hash::MessageDigest; use openssl::sha::sha256; use openssl::stack; @@ -1291,18 +1292,21 @@ pub(crate) fn verify_android_safetynet_attestation( |token: &str| -> Result<(Vec, SafteyNetAttestResponse), SafetyNetError> { trace!(?token); use std::str::FromStr; - let jwsu = compact_jwt::JwsUnverified::from_str(token)?; + let jwsu = JwsCompact::from_str(token)?; let certs = jwsu .get_x5c_chain()? .ok_or(SafetyNetError::MissingCertChain)?; - let leaf_cert = certs.first().ok_or(SafetyNetError::BadCert)?; + let leaf_cert = certs.first().cloned().ok_or(SafetyNetError::BadCert)?; // Verify with the internal certificate. - let jws: compact_jwt::Jws = jwsu.validate_embeded()?; + let verifier = JwsX509Verifier::from_x509(leaf_cert.clone()) + .map_err(|_| SafetyNetError::BadCert)?; - let verified_claims = jws.into_inner(); + let verified_claims: SafteyNetAttestResponse = verifier + .verify(&jwsu) + .and_then(|jws| jws.from_json().map_err(|_| compact_jwt::JwtError::Serde))?; // 3. Verify that the nonce attribute in the payload of response is identical to the Base64 encoding of the SHA-256 hash of the concatenation of authenticatorData and clientDataHash. if verified_claims.nonce != data_to_verify.as_slice() {