fix credMgmt, passing all tests

This commit is contained in:
Conor Patrick
2021-04-25 16:31:25 +02:00
committed by Nicolas Stalder
parent b146c415a8
commit 3b911667d6
4 changed files with 78 additions and 268 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ use trussed::{
pub(crate) use ctap_types::{
Bytes, Bytes32, consts, String, Vec,
// authenticator::{ctap1, ctap2, Error, Request, Response},
ctap2::make_credential::CredentialProtectionPolicy,
ctap2::credential_management::CredentialProtectionPolicy,
sizes::*,
webauthn::PublicKeyCredentialDescriptor,
};
+57 -260
View File
@@ -17,7 +17,10 @@ use ctap_types::{
Error,
ctap2::{
self,
credential_management::*,
credential_management::{
CredentialProtectionPolicy,
Response,
},
},
},
cose::PublicKey,
@@ -33,7 +36,10 @@ use crate::{
Result,
UserPresence,
credential::Credential,
state::CommandCache,
state::{
CredentialManagementEnumerateRps,
CredentialManagementEnumerateCredentials,
},
};
pub struct CredentialManagement<'a, UP, T>
@@ -103,11 +109,12 @@ where UP: UserPresence,
let mut last_rp = PathBuf::from(first_rp.file_name());
loop {
let maybe_next_rp = syscall!(self.trussed.read_dir_first(
syscall!(self.trussed.read_dir_first(
Location::Internal,
dir.clone(),
Some(last_rp),
)).entry;
)).entry.unwrap();
let maybe_next_rp = syscall!(self.trussed.read_dir_next()).entry;
match maybe_next_rp {
None => {
@@ -123,8 +130,10 @@ where UP: UserPresence,
}
Some(rp) => {
last_rp = PathBuf::from(rp.file_name());
info!("counting..");
let (this_rp_rk_count, _) =
self.count_rp_rks(PathBuf::from(rp.path()))?;
info!("{:?}", this_rp_rk_count);
num_rks += this_rp_rk_count;
}
}
@@ -195,7 +204,7 @@ where UP: UserPresence,
if let Some(total_rps) = response.total_rps {
if total_rps > 1 {
let rp_id_hash = response.rp_id_hash.as_ref().unwrap().clone();
self.state.runtime.cache = Some(CommandCache::
self.state.runtime.cache_rp = Some(
CredentialManagementEnumerateRps(total_rps - 1, rp_id_hash));
}
}
@@ -207,11 +216,11 @@ where UP: UserPresence,
pub fn next_relying_party(&mut self) -> Result<Response> {
info!("next rp");
let (remaining, last_rp_id_hash) = match self.state.runtime.cache {
Some(CommandCache::CredentialManagementEnumerateRps(
let (remaining, last_rp_id_hash) = match self.state.runtime.cache_rp {
Some(CredentialManagementEnumerateRps(
remaining, ref rp_id_hash)) =>
(remaining, rp_id_hash),
_ => return Err(Error::InvalidCommand),
_ => return Err(Error::NotAllowed),
};
let dir = PathBuf::from(b"rk");
@@ -220,12 +229,19 @@ where UP: UserPresence,
super::format_hex(&last_rp_id_hash[..8], &mut hex);
let filename = PathBuf::from(&hex);
let maybe_next_rp = syscall!(self.trussed.read_dir_first(
let mut maybe_next_rp = syscall!(self.trussed.read_dir_first(
Location::Internal,
dir,
Some(filename),
)).entry;
// Advance to the next
if maybe_next_rp.is_some() {
maybe_next_rp = syscall!(self.trussed.read_dir_next()).entry;
} else {
return Err(Error::NotAllowed);
}
let mut response: ctap2::credential_management::Response = Default::default();
if let Some(rp) = maybe_next_rp {
@@ -256,16 +272,16 @@ where UP: UserPresence,
// cache state for next call
if remaining > 1 {
let rp_id_hash = response.rp_id_hash.as_ref().unwrap().clone();
self.state.runtime.cache = Some(CommandCache::
CredentialManagementEnumerateRps(
remaining - 1, rp_id_hash));
self.state.runtime.cache_rp = Some(CredentialManagementEnumerateRps(
remaining - 1, rp_id_hash
));
} else {
self.state.runtime.cache = None;
self.state.runtime.cache_rp = None;
}
}
}
} else {
self.state.runtime.cache = None;
self.state.runtime.cache_rp = None;
}
Ok(response)
@@ -291,7 +307,7 @@ where UP: UserPresence,
pub fn first_credential(&mut self, rp_id_hash: &Bytes32) -> Result<Response> {
info!("first credential");
self.state.runtime.cache = None;
self.state.runtime.cache_rk = None;
let mut hex = [b'0'; 16];
super::format_hex(&rp_id_hash[..8], &mut hex);
@@ -308,14 +324,11 @@ where UP: UserPresence,
if let Some(num_rks) = response.total_credentials {
if num_rks > 1 {
// let rp_id_hash = response.rp_id_hash.as_ref().unwrap().clone();
self.state.runtime.cache = Some(CommandCache::
CredentialManagementEnumerateCredentials(
num_rks - 1,
first_rk.path().parent().unwrap(),
PathBuf::from(first_rk.file_name()),
));
} else {
self.state.runtime.cache = None;
self.state.runtime.cache_rk = Some(CredentialManagementEnumerateCredentials(
num_rks - 1,
first_rk.path().parent().unwrap(),
PathBuf::from(first_rk.file_name()),
));
}
}
@@ -325,25 +338,32 @@ where UP: UserPresence,
pub fn next_credential(&mut self) -> Result<Response> {
info!("next credential");
let (remaining, rp_dir, prev_filename) = match self.state.runtime.cache {
Some(CommandCache::CredentialManagementEnumerateCredentials(
let (remaining, rp_dir, prev_filename) = match self.state.runtime.cache_rk {
Some(CredentialManagementEnumerateCredentials(
x, ref y, ref z))
=> (x, y.clone(), z.clone()),
_ => return Err(Error::InvalidCommand),
_ => return Err(Error::NotAllowed),
};
self.state.runtime.cache = None;
self.state.runtime.cache_rk = None;
// let mut hex = [b'0'; 16];
// super::format_hex(&rp_id_hash[..8], &mut hex);
// let rp_dir = PathBuf::from(b"rk").join(&PathBuf::from(&hex));
let maybe_next_rk = syscall!(self.trussed.read_dir_first(
let mut maybe_next_rk = syscall!(self.trussed.read_dir_first(
Location::Internal,
rp_dir,
Some(prev_filename)
)).entry;
// Advance to the next
if maybe_next_rk.is_some() {
maybe_next_rk = syscall!(self.trussed.read_dir_next()).entry;
} else {
return Err(Error::NotAllowed);
}
match maybe_next_rk {
Some(rk) => {
// extract data required into response
@@ -352,13 +372,11 @@ where UP: UserPresence,
// cache state for next call
if remaining > 1 {
self.state.runtime.cache = Some(CommandCache::
CredentialManagementEnumerateCredentials(
remaining - 1,
rk.path().parent().unwrap(),
PathBuf::from(rk.file_name()),
)
);
self.state.runtime.cache_rk = Some(CredentialManagementEnumerateCredentials(
remaining - 1,
rk.path().parent().unwrap(),
PathBuf::from(rk.file_name()),
));
}
Ok(response)
@@ -437,7 +455,10 @@ where UP: UserPresence,
}
};
response.public_key = Some(cose_public_key);
// response.cred_protect = Some(credential.data.cred_protect as u8);
response.cred_protect = match credential.cred_protect {
Some(x) => Some(x),
None => Some(CredentialProtectionPolicy::Optional),
};
Ok(response)
}
@@ -494,227 +515,3 @@ where UP: UserPresence,
}
}
// pub fn get_creds_metadata<S, UP>(
// authnr: &mut Authenticator<UP>,
// ) -> Result<Response>
// where
// S: CryptoSyscall,
// UP: UserPresence
// {
// let mut response: ctap2::credential_management::Response = Default::default();
// response.max_possible_remaining_residential_credentials_count =
// Some(self.state.persistent.max_resident_credentials_guesstimate());
// // count number of existing RKs
// todo!();
// Ok(response)
// }
// pub fn enumerate_rps_begin<S, UP>(
// authnr: &mut Authenticator<UP>,
// ) -> Result<Response>
// where
// S: CryptoSyscall,
// UP: UserPresence
// {
// let mut response: ctap2::credential_management::Response = Default::default();
// // rp (0x03): PublicKeyCredentialRpEntity
// // rpIDHash (0x04) : RP ID SHA-256 hash.
// // totalRPs (0x05) : Total number of RPs present on the authenticator.
// let dir = PathBuf::from(b"rk");
// let maybe_first_rp = syscall!(self.trussed.read_dir_first(
// Location::Internal,
// dir,
// None,
// )).entry;
// response.total_rps = Some(match maybe_first_rp {
// None => {
// 0
// }
// _ => {
// let mut num_rps = 1;
// loop {
// let maybe_next_rp = syscall!(self.trussed.read_dir_next())
// .entry;
// match maybe_next_rp {
// None => break,
// _ => num_rps += 1,
// }
// }
// num_rps
// }
// });
// if let Some(rp) = maybe_first_rp {
// // load credential and extract rp and rpIdHash
// let maybe_first_credential = syscall!(self.trussed.read_dir_first(
// Location::Internal,
// PathBuf::from(rp.path()),
// None
// )).entry;
// match maybe_first_credential {
// None => panic!("chaos! disorder!"),
// Some(rk_entry) => {
// let serialized = syscall!(self.trussed.read_file(
// Location::Internal,
// rk_entry.path().into(),
// )).data;
// let credential = Credential::deserialize(&serialized)
// // this may be a confusing error message
// .map_err(|_| Error::InvalidCredential)?;
// let rp = credential.data.rp;
// response.rp_id_hash = Some(self.hash(&rp.id.as_ref())?);
// response.rp = Some(rp);
// }
// }
// // cache state for next call
// if let Some(total_rps) = response.total_rps {
// if total_rps > 1 {
// let rp_id_hash = response.rp_id_hash.as_ref().unwrap().clone();
// self.state.runtime.cache = Some(CommandCache::
// CredentialManagementEnumerateRps(total_rps - 1, rp_id_hash));
// }
// }
// }
// Ok(response)
// }
// pub fn enumerate_rps_get_next_rp<S, UP>(
// authnr: &mut Authenticator<UP>,
// ) -> Result<Response>
// where
// S: CryptoSyscall,
// UP: UserPresence
// {
// let (remaining, last_rp_id_hash) = match self.state.runtime.cache {
// Some(CommandCache::CredentialManagementEnumerateRps(
// remaining, ref rp_id_hash)) =>
// (remaining, rp_id_hash),
// _ => return Err(Error::InvalidCommand),
// };
// let dir = PathBuf::from(b"rk");
// let mut hex = [b'0'; 16];
// super::format_hex(&last_rp_id_hash[..8], &mut hex);
// let filename = PathBuf::from(&hex);
// let maybe_next_rp = syscall!(self.trussed.read_dir_first(
// Location::Internal,
// dir,
// Some(filename),
// )).entry;
// let mut response: ctap2::credential_management::Response = Default::default();
// if let Some(rp) = maybe_next_rp {
// // load credential and extract rp and rpIdHash
// let maybe_first_credential = syscall!(self.trussed.read_dir_first(
// Location::Internal,
// PathBuf::from(rp.path()),
// None
// )).entry;
// match maybe_first_credential {
// None => panic!("chaos! disorder!"),
// Some(rk_entry) => {
// let serialized = syscall!(self.trussed.read_file(
// Location::Internal,
// rk_entry.path().into(),
// )).data;
// let credential = Credential::deserialize(&serialized)
// // this may be a confusing error message
// .map_err(|_| Error::InvalidCredential)?;
// let rp = credential.data.rp;
// response.rp_id_hash = Some(self.hash(&rp.id.as_ref())?);
// response.rp = Some(rp);
// // cache state for next call
// if remaining > 1 {
// let rp_id_hash = response.rp_id_hash.as_ref().unwrap().clone();
// self.state.runtime.cache = Some(CommandCache::
// CredentialManagementEnumerateRps(
// remaining - 1, rp_id_hash));
// } else {
// self.state.runtime.cache = None;
// }
// }
// }
// } else {
// self.state.runtime.cache = None;
// }
// Ok(response)
// }
// pub fn delete_credential<S, UP>(
// authnr: &mut Authenticator<UP>,
// credential_descriptor: &PublicKeyCredentialDescriptor,
// ) -> Result<Response>
// where
// S: CryptoSyscall,
// UP: UserPresence
// {
// let credential_id_hash = self.hash(&credential_descriptor.id[..])?;
// let mut hex = [b'0'; 16];
// super::format_hex(&credential_id_hash[..8], &mut hex);
// let dir = PathBuf::from(b"rk");
// let filename = PathBuf::from(&hex);
// let rk_path = syscall!(self.trussed.locate_file(
// Location::Internal,
// Some(dir.clone()),
// filename,
// )).path.ok_or(Error::InvalidCredential)?;
// // DELETE
// self.delete_resident_key_by_path(&rk_path)?;
// // get rid of directory if it's now empty
// let rp_path = rk_path.parent()
// // by construction, RK has a parent, its RP
// .unwrap();
// let maybe_first_remaining_rk = syscall!(self.trussed.read_dir_first(
// Location::Internal,
// rp_path.clone(),
// None,
// )).entry;
// if maybe_first_remaining_rk.is_none() {
// // info!("deleting parent {:?} as this was its last RK",
// // &rp_path);
// syscall!(self.trussed.remove_dir(
// Location::Internal,
// rp_path,
// ));
// }
// // just return OK
// let response: ctap2::credential_management::Response = Default::default();
// Ok(response)
// }
+14 -2
View File
@@ -769,7 +769,10 @@ where UP: UserPresence,
// check pinAuth
let pin_token = self.state.runtime.pin_token(&mut self.trussed);
let mut data: Bytes<consts::U256> =
type BufSize = <
heapless::consts::U256 as core::ops::Add<ctap_types::sizes::MAX_CREDENTIAL_ID_LENGTH>
>::Output;
let mut data: Bytes<BufSize> =
Bytes::try_from_slice(&[sub_command as u8]).unwrap();
let len = 1 + match sub_command {
Subcommand::EnumerateCredentialsBegin |
@@ -801,7 +804,15 @@ where UP: UserPresence,
} else {
info!("failed pinauth!");
self.state.decrement_retries(&mut self.trussed)?;
self.state.pin_blocked()
let maybe_blocked = self.state.pin_blocked();
if maybe_blocked.is_err() {
info!("blocked");
maybe_blocked
} else {
info!("pinAuthInvalid");
Err(Error::PinAuthInvalid)
}
}
}
@@ -2089,6 +2100,7 @@ where UP: UserPresence,
max_msg_size: Some(ctap_types::sizes::MESSAGE_SIZE),
pin_protocols: Some(pin_protocols),
max_creds_in_list: Some(ctap_types::sizes::MAX_CREDENTIAL_COUNT_IN_LIST_VALUE),
max_cred_id_length: Some(ctap_types::sizes::MAX_CREDENTIAL_ID_LENGTH_VALUE),
..ctap2::get_info::Response::default()
}
}
+6 -5
View File
@@ -149,10 +149,10 @@ impl Identity {
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum CommandCache {
CredentialManagementEnumerateRps(u32, Bytes32),
CredentialManagementEnumerateCredentials(u32, PathBuf, PathBuf),
}
pub struct CredentialManagementEnumerateRps(pub u32, pub Bytes32);
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct CredentialManagementEnumerateCredentials(pub u32, pub PathBuf, pub PathBuf);
#[derive(Clone, Debug, /*uDebug,*/ Default, /*PartialEq,*/ serde::Deserialize, serde::Serialize)]
pub struct ActiveGetAssertionData {
@@ -176,7 +176,8 @@ pub struct RuntimeState {
credentials: Option<MaxCredentialHeap>,
pub active_get_assertion: Option<ActiveGetAssertionData>,
channel: Option<u32>,
pub cache: Option<CommandCache>,
pub cache_rp: Option<CredentialManagementEnumerateRps>,
pub cache_rk: Option<CredentialManagementEnumerateCredentials>,
}
// TODO: Plan towards future extensibility