credential_management: Implement UpdateUserInformation

This commit is contained in:
Robin Krahl
2024-03-01 17:29:58 +01:00
parent 87e3aef895
commit 079edd84c9
3 changed files with 79 additions and 16 deletions
+1
View File
@@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Extract PIN protocol implementation into separate module ([#62][])
- Implement PIN protocol 2 ([#63][])
- Implement PIN token permissions ([#63][])
- Implement UpdateUserInformation subcommand for CredentialManagement
[#26]: https://github.com/solokeys/fido-authenticator/issues/26
[#28]: https://github.com/solokeys/fido-authenticator/issues/28
+18 -6
View File
@@ -990,7 +990,19 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
}
// 0x7
Subcommand::UpdateUserInformation => Err(Error::InvalidParameter),
Subcommand::UpdateUserInformation => {
let sub_parameters = sub_parameters.as_ref().ok_or(Error::MissingParameter)?;
let credential_id = sub_parameters
.credential_id
.as_ref()
.ok_or(Error::MissingParameter)?;
let user = sub_parameters
.user
.as_ref()
.ok_or(Error::MissingParameter)?;
cred_mgmt.update_user_information(credential_id, user)
}
}
}
@@ -1338,7 +1350,8 @@ impl<UP: UserPresence, T: TrussedRequirements> crate::Authenticator<UP, T> {
sub_command @ Subcommand::GetCredsMetadata
| sub_command @ Subcommand::EnumerateRpsBegin
| sub_command @ Subcommand::EnumerateCredentialsBegin
| sub_command @ Subcommand::DeleteCredential => {
| sub_command @ Subcommand::DeleteCredential
| sub_command @ Subcommand::UpdateUserInformation => {
// check pinProtocol
let pin_protocol = parameters
// .sub_command_params.as_ref().ok_or(Error::MissingParameter)?
@@ -1350,7 +1363,9 @@ impl<UP: UserPresence, T: TrussedRequirements> crate::Authenticator<UP, T> {
let mut data: Bytes<{ sizes::MAX_CREDENTIAL_ID_LENGTH_PLUS_256 }> =
Bytes::from_slice(&[sub_command as u8]).unwrap();
let len = 1 + match sub_command {
Subcommand::EnumerateCredentialsBegin | Subcommand::DeleteCredential => {
Subcommand::EnumerateCredentialsBegin
| Subcommand::DeleteCredential
| Subcommand::UpdateUserInformation => {
data.resize_to_capacity();
// ble, need to reserialize
ctap_types::serde::cbor_serialize(
@@ -1395,9 +1410,6 @@ impl<UP: UserPresence, T: TrussedRequirements> crate::Authenticator<UP, T> {
// of already checked CredMgmt subcommands
Subcommand::EnumerateRpsGetNextRp
| Subcommand::EnumerateCredentialsGetNextCredential => Ok(()),
// not implemented
Subcommand::UpdateUserInformation => Err(Error::InvalidParameter),
}
}
+60 -10
View File
@@ -3,7 +3,7 @@
use core::convert::TryFrom;
use trussed::{
syscall,
syscall, try_syscall,
types::{DirEntry, Location, Path, PathBuf},
};
@@ -11,7 +11,7 @@ use ctap_types::{
cose::PublicKey,
ctap2::credential_management::{CredentialProtectionPolicy, Response},
heapless_bytes::Bytes,
webauthn::PublicKeyCredentialDescriptor,
webauthn::{PublicKeyCredentialDescriptor, PublicKeyCredentialUserEntity},
Error,
};
@@ -460,22 +460,27 @@ where
Ok(response)
}
pub fn delete_credential(
&mut self,
credential_descriptor: &PublicKeyCredentialDescriptor,
) -> Result<Response> {
info!("delete credential");
let credential_id_hash = self.hash(&credential_descriptor.id[..]);
fn find_credential(&mut self, credential: &PublicKeyCredentialDescriptor) -> Option<PathBuf> {
let credential_id_hash = self.hash(&credential.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
syscall!(self
.trussed
.locate_file(Location::Internal, Some(dir), filename,))
.path
.ok_or(Error::InvalidCredential)?;
}
pub fn delete_credential(
&mut self,
credential_descriptor: &PublicKeyCredentialDescriptor,
) -> Result<Response> {
info!("delete credential");
let rk_path = self
.find_credential(credential_descriptor)
.ok_or(Error::InvalidCredential)?;
// DELETE
self.delete_resident_key_by_path(&rk_path)?;
@@ -491,4 +496,49 @@ where
let response = Default::default();
Ok(response)
}
pub fn update_user_information(
&mut self,
credential_descriptor: &PublicKeyCredentialDescriptor,
user: &PublicKeyCredentialUserEntity,
) -> Result<Response> {
info!("update user information");
// locate and parse existing credential
let rk_path = self
.find_credential(credential_descriptor)
.ok_or(Error::NoCredentials)?;
let serialized = syscall!(self.trussed.read_file(Location::Internal, rk_path.clone())).data;
let mut credential =
FullCredential::deserialize(&serialized).map_err(|_| Error::InvalidCredential)?;
// TODO: check remaining space, return KeyStoreFull
// the updated user ID must match the stored user ID
if credential.user.id != user.id {
error!("updated user ID does not match original user ID");
return Err(Error::InvalidParameter);
}
// update user name and display name unless the values are not set or empty
credential.data.user.name = user
.name
.as_ref()
.filter(|s| !s.is_empty())
.map(Clone::clone);
credential.data.user.display_name = user
.display_name
.as_ref()
.filter(|s| !s.is_empty())
.map(Clone::clone);
// write updated credential
let serialized = credential.serialize()?;
try_syscall!(self
.trussed
.write_file(Location::Internal, rk_path, serialized, None))
.map_err(|_| Error::KeyStoreFull)?;
Ok(Default::default())
}
}