// Copyright 2019-2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /// Number of keys that persist the CTAP reset command. pub const NUM_PERSISTENT_KEYS: usize = 20; /// Defines a key given its name and value or range of values. macro_rules! make_key { ($(#[$doc: meta])* $name: ident = $key: literal..$end: literal) => { $(#[$doc])* pub const $name: core::ops::Range = $key..$end; }; ($(#[$doc: meta])* $name: ident = $key: literal) => { $(#[$doc])* pub const $name: usize = $key; }; } /// Returns the range of values of a key given its value description. #[cfg(test)] macro_rules! make_range { ($key: literal..$end: literal) => { $key..$end }; ($key: literal) => { $key..$key + 1 }; } /// Helper to define keys as a partial partition of a range. macro_rules! make_partition { ($range: expr, $( $(#[$doc: meta])* $name: ident = $key: literal $(.. $end: literal)?; )*) => { $( make_key!($(#[$doc])* $name = $key $(.. $end)?); )* #[cfg(test)] const KEY_RANGE: core::ops::Range = $range; #[cfg(test)] const ALL_KEYS: &[core::ops::Range] = &[$(make_range!($key $(.. $end)?)),*]; }; } make_partition! { // We reserve 0 and 2048+ for possible migration purposes. We add persistent entries starting // from 1 and going up. We add non-persistent entries starting from 2047 and going down. This // way, we don't commit to a fixed number of persistent keys. 1..2048, // WARNING: Keys should not be deleted but prefixed with `_` to avoid accidentally reusing them. /// The attestation private key. ATTESTATION_PRIVATE_KEY = 1; /// The attestation certificate. ATTESTATION_CERTIFICATE = 2; /// The aaguid. AAGUID = 3; // This is the persistent key limit: // - When adding a (persistent) key above this message, make sure its value is smaller than // NUM_PERSISTENT_KEYS. // - When adding a (non-persistent) key below this message, make sure its value is bigger or // equal than NUM_PERSISTENT_KEYS. /// Reserved for future credential-related objects. /// /// In particular, additional credentials could be added there by reducing the lower bound of /// the credential range below as well as the upper bound of this range in a similar manner. _RESERVED_CREDENTIALS = 1000..1700; /// The credentials. /// /// Depending on `Customization::max_supported_resident_keys()`, only a prefix of those keys is used. /// Each board may configure `Customization::max_supported_resident_keys()` depending on the /// storage size. CREDENTIALS = 1700..2000; /// Storage for the serialized large blob array. /// /// The stored large blob can be too big for one key, so it has to be sharded. LARGE_BLOB_SHARDS = 2000..2004; /// If this entry exists and is empty, alwaysUv is enabled. ALWAYS_UV = 2038; /// If this entry exists and is empty, enterprise attestation is enabled. ENTERPRISE_ATTESTATION = 2039; /// If this entry exists and is empty, the PIN needs to be changed. FORCE_PIN_CHANGE = 2040; /// The secret of the CredRandom feature. CRED_RANDOM_SECRET = 2041; /// List of RP IDs allowed to read the minimum PIN length. MIN_PIN_LENGTH_RP_IDS = 2042; /// The minimum PIN length. /// /// If the entry is absent, the minimum PIN length is `Customization::default_min_pin_length()`. MIN_PIN_LENGTH = 2043; /// The number of PIN retries. /// /// If the entry is absent, the number of PIN retries is `Customization::max_pin_retries()`. PIN_RETRIES = 2044; /// The PIN hash and length. /// /// If the entry is absent, there is no PIN set. The first byte represents /// the length, the following are an array with the hash. PIN_PROPERTIES = 2045; /// Reserved for the key store implementation of the environment. _RESERVED_KEY_STORE = 2046; /// The global signature counter. /// /// If the entry is absent, the counter is 0. GLOBAL_SIGNATURE_COUNTER = 2047; } #[cfg(test)] mod test { use super::*; use crate::api::customization::Customization; use crate::env::test::TestEnv; use crate::env::Env; #[test] fn enough_credentials() { let env = TestEnv::new(); assert!( env.customization().max_supported_resident_keys() <= CREDENTIALS.end - CREDENTIALS.start ); } #[test] fn keys_are_disjoint() { // Check that keys are in the range. for keys in ALL_KEYS { assert!(KEY_RANGE.start <= keys.start && keys.end <= KEY_RANGE.end); } // Check that keys are assigned at most once, essentially partitioning the range. for key in KEY_RANGE { assert!(ALL_KEYS.iter().filter(|keys| keys.contains(&key)).count() <= 1); } } }