Switch to libfido2 bindings

This commit is contained in:
pjht 2023-08-28 09:13:54 -05:00
parent 7f822885d8
commit f1dacd4bea
Signed by: pjht
GPG Key ID: 7B5F6AFBEC7EE78E
6 changed files with 142 additions and 281 deletions

327
Cargo.lock generated
View File

@ -2,17 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "aes"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "anyhow"
version = "1.0.72"
@ -70,12 +59,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]]
name = "base64ct"
version = "1.6.0"
@ -91,15 +74,6 @@ dependencies = [
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.13.0"
@ -112,15 +86,6 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
dependencies = [
"cipher",
]
[[package]]
name = "cc"
version = "1.0.82"
@ -136,33 +101,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "ciborium"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
[[package]]
name = "ciborium-ll"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "cipher"
version = "0.4.4"
@ -210,29 +148,6 @@ dependencies = [
"typenum",
]
[[package]]
name = "ctap-hid-fido2"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3726fa3f7f978ce0a222ea73c13490115f95e7a31db3061d7c4c91bea58ea01a"
dependencies = [
"aes",
"anyhow",
"base64",
"byteorder",
"cbc",
"hex",
"hidapi",
"num",
"pad",
"ring",
"serde",
"serde_cbor",
"strum",
"strum_macros",
"x509-parser",
]
[[package]]
name = "data-encoding"
version = "2.4.0"
@ -325,6 +240,12 @@ dependencies = [
"zeroize",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "ff"
version = "0.13.0"
@ -369,34 +290,10 @@ dependencies = [
]
[[package]]
name = "half"
version = "1.8.2"
name = "hashbrown"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hidapi"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "798154e4b6570af74899d71155fb0072d5b17e6aa12f39c8ef22c60fb8ec99e7"
dependencies = [
"cc",
"libc",
"pkg-config",
"winapi",
]
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]]
name = "hmac"
@ -407,13 +304,22 @@ dependencies = [
"digest",
]
[[package]]
name = "indexmap"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"block-padding",
"generic-array",
]
@ -447,6 +353,26 @@ version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "libfido2"
version = "0.1.0"
dependencies = [
"libfido2-sys",
"num_enum",
"paste",
"thiserror",
]
[[package]]
name = "libfido2-sys"
version = "0.2.0"
dependencies = [
"anyhow",
"cfg-if",
"pkg-config",
"vcpkg",
]
[[package]]
name = "libm"
version = "0.2.7"
@ -481,20 +407,6 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "num"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.3"
@ -524,12 +436,14 @@ dependencies = [
]
[[package]]
name = "num-complex"
version = "0.4.3"
name = "num-derive"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e"
dependencies = [
"num-traits",
"proc-macro2",
"quote",
"syn 2.0.28",
]
[[package]]
@ -553,18 +467,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.16"
@ -575,6 +477,27 @@ dependencies = [
"libm",
]
[[package]]
name = "num_enum"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.28",
]
[[package]]
name = "oid-registry"
version = "0.6.1"
@ -615,13 +538,10 @@ dependencies = [
]
[[package]]
name = "pad"
version = "0.1.6"
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3"
dependencies = [
"unicode-width",
]
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "pem-rfc7468"
@ -674,6 +594,16 @@ dependencies = [
"elliptic-curve",
]
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.66"
@ -778,12 +708,6 @@ dependencies = [
"nom",
]
[[package]]
name = "rustversion"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]]
name = "sec1"
version = "0.7.3"
@ -803,30 +727,6 @@ name = "serde"
version = "1.0.183"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_cbor"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.183"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
]
[[package]]
name = "sha2"
@ -916,33 +816,14 @@ name = "ssh_attest_verifier"
version = "0.1.0"
dependencies = [
"anyhow",
"ciborium",
"ctap-hid-fido2",
"libfido2",
"num-derive",
"ssh-encoding",
"ssh-key",
"thiserror",
"x509-parser",
]
[[package]]
name = "strum"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
[[package]]
name = "strum_macros"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn 1.0.109",
]
[[package]]
name = "subtle"
version = "2.5.0"
@ -985,18 +866,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.44"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90"
checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.44"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
dependencies = [
"proc-macro2",
"quote",
@ -1031,6 +912,23 @@ dependencies = [
"time-core",
]
[[package]]
name = "toml_datetime"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
[[package]]
name = "toml_edit"
version = "0.19.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a"
dependencies = [
"indexmap",
"toml_datetime",
"winnow",
]
[[package]]
name = "typenum"
version = "1.16.0"
@ -1043,12 +941,6 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "unicode-xid"
version = "0.2.4"
@ -1061,6 +953,12 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
@ -1159,6 +1057,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "winnow"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
dependencies = [
"memchr",
]
[[package]]
name = "x509-parser"
version = "0.15.1"

View File

@ -7,8 +7,8 @@ edition = "2021"
[dependencies]
anyhow = "1.0.72"
ciborium = "0.2.1"
ctap-hid-fido2 = "3.5.0"
libfido2 = { version = "0.1.0", path = "../libfido2" }
num-derive = "0.4.0"
ssh-encoding = "0.2.0"
ssh-key = { version = "0.6.0" }
thiserror = "1.0.44"

View File

@ -1,4 +1,3 @@
use ssh_key::Algorithm;
use thiserror::Error;
use x509_parser::prelude::X509Error;
@ -20,8 +19,6 @@ pub enum Error {
InvalidRootCa(String),
#[error("FIDO public key type unsupported")]
UnsupportedFidoKeyType,
#[error("SSH public key type {} does not match FIDO public key type {}", .ssh.as_str(), .fido.as_str())]
SshFidoTypeMismatch { ssh: Algorithm, fido: Algorithm },
#[error("SSH public key des not match FIDO public key")]
SshFidoKeyMismatch,
}

View File

@ -1,14 +1,11 @@
mod error;
mod ssh_attest;
use ctap_hid_fido2::{
public_key::PublicKeyType,
verifier,
};
pub use error::Error;
use libfido2::{cred::{Credential, CredType}, fido_init};
use ssh_attest::SshAttestationInfo;
use ssh_encoding::Decode;
use ssh_key::{public::KeyData, Algorithm, PublicKey};
use ssh_key::{public::KeyData, PublicKey, Algorithm};
pub use x509_parser;
use x509_parser::{prelude::*, x509::X509Name};
@ -25,13 +22,26 @@ pub fn verify_attestation(
challenge: Vec<u8>,
ssh_pubkey: PublicKey,
) -> Result<(), Error> {
fido_init(true);
// Decode the SSH attestation file
let attestation = SshAttestationInfo::decode(&mut ssh_attest.as_slice())
.map_err(|e| Error::AttestationDecodeError(e))?;
let attestation_cert = attestation
.decode_cert()
.map_err(|e| Error::AttestationCertDecodeError(e))?;
let attestation = attestation.to_fido_attestation().map_err(|_e| Error::AttestationInvalidAuthData)?;
// Reconstruct the credential from the attestation
let mut credential = Credential::new().unwrap();
match ssh_pubkey.algorithm() {
Algorithm::SkEcdsaSha2NistP256 => credential.set_type(CredType::Ecdsa256).unwrap(),
Algorithm::SkEd25519 => credential.set_type(CredType::Ed25519).unwrap(),
_ => return Err(Error::NonFidoSshKey.into()),
};
credential.set_authdata_raw(attestation.auth_data.as_ref().unwrap().as_slice()).unwrap();
credential.set_sig(attestation.enroll_sig.as_slice()).unwrap();
credential.set_clientdata(challenge.as_slice()).unwrap();
credential.set_x509(attestation.attestation_cert.as_slice()).unwrap();
credential.set_fmt("packed").unwrap();
// Extract the RPID from the SSH public key
let application = match ssh_pubkey.key_data() {
@ -40,9 +50,10 @@ pub fn verify_attestation(
_ => return Err(Error::NonFidoSshKey.into()),
};
credential.set_rp(application, "").unwrap();
// Verify the attestation RPID & signature
let verify_result = verifier::verify_attestation(&application, &challenge, &attestation);
if !verify_result.is_success {
if credential.verify().is_err() {
return Err(Error::AttestationVerificationFailed);
}
@ -61,21 +72,6 @@ pub fn verify_attestation(
return Err(Error::InvalidAttestationCertSignature.into());
}
let fido_pubkey = &verify_result.credential_public_key;
// Verify the FIDO public key is the same type as the SSH public key
let fido_pubkey_alg = match fido_pubkey.key_type {
PublicKeyType::Ecdsa256 => Algorithm::SkEcdsaSha2NistP256,
PublicKeyType::Ed25519 => Algorithm::SkEd25519,
PublicKeyType::Unknown => return Err(Error::UnsupportedFidoKeyType),
};
if fido_pubkey_alg != ssh_pubkey.algorithm() {
return Err(Error::SshFidoTypeMismatch {
ssh: ssh_pubkey.algorithm(),
fido: fido_pubkey_alg,
});
}
// Verify that the SSH public key data matches the FIDO public key data
let key_data = match ssh_pubkey.key_data() {
@ -83,7 +79,7 @@ pub fn verify_attestation(
KeyData::SkEd25519(key) => key.public_key().0.as_slice(),
_ => unreachable!(),
};
if key_data != fido_pubkey.der {
if key_data != credential.pubkey() {
return Err(Error::SshFidoKeyMismatch.into());
}
Ok(())

View File

@ -27,9 +27,9 @@ fn main() -> anyhow::Result<()> {
})
.collect::<Result<Vec<_>, _>>()?;
let ssh_attest = std::fs::read("id_ecdsa_rustgen_sk_attest.bin")?;
let challenge = std::fs::read("id_ecdsa_rustgen_sk_attest_chall.bin")?;
let ssh_pubkey = ssh_key::PublicKey::read_openssh_file(Path::new("id_ecdsa_rustgen_sk.pub"))?;
let ssh_attest = std::fs::read("id_ed25519-sk_attest.bin")?;
let challenge = std::fs::read("id_ed25519-sk_attest_chall.bin")?;
let ssh_pubkey = ssh_key::PublicKey::read_openssh_file(Path::new("id_ed25519-sk.pub"))?;
verify_attestation(root_cas, ssh_attest, challenge, ssh_pubkey)?;
println!("Attestation verified successfully");

View File

@ -1,7 +1,5 @@
use std::str::FromStr;
use ciborium::Value;
use ctap_hid_fido2::fidokey::make_credential::{Attestation, make_credential_response};
use ssh_encoding::{Decode, Label, LabelError};
use x509_parser::{nom::Finish, prelude::*};
@ -49,43 +47,6 @@ impl SshAttestationInfo {
.finish()
.map(|(_, cert)| cert)
}
pub fn to_fido_attestation(&self) -> Result<Attestation, anyhow::Error> {
let mut attestation_map_bytes = Vec::new();
ciborium::into_writer(
&Value::from(
[
(0x01.into(), "packed".into()),
(
0x02.into(),
self.auth_data.as_deref().unwrap().into(),
),
(
0x03.into(),
[
// TODO: Don't hardcode algorithm
// -7: ECDSA P256
("alg".into(), (-7).into()),
("sig".into(), self.enroll_sig.as_slice().into()),
(
"x5c".into(),
[Value::from(self.attestation_cert.as_slice())]
.as_slice()
.into(),
),
]
.as_slice()
.into(),
),
]
.as_slice(),
),
&mut attestation_map_bytes,
)
.expect("Failed to serialize CBOR attestation info");
make_credential_response::parse_cbor(&attestation_map_bytes)
}
}
impl Decode for SshAttestationInfo {