Access the persistent keys through the store
This permits to set them using a vendor command and thus not embed their value in the application.
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
|
||||
use crate::crypto::rng256::Rng256;
|
||||
use crate::ctap::data_formats::PublicKeyCredentialSource;
|
||||
use crate::ctap::key_material;
|
||||
use crate::ctap::status_code::Ctap2StatusCode;
|
||||
use crate::ctap::PIN_AUTH_LENGTH;
|
||||
use alloc::string::String;
|
||||
@@ -56,9 +57,14 @@ const GLOBAL_SIGNATURE_COUNTER: usize = 1;
|
||||
const MASTER_KEYS: usize = 2;
|
||||
const PIN_HASH: usize = 3;
|
||||
const PIN_RETRIES: usize = 4;
|
||||
const NUM_TAGS: usize = 5;
|
||||
const ATTESTATION_PRIVATE_KEY: usize = 5;
|
||||
const ATTESTATION_CERTIFICATE: usize = 6;
|
||||
const AAGUID: usize = 7;
|
||||
const NUM_TAGS: usize = 8;
|
||||
|
||||
const MAX_PIN_RETRIES: u8 = 6;
|
||||
const ATTESTATION_PRIVATE_KEY_LENGTH: usize = 32;
|
||||
const AAGUID_LENGTH: usize = 16;
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum Key {
|
||||
@@ -73,6 +79,9 @@ enum Key {
|
||||
MasterKeys,
|
||||
PinHash,
|
||||
PinRetries,
|
||||
AttestationPrivateKey,
|
||||
AttestationCertificate,
|
||||
Aaguid,
|
||||
}
|
||||
|
||||
pub struct MasterKeys<'a> {
|
||||
@@ -124,6 +133,9 @@ impl StoreConfig for Config {
|
||||
MASTER_KEYS => add(Key::MasterKeys),
|
||||
PIN_HASH => add(Key::PinHash),
|
||||
PIN_RETRIES => add(Key::PinRetries),
|
||||
ATTESTATION_PRIVATE_KEY => add(Key::AttestationPrivateKey),
|
||||
ATTESTATION_CERTIFICATE => add(Key::AttestationCertificate),
|
||||
AAGUID => add(Key::Aaguid),
|
||||
_ => debug_assert!(false),
|
||||
}
|
||||
}
|
||||
@@ -211,6 +223,33 @@ impl PersistentStore {
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
if self.store.find_one(&Key::AttestationPrivateKey).is_none() {
|
||||
self.store
|
||||
.insert(StoreEntry {
|
||||
tag: ATTESTATION_PRIVATE_KEY,
|
||||
data: key_material::ATTESTATION_PRIVATE_KEY,
|
||||
sensitive: false,
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
if self.store.find_one(&Key::AttestationCertificate).is_none() {
|
||||
self.store
|
||||
.insert(StoreEntry {
|
||||
tag: ATTESTATION_CERTIFICATE,
|
||||
data: key_material::ATTESTATION_CERTIFICATE,
|
||||
sensitive: false,
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
if self.store.find_one(&Key::Aaguid).is_none() {
|
||||
self.store
|
||||
.insert(StoreEntry {
|
||||
tag: AAGUID,
|
||||
data: key_material::AAGUID,
|
||||
sensitive: false,
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_credential(
|
||||
@@ -394,10 +433,44 @@ impl PersistentStore {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn attestation_private_key(
|
||||
&self,
|
||||
) -> Result<&[u8; ATTESTATION_PRIVATE_KEY_LENGTH], Ctap2StatusCode> {
|
||||
let (_, entry) = self
|
||||
.store
|
||||
.find_one(&Key::AttestationPrivateKey)
|
||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?;
|
||||
let data = entry.data;
|
||||
if data.len() != ATTESTATION_PRIVATE_KEY_LENGTH {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
|
||||
}
|
||||
Ok(array_ref!(data, 0, ATTESTATION_PRIVATE_KEY_LENGTH))
|
||||
}
|
||||
|
||||
pub fn attestation_certificate(&self) -> Result<Vec<u8>, Ctap2StatusCode> {
|
||||
let (_, entry) = self
|
||||
.store
|
||||
.find_one(&Key::AttestationCertificate)
|
||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?;
|
||||
Ok(entry.data.to_vec())
|
||||
}
|
||||
|
||||
pub fn aaguid(&self) -> Result<&[u8; AAGUID_LENGTH], Ctap2StatusCode> {
|
||||
let (_, entry) = self
|
||||
.store
|
||||
.find_one(&Key::Aaguid)
|
||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?;
|
||||
let data = entry.data;
|
||||
if data.len() != AAGUID_LENGTH {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
|
||||
}
|
||||
Ok(array_ref!(data, 0, AAGUID_LENGTH))
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, rng: &mut impl Rng256) {
|
||||
loop {
|
||||
let index = {
|
||||
let mut iter = self.store.iter();
|
||||
let mut iter = self.store.iter().filter(|(_, entry)| should_reset(entry));
|
||||
match iter.next() {
|
||||
None => break,
|
||||
Some((index, _)) => index,
|
||||
@@ -419,6 +492,13 @@ impl From<StoreError> for Ctap2StatusCode {
|
||||
}
|
||||
}
|
||||
|
||||
fn should_reset<'a>(entry: &StoreEntry<'a>) -> bool {
|
||||
match entry.tag {
|
||||
ATTESTATION_PRIVATE_KEY | ATTESTATION_CERTIFICATE | AAGUID => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_credential(data: &[u8]) -> Option<PublicKeyCredentialSource> {
|
||||
let cbor = cbor::read(data).ok()?;
|
||||
cbor.try_into().ok()
|
||||
@@ -696,4 +776,33 @@ mod test {
|
||||
persistent_store.reset_pin_retries();
|
||||
assert_eq!(persistent_store.pin_retries(), MAX_PIN_RETRIES);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_persistent_keys() {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
|
||||
// The persistent keys are initialized on a fresh store.
|
||||
assert_eq!(
|
||||
persistent_store.attestation_private_key().unwrap(),
|
||||
key_material::ATTESTATION_PRIVATE_KEY
|
||||
);
|
||||
assert_eq!(
|
||||
persistent_store.attestation_certificate().unwrap(),
|
||||
key_material::ATTESTATION_CERTIFICATE
|
||||
);
|
||||
assert_eq!(persistent_store.aaguid().unwrap(), key_material::AAGUID);
|
||||
|
||||
// The persistent keys stay initialized and preserve their value after a reset.
|
||||
persistent_store.reset(&mut rng);
|
||||
assert_eq!(
|
||||
persistent_store.attestation_private_key().unwrap(),
|
||||
key_material::ATTESTATION_PRIVATE_KEY
|
||||
);
|
||||
assert_eq!(
|
||||
persistent_store.attestation_certificate().unwrap(),
|
||||
key_material::ATTESTATION_CERTIFICATE
|
||||
);
|
||||
assert_eq!(persistent_store.aaguid().unwrap(), key_material::AAGUID);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user