webauthn-attestation-ca-0.5.2/.cargo_vcs_info.json0000644000000001540000000000100155730ustar { "git": { "sha1": "1de85dbaa2f6c515d6a9cf2cdccf8844ab3d88a6" }, "path_in_vcs": "attestation-ca" }webauthn-attestation-ca-0.5.2/Cargo.lock0000644000000205250000000000100135520ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64urlsafedata" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5913e643e4dfb43d5908e9e6f1386f8e0dfde086ecef124a6450c6195d89160" dependencies = [ "base64", "pastey", "serde", ] [[package]] name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bumpalo" version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "cc" version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "shlex", ] [[package]] name = "cfg-if" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ "foreign-types-shared", ] [[package]] name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "js-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "libc" version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "openssl" version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ "bitflags", "cfg-if", "foreign-types", "libc", "once_cell", "openssl-macros", "openssl-sys", ] [[package]] name = "openssl-macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "openssl-sys" version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "pastey" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3a8cb46bdc156b1c90460339ae6bfd45ba0394e5effbaa640badb4987fdc261" [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "proc-macro2" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "rustversion" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tracing-core" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "uuid" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ "js-sys", "serde", "wasm-bindgen", ] [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "wasm-bindgen" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] [[package]] name = "webauthn-attestation-ca" version = "0.5.2" dependencies = [ "base64urlsafedata", "openssl", "openssl-sys", "serde", "tracing", "uuid", ] webauthn-attestation-ca-0.5.2/Cargo.toml0000644000000025230000000000100135730ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.85" name = "webauthn-attestation-ca" version = "0.5.2" authors = [ "William Brown ", "Michael Farrell ", ] build = "build.rs" autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Webauthn Attestation CA Descriptions" homepage = "https://github.com/kanidm/webauthn-rs" readme = "README.md" license = "MPL-2.0" repository = "https://github.com/kanidm/webauthn-rs" [lib] name = "webauthn_attestation_ca" path = "src/lib.rs" [dependencies.base64urlsafedata] version = "=0.5.2" [dependencies.openssl] version = "^0.10.56" [dependencies.openssl-sys] version = "^0.9.109" [dependencies.serde] version = "^1.0.141" features = ["derive"] [dependencies.tracing] version = "^0.1.35" [dependencies.uuid] version = "^1.1.2" features = ["serde"] webauthn-attestation-ca-0.5.2/Cargo.toml.orig000064400000000000000000000012051046102023000172500ustar 00000000000000[package] name = "webauthn-attestation-ca" readme = "README.md" description = "Webauthn Attestation CA Descriptions" version = { workspace = true } authors = { workspace = true } rust-version = { workspace = true } edition = { workspace = true } license = { workspace = true } homepage = { workspace = true } repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] base64urlsafedata.workspace = true serde.workspace = true tracing.workspace = true openssl.workspace = true openssl-sys.workspace = true uuid = { workspace = true, features = ["serde"] } webauthn-attestation-ca-0.5.2/README.md000064400000000000000000000000001046102023000156300ustar 00000000000000webauthn-attestation-ca-0.5.2/build.rs000064400000000000000000000014521046102023000160320ustar 00000000000000use std::env; const OPENSSL_DOC: &str = "https://github.com/kanidm/webauthn-rs/blob/master/OpenSSL.md"; fn main() { println!(); if let Ok(v) = env::var("DEP_OPENSSL_VERSION_NUMBER") { let version = u64::from_str_radix(&v, 16).expect("Failed to parse OpenSSL version in build.rs"); #[allow(clippy::unusual_byte_groupings)] if version >= 0x3_00_00_00_0 { return; } else { println!( r#" Your version of OpenSSL is out of date, and not supported by this library. Please upgrade to OpenSSL v3.0.0 or later. More info: {OPENSSL_DOC} OpenSSL version string: {version} "# ); panic!("The installed version of OpenSSL is unusable."); } } panic!("No version of OpenSSL is found."); } webauthn-attestation-ca-0.5.2/src/lib.rs000064400000000000000000000221621046102023000162710ustar 00000000000000use base64urlsafedata::Base64UrlSafeData; use openssl::error::ErrorStack as OpenSSLErrorStack; use openssl::{hash, x509}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use uuid::Uuid; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct DeviceDescription { pub(crate) en: String, pub(crate) localised: BTreeMap, } impl DeviceDescription { /// A default description of device. pub fn description_en(&self) -> &str { self.en.as_str() } /// A map of locale identifiers to a localised description of the device. /// If the request locale is not found, you should try other user preferenced locales /// falling back to the default value. pub fn description_localised(&self) -> &BTreeMap { &self.localised } } /// A serialised Attestation CA. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SerialisableAttestationCa { pub(crate) ca: Base64UrlSafeData, pub(crate) aaguids: BTreeMap, pub(crate) blanket_allow: bool, } /// A structure representing an Attestation CA and other options associated to this CA. /// /// Generally depending on the Attestation CA in use, this can help determine properties /// of the authenticator that is in use. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde( try_from = "SerialisableAttestationCa", into = "SerialisableAttestationCa" )] pub struct AttestationCa { /// The x509 root CA of the attestation chain that a security key will be attested to. ca: x509::X509, /// If not empty, the set of acceptable AAGUIDS (Device Ids) that are allowed to be /// attested as trusted by this CA. AAGUIDS that are not in this set, but signed by /// this CA will NOT be trusted. aaguids: BTreeMap, blanket_allow: bool, } #[allow(clippy::from_over_into)] impl Into for AttestationCa { fn into(self) -> SerialisableAttestationCa { SerialisableAttestationCa { ca: Base64UrlSafeData::from(self.ca.to_der().expect("Invalid DER")), aaguids: self.aaguids, blanket_allow: self.blanket_allow, } } } impl TryFrom for AttestationCa { type Error = OpenSSLErrorStack; fn try_from(data: SerialisableAttestationCa) -> Result { Ok(AttestationCa { ca: x509::X509::from_der(data.ca.as_slice())?, aaguids: data.aaguids, blanket_allow: data.blanket_allow, }) } } impl AttestationCa { pub fn ca(&self) -> &x509::X509 { &self.ca } pub fn aaguids(&self) -> &BTreeMap { &self.aaguids } pub fn blanket_allow(&self) -> bool { self.blanket_allow } /// Retrieve the Key Identifier for this Attestation Ca pub fn get_kid(&self) -> Result, OpenSSLErrorStack> { self.ca .digest(hash::MessageDigest::sha256()) .map(|bytes| bytes.to_vec()) } fn insert_device( &mut self, aaguid: Uuid, desc_english: String, desc_localised: BTreeMap, ) { self.blanket_allow = false; self.aaguids.insert( aaguid, DeviceDescription { en: desc_english, localised: desc_localised, }, ); } fn new_from_pem(data: &[u8]) -> Result { Ok(AttestationCa { ca: x509::X509::from_pem(data)?, aaguids: BTreeMap::default(), blanket_allow: true, }) } fn union(&mut self, other: &Self) { // if either is a blanket allow, we just do that. if self.blanket_allow || other.blanket_allow { self.blanket_allow = true; self.aaguids.clear(); } else { self.blanket_allow = false; for (o_aaguid, o_device) in other.aaguids.iter() { // We can use the entry api here since o_aaguid is copy. self.aaguids .entry(*o_aaguid) .or_insert_with(|| o_device.clone()); } } } fn intersection(&mut self, other: &Self) { // If they are a blanket allow, do nothing, we are already // more restrictive, or we also are a blanket allow if other.blanket_allow() { // Do nothing } else if self.blanket_allow { // Just set our aaguids to other, and remove our blanket allow. self.blanket_allow = false; self.aaguids = other.aaguids.clone(); } else { // Only keep what is also in other. self.aaguids .retain(|s_aaguid, _| other.aaguids.contains_key(s_aaguid)) } } fn can_retain(&self) -> bool { // Only retain a CA if it's a blanket allow, or has aaguids remaining. self.blanket_allow || !self.aaguids.is_empty() } } /// A list of AttestationCas and associated options. #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct AttestationCaList { /// The set of CA's that we trust in this Operation cas: BTreeMap, } impl TryFrom<&[u8]> for AttestationCaList { type Error = OpenSSLErrorStack; fn try_from(data: &[u8]) -> Result { let mut new = Self::default(); let att_ca = AttestationCa::new_from_pem(data)?; new.insert(att_ca)?; Ok(new) } } impl AttestationCaList { pub fn cas(&self) -> &BTreeMap { &self.cas } pub fn clear(&mut self) { self.cas.clear() } pub fn len(&self) -> usize { self.cas.len() } /// Determine if this attestation list contains any members. pub fn is_empty(&self) -> bool { self.cas.is_empty() } /// Insert a new att_ca into this Attestation Ca List pub fn insert( &mut self, att_ca: AttestationCa, ) -> Result, OpenSSLErrorStack> { // Get the key id (kid, digest). let att_ca_dgst = att_ca.get_kid()?; Ok(self.cas.insert(att_ca_dgst.into(), att_ca)) } /// Join two CA lists into one, taking all elements from both. pub fn union(&mut self, other: &Self) { for (o_kid, o_att_ca) in other.cas.iter() { if let Some(s_att_ca) = self.cas.get_mut(o_kid) { s_att_ca.union(o_att_ca) } else { self.cas.insert(o_kid.clone(), o_att_ca.clone()); } } } /// Retain only the CA's and devices that exist in self and other. pub fn intersection(&mut self, other: &Self) { self.cas.retain(|s_kid, s_att_ca| { // First, does this exist in our partner? if let Some(o_att_ca) = other.cas.get(s_kid) { // Now, intersect. s_att_ca.intersection(o_att_ca); if s_att_ca.can_retain() { // Still as elements, retain. true } else { // Nothing remains, remove. false } } else { // Not in other, remove. false } }) } } #[derive(Default)] pub struct AttestationCaListBuilder { cas: BTreeMap, AttestationCa>, } impl AttestationCaListBuilder { pub fn new() -> Self { Self::default() } pub fn insert_device_x509( &mut self, ca: x509::X509, aaguid: Uuid, desc_english: String, desc_localised: BTreeMap, ) -> Result<(), OpenSSLErrorStack> { let kid = ca .digest(hash::MessageDigest::sha256()) .map(|bytes| bytes.to_vec())?; let mut att_ca = if let Some(att_ca) = self.cas.remove(&kid) { att_ca } else { AttestationCa { ca, aaguids: BTreeMap::default(), blanket_allow: false, } }; att_ca.insert_device(aaguid, desc_english, desc_localised); self.cas.insert(kid, att_ca); Ok(()) } pub fn insert_device_der( &mut self, ca_der: &[u8], aaguid: Uuid, desc_english: String, desc_localised: BTreeMap, ) -> Result<(), OpenSSLErrorStack> { let ca = x509::X509::from_der(ca_der)?; self.insert_device_x509(ca, aaguid, desc_english, desc_localised) } pub fn insert_device_pem( &mut self, ca_pem: &[u8], aaguid: Uuid, desc_english: String, desc_localised: BTreeMap, ) -> Result<(), OpenSSLErrorStack> { let ca = x509::X509::from_pem(ca_pem)?; self.insert_device_x509(ca, aaguid, desc_english, desc_localised) } pub fn build(self) -> AttestationCaList { let cas = self .cas .into_iter() .map(|(kid, att_ca)| (kid.into(), att_ca)) .collect(); AttestationCaList { cas } } }