From 29fc403d81e251d62d0564bb58abd8536fbc0fc5 Mon Sep 17 00:00:00 2001 From: pjht Date: Tue, 29 Aug 2023 10:36:45 -0500 Subject: [PATCH] Add attestation support --- func_avail | 76 +++++------ src/assert.rs | 306 +++++++++++++++++++++++++++++++++++++++++++++ src/device.rs | 2 +- src/device/info.rs | 33 ++--- src/lib.rs | 1 + src/pkey.rs | 97 ++++++++++++++ 6 files changed, 462 insertions(+), 53 deletions(-) create mode 100644 src/pkey.rs diff --git a/func_avail b/func_avail index 49b0d06..7b8ed13 100644 --- a/func_avail +++ b/func_avail @@ -1,43 +1,43 @@ - fido_assert_allow_cred -- fido_assert_authdata_len -- fido_assert_authdata_ptr -- fido_assert_blob_len -- fido_assert_blob_ptr -- fido_assert_clientdata_hash_len -- fido_assert_clientdata_hash_ptr -- fido_assert_count +Y fido_assert_authdata_len +Y fido_assert_authdata_ptr +Y fido_assert_blob_len +Y fido_assert_blob_ptr +Y fido_assert_clientdata_hash_len +Y fido_assert_clientdata_hash_ptr +Y fido_assert_count - fido_assert_empty_allow_list -- fido_assert_flags -- fido_assert_free -- fido_assert_hmac_secret_len -- fido_assert_hmac_secret_ptr -- fido_assert_id_len -- fido_assert_id_ptr -- fido_assert_largeblob_key_len -- fido_assert_largeblob_key_ptr -- fido_assert_new -- fido_assert_rp_id -- fido_assert_set_authdata -- fido_assert_set_authdata_raw -- fido_assert_set_clientdata -- fido_assert_set_clientdata_hash -- fido_assert_set_count -- fido_assert_set_extensions -- fido_assert_set_hmac_salt -- fido_assert_set_hmac_secret -- fido_assert_set_rp -- fido_assert_set_sig -- fido_assert_set_up -- fido_assert_set_uv -- fido_assert_sig_len -- fido_assert_sig_ptr -- fido_assert_sigcount -- fido_assert_user_display_name -- fido_assert_user_icon -- fido_assert_user_id_len -- fido_assert_user_id_ptr -- fido_assert_user_name -- fido_assert_verify +Y fido_assert_flags +Y fido_assert_free +Y fido_assert_hmac_secret_len +Y fido_assert_hmac_secret_ptr +Y fido_assert_id_len +Y fido_assert_id_ptr +Y fido_assert_largeblob_key_len +Y fido_assert_largeblob_key_ptr +Y fido_assert_new +Y fido_assert_rp_id +Y fido_assert_set_authdata +Y fido_assert_set_authdata_raw +Y fido_assert_set_clientdata +Y fido_assert_set_clientdata_hash +Y fido_assert_set_count +Y fido_assert_set_extensions +Y fido_assert_set_hmac_salt +Y fido_assert_set_hmac_secret +Y fido_assert_set_rp +Y fido_assert_set_sig +Y fido_assert_set_up +Y fido_assert_set_uv +Y fido_assert_sig_len +Y fido_assert_sig_ptr +Y fido_assert_sigcount +Y fido_assert_user_display_name +Y fido_assert_user_icon +Y fido_assert_user_id_len +Y fido_assert_user_id_ptr +Y fido_assert_user_name +Y fido_assert_verify - fido_bio_dev_enroll_begin - fido_bio_dev_enroll_cancel diff --git a/src/assert.rs b/src/assert.rs index 47c42a5..027787c 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -1,3 +1,309 @@ +use std::ffi::{CStr, CString}; + +use crate::{ + error::{check, Result}, + pkey::PubKey, + util::{check_initialized, opt_bool_to_fido_opt}, + CoseAlg, +}; use libfido2_sys::*; c_type_wrapper!(fido_assert_t, Assertion, free = fido_assert_free); + +impl Assertion { + pub fn new(&self) -> Result { + check_initialized()?; + unsafe { Ok(Self::from_ptr(fido_assert_new())) } + } +} + +impl AssertionRef { + pub fn authdata(&self, idx: usize) -> Option<&[u8]> { + assert!(idx < self.count()); + unsafe { + if fido_assert_authdata_ptr(self.as_ptr(), idx).is_null() { + None + } else { + Some(std::slice::from_raw_parts( + fido_assert_authdata_ptr(self.as_ptr(), idx), + fido_assert_authdata_len(self.as_ptr(), idx), + )) + } + } + } + + pub fn blob(&self, idx: usize) -> Option<&[u8]> { + assert!(idx < self.count()); + unsafe { + if fido_assert_blob_ptr(self.as_ptr(), idx).is_null() { + None + } else { + Some(std::slice::from_raw_parts( + fido_assert_blob_ptr(self.as_ptr(), idx), + fido_assert_blob_len(self.as_ptr(), idx), + )) + } + } + } + + pub fn clientdata_hash(&self) -> Option<&[u8]> { + unsafe { + if fido_assert_clientdata_hash_ptr(self.as_ptr()).is_null() { + None + } else { + Some(std::slice::from_raw_parts( + fido_assert_clientdata_hash_ptr(self.as_ptr()), + fido_assert_clientdata_hash_len(self.as_ptr()), + )) + } + } + } + + pub fn count(&self) -> usize { + unsafe { fido_assert_count(self.as_ptr()) } + } + + pub fn flags(&self, idx: usize) -> u8 { + assert!(idx < self.count()); + unsafe { fido_assert_flags(self.as_ptr(), idx) } + } + + pub fn hmac_secret(&self, idx: usize) -> Option<&[u8]> { + assert!(idx < self.count()); + unsafe { + if fido_assert_hmac_secret_ptr(self.as_ptr(), idx).is_null() { + None + } else { + Some(std::slice::from_raw_parts( + fido_assert_hmac_secret_ptr(self.as_ptr(), idx), + fido_assert_hmac_secret_len(self.as_ptr(), idx), + )) + } + } + } + + pub fn id(&self, idx: usize) -> Option<&[u8]> { + assert!(idx < self.count()); + unsafe { + if fido_assert_id_ptr(self.as_ptr(), idx).is_null() { + None + } else { + Some(std::slice::from_raw_parts( + fido_assert_id_ptr(self.as_ptr(), idx), + fido_assert_id_len(self.as_ptr(), idx), + )) + } + } + } + + pub fn largeblob_key(&self, idx: usize) -> &[u8] { + assert!(idx < self.count()); + unsafe { + std::slice::from_raw_parts( + fido_assert_largeblob_key_ptr(self.as_ptr(), idx), + fido_assert_largeblob_key_len(self.as_ptr(), idx), + ) + } + } + + pub fn rp_id(&self) -> Option<&str> { + unsafe { + let ptr = fido_assert_rp_id(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(CStr::from_ptr(ptr).to_str().unwrap()) + } + } + } + + pub fn set_authdata(&mut self, idx: usize, data: &[u8]) -> Result<()> { + assert!(idx < self.count()); + unsafe { + check(fido_assert_set_authdata( + self.as_ptr_mut(), + idx, + &data[0], + data.len(), + )) + } + } + + pub fn set_authdata_raw(&mut self, idx: usize, data: &[u8]) -> Result<()> { + assert!(idx < self.count()); + unsafe { + check(fido_assert_set_authdata_raw( + self.as_ptr_mut(), + idx, + &data[0], + data.len(), + )) + } + } + + pub fn set_clientdata(&mut self, data: &[u8]) -> Result<()> { + unsafe { + check(fido_assert_set_clientdata( + self.as_ptr_mut(), + &data[0], + data.len(), + )) + } + } + + pub fn set_clientdata_hash(&mut self, hash: &[u8]) -> Result<()> { + unsafe { + check(fido_assert_set_clientdata_hash( + self.as_ptr_mut(), + &hash[0], + hash.len(), + )) + } + } + + pub fn set_count(&mut self, count: usize) -> Result<()> { + unsafe { check(fido_assert_set_count(self.as_ptr_mut(), count)) } + } + + pub fn set_extensions(&mut self, extensions: i32) -> Result<()> { + unsafe { check(fido_assert_set_extensions(self.as_ptr_mut(), extensions)) } + } + + pub fn set_hmac_salt(&mut self, data: &[u8]) -> Result<()> { + unsafe { + check(fido_assert_set_hmac_salt( + self.as_ptr_mut(), + &data[0], + data.len(), + )) + } + } + + pub fn set_hmac_secret(&mut self, idx: usize, data: &[u8]) -> Result<()> { + assert!(idx < self.count()); + unsafe { + check(fido_assert_set_hmac_secret( + self.as_ptr_mut(), + idx, + &data[0], + data.len(), + )) + } + } + + pub fn set_rp(&mut self, rp: impl AsRef) -> Result<()> { + let rp = CString::new(rp.as_ref()).unwrap(); + unsafe { check(fido_assert_set_rp(self.as_ptr_mut(), rp.as_ptr())) } + } + + pub fn set_sig(&mut self, idx: usize, data: &[u8]) -> Result<()> { + assert!(idx < self.count()); + unsafe { + check(fido_assert_set_sig( + self.as_ptr_mut(), + idx, + &data[0], + data.len(), + )) + } + } + + pub fn set_up(&mut self, up: Option) -> Result<()> { + unsafe { + check(fido_assert_set_up( + self.as_ptr_mut(), + opt_bool_to_fido_opt(up), + )) + } + } + + pub fn set_uv(&mut self, uv: Option) -> Result<()> { + unsafe { + check(fido_assert_set_uv( + self.as_ptr_mut(), + opt_bool_to_fido_opt(uv), + )) + } + } + + pub fn sig(&self, idx: usize) -> Option<&[u8]> { + assert!(idx < self.count()); + unsafe { + if fido_assert_sig_ptr(self.as_ptr(), idx).is_null() { + None + } else { + Some(std::slice::from_raw_parts( + fido_assert_sig_ptr(self.as_ptr(), idx), + fido_assert_sig_len(self.as_ptr(), idx), + )) + } + } + } + + pub fn sigcount(&self, idx: usize) -> u32 { + assert!(idx < self.count()); + unsafe { fido_assert_sigcount(self.as_ptr(), idx) } + } + + pub fn user_display_name(&self, idx: usize) -> Option<&str> { + assert!(idx < self.count()); + unsafe { + let ptr = fido_assert_user_display_name(self.as_ptr(), idx); + if ptr.is_null() { + None + } else { + Some(CStr::from_ptr(ptr).to_str().unwrap()) + } + } + } + + pub fn user_icon(&self, idx: usize) -> Option<&str> { + assert!(idx < self.count()); + unsafe { + let ptr = fido_assert_user_icon(self.as_ptr(), idx); + if ptr.is_null() { + None + } else { + Some(CStr::from_ptr(ptr).to_str().unwrap()) + } + } + } + + pub fn user_id(&self, idx: usize) -> Option<&[u8]> { + assert!(idx < self.count()); + unsafe { + if fido_assert_user_id_ptr(self.as_ptr(), idx).is_null() { + None + } else { + Some(std::slice::from_raw_parts( + fido_assert_user_id_ptr(self.as_ptr(), idx), + fido_assert_user_id_len(self.as_ptr(), idx), + )) + } + } + } + + pub fn user_name(&self, idx: usize) -> Option<&str> { + assert!(idx < self.count()); + unsafe { + let ptr = fido_assert_user_name(self.as_ptr(), idx); + if ptr.is_null() { + None + } else { + Some(CStr::from_ptr(ptr).to_str().unwrap()) + } + } + } + + pub fn verify(&self, idx: usize, alg: CoseAlg, pubkey: PubKey) -> Result<()> { + assert!(idx < self.count()); + unsafe { + check(fido_assert_verify( + self.as_ptr(), + idx, + alg as i32, + pubkey.as_ptr(), + )) + } + } +} diff --git a/src/device.rs b/src/device.rs index 8c71c55..7582b91 100644 --- a/src/device.rs +++ b/src/device.rs @@ -4,7 +4,7 @@ use std::{ ffi::{CStr, CString}, fmt::Debug, ops::Index, - ptr::{self, NonNull}, + ptr::NonNull, }; use libfido2_sys::*; diff --git a/src/device/info.rs b/src/device/info.rs index 8da42f3..58e01c2 100644 --- a/src/device/info.rs +++ b/src/device/info.rs @@ -96,7 +96,6 @@ impl DeviceCborInfoRef { unsafe { fido_cbor_info_new_pin_required(self.as_ptr()) } } - pub fn options(&self) -> HashMap { let mut map = HashMap::new(); unsafe { @@ -126,10 +125,12 @@ impl DeviceCborInfoRef { } pub fn rk_remaining(&self) -> Option { - unsafe { match fido_cbor_info_rk_remaining(self.as_ptr()) { - -1 => None, - x => Some(x as u64), - }} + unsafe { + match fido_cbor_info_rk_remaining(self.as_ptr()) { + -1 => None, + x => Some(x as u64), + } + } } pub fn transports(&self) -> Vec { @@ -147,20 +148,24 @@ impl DeviceCborInfoRef { } pub fn uv_attempts(&self) -> Option { - unsafe { match fido_cbor_info_uv_attempts(self.as_ptr()) { - 0 => None, - x => Some(x), - }} + unsafe { + match fido_cbor_info_uv_attempts(self.as_ptr()) { + 0 => None, + x => Some(x), + } + } } pub fn uv_modality(&self) -> Option { - unsafe { match fido_cbor_info_uv_modality(self.as_ptr()) { - 0 => None, - x => Some(x), - }} + unsafe { + match fido_cbor_info_uv_modality(self.as_ptr()) { + 0 => None, + x => Some(x), + } + } } - pub fn versions(&self) -> Vec { + pub fn versions(&self) -> Vec { let mut list = Vec::new(); unsafe { let versions_ptr = fido_cbor_info_versions_ptr(self.as_ptr()); diff --git a/src/lib.rs b/src/lib.rs index 5b85ea4..4a462aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ pub mod assert; pub mod cred; pub mod device; pub mod error; +pub mod pkey; #[repr(i32)] #[derive(Debug, Clone, Copy, TryFromPrimitive)] diff --git a/src/pkey.rs b/src/pkey.rs new file mode 100644 index 0000000..d556118 --- /dev/null +++ b/src/pkey.rs @@ -0,0 +1,97 @@ +use std::ffi::c_void; + +use libfido2_sys::*; + +use crate::error::Result; +use crate::util::check_initialized; + +c_type_wrapper!(eddsa_pk_t, Ec25519PubKey, free = eddsa_pk_free); +c_type_wrapper!(es256_pk_t, Ecdsa256PubKey, free = es256_pk_free); +c_type_wrapper!(es384_pk, Ecdsa384PubKey, free = es384_pk_free); +c_type_wrapper!(rs256_pk, Rsa256PubKey, free = rs256_pk_free); + +impl Ec25519PubKey { + pub fn new(data: &[u8]) -> Result { + check_initialized()?; + unsafe { + let ptr = eddsa_pk_new(); + eddsa_pk_from_ptr(ptr, &data[0] as *const u8 as *const c_void, data.len()); + Ok(Self::from_ptr(ptr)) + } + } +} + +impl Ecdsa256PubKey { + pub fn new(data: &[u8]) -> Result { + check_initialized()?; + unsafe { + let ptr = es256_pk_new(); + es256_pk_from_ptr(ptr, &data[0] as *const u8 as *const c_void, data.len()); + Ok(Self::from_ptr(ptr)) + } + } +} + +impl Ecdsa384PubKey { + pub fn new(data: &[u8]) -> Result { + check_initialized()?; + unsafe { + let ptr = es384_pk_new(); + es384_pk_from_ptr(ptr, &data[0] as *const u8 as *const c_void, data.len()); + Ok(Self::from_ptr(ptr)) + } + } +} + +impl Rsa256PubKey { + pub fn new(data: &[u8]) -> Result { + check_initialized()?; + unsafe { + let ptr = rs256_pk_new(); + rs256_pk_from_ptr(ptr, &data[0] as *const u8 as *const c_void, data.len()); + Ok(Self::from_ptr(ptr)) + } + } +} + +pub enum PubKey { + Ec25519(Ec25519PubKey), + Ecdsa256(Ecdsa256PubKey), + Ecdsa384(Ecdsa384PubKey), + Rsa256PubKey(Rsa256PubKey), +} + +impl From for PubKey { + fn from(v: Rsa256PubKey) -> Self { + Self::Rsa256PubKey(v) + } +} + +impl From for PubKey { + fn from(v: Ecdsa384PubKey) -> Self { + Self::Ecdsa384(v) + } +} + +impl From for PubKey { + fn from(v: Ecdsa256PubKey) -> Self { + Self::Ecdsa256(v) + } +} + +impl From for PubKey { + fn from(v: Ec25519PubKey) -> Self { + Self::Ec25519(v) + } +} + +impl PubKey { + pub(crate) fn as_ptr(&self) -> *const c_void { + match self { + PubKey::Ec25519(pk) => pk.as_ptr() as *const c_void, + PubKey::Ecdsa256(pk) => pk.as_ptr() as *const c_void, + PubKey::Ecdsa384(pk) => pk.as_ptr() as *const c_void, + PubKey::Rsa256PubKey(pk) => pk.as_ptr() as *const c_void, + } + } +}