Cargo fmt round

This commit is contained in:
Nicolas Stalder
2022-03-17 02:39:21 +01:00
parent ea9f133c30
commit 0aa05b594f
10 changed files with 916 additions and 723 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
use trussed::types::{CertId, KeyId};
pub const FIDO2_UP_TIMEOUT: u32 = 30_000;
pub const U2F_UP_TIMEOUT: u32 = 250;
pub const U2F_UP_TIMEOUT: u32 = 250;
pub const ATTESTATION_CERT_ID: CertId = CertId::from_special(0);
pub const ATTESTATION_KEY_ID: KeyId = KeyId::from_special(0);
+55 -56
View File
@@ -2,26 +2,18 @@
use core::cmp::Ordering;
use trussed::{
client, syscall, try_syscall,
types::KeyId,
};
use trussed::{client, syscall, try_syscall, types::KeyId};
pub(crate) use ctap_types::{
Bytes, String,
// authenticator::{ctap1, ctap2, Error, Request, Response},
ctap2::credential_management::CredentialProtectionPolicy,
sizes::*,
webauthn::PublicKeyCredentialDescriptor,
Bytes,
String,
};
use crate::{
Authenticator,
Error,
Result,
UserPresence,
};
use crate::{Authenticator, Error, Result, UserPresence};
/// As signaled in `get_info`.
///
@@ -49,7 +41,9 @@ impl TryFrom<EncryptedSerializedCredential> for CredentialId {
type Error = Error;
fn try_from(esc: EncryptedSerializedCredential) -> Result<CredentialId> {
Ok(CredentialId(trussed::cbor_serialize_bytes(&esc.0).map_err(|_| Error::Other)?))
Ok(CredentialId(
trussed::cbor_serialize_bytes(&esc.0).map_err(|_| Error::Other)?,
))
}
}
@@ -60,7 +54,7 @@ impl TryFrom<CredentialId> for EncryptedSerializedCredential {
fn try_from(cid: CredentialId) -> Result<EncryptedSerializedCredential> {
let encrypted_serialized_credential = EncryptedSerializedCredential(
ctap_types::serde::cbor_deserialize(&cid.0).map_err(|_| Error::InvalidCredential)?
ctap_types::serde::cbor_deserialize(&cid.0).map_err(|_| Error::InvalidCredential)?,
);
Ok(encrypted_serialized_credential)
}
@@ -78,7 +72,9 @@ pub enum Key {
}
/// The main content of a `Credential`.
#[derive(Clone, Debug, PartialEq, serde_indexed::DeserializeIndexed, serde_indexed::SerializeIndexed)]
#[derive(
Clone, Debug, PartialEq, serde_indexed::DeserializeIndexed, serde_indexed::SerializeIndexed,
)]
pub struct CredentialData {
// id, name, url
pub rp: ctap_types::webauthn::PublicKeyCredentialRpEntity,
@@ -103,7 +99,6 @@ pub struct CredentialData {
pub hmac_secret: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cred_protect: Option<CredentialProtectionPolicy>,
// TODO: add `sig_counter: Option<CounterId>`,
// and grant RKs a per-credential sig-counter.
}
@@ -139,9 +134,7 @@ impl core::ops::Deref for Credential {
/// Likely comparison based on timestamp would be good enough?
impl PartialEq for Credential {
fn eq(&self, other: &Self) -> bool {
(self.creation_time == other.creation_time)
&&
(self.key == other.key)
(self.creation_time == other.creation_time) && (self.key == other.key)
}
}
@@ -183,7 +176,7 @@ impl From<CredentialId> for PublicKeyCredentialDescriptor {
let mut key_type = String::new();
key_type.push_str("public-key").unwrap();
key_type
}
},
}
}
}
@@ -201,9 +194,7 @@ impl Credential {
hmac_secret: Option<bool>,
cred_protect: Option<CredentialProtectionPolicy>,
nonce: [u8; 12],
)
-> Self
{
) -> Self {
info!("credential for algorithm {}", algorithm);
let data = CredentialData {
rp: rp.clone(),
@@ -243,9 +234,7 @@ impl Credential {
trussed: &mut T,
key_encryption_key: KeyId,
rp_id_hash: Option<&Bytes<32>>,
)
-> Result<CredentialId>
{
) -> Result<CredentialId> {
let serialized_credential = self.strip().serialize()?;
let message = &serialized_credential;
// info!("serialized cred = {:?}", message).ok();
@@ -255,14 +244,14 @@ impl Credential {
} else {
syscall!(trussed.hash_sha256(self.rp.id.as_ref()))
.hash
.to_bytes().map_err(|_| Error::Other)?
.to_bytes()
.map_err(|_| Error::Other)?
};
let associated_data = &rp_id_hash[..];
let nonce: [u8; 12] = self.nonce.as_slice().try_into().unwrap();
let encrypted_serialized_credential = EncryptedSerializedCredential(
syscall!(trussed.encrypt_chacha8poly1305(
key_encryption_key, message, associated_data, Some(&nonce))));
let encrypted_serialized_credential = EncryptedSerializedCredential(syscall!(trussed
.encrypt_chacha8poly1305(key_encryption_key, message, associated_data, Some(&nonce))));
let credential_id: CredentialId = encrypted_serialized_credential.try_into().unwrap();
Ok(credential_id)
@@ -283,12 +272,10 @@ impl Credential {
}
pub fn try_from<UP: UserPresence, T: client::Client + client::Chacha8Poly1305>(
authnr: &mut Authenticator<UP,T>,
authnr: &mut Authenticator<UP, T>,
rp_id_hash: &Bytes<32>,
descriptor: &PublicKeyCredentialDescriptor,
)
-> Result<Self>
{
) -> Result<Self> {
Self::try_from_bytes(authnr, rp_id_hash, &descriptor.id)
}
@@ -296,18 +283,17 @@ impl Credential {
authnr: &mut Authenticator<UP, T>,
rp_id_hash: &Bytes<32>,
id: &[u8],
)
-> Result<Self>
{
) -> Result<Self> {
let mut cred: Bytes<MAX_CREDENTIAL_ID_LENGTH> = Bytes::new();
cred.extend_from_slice(id).map_err(|_| Error::InvalidCredential)?;
cred.extend_from_slice(id)
.map_err(|_| Error::InvalidCredential)?;
let encrypted_serialized = EncryptedSerializedCredential::try_from(
CredentialId(cred)
)?;
let encrypted_serialized = EncryptedSerializedCredential::try_from(CredentialId(cred))?;
let kek = authnr.state.persistent.key_encryption_key(&mut authnr.trussed)?;
let kek = authnr
.state
.persistent
.key_encryption_key(&mut authnr.trussed)?;
let serialized = try_syscall!(authnr.trussed.decrypt_chacha8poly1305(
// TODO: use RpId as associated data here?
@@ -317,11 +303,12 @@ impl Credential {
&encrypted_serialized.0.nonce,
&encrypted_serialized.0.tag,
))
.map_err(|_| Error::InvalidCredential)?.plaintext
.ok_or(Error::InvalidCredential)?;
.map_err(|_| Error::InvalidCredential)?
.plaintext
.ok_or(Error::InvalidCredential)?;
let credential = Credential::deserialize(&serialized)
.map_err(|_| Error::InvalidCredential)?;
let credential =
Credential::deserialize(&serialized).map_err(|_| Error::InvalidCredential)?;
Ok(credential)
}
@@ -362,7 +349,7 @@ mod test {
url: None,
},
user: PublicKeyCredentialUserEntity {
id: Bytes::from_slice(&[1,2,3]).unwrap(),
id: Bytes::from_slice(&[1, 2, 3]).unwrap(),
icon: None,
name: None,
display_name: None,
@@ -370,7 +357,7 @@ mod test {
creation_time: 123,
use_counter: false,
algorithm: -7,
key: Key::WrappedKey(Bytes::from_slice(&[1,2,3]).unwrap()),
key: Key::WrappedKey(Bytes::from_slice(&[1, 2, 3]).unwrap()),
hmac_secret: Some(false),
cred_protect: None,
};
@@ -378,7 +365,11 @@ mod test {
}
fn random_bytes<const N: usize>() -> Bytes<N> {
use rand::{RngCore, distributions::{Distribution, Uniform}, rngs::OsRng};
use rand::{
distributions::{Distribution, Uniform},
rngs::OsRng,
RngCore,
};
let mut bytes = Bytes::default();
let between = Uniform::from(0..(N + 1));
@@ -392,7 +383,7 @@ mod test {
#[allow(dead_code)]
fn maybe_random_bytes<const N: usize>() -> Option<Bytes<N>> {
use rand::{RngCore, rngs::OsRng};
use rand::{rngs::OsRng, RngCore};
if OsRng.next_u32() & 1 != 0 {
Some(random_bytes())
} else {
@@ -401,18 +392,26 @@ mod test {
}
fn random_string<const N: usize>() -> String<N> {
use rand::{
distributions::{Alphanumeric, Distribution, Uniform},
rngs::OsRng,
Rng,
};
use std::str::FromStr;
use rand::{Rng, distributions::{Alphanumeric, Distribution, Uniform}, rngs::OsRng};
let between = Uniform::from(0..(N + 1));
let n = between.sample(&mut OsRng);
let std_string: std::string::String = OsRng.sample_iter(&Alphanumeric).take(n).map(char::from).collect();
let std_string: std::string::String = OsRng
.sample_iter(&Alphanumeric)
.take(n)
.map(char::from)
.collect();
String::from_str(&std_string).unwrap()
}
fn maybe_random_string<const N: usize>() -> Option<String<N>> {
use rand::{RngCore, rngs::OsRng};
use rand::{rngs::OsRng, RngCore};
if OsRng.next_u32() & 1 != 0 {
Some(random_string())
} else {
@@ -430,7 +429,7 @@ mod test {
url: maybe_random_string(),
},
user: PublicKeyCredentialUserEntity {
id: random_bytes(),//Bytes::from_slice(&[1,2,3]).unwrap(),
id: random_bytes(), //Bytes::from_slice(&[1,2,3]).unwrap(),
icon: maybe_random_string(),
name: maybe_random_string(),
display_name: maybe_random_string(),
@@ -518,5 +517,5 @@ mod test {
// TestResult::from_bool(credential_data == deserialized)
// }
// }
// }
}
+84 -65
View File
@@ -1,39 +1,22 @@
//! The `ctap_types::ctap1::Authenticator` implementation.
use ctap_types::{
ctap1::{
Authenticator,
ControlByte,
register, authenticate,
Result,
Error,
},
ctap1::{authenticate, register, Authenticator, ControlByte, Error, Result},
heapless_bytes::Bytes,
};
use trussed::{
syscall,
types::{
KeySerialization,
Mechanism,
SignatureSerialization,
Location,
},
types::{KeySerialization, Location, Mechanism, SignatureSerialization},
};
use crate::{
credential::{
self,
Credential,
Key,
},
constants,
SigningAlgorithm,
TrussedRequirements,
UserPresence,
credential::{self, Credential, Key},
SigningAlgorithm, TrussedRequirements, UserPresence,
};
type Commitment = Bytes::<324>;
type Commitment = Bytes<324>;
/// Implement `ctap1::Authenticator` for our Authenticator.
///
@@ -41,8 +24,7 @@ type Commitment = Bytes::<324>;
/// The "proposed standard" of U2F V1.2 applies to CTAP1.
/// - [Message formats](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html)
/// - [App ID](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-appid-and-facets-v1.2-ps-20170411.html)
impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenticator<UP, T>
{
impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenticator<UP, T> {
/// Register a new credential, this always uses P-256 keys.
///
/// Note that attestation is mandatory in CTAP1/U2F, so if the state
@@ -52,35 +34,51 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
/// Also note that CTAP1 credentials should be assertable over CTAP2. I believe this is
/// currently not the case.
fn register(&mut self, reg: &register::Request) -> Result<register::Response> {
self.up.user_present(&mut self.trussed, constants::U2F_UP_TIMEOUT)
self.up
.user_present(&mut self.trussed, constants::U2F_UP_TIMEOUT)
.map_err(|_| Error::ConditionsOfUseNotSatisfied)?;
// Generate a new P256 key pair.
let private_key = syscall!(self.trussed.generate_p256_private_key(Location::Volatile)).key;
let public_key = syscall!(self.trussed.derive_p256_public_key(private_key, Location::Volatile)).key;
let public_key = syscall!(self
.trussed
.derive_p256_public_key(private_key, Location::Volatile))
.key;
let serialized_cose_public_key = syscall!(self.trussed.serialize_p256_key(
public_key, KeySerialization::EcdhEsHkdf256
)).serialized_key;
let serialized_cose_public_key = syscall!(self
.trussed
.serialize_p256_key(public_key, KeySerialization::EcdhEsHkdf256))
.serialized_key;
syscall!(self.trussed.delete(public_key));
let cose_key: ctap_types::cose::EcdhEsHkdf256PublicKey
= trussed::cbor_deserialize(&serialized_cose_public_key).unwrap();
let cose_key: ctap_types::cose::EcdhEsHkdf256PublicKey =
trussed::cbor_deserialize(&serialized_cose_public_key).unwrap();
let wrapping_key = self.state.persistent.key_wrapping_key(&mut self.trussed)
let wrapping_key = self
.state
.persistent
.key_wrapping_key(&mut self.trussed)
.map_err(|_| Error::UnspecifiedCheckingError)?;
// debug!("wrapping u2f private key");
let wrapped_key = syscall!(self.trussed.wrap_key_chacha8poly1305(
wrapping_key,
private_key,
&reg.app_id,
)).wrapped_key;
let wrapped_key =
syscall!(self
.trussed
.wrap_key_chacha8poly1305(wrapping_key, private_key, &reg.app_id,))
.wrapped_key;
// debug!("wrapped_key = {:?}", &wrapped_key);
syscall!(self.trussed.delete(private_key));
let key = Key::WrappedKey(wrapped_key.to_bytes().map_err(|_| Error::UnspecifiedCheckingError)?);
let nonce = syscall!(self.trussed.random_bytes(12)).bytes.as_slice().try_into().unwrap();
let key = Key::WrappedKey(
wrapped_key
.to_bytes()
.map_err(|_| Error::UnspecifiedCheckingError)?,
);
let nonce = syscall!(self.trussed.random_bytes(12))
.bytes
.as_slice()
.try_into()
.unwrap();
let mut rp_id = heapless::String::new();
@@ -88,7 +86,7 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
// TODO: Is this true?
// <https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#cross-version-credentials>
rp_id.push_str("u2f").ok();
let rp = ctap_types::webauthn::PublicKeyCredentialRpEntity{
let rp = ctap_types::webauthn::PublicKeyCredentialRpEntity {
id: rp_id,
name: None,
url: None,
@@ -105,10 +103,12 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
credential::CtapVersion::U2fV2,
&rp,
&user,
SigningAlgorithm::P256 as i32,
key,
self.state.persistent.timestamp(&mut self.trussed).map_err(|_| Error::NotEnoughMemory)?,
self.state
.persistent
.timestamp(&mut self.trussed)
.map_err(|_| Error::NotEnoughMemory)?,
None,
None,
nonce,
@@ -117,18 +117,24 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
// info!("made credential {:?}", &credential);
// 12.b generate credential ID { = AEAD(Serialize(Credential)) }
let kek = self.state.persistent.key_encryption_key(&mut self.trussed).map_err(|_| Error::NotEnoughMemory)?;
let credential_id = credential.id(&mut self.trussed, kek, Some(&reg.app_id)).map_err(|_| Error::NotEnoughMemory)?;
let kek = self
.state
.persistent
.key_encryption_key(&mut self.trussed)
.map_err(|_| Error::NotEnoughMemory)?;
let credential_id = credential
.id(&mut self.trussed, kek, Some(&reg.app_id))
.map_err(|_| Error::NotEnoughMemory)?;
let mut commitment = Commitment::new();
commitment.push(0).unwrap(); // reserve byte
commitment.push(0).unwrap(); // reserve byte
commitment.extend_from_slice(&reg.app_id).unwrap();
commitment.extend_from_slice(&reg.challenge).unwrap();
commitment.extend_from_slice(&credential_id.0).unwrap();
commitment.push(0x04).unwrap(); // public key uncompressed byte
commitment.push(0x04).unwrap(); // public key uncompressed byte
commitment.extend_from_slice(&cose_key.x).unwrap();
commitment.extend_from_slice(&cose_key.y).unwrap();
@@ -138,22 +144,24 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
(Some((key, cert)), _aaguid) => {
info!("aaguid: {}", hex_str!(&_aaguid));
(
syscall!(
self.trussed.sign(Mechanism::P256,
syscall!(self.trussed.sign(
Mechanism::P256,
key,
&commitment,
SignatureSerialization::Asn1Der
)).signature.to_bytes().unwrap(),
cert
))
.signature
.to_bytes()
.unwrap(),
cert,
)
},
}
_ => {
info!("Not provisioned with attestation key!");
return Err(Error::KeyReferenceNotFound);
}
};
Ok(register::Response::new(
0x05,
&cose_key,
@@ -176,12 +184,13 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
} else {
Err(Error::IncorrectDataParameter)
};
},
}
ControlByte::EnforceUserPresenceAndSign => {
self.up.user_present(&mut self.trussed, constants::U2F_UP_TIMEOUT)
self.up
.user_present(&mut self.trussed, constants::U2F_UP_TIMEOUT)
.map_err(|_| Error::ConditionsOfUseNotSatisfied)?;
0x01
},
}
ControlByte::DontEnforceUserPresenceAndSign => 0x00,
};
@@ -189,14 +198,18 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
let key = match &cred.key {
Key::WrappedKey(bytes) => {
let wrapping_key = self.state.persistent.key_wrapping_key(&mut self.trussed)
let wrapping_key = self
.state
.persistent
.key_wrapping_key(&mut self.trussed)
.map_err(|_| Error::IncorrectDataParameter)?;
let key_result = syscall!(self.trussed.unwrap_key_chacha8poly1305(
wrapping_key,
bytes,
b"",
Location::Volatile,
)).key;
))
.key;
match key_result {
Some(key) => {
info!("loaded u2f key!");
@@ -216,22 +229,30 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
return Err(Error::IncorrectDataParameter);
}
let sig_count = self.state.persistent.timestamp(&mut self.trussed).
map_err(|_| Error::UnspecifiedNonpersistentExecutionError)?;
let sig_count = self
.state
.persistent
.timestamp(&mut self.trussed)
.map_err(|_| Error::UnspecifiedNonpersistentExecutionError)?;
let mut commitment = Commitment::new();
commitment.extend_from_slice(&auth.app_id).unwrap();
commitment.push(user_presence_byte).unwrap();
commitment.extend_from_slice(&sig_count.to_be_bytes()).unwrap();
commitment
.extend_from_slice(&sig_count.to_be_bytes())
.unwrap();
commitment.extend_from_slice(&auth.challenge).unwrap();
let signature = syscall!(
self.trussed.sign(Mechanism::P256,
let signature = syscall!(self.trussed.sign(
Mechanism::P256,
key,
&commitment,
SignatureSerialization::Asn1Der
)).signature.to_bytes().unwrap();
))
.signature
.to_bytes()
.unwrap();
Ok(authenticate::Response {
user_presence: user_presence_byte,
@@ -239,6 +260,4 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
signature,
})
}
}
+402 -296
View File
File diff suppressed because it is too large Load Diff
+131 -136
View File
@@ -4,45 +4,35 @@ use core::convert::TryFrom;
use trussed::{
syscall,
types::{
DirEntry,
Location,
},
types::{DirEntry, Location},
};
use ctap_types::{
heapless_bytes::Bytes,
Error,
ctap2::credential_management::{
CredentialProtectionPolicy,
Response,
},
cose::PublicKey,
ctap2::credential_management::{CredentialProtectionPolicy, Response},
heapless_bytes::Bytes,
webauthn::PublicKeyCredentialDescriptor,
Error,
};
use littlefs2::path::{Path, PathBuf};
use crate::{
Authenticator,
Result,
UserPresence,
credential::Credential,
state::{
CredentialManagementEnumerateRps,
CredentialManagementEnumerateCredentials,
},
TrussedRequirements,
state::{CredentialManagementEnumerateCredentials, CredentialManagementEnumerateRps},
Authenticator, Result, TrussedRequirements, UserPresence,
};
pub(crate) struct CredentialManagement<'a, UP, T>
where UP: UserPresence,
where
UP: UserPresence,
{
authnr: &'a mut Authenticator<UP, T>,
}
impl<UP, T> core::ops::Deref for CredentialManagement<'_, UP, T>
where UP: UserPresence,
where
UP: UserPresence,
{
type Target = Authenticator<UP, T>;
fn deref(&self) -> &Self::Target {
@@ -51,7 +41,8 @@ where UP: UserPresence,
}
impl<UP, T> core::ops::DerefMut for CredentialManagement<'_, UP, T>
where UP: UserPresence,
where
UP: UserPresence,
{
fn deref_mut(&mut self) -> &mut Self::Target {
self.authnr
@@ -59,7 +50,8 @@ where UP: UserPresence,
}
impl<'a, UP, T> CredentialManagement<'a, UP, T>
where UP: UserPresence,
where
UP: UserPresence,
{
pub fn new(authnr: &'a mut Authenticator<UP, T>) -> Self {
Self { authnr }
@@ -67,25 +59,26 @@ where UP: UserPresence,
}
impl<UP, T> CredentialManagement<'_, UP, T>
where UP: UserPresence,
T: TrussedRequirements,
where
UP: UserPresence,
T: TrussedRequirements,
{
pub fn get_creds_metadata(&mut self) -> Result<Response> {
info!("get metadata");
let mut response: Response =
Default::default();
let mut response: Response = Default::default();
let guesstimate = self.state.persistent
.max_resident_credentials_guesstimate();
let guesstimate = self.state.persistent.max_resident_credentials_guesstimate();
response.existing_resident_credentials_count = Some(0);
response.max_possible_remaining_residential_credentials_count =
Some(guesstimate);
response.max_possible_remaining_residential_credentials_count = Some(guesstimate);
let dir = PathBuf::from(b"rk");
let maybe_first_rp = syscall!(self.trussed.read_dir_first(
Location::Internal, dir.clone(), None)).entry;
let maybe_first_rp =
syscall!(self
.trussed
.read_dir_first(Location::Internal, dir.clone(), None))
.entry;
let first_rp = match maybe_first_rp{
let first_rp = match maybe_first_rp {
None => return Ok(response),
Some(rp) => rp,
};
@@ -94,17 +87,16 @@ where UP: UserPresence,
let mut last_rp = PathBuf::from(first_rp.file_name());
loop {
syscall!(self.trussed.read_dir_first(
Location::Internal,
dir.clone(),
Some(last_rp),
)).entry.unwrap();
syscall!(self
.trussed
.read_dir_first(Location::Internal, dir.clone(), Some(last_rp),))
.entry
.unwrap();
let maybe_next_rp = syscall!(self.trussed.read_dir_next()).entry;
match maybe_next_rp {
None => {
response.existing_resident_credentials_count =
Some(num_rks);
response.existing_resident_credentials_count = Some(num_rks);
response.max_possible_remaining_residential_credentials_count =
Some(if num_rks >= guesstimate {
0
@@ -116,8 +108,7 @@ 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()))?;
let (this_rp_rk_count, _) = self.count_rp_rks(PathBuf::from(rp.path()))?;
info!("{:?}", this_rp_rk_count);
num_rks += this_rp_rk_count;
}
@@ -136,16 +127,15 @@ where UP: UserPresence,
let dir = PathBuf::from(b"rk");
let maybe_first_rp = syscall!(self.trussed.read_dir_first(
Location::Internal, dir, None)).entry;
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;
let maybe_next_rp = syscall!(self.trussed.read_dir_next()).entry;
match maybe_next_rp {
None => break,
_ => num_rps += 1,
@@ -156,21 +146,21 @@ where UP: UserPresence,
});
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;
))
.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 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
@@ -180,7 +170,6 @@ where UP: UserPresence,
response.rp_id_hash = Some(self.hash(rp.id.as_ref()));
response.rp = Some(rp);
}
}
@@ -188,11 +177,10 @@ 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.cached_rp = Some(
CredentialManagementEnumerateRps {
remaining: total_rps - 1,
rp_id_hash,
});
self.state.runtime.cached_rp = Some(CredentialManagementEnumerateRps {
remaining: total_rps - 1,
rp_id_hash,
});
}
}
}
@@ -206,7 +194,12 @@ where UP: UserPresence,
let CredentialManagementEnumerateRps {
remaining,
rp_id_hash: last_rp_id_hash,
} = self.state.runtime.cached_rp.clone().ok_or(Error::NotAllowed)?;
} = self
.state
.runtime
.cached_rp
.clone()
.ok_or(Error::NotAllowed)?;
let dir = PathBuf::from(b"rk");
@@ -214,11 +207,11 @@ where UP: UserPresence,
super::format_hex(&last_rp_id_hash[..8], &mut hex);
let filename = PathBuf::from(&hex);
let mut maybe_next_rp = syscall!(self.trussed.read_dir_first(
Location::Internal,
dir,
Some(filename),
)).entry;
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() {
@@ -235,15 +228,16 @@ where UP: UserPresence,
Location::Internal,
PathBuf::from(rp.path()),
None
)).entry;
))
.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 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
@@ -274,11 +268,11 @@ where UP: UserPresence,
}
fn count_rp_rks(&mut self, rp_dir: PathBuf) -> Result<(u32, DirEntry)> {
let maybe_first_rk = syscall!(self.trussed.read_dir_first(
Location::Internal,
rp_dir,
None
)).entry;
let maybe_first_rk =
syscall!(self
.trussed
.read_dir_first(Location::Internal, rp_dir, None))
.entry;
let first_rk = maybe_first_rk.ok_or(Error::NoCredentials)?;
@@ -302,8 +296,7 @@ where UP: UserPresence,
let (num_rks, first_rk) = self.count_rp_rks(rp_dir)?;
// extract data required into response
let mut response = self.extract_response_from_credential_file(
first_rk.path())?;
let mut response = self.extract_response_from_credential_file(first_rk.path())?;
response.total_credentials = Some(num_rks);
// cache state for next call
@@ -328,7 +321,12 @@ where UP: UserPresence,
remaining,
rp_dir,
prev_filename,
} = self.state.runtime.cached_rk.clone().ok_or(Error::NotAllowed)?;
} = self
.state
.runtime
.cached_rk
.clone()
.ok_or(Error::NotAllowed)?;
// let (remaining, rp_dir, prev_filename) = match self.state.runtime.cached_rk {
// Some(CredentialManagementEnumerateCredentials(
// x, ref y, ref z))
@@ -342,11 +340,11 @@ where UP: UserPresence,
// super::format_hex(&rp_id_hash[..8], &mut hex);
// let rp_dir = PathBuf::from(b"rk").join(&PathBuf::from(&hex));
let mut maybe_next_rk = syscall!(self.trussed.read_dir_first(
Location::Internal,
rp_dir,
Some(prev_filename)
)).entry;
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() {
@@ -358,8 +356,7 @@ where UP: UserPresence,
match maybe_next_rk {
Some(rk) => {
// extract data required into response
let response = self.extract_response_from_credential_file(
rk.path())?;
let response = self.extract_response_from_credential_file(rk.path())?;
// cache state for next call
if remaining > 1 {
@@ -376,19 +373,14 @@ where UP: UserPresence,
}
}
fn extract_response_from_credential_file(&mut self, rk_path: &Path) -> Result<Response> {
// user (0x06)
// credentialID (0x07): PublicKeyCredentialDescriptor
// publicKey (0x08): public key of the credential in COSE_Key format
// totalCredentials (0x09): total number of credentials for this RP
// credProtect (0x0A): credential protection policy
let serialized = syscall!(self.trussed.read_file(
Location::Internal,
rk_path.into(),
)).data;
let serialized = syscall!(self.trussed.read_file(Location::Internal, rk_path.into(),)).data;
let credential = Credential::deserialize(&serialized)
// this may be a confusing error message
@@ -398,7 +390,10 @@ where UP: UserPresence,
// why these contortions to get kek. sheesh
let authnr = &mut self.authnr;
let kek = authnr.state.persistent.key_encryption_key(&mut authnr.trussed)?;
let kek = authnr
.state
.persistent
.key_encryption_key(&mut authnr.trussed)?;
let credential_id = credential.id(&mut self.trussed, kek, None)?;
@@ -412,33 +407,38 @@ where UP: UserPresence,
use trussed::types::{KeySerialization, Mechanism};
let algorithm = SigningAlgorithm::try_from(credential.algorithm)?;
let cose_public_key = match algorithm {
let cose_public_key = match algorithm {
SigningAlgorithm::P256 => {
let public_key = syscall!(self.trussed.derive_p256_public_key(private_key, Location::Volatile)).key;
let public_key = syscall!(self
.trussed
.derive_p256_public_key(private_key, Location::Volatile))
.key;
let cose_public_key = syscall!(self.trussed.serialize_key(
Mechanism::P256, public_key,
Mechanism::P256,
public_key,
// KeySerialization::EcdhEsHkdf256
KeySerialization::Cose,
)).serialized_key;
))
.serialized_key;
syscall!(self.trussed.delete(public_key));
PublicKey::P256Key(
ctap_types::serde::cbor_deserialize(&cose_public_key)
.unwrap())
PublicKey::P256Key(ctap_types::serde::cbor_deserialize(&cose_public_key).unwrap())
}
SigningAlgorithm::Ed25519 => {
let public_key = syscall!(self.trussed.derive_ed255_public_key(
private_key, Location::Volatile)).key;
let cose_public_key = syscall!(self.trussed.serialize_ed255_key(
public_key, KeySerialization::Cose
)).serialized_key;
let public_key = syscall!(self
.trussed
.derive_ed255_public_key(private_key, Location::Volatile))
.key;
let cose_public_key = syscall!(self
.trussed
.serialize_ed255_key(public_key, KeySerialization::Cose))
.serialized_key;
syscall!(self.trussed.delete(public_key));
PublicKey::Ed25519Key(
ctap_types::serde::cbor_deserialize(&cose_public_key)
.unwrap())
}
// SigningAlgorithm::Totp => {
// PublicKey::TotpKey(Default::default())
// }
ctap_types::serde::cbor_deserialize(&cose_public_key).unwrap(),
)
} // SigningAlgorithm::Totp => {
// PublicKey::TotpKey(Default::default())
// }
};
let cred_protect = match credential.cred_protect {
Some(x) => Some(x),
@@ -456,11 +456,10 @@ where UP: UserPresence,
Ok(response)
}
pub fn delete_credential(&mut self,
pub fn delete_credential(
&mut self,
credential_descriptor: &PublicKeyCredentialDescriptor,
)
-> Result<Response>
{
) -> Result<Response> {
info!("delete credential");
let credential_id_hash = self.hash(&credential_descriptor.id[..]);
let mut hex = [b'0'; 16];
@@ -468,43 +467,39 @@ where UP: UserPresence,
let dir = PathBuf::from(b"rk");
let filename = PathBuf::from(&hex);
let rk_path = syscall!(self.trussed.locate_file(
Location::Internal,
Some(dir),
filename,
)).path.ok_or(Error::InvalidCredential)?;
let rk_path = syscall!(self
.trussed
.locate_file(Location::Internal, Some(dir), 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()
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;
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,
));
info!("deleting parent {:?} as this was its last RK", &rp_path);
syscall!(self.trussed.remove_dir(Location::Internal, rp_path,));
} else {
info!("not deleting deleting parent {:?} as there is {:?}",
&rp_path,
&maybe_first_remaining_rk.unwrap().path(),
);
info!(
"not deleting deleting parent {:?} as there is {:?}",
&rp_path,
&maybe_first_remaining_rk.unwrap().path(),
);
}
// just return OK
let response = Default::default();
Ok(response)
}
}
+61 -26
View File
@@ -3,30 +3,36 @@
mod apdu;
mod ctaphid;
use crate::{Authenticator, TrussedRequirements, UserPresence};
#[allow(unused_imports)]
use crate::msp;
use crate::{Authenticator, TrussedRequirements, UserPresence};
use ctap_types::{ctap1, ctap2};
use iso7816::Status;
impl<UP, T> iso7816::App for Authenticator<UP, T>
where UP: UserPresence,
where
UP: UserPresence,
{
fn aid(&self) -> iso7816::Aid {
iso7816::Aid::new(&[ 0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01])
iso7816::Aid::new(&[0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01])
}
}
#[inline(never)]
/// Deserialize U2F, call authenticator, serialize response *Result*.
fn handle_ctap1<T, UP>(authenticator: &mut Authenticator<UP, T>, data: &[u8], response: &mut apdu_dispatch::response::Data)
where
fn handle_ctap1<T, UP>(
authenticator: &mut Authenticator<UP, T>,
data: &[u8],
response: &mut apdu_dispatch::response::Data,
) where
T: TrussedRequirements,
UP: UserPresence,
{
debug_now!("handle CTAP1: remaining stack: {} bytes", msp() - 0x2000_0000);
debug_now!(
"handle CTAP1: remaining stack: {} bytes",
msp() - 0x2000_0000
);
// debug_now!("1A SP: {:X}", msp());
match try_handle_ctap1(authenticator, data, response) {
Ok(()) => {
@@ -34,12 +40,12 @@ where
// Need to add x9000 success code (normally the apdu-dispatch does this, but
// since u2f uses apdus over ctaphid, we must do it here.)
response.extend_from_slice(&[0x90, 0x00]).ok();
},
}
Err(status) => {
let code: [u8; 2] = status.into();
debug_now!("CTAP1 error: {:?} ({})", status, hex_str!(&code));
response.extend_from_slice(&code).ok();
},
}
}
// debug_now!("1B SP: {:X}", msp());
debug_now!("end handle CTAP1");
@@ -47,12 +53,18 @@ where
#[inline(never)]
/// Deserialize CBOR, call authenticator, serialize response *Result*.
fn handle_ctap2<T, UP>(authenticator: &mut Authenticator<UP, T>, data: &[u8], response: &mut apdu_dispatch::response::Data)
where
fn handle_ctap2<T, UP>(
authenticator: &mut Authenticator<UP, T>,
data: &[u8],
response: &mut apdu_dispatch::response::Data,
) where
T: TrussedRequirements,
UP: UserPresence,
{
debug_now!("handle CTAP2: remaining stack: {} bytes", msp() - 0x2000_0000);
debug_now!(
"handle CTAP2: remaining stack: {} bytes",
msp() - 0x2000_0000
);
// debug_now!("2A SP: {:X}", msp());
if let Err(error) = try_handle_ctap2(authenticator, data, response) {
debug_now!("CTAP2 error: {:02X}", error);
@@ -63,14 +75,20 @@ where
}
#[inline(never)]
fn try_handle_ctap1<T, UP>(authenticator: &mut Authenticator<UP, T>, data: &[u8], response: &mut apdu_dispatch::response::Data)
-> Result<(), Status>
fn try_handle_ctap1<T, UP>(
authenticator: &mut Authenticator<UP, T>,
data: &[u8],
response: &mut apdu_dispatch::response::Data,
) -> Result<(), Status>
where
T: TrussedRequirements,
UP: UserPresence,
{
// Annoyance: We can't load in fido-authenticator constructor.
authenticator.state.persistent.load_if_not_initialised(&mut authenticator.trussed);
authenticator
.state
.persistent
.load_if_not_initialised(&mut authenticator.trussed);
// let command = apdu_dispatch::Command::try_from(data)
// .map_err(|_| Status::IncorrectDataParameter)?;
@@ -97,16 +115,25 @@ where
}
#[inline(never)]
fn try_handle_ctap2<T, UP>(authenticator: &mut Authenticator<UP, T>, data: &[u8], response: &mut apdu_dispatch::response::Data)
-> Result<(), u8>
fn try_handle_ctap2<T, UP>(
authenticator: &mut Authenticator<UP, T>,
data: &[u8],
response: &mut apdu_dispatch::response::Data,
) -> Result<(), u8>
where
T: TrussedRequirements,
UP: UserPresence,
{
// Annoyance: We can't load in fido-authenticator constructor.
authenticator.state.persistent.load_if_not_initialised(&mut authenticator.trussed);
authenticator
.state
.persistent
.load_if_not_initialised(&mut authenticator.trussed);
debug_now!("try_handle CTAP2: remaining stack: {} bytes", msp() - 0x2000_0000);
debug_now!(
"try_handle CTAP2: remaining stack: {} bytes",
msp() - 0x2000_0000
);
// let ctap_request = ctap2::Request::deserialize(data)
// .map_err(|error| error as u8)?;
@@ -120,22 +147,30 @@ where
}
#[inline(never)]
fn try_get_ctap2_response<T, UP>(authenticator: &mut Authenticator<UP, T>, data: &[u8])
-> Result<ctap2::Response, u8>
fn try_get_ctap2_response<T, UP>(
authenticator: &mut Authenticator<UP, T>,
data: &[u8],
) -> Result<ctap2::Response, u8>
where
T: TrussedRequirements,
UP: UserPresence,
{
// Annoyance: We can't load in fido-authenticator constructor.
authenticator.state.persistent.load_if_not_initialised(&mut authenticator.trussed);
authenticator
.state
.persistent
.load_if_not_initialised(&mut authenticator.trussed);
debug_now!("try_get CTAP2: remaining stack: {} bytes", msp() - 0x2000_0000);
debug_now!(
"try_get CTAP2: remaining stack: {} bytes",
msp() - 0x2000_0000
);
// Goal of these nested scopes is to keep stack small.
let ctap_request = ctap2::Request::deserialize(data)
.map_err(|error| error as u8)?;
let ctap_request = ctap2::Request::deserialize(data).map_err(|error| error as u8)?;
debug_now!("2a SP: {:X}", msp());
use ctap2::Authenticator;
authenticator.call_ctap2(&ctap_request)
authenticator
.call_ctap2(&ctap_request)
.map_err(|error| error as u8)
}
+19 -23
View File
@@ -1,9 +1,6 @@
use apdu_dispatch::{Command, response::Data, app as apdu};
use apdu_dispatch::{app as apdu, response::Data, Command};
use ctap_types::{serde::error::Error as SerdeError, Error};
use ctaphid_dispatch::app as ctaphid;
use ctap_types::{
Error,
serde::error::Error as SerdeError,
};
use iso7816::Status;
use crate::{Authenticator, TrussedRequirements, UserPresence};
@@ -16,26 +13,21 @@ pub enum CtapMappingError {
impl From<CtapMappingError> for Error {
fn from(mapping_error: CtapMappingError) -> Error {
match mapping_error {
CtapMappingError::InvalidCommand(_cmd) => {
Error::InvalidCommand
}
CtapMappingError::ParsingError(cbor_error) => {
match cbor_error {
SerdeError::SerdeMissingField => Error::MissingParameter,
_ => Error::InvalidCbor
}
}
CtapMappingError::InvalidCommand(_cmd) => Error::InvalidCommand,
CtapMappingError::ParsingError(cbor_error) => match cbor_error {
SerdeError::SerdeMissingField => Error::MissingParameter,
_ => Error::InvalidCbor,
},
}
}
}
impl<UP, T> apdu::App<{apdu_dispatch::command::SIZE}, {apdu_dispatch::response::SIZE} >
for Authenticator<UP, T>
where UP: UserPresence,
T: TrussedRequirements,
impl<UP, T> apdu::App<{ apdu_dispatch::command::SIZE }, { apdu_dispatch::response::SIZE }>
for Authenticator<UP, T>
where
UP: UserPresence,
T: TrussedRequirements,
{
fn select(&mut self, _: &Command, reply: &mut Data) -> apdu::Result {
reply.extend_from_slice(b"U2F_V2").unwrap();
Ok(())
@@ -43,7 +35,12 @@ for Authenticator<UP, T>
fn deselect(&mut self) {}
fn call(&mut self, interface: apdu::Interface, apdu: &Command, response: &mut Data) -> apdu::Result {
fn call(
&mut self,
interface: apdu::Interface,
apdu: &Command,
response: &mut Data,
) -> apdu::Result {
// FIDO-over-CCID does not seem to officially be a thing; we don't support it.
// If we would, need to review the following cases catering to semi-documented U2F legacy.
if interface != apdu::Interface::Contactless {
@@ -62,7 +59,7 @@ for Authenticator<UP, T>
Ok(match instruction {
// U2F instruction codes
// NB(nickray): I don't think 0x00 is a valid case.
0x00 | 0x01 | 0x02 => super::handle_ctap1(self, apdu.data(), response),//self.call_authenticator_u2f(apdu, response),
0x00 | 0x01 | 0x02 => super::handle_ctap1(self, apdu.data(), response), //self.call_authenticator_u2f(apdu, response),
_ => {
match ctaphid::Command::try_from(instruction) {
@@ -79,4 +76,3 @@ for Authenticator<UP, T>
})
}
}
+16 -10
View File
@@ -1,22 +1,29 @@
use ctaphid_dispatch::app as ctaphid;
use crate::{Authenticator, TrussedRequirements, UserPresence};
#[allow(unused_imports)]
use crate::msp;
use crate::{Authenticator, TrussedRequirements, UserPresence};
impl<UP, T> ctaphid::App for Authenticator<UP, T>
where UP: UserPresence,
T: TrussedRequirements,
where
UP: UserPresence,
T: TrussedRequirements,
{
fn commands(&self,) -> &'static [ctaphid::Command] {
&[ ctaphid::Command::Cbor, ctaphid::Command::Msg ]
fn commands(&self) -> &'static [ctaphid::Command] {
&[ctaphid::Command::Cbor, ctaphid::Command::Msg]
}
#[inline(never)]
fn call(&mut self, command: ctaphid::Command, request: &ctaphid::Message, response: &mut ctaphid::Message) -> ctaphid::AppResult {
debug_now!("ctaphid-dispatch: remaining stack: {} bytes", msp() - 0x2000_0000);
fn call(
&mut self,
command: ctaphid::Command,
request: &ctaphid::Message,
response: &mut ctaphid::Message,
) -> ctaphid::AppResult {
debug_now!(
"ctaphid-dispatch: remaining stack: {} bytes",
msp() - 0x2000_0000
);
if request.len() < 1 {
debug_now!("invalid request length in ctaphid.call");
@@ -26,7 +33,6 @@ where UP: UserPresence,
// info_now!("request: ");
// blocking::dump_hex(request, request.len());
Ok(match command {
ctaphid::Command::Cbor => super::handle_ctap2(self, request, response),
ctaphid::Command::Msg => super::handle_ctap1(self, request, response),
_ => {
+48 -38
View File
@@ -15,17 +15,11 @@
extern crate delog;
generate_macros!();
use trussed::{
client, syscall,
Client as TrussedClient,
types::{
Message,
},
};
use trussed::{client, syscall, types::Message, Client as TrussedClient};
use ctap_types::{
heapless_bytes::Bytes,
authenticator::{Request, Response},
heapless_bytes::Bytes,
};
/// Re-export of `ctap-types` authenticator errors.
@@ -34,7 +28,7 @@ pub use ctap_types::Error;
mod ctap1;
mod ctap2;
#[cfg(feature="dispatch")]
#[cfg(feature = "dispatch")]
mod dispatch;
pub mod constants;
@@ -44,7 +38,6 @@ pub mod state;
/// Results with our [`Error`].
pub type Result<T> = core::result::Result<T, Error>;
/// Trait bound on our implementation's requirements from a Trussed client.
///
/// - Client is core Trussed client functionality.
@@ -58,21 +51,20 @@ pub trait TrussedRequirements:
+ client::Aes256Cbc
+ client::Sha256
+ client::HmacSha256
+ client::Ed255
// + client::Totp
{}
+ client::Ed255 // + client::Totp
{
}
impl<T> TrussedRequirements for T
where T:
client::Client
+ client::P256
+ client::Chacha8Poly1305
+ client::Aes256Cbc
+ client::Sha256
+ client::HmacSha256
+ client::Ed255
// + client::Totp
{}
impl<T> TrussedRequirements for T where
T: client::Client
+ client::P256
+ client::Chacha8Poly1305
+ client::Aes256Cbc
+ client::Sha256
+ client::HmacSha256
+ client::Ed255 // + client::Totp
{
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
/// Externally defined configuration.
@@ -140,7 +132,9 @@ fn format_hex(data: &[u8], mut buffer: &mut [u8]) {
#[inline]
#[allow(dead_code)]
pub(crate) fn msp() -> u32 { 0x2000_0000 }
pub(crate) fn msp() -> u32 {
0x2000_0000
}
/// Currently Ed25519 and P256.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -169,10 +163,14 @@ impl core::convert::TryFrom<i32> for SigningAlgorithm {
/// Method to check for user presence.
pub trait UserPresence: Copy {
fn user_present<T: TrussedClient>(self, trussed: &mut T, timeout_milliseconds: u32) -> Result<()>;
fn user_present<T: TrussedClient>(
self,
trussed: &mut T,
timeout_milliseconds: u32,
) -> Result<()>;
}
#[deprecated(note="use `Silent` directly`")]
#[deprecated(note = "use `Silent` directly`")]
#[doc(hidden)]
pub type SilentAuthenticator = Silent;
@@ -181,12 +179,12 @@ pub type SilentAuthenticator = Silent;
pub struct Silent {}
impl UserPresence for Silent {
fn user_present<T: TrussedClient>(self, _: &mut T, _:u32) -> Result<()> {
fn user_present<T: TrussedClient>(self, _: &mut T, _: u32) -> Result<()> {
Ok(())
}
}
#[deprecated(note="use `Conforming` directly")]
#[deprecated(note = "use `Conforming` directly")]
#[doc(hidden)]
pub type NonSilentAuthenticator = Conforming;
@@ -195,7 +193,11 @@ pub type NonSilentAuthenticator = Conforming;
pub struct Conforming {}
impl UserPresence for Conforming {
fn user_present<T: TrussedClient>(self, trussed: &mut T, timeout_milliseconds: u32) -> Result<()> {
fn user_present<T: TrussedClient>(
self,
trussed: &mut T,
timeout_milliseconds: u32,
) -> Result<()> {
let result = syscall!(trussed.confirm_user_present(timeout_milliseconds)).result;
result.map_err(|err| match err {
trussed::types::consent::Error::TimedOut => Error::UserActionTimeout,
@@ -205,22 +207,31 @@ impl UserPresence for Conforming {
}
}
fn cbor_serialize_message<T: serde::Serialize>(object: &T) -> core::result::Result<Message, ctap_types::serde::Error> {
fn cbor_serialize_message<T: serde::Serialize>(
object: &T,
) -> core::result::Result<Message, ctap_types::serde::Error> {
trussed::cbor_serialize_bytes(object)
}
impl<UP, T> Authenticator<UP, T>
where UP: UserPresence,
T: TrussedRequirements,
where
UP: UserPresence,
T: TrussedRequirements,
{
pub fn new(trussed: T, up: UP, config: Config) -> Self {
let state = state::State::new();
Self { trussed, state, up, config }
Self {
trussed,
state,
up,
config,
}
}
pub fn call(&mut self, request: &Request) -> Result<Response> {
self.state.persistent.load_if_not_initialised(&mut self.trussed);
self.state
.persistent
.load_if_not_initialised(&mut self.trussed);
match request {
Request::Ctap2(request) => {
@@ -245,5 +256,4 @@ where UP: UserPresence,
}
#[cfg(test)]
mod test {
}
mod test {}
+99 -72
View File
@@ -2,32 +2,24 @@
//!
//! Needs cleanup.
use trussed::{
client, syscall, try_syscall,
Client as TrussedClient,
types::{
self,
KeyId,
Location,
Mechanism,
},
};
use ctap_types::{
Bytes, String,
Error,
cose::EcdhEsHkdf256PublicKey as CoseEcdhEsHkdf256PublicKey,
// 2022-02-27: 10 credentials
sizes::MAX_CREDENTIAL_COUNT_IN_LIST, // U8 currently
Bytes,
Error,
String,
};
use trussed::{
client, syscall, try_syscall,
types::{self, KeyId, Location, Mechanism},
Client as TrussedClient,
};
use heapless::binary_heap::{BinaryHeap, Max};
use littlefs2::path::PathBuf;
use crate::{
cbor_serialize_message,
credential::Credential,
Result,
};
use crate::{cbor_serialize_message, credential::Credential, Result};
#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct CachedCredential {
@@ -81,7 +73,6 @@ pub type CredentialCache = CredentialCacheGeneric<MAX_CREDENTIAL_COUNT_IN_LIST>;
#[derive(Clone, Debug, /*uDebug, Eq, PartialEq,*/ serde::Deserialize, serde::Serialize)]
pub struct State {
/// Batch device identity (aaguid, certificate, key).
pub identity: Identity,
pub persistent: PersistentState,
@@ -95,7 +86,6 @@ impl Default for State {
}
impl State {
// pub fn new(trussed: &mut TrussedClient) -> Self {
pub fn new() -> Self {
// let identity = Identity::get(trussed);
@@ -104,7 +94,11 @@ impl State {
// let persistent = PersistentState::load_or_reset(trussed);
let persistent = Default::default();
Self { identity, persistent, runtime }
Self {
identity,
persistent,
runtime,
}
}
pub fn decrement_retries<T: TrussedClient>(&mut self, trussed: &mut T) -> Result<()> {
@@ -119,9 +113,7 @@ impl State {
Ok(())
}
pub fn pin_blocked(&self) -> Result<()> {
if self.persistent.pin_blocked() {
return Err(Error::PinBlocked);
}
@@ -131,7 +123,6 @@ impl State {
Ok(())
}
}
/// Batch device identity (aaguid, certificate, key).
@@ -146,16 +137,13 @@ pub type Aaguid = [u8; 16];
pub type Certificate = trussed::types::Message;
impl Identity {
// Attempt to yank out the aaguid of a certificate.
fn yank_aaguid(&mut self, der: &[u8]) -> Option<[u8; 16]> {
let aaguid_start_sequence = [
// OBJECT IDENTIFIER 1.3.6.1.4.1.45724.1.1.4 (AAGUID)
0x06u8, 0x0B, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xE5, 0x1C, 0x01, 0x01, 0x04,
// Sequence, 16 bytes
0x04, 0x12, 0x04, 0x10
0x04, 0x12, 0x04, 0x10,
];
// Scan for the beginning sequence for AAGUID.
@@ -180,16 +168,16 @@ impl Identity {
}
/// Lookup batch key and certificate, together with AAUGID.
pub fn attestation<T: TrussedClient>(&mut self, trussed: &mut T) -> (Option<(KeyId, Certificate)>, Aaguid)
{
pub fn attestation<T: TrussedClient>(
&mut self,
trussed: &mut T,
) -> (Option<(KeyId, Certificate)>, Aaguid) {
let key = crate::constants::ATTESTATION_KEY_ID;
let attestation_key_exists = syscall!(trussed.exists(Mechanism::P256, key)).exists;
if attestation_key_exists {
// Will panic if certificate does not exist.
let cert = syscall!(trussed.read_certificate(
crate::constants::ATTESTATION_CERT_ID
)).der;
let cert =
syscall!(trussed.read_certificate(crate::constants::ATTESTATION_CERT_ID)).der;
let mut aaguid = self.yank_aaguid(cert.as_slice());
@@ -204,7 +192,6 @@ impl Identity {
(None, *b"AAGUID0123456789")
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
@@ -220,7 +207,9 @@ pub struct CredentialManagementEnumerateCredentials {
pub prev_filename: PathBuf,
}
#[derive(Clone, Debug, /*uDebug,*/ Default, /*PartialEq,*/ serde::Deserialize, serde::Serialize)]
#[derive(
Clone, Debug, /*uDebug,*/ Default, /*PartialEq,*/ serde::Deserialize, serde::Serialize,
)]
pub struct ActiveGetAssertionData {
pub rp_id_hash: [u8; 32],
pub client_data_hash: [u8; 32],
@@ -230,7 +219,9 @@ pub struct ActiveGetAssertionData {
pub extensions: Option<ctap_types::ctap2::get_assertion::ExtensionsInput>,
}
#[derive(Clone, Debug, /*uDebug,*/ Default, /*PartialEq,*/ serde::Deserialize, serde::Serialize)]
#[derive(
Clone, Debug, /*uDebug,*/ Default, /*PartialEq,*/ serde::Deserialize, serde::Serialize,
)]
pub struct RuntimeState {
key_agreement_key: Option<KeyId>,
pin_token: Option<KeyId>,
@@ -280,7 +271,6 @@ pub struct PersistentState {
}
impl PersistentState {
const RESET_RETRIES: u8 = 8;
const FILENAME: &'static [u8] = b"persistent-state.cbor";
const MAX_RESIDENT_CREDENTIALS_GUESSTIMATE: u32 = 100;
@@ -290,12 +280,10 @@ impl PersistentState {
}
pub fn load<T: client::Client + client::Chacha8Poly1305>(trussed: &mut T) -> Result<Self> {
// TODO: add "exists_file" method instead?
let result = try_syscall!(trussed.read_file(
Location::Internal,
PathBuf::from(Self::FILENAME),
)).map_err(|_| Error::Other);
let result =
try_syscall!(trussed.read_file(Location::Internal, PathBuf::from(Self::FILENAME),))
.map_err(|_| Error::Other);
if result.is_err() {
info!("err loading: {:?}", result.err().unwrap());
@@ -342,13 +330,16 @@ impl PersistentState {
self.save(trussed)
}
pub fn load_if_not_initialised<T: client::Client + client::Chacha8Poly1305>(&mut self, trussed: &mut T) {
pub fn load_if_not_initialised<T: client::Client + client::Chacha8Poly1305>(
&mut self,
trussed: &mut T,
) {
if !self.initialised {
match Self::load(trussed) {
Ok(previous_self) => {
info!("loaded previous state!");
*self = previous_self
},
}
Err(_err) => {
info!("error with previous state! {:?}", _err);
}
@@ -364,33 +355,47 @@ impl PersistentState {
Ok(now)
}
pub fn key_encryption_key<T: client::Client + client::Chacha8Poly1305>(&mut self, trussed: &mut T) -> Result<KeyId>
{
pub fn key_encryption_key<T: client::Client + client::Chacha8Poly1305>(
&mut self,
trussed: &mut T,
) -> Result<KeyId> {
match self.key_encryption_key {
Some(key) => Ok(key),
None => self.rotate_key_encryption_key(trussed),
}
}
pub fn rotate_key_encryption_key<T: client::Client + client::Chacha8Poly1305>(&mut self, trussed: &mut T) -> Result<KeyId> {
if let Some(key) = self.key_encryption_key { syscall!(trussed.delete(key)); }
pub fn rotate_key_encryption_key<T: client::Client + client::Chacha8Poly1305>(
&mut self,
trussed: &mut T,
) -> Result<KeyId> {
if let Some(key) = self.key_encryption_key {
syscall!(trussed.delete(key));
}
let key = syscall!(trussed.generate_chacha8poly1305_key(Location::Internal)).key;
self.key_encryption_key = Some(key);
self.save(trussed)?;
Ok(key)
}
pub fn key_wrapping_key<T: client::Client + client::Chacha8Poly1305>(&mut self, trussed: &mut T) -> Result<KeyId>
{
pub fn key_wrapping_key<T: client::Client + client::Chacha8Poly1305>(
&mut self,
trussed: &mut T,
) -> Result<KeyId> {
match self.key_wrapping_key {
Some(key) => Ok(key),
None => self.rotate_key_wrapping_key(trussed),
}
}
pub fn rotate_key_wrapping_key<T: client::Client + client::Chacha8Poly1305>(&mut self, trussed: &mut T) -> Result<KeyId> {
pub fn rotate_key_wrapping_key<T: client::Client + client::Chacha8Poly1305>(
&mut self,
trussed: &mut T,
) -> Result<KeyId> {
self.load_if_not_initialised(trussed);
if let Some(key) = self.key_wrapping_key { syscall!(trussed.delete(key)); }
if let Some(key) = self.key_wrapping_key {
syscall!(trussed.delete(key));
}
let key = syscall!(trussed.generate_chacha8poly1305_key(Location::Internal)).key;
self.key_wrapping_key = Some(key);
self.save(trussed)?;
@@ -409,7 +414,7 @@ impl PersistentState {
self.consecutive_pin_mismatches >= Self::RESET_RETRIES
}
fn reset_retries<T: TrussedClient>(&mut self, trussed: &mut T) -> Result<()> {
fn reset_retries<T: TrussedClient>(&mut self, trussed: &mut T) -> Result<()> {
if self.consecutive_pin_mismatches > 0 {
self.consecutive_pin_mismatches = 0;
self.save(trussed)?;
@@ -433,17 +438,18 @@ impl PersistentState {
self.pin_hash
}
pub fn set_pin_hash<T: TrussedClient>(&mut self, trussed: &mut T, pin_hash: [u8; 16]) -> Result<()> {
pub fn set_pin_hash<T: TrussedClient>(
&mut self,
trussed: &mut T,
pin_hash: [u8; 16],
) -> Result<()> {
self.pin_hash = Some(pin_hash);
self.save(trussed)?;
Ok(())
}
}
impl RuntimeState {
const POWERCYCLE_RETRIES: u8 = 3;
fn decrement_retries(&mut self) -> Result<()> {
@@ -461,7 +467,6 @@ impl RuntimeState {
self.consecutive_pin_mismatches = 0;
}
pub fn pin_blocked(&self) -> bool {
self.consecutive_pin_mismatches >= Self::POWERCYCLE_RETRIES
}
@@ -482,13 +487,17 @@ impl RuntimeState {
self.cached_credentials.push(credential);
}
pub fn pop_credential<T: client::FilesystemClient>(&mut self, trussed: &mut T) -> Option<Credential> {
pub fn pop_credential<T: client::FilesystemClient>(
&mut self,
trussed: &mut T,
) -> Option<Credential> {
let cached_credential = self.cached_credentials.pop()?;
let credential_data = syscall!(trussed.read_file(
Location::Internal,
PathBuf::from(cached_credential.path.as_str()),
)).data;
))
.data;
Credential::deserialize(&credential_data).ok()
}
@@ -528,13 +537,18 @@ impl RuntimeState {
pub fn rotate_pin_token<T: client::HmacSha256>(&mut self, trussed: &mut T) -> KeyId {
// TODO: need to rotate key agreement key?
if let Some(token) = self.pin_token { syscall!(trussed.delete(token)); }
if let Some(token) = self.pin_token {
syscall!(trussed.delete(token));
}
let token = syscall!(trussed.generate_secret_key(16, Location::Volatile)).key;
self.pin_token = Some(token);
token
}
pub fn reset<T: client::HmacSha256 + client::P256 + client::FilesystemClient>(&mut self, trussed: &mut T) {
pub fn reset<T: client::HmacSha256 + client::P256 + client::FilesystemClient>(
&mut self,
trussed: &mut T,
) {
// Could use `free_credential_heap`, but since we're deleting everything here, this is quicker.
syscall!(trussed.delete_all(Location::Volatile));
self.clear_credential_cache();
@@ -542,22 +556,32 @@ impl RuntimeState {
self.rotate_pin_token(trussed);
self.rotate_key_agreement_key(trussed);
}
pub fn generate_shared_secret<T: client::P256>(&mut self, trussed: &mut T, platform_key_agreement_key: &CoseEcdhEsHkdf256PublicKey) -> Result<KeyId> {
pub fn generate_shared_secret<T: client::P256>(
&mut self,
trussed: &mut T,
platform_key_agreement_key: &CoseEcdhEsHkdf256PublicKey,
) -> Result<KeyId> {
let private_key = self.key_agreement_key(trussed);
let serialized_pkak = cbor_serialize_message(platform_key_agreement_key).map_err(|_| Error::InvalidParameter)?;
let serialized_pkak = cbor_serialize_message(platform_key_agreement_key)
.map_err(|_| Error::InvalidParameter)?;
let platform_kak = try_syscall!(trussed.deserialize_p256_key(
&serialized_pkak, types::KeySerialization::EcdhEsHkdf256,
&serialized_pkak,
types::KeySerialization::EcdhEsHkdf256,
types::StorageAttributes::new().set_persistence(types::Location::Volatile)
)).map_err(|_| Error::InvalidParameter)?.key;
))
.map_err(|_| Error::InvalidParameter)?
.key;
let pre_shared_secret = syscall!(trussed.agree(
types::Mechanism::P256, private_key, platform_kak,
types::Mechanism::P256,
private_key,
platform_kak,
types::StorageAttributes::new().set_persistence(types::Location::Volatile),
)).shared_secret;
))
.shared_secret;
syscall!(trussed.delete(platform_kak));
if let Some(previous_shared_secret) = self.shared_secret {
@@ -565,13 +589,16 @@ impl RuntimeState {
}
let shared_secret = syscall!(trussed.derive_key(
types::Mechanism::Sha256, pre_shared_secret, None, types::StorageAttributes::new().set_persistence(types::Location::Volatile)
)).key;
types::Mechanism::Sha256,
pre_shared_secret,
None,
types::StorageAttributes::new().set_persistence(types::Location::Volatile)
))
.key;
self.shared_secret = Some(shared_secret);
syscall!(trussed.delete(pre_shared_secret));
Ok(shared_secret)
}
}