Add storage test cases for multi-PIN (#567)

* Add storage test cases for multi-PIN

* Fixed proc-macro2 version (#550)

* fixes proc-macro2 in dependencies

* adds missing locked versions, and a verbose print for cargo check

* commits Cargo.lock files

* removes unnecessary Cargo.lock entries

* adds missing Cargo.lock

Co-authored-by: kaczmarczyck <43844792+kaczmarczyck@users.noreply.github.com>
This commit is contained in:
hcyang
2022-10-26 11:29:56 +08:00
committed by GitHub
parent 1c6c7a4eae
commit 31774ef316
17 changed files with 3460 additions and 14 deletions

View File

@@ -369,8 +369,9 @@ pub fn set_pin(
) -> Result<(), Ctap2StatusCode> {
concat::delete(env.store(), key::FORCE_PIN_CHANGE, slot_id as u8)?;
if slot_id == 0 {
// Backward compatibility: data might be stored in this entry for slot 0 as well.
// Backward compatibility: data might be stored in these entries for slot 0 as well.
env.store().remove(key::FIRST_FORCE_PIN_CHANGE)?;
env.store().remove(key::FIRST_PIN_PROPERTIES)?;
}
let mut pin_properties = [0; 1 + PIN_AUTH_LENGTH];
pin_properties[0] = pin_code_point_length;
@@ -754,6 +755,7 @@ mod test {
CredentialProtectionPolicy, PublicKeyCredentialSource, PublicKeyCredentialType,
};
use crate::env::test::TestEnv;
use persistent_store::StoreUpdate;
use rng256::Rng256;
fn create_credential_source(
@@ -1031,6 +1033,114 @@ mod test {
assert!(pin_code_point_length(&mut env, 0).unwrap().is_none());
}
#[test]
fn test_pin_hash_and_length_multi_pin() {
let mut env = TestEnv::new();
for i in 0..env.customization().slot_count() {
// Pin hash is initially not set.
assert!(pin_hash(&mut env, i).unwrap().is_none());
assert!(pin_code_point_length(&mut env, i).unwrap().is_none());
}
// Setting the pin sets the pin hash.
let random_data = env.rng().gen_uniform_u8x32();
assert_eq!(random_data.len(), 2 * PIN_AUTH_LENGTH);
let pin_hash_1 = *array_ref!(random_data, 0, PIN_AUTH_LENGTH);
let pin_hash_2 = *array_ref!(random_data, PIN_AUTH_LENGTH, PIN_AUTH_LENGTH);
let pin_length_1 = 4;
let pin_length_2 = 63;
set_pin(&mut env, 1, &pin_hash_1, pin_length_1).unwrap();
assert_eq!(pin_hash(&mut env, 1).unwrap(), Some(pin_hash_1));
assert_eq!(
pin_code_point_length(&mut env, 1).unwrap(),
Some(pin_length_1)
);
// Other slots aren't affected.
assert!(pin_hash(&mut env, 0).unwrap().is_none());
assert!(pin_code_point_length(&mut env, 0).unwrap().is_none());
set_pin(&mut env, 1, &pin_hash_2, pin_length_2).unwrap();
assert_eq!(pin_hash(&mut env, 1).unwrap(), Some(pin_hash_2));
assert_eq!(
pin_code_point_length(&mut env, 1).unwrap(),
Some(pin_length_2)
);
// Other slots aren't affected.
assert!(pin_hash(&mut env, 0).unwrap().is_none());
assert!(pin_code_point_length(&mut env, 0).unwrap().is_none());
// Set PIN for multiple slots is supported.
set_pin(&mut env, 2, &pin_hash_1, pin_length_1).unwrap();
assert_eq!(pin_hash(&mut env, 2).unwrap(), Some(pin_hash_1));
assert_eq!(
pin_code_point_length(&mut env, 2).unwrap(),
Some(pin_length_1)
);
// Resetting the storage resets the pin hash.
reset(&mut env).unwrap();
for i in 0..env.customization().slot_count() {
assert!(pin_hash(&mut env, i).unwrap().is_none());
assert!(pin_code_point_length(&mut env, i).unwrap().is_none());
}
}
fn set_pin_legacy(
env: &mut impl Env,
pin_hash: &[u8; PIN_AUTH_LENGTH],
pin_code_point_length: u8,
) -> Result<(), Ctap2StatusCode> {
let mut pin_properties = [0; 1 + PIN_AUTH_LENGTH];
pin_properties[0] = pin_code_point_length;
pin_properties[1..].clone_from_slice(pin_hash);
Ok(env.store().transaction(&[
StoreUpdate::Insert {
key: key::FIRST_PIN_PROPERTIES,
value: &pin_properties[..],
},
StoreUpdate::Remove {
key: key::FIRST_FORCE_PIN_CHANGE,
},
])?)
}
#[test]
fn test_pin_hash_and_length_backward_compat() {
let mut env = TestEnv::new();
// Setting the pin sets the pin hash.
let random_data = env.rng().gen_uniform_u8x32();
assert_eq!(random_data.len(), 2 * PIN_AUTH_LENGTH);
let pin_hash_1 = *array_ref!(random_data, 0, PIN_AUTH_LENGTH);
let pin_hash_2 = *array_ref!(random_data, PIN_AUTH_LENGTH, PIN_AUTH_LENGTH);
let pin_length_1 = 4;
let pin_length_2 = 63;
assert!(set_pin_legacy(&mut env, &pin_hash_1, pin_length_1).is_ok());
// Should fallback to read from legacy storage location successfully.
assert_eq!(pin_hash(&mut env, 0).unwrap(), Some(pin_hash_1));
assert_eq!(
pin_code_point_length(&mut env, 0).unwrap(),
Some(pin_length_1)
);
// Fallback logic should only apply to slot 0.
assert!(pin_hash(&mut env, 1).unwrap().is_none());
assert!(pin_code_point_length(&mut env, 1).unwrap().is_none());
// Setting PIN again should use the new storage location, and erase the old one.
set_pin(&mut env, 0, &pin_hash_2, pin_length_2).unwrap();
assert_eq!(pin_hash(&mut env, 0).unwrap(), Some(pin_hash_2));
assert_eq!(
pin_code_point_length(&mut env, 0).unwrap(),
Some(pin_length_2)
);
assert_eq!(env.store().find(key::FIRST_PIN_PROPERTIES), Ok(None));
assert_eq!(env.store().find(key::FIRST_FORCE_PIN_CHANGE), Ok(None));
}
#[test]
fn test_pin_retries() {
let mut env = TestEnv::new();
@@ -1059,6 +1169,58 @@ mod test {
);
}
#[test]
fn test_pin_retries_multi_pin() {
let mut env = TestEnv::new();
for i in 0..env.customization().slot_count() {
// The pin retries is initially at the maximum.
assert_eq!(
pin_retries(&mut env, i),
Ok(env.customization().max_pin_retries())
);
}
// Decrementing the pin retries decrements the pin retries.
for retries in (0..env.customization().max_pin_retries()).rev() {
decr_pin_retries(&mut env, 1).unwrap();
assert_eq!(pin_retries(&mut env, 1), Ok(retries));
// Other slots shouldn't be affected.
assert_eq!(
pin_retries(&mut env, 2),
Ok(env.customization().max_pin_retries())
);
}
// Decrementing the pin retries after zero does not modify the pin retries.
decr_pin_retries(&mut env, 1).unwrap();
assert_eq!(pin_retries(&mut env, 1), Ok(0));
// Operating on multiple slots is supported.
decr_pin_retries(&mut env, 2).unwrap();
assert_eq!(
pin_retries(&mut env, 2),
Ok(env.customization().max_pin_retries() - 1)
);
// Resetting the pin retries resets the pin retries.
reset_pin_retries(&mut env, 1).unwrap();
assert_eq!(
pin_retries(&mut env, 1),
Ok(env.customization().max_pin_retries())
);
// Other slots shouldn't be affected.
assert_eq!(
pin_retries(&mut env, 2),
Ok(env.customization().max_pin_retries() - 1)
);
assert_eq!(
pin_retries(&mut env, 3),
Ok(env.customization().max_pin_retries())
);
}
#[test]
fn test_persistent_keys() {
let mut env = TestEnv::new();
@@ -1211,6 +1373,45 @@ mod test {
}
}
#[test]
fn test_global_signature_counter_multi_pin() {
let mut env = TestEnv::new();
let mut counter_value = 1;
for i in 0..env.customization().slot_count() {
assert_eq!(
global_signature_counter(&mut env, i).unwrap(),
counter_value
);
}
for increment in 1..10 {
assert!(incr_global_signature_counter(&mut env, 1, increment).is_ok());
counter_value += increment;
assert_eq!(
global_signature_counter(&mut env, 1).unwrap(),
counter_value
);
// Other slots aren't affected.
assert_eq!(global_signature_counter(&mut env, 2).unwrap(), 1);
}
let counter_value_for_slot_1 = counter_value;
counter_value = 1;
for increment in 1..10 {
assert!(incr_global_signature_counter(&mut env, 2, increment).is_ok());
counter_value += increment;
assert_eq!(
global_signature_counter(&mut env, 2).unwrap(),
counter_value
);
// Other slots aren't affected.
assert_eq!(
global_signature_counter(&mut env, 1).unwrap(),
counter_value_for_slot_1
);
}
}
#[test]
fn test_force_pin_change() {
let mut env = TestEnv::new();
@@ -1222,6 +1423,55 @@ mod test {
assert!(!has_force_pin_change(&mut env, 0).unwrap());
}
#[test]
fn test_force_pin_change_multi_pin() {
let mut env = TestEnv::new();
for i in 0..env.customization().slot_count() {
assert!(!has_force_pin_change(&mut env, i).unwrap());
}
assert_eq!(force_pin_change(&mut env, 1), Ok(()));
assert!(has_force_pin_change(&mut env, 1).unwrap());
// Other slots shouldn't be affected.
assert!(!has_force_pin_change(&mut env, 2).unwrap());
assert_eq!(set_pin(&mut env, 1, &[0x88; 16], 8), Ok(()));
assert!(!has_force_pin_change(&mut env, 1).unwrap());
// Other slots shouldn't be affected.
assert!(!has_force_pin_change(&mut env, 2).unwrap());
// Operating on multiple slots is supported.
assert_eq!(force_pin_change(&mut env, 2), Ok(()));
assert!(has_force_pin_change(&mut env, 2).unwrap());
assert!(!has_force_pin_change(&mut env, 1).unwrap());
}
fn force_pin_change_legacy(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
Ok(env.store().insert(key::FIRST_FORCE_PIN_CHANGE, &[])?)
}
#[test]
fn test_force_pin_change_backward_compat() {
let mut env = TestEnv::new();
assert!(!has_force_pin_change(&mut env, 0).unwrap());
assert!(!has_force_pin_change(&mut env, 1).unwrap());
assert_eq!(force_pin_change_legacy(&mut env), Ok(()));
// Should fallback to read from legacy storage location successfully.
assert!(has_force_pin_change(&mut env, 0).unwrap());
// Fallback logic should only apply to slot 0.
assert!(!has_force_pin_change(&mut env, 1).unwrap());
assert_eq!(set_pin(&mut env, 0, &[0x88; 16], 8), Ok(()));
assert!(!has_force_pin_change(&mut env, 0).unwrap());
// Old storage location should be cleared.
assert_eq!(env.store().find(key::FIRST_FORCE_PIN_CHANGE), Ok(None));
assert_eq!(force_pin_change(&mut env, 0), Ok(()));
assert!(has_force_pin_change(&mut env, 0).unwrap());
}
#[test]
fn test_enterprise_attestation() {
let mut env = TestEnv::new();