ronantakizawa/codereview-bench huggingface dataset:
Repository: google/OpenSK
File: src/ctap/mod.rs (Rust)

Code before:
                .attestation_private_key()
                .unwrap()
                .unwrap(),
            dummy_key
        );

        // Now try to lock the device
        let response = ctap_state.process_vendor_configure(
            AuthenticatorVendorConfigureParameters {
                lockdown: true,
                attestation_material: None,
            },
            DUMMY_CHANNEL_ID,
        );
        assert_eq!(
            response,
            Ok(ResponseData::AuthenticatorVendor(
                AuthenticatorVendorResponse {
                    cert_programmed: true,
                    pkey_programmed: true,
                }
            ))
        );
    }

    #[test]
    #[allow(clippy::assertions_on_constants)]
    /// Make sure that privacy guarantees are uphold.
    ///
    /// The current enterprise attestation implementation reuses batch
    /// attestation. Enterprise attestation would imply a batch size of 1, but
    /// batch attestation needs a batch size of at least 100k. To prevent
    /// accidential misconfiguration, this test allows only one of the constants
    /// to be set. If you implement your own enterprise attestation mechanism,
    /// and you want batch attestation at the same time, feel free to proceed
    /// carefully and remove this test.
    fn check_attestation_privacy() {
        assert!(!USE_BATCH_ATTESTATION || ENTERPRISE_ATTESTATION_MODE.is_none());
    }
}


Review comment (bug):
I would have put the `assert!` directly in the code, not as a test.
Otherwise nothing prevents me from configuring a wrong firmware and compiling it. I will never see the error unless I run `cargo test`.

Code after:
            ctap_state
                .persistent_store
                .attestation_certificate()
                .unwrap()
                .unwrap(),
            dummy_cert
        );
        assert_eq!(
            ctap_state
                .persistent_store
                .attestation_private_key()
                .unwrap()
                .unwrap(),
            dummy_key
        );

        // Now try to lock the device
        let response = ctap_state.process_vendor_configure(
            AuthenticatorVendorConfigureParameters {
                lockdown: true,
                attestation_material: None,
            },
            DUMMY_CHANNEL_ID,
        );
        assert_eq!(
            response,
            Ok(ResponseData::AuthenticatorVendor(
                AuthenticatorVendorResponse {
                    cert_programmed: true,
                    pkey_programmed: true,
                }
            ))
        );
    }
}
Repository: google/OpenSK
File: libraries/cbor/src/writer.rs (Rust)

Code before:
            b"b" => "f",
            "" => "g",
            "c" => "h",
        };
        let unsorted_map = cbor_map! {
            1 => "b",
            -2 => "d",
            b"b" => "f",
            "c" => "h",
            "" => "g",
            b"a" => "e",
            -1 => "c",
            0 => "a",
        };
        assert_eq!(write_return(sorted_map), write_return(unsorted_map));
    }

    #[test]
    fn test_write_map_duplicates() {
        let unique_map = cbor_map! {
            0 => "a",
            -1 => "c",
            b"a" => "e",
            "c" => "h",
        };
        let duplicate_map = cbor_map! {
            0 => "a",
            -1 => "c",
            b"a" => "e",
            "c" => "h",
            0 => "a",
            -1 => "c",
            b"a" => "e",
            "c" => "h",
        };
        assert_eq!(write_return(unique_map), write_return(duplicate_map));
    }

    #[test]
    fn test_write_map_with_array() {
        let value_map = cbor_map! {
            "a" => 1,
            "b" => cbor_array![2, 3],
        };
        let expected_cbor = vec![
            0xa2, // map of 2 pairs
            0x61, 0x61, // "a"
            0x01, 0x61, 0x62, // "b"
            0x82, // array with 2 elements
            0x02, 0x03,
        ];

Review comment (refactor):
I think map duplicate keys are invalid CBOR. Otherwise it can't be canonical :)
This should be a rejected input.
Especially here, your test case has the same values for each key but appearing twice but if values were different, there would be no way figuring out which one is correct.

Code after:
            1 => "b",
            -1 => "c",
            -2 => "d",
            b"a" => "e",
            b"b" => "f",
            "" => "g",
            "c" => "h",
        };
        let unsorted_map = cbor_map! {
            1 => "b",
            -2 => "d",
            b"b" => "f",
            "c" => "h",
            "" => "g",
            b"a" => "e",
            -1 => "c",
            0 => "a",
        };
        assert_eq!(write_return(sorted_map), write_return(unsorted_map));
    }

    #[test]
    fn test_write_map_duplicates() {
        let duplicate0 = cbor_map! {
            0 => "a",
            -1 => "c",
            b"a" => "e",
            "c" => "g",
            0 => "b",
        };
        assert_eq!(write_return(duplicate0), None);
        let duplicate1 = cbor_map! {
            0 => "a",
            -1 => "c",
            b"a" => "e",
            "c" => "g",
            -1 => "d",
        };
        assert_eq!(write_return(duplicate1), None);
        let duplicate2 = cbor_map! {
            0 => "a",
            -1 => "c",
            b"a" => "e",
            "c" => "g",
            b"a" => "f",
        };
        assert_eq!(write_return(duplicate2), None);
        let duplicate3 = cbor_map! {
            0 => "a",
            -1 => "c",
            b"a" => "e",
Repository: google/OpenSK
File: libraries/cbor/src/values.rs (Rust)

Code before:
        Value::Map(map)
    }
}

impl From<bool> for Value {
    fn from(b: bool) -> Self {
        Value::bool_value(b)
    }
}

/// Trait that indicates that a type can be infallibly converted to a CBOR [`Value`].
pub trait IntoCborValue {
    /// Convert `self` into a CBOR [`Value`], consuming it along the way.
    fn into_cbor_value(self) -> Value;
}

impl<T> IntoCborValue for T
where
    Value: From<T>,
{
    fn into_cbor_value(self) -> Value {
        Value::from(self)
    }
}

/// Trait that indicates that a type can be (fallibly) converted to a CBOR [`Value`].
pub trait IntoCborValueOption {
    /// Convert `self` into a CBOR [`Value`], consuming it along the way.
    fn into_cbor_value_option(self) -> Option<Value>;
}

impl<T> IntoCborValueOption for T
where
    Value: From<T>,
{
    fn into_cbor_value_option(self) -> Option<Value> {
        Some(Value::from(self))
    }
}

impl<T> IntoCborValueOption for Option<T>
where
    Value: From<T>,
{
    fn into_cbor_value_option(self) -> Option<Value> {
        self.map(Value::from)
    }
}

#[cfg(test)]
mod test {

Review comment (bug):
This is not how we actually use it. Maybe we have to rename it? What we do is implement it for `Option<T>` and just `map` back to `Option`. You could implement it for a type that fallibly converts, but that might make it less intuitive. I.e. you use `cbor_map_options!` and some keys just disappear instead of failing to compile, because they were a fallibly conversion.

Code after:
        Value::Map(map)
    }
}

impl From<bool> for Value {
    fn from(b: bool) -> Self {
        Value::bool_value(b)
    }
}

/// Trait that indicates that a type can be converted to a CBOR [`Value`].
pub trait IntoCborValue {
    /// Convert `self` into a CBOR [`Value`], consuming it along the way.
    fn into_cbor_value(self) -> Value;
}

impl<T> IntoCborValue for T
where
    Value: From<T>,
{
    fn into_cbor_value(self) -> Value {
        Value::from(self)
    }
}

/// Trait that indicates that a type can be converted to a CBOR [`Option<Value>`].
pub trait IntoCborValueOption {
    /// Convert `self` into a CBOR [`Option<Value>`], consuming it along the way.
    fn into_cbor_value_option(self) -> Option<Value>;
}

impl<T> IntoCborValueOption for T
where
    Value: From<T>,
{
    fn into_cbor_value_option(self) -> Option<Value> {
        Some(Value::from(self))
    }
}

impl<T> IntoCborValueOption for Option<T>
where
    Value: From<T>,
{
    fn into_cbor_value_option(self) -> Option<Value> {
        self.map(Value::from)
    }
}

#[cfg(test)]
mod test {
Repository: google/OpenSK
File: tools/perform_upgrade.sh (Shell)

Code before:
# Copyright 2021 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.

# Creates a signature key and configures the public key.
# The device will not be locked down for testing purposes.
# Generates the binary and upgrades OpenSK.
# To be run from the OpenSK base path.

set -e

BOARD="$1"

./deploy.py --board=$BOARD --opensk --programmer=none
python -m tools.deploy_partition --board=$BOARD
nrfjprog --reset --family NRF52


Review comment (bug):
This should only work with the devkit because `nrfjprog` implies using a JLink debugger IIRC.

Code after:
# Copyright 2021 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.

# Creates a signature key and configures the public key.
# The device will not be locked down for testing purposes.
# Generates the binary and upgrades OpenSK.
# To be run from the OpenSK base path.

set -e

BOARD="$1"

./deploy.py --board=$BOARD --opensk --programmer=none
python3 -m tools.deploy_partition --board=$BOARD
if nrfjprog --reset --family NRF52 ; then
  echo "Upgrade finished!"
else
  echo "Please replug OpenSK to reboot"
fi
Repository: google/OpenSK
File: libraries/crypto/src/hmac.rs (Rust)

Code before:
use subtle::ConstantTimeEq;

const BLOCK_SIZE: usize = 64;
const HASH_SIZE: usize = 32;

pub fn verify_hmac_256<H>(key: &[u8], contents: &[u8], mac: &[u8; HASH_SIZE]) -> bool
where
    H: Hash256 + HashBlockSize64Bytes,
{
    let expected_mac = hmac_256::<H>(key, contents);
    bool::from(expected_mac.ct_eq(mac))
}

// FIDO2's PIN verification is just matching the first 16 bytes of the HMAC
// against the pin ¯\_(ツ)_/¯
pub fn verify_hmac_256_first_128bits<H>(key: &[u8], contents: &[u8], pin: &[u8; 16]) -> bool
where
    H: Hash256 + HashBlockSize64Bytes,
{
    let expected_mac = hmac_256::<H>(key, contents);
    bool::from(array_ref![expected_mac, 0, 16].ct_eq(pin))
}

pub fn hmac_256<H>(key: &[u8], contents: &[u8]) -> [u8; HASH_SIZE]
where
    H: Hash256 + HashBlockSize64Bytes,
{
    let mut ipad: [u8; BLOCK_SIZE] = [0x36; BLOCK_SIZE];
    let mut opad: [u8; BLOCK_SIZE] = [0x5c; BLOCK_SIZE];
    if key.len() <= BLOCK_SIZE {
        xor_pads(&mut ipad, &mut opad, key);
    } else {
        xor_pads(&mut ipad, &mut opad, &H::hash(key));
    }

    let mut ihasher = H::new();
    ihasher.update(&ipad);
    ihasher.update(contents);
    let ihash = ihasher.finalize();

    let mut ohasher = H::new();
    ohasher.update(&opad);
    ohasher.update(&ihash);

    ohasher.finalize()
}

fn xor_pads(ipad: &mut [u8; BLOCK_SIZE], opad: &mut [u8; BLOCK_SIZE], key: &[u8]) {
    for (i, k) in key.iter().enumerate() {
        ipad[i] ^= k;
        opad[i] ^= k;

Review comment (suggestion):
I thought that `HashBlockSize64Bytes` was defining `BLOCK_SIZE` which is needed for hmac implementation.

Code after:
use subtle::ConstantTimeEq;

const BLOCK_SIZE: usize = 64;
const HASH_SIZE: usize = 32;

pub fn verify_hmac_256<H>(key: &[u8], contents: &[u8], mac: &[u8; HASH_SIZE]) -> bool
where
    H: Hash256,
{
    let expected_mac = hmac_256::<H>(key, contents);
    bool::from(expected_mac.ct_eq(mac))
}

// FIDO2's PIN verification is just matching the first 16 bytes of the HMAC
// against the pin ¯\_(ツ)_/¯
pub fn verify_hmac_256_first_128bits<H>(key: &[u8], contents: &[u8], pin: &[u8; 16]) -> bool
where
    H: Hash256,
{
    let expected_mac = hmac_256::<H>(key, contents);
    bool::from(array_ref![expected_mac, 0, 16].ct_eq(pin))
}

pub fn hmac_256<H>(key: &[u8], contents: &[u8]) -> [u8; HASH_SIZE]
where
    H: Hash256,
{
    let mut ipad: [u8; BLOCK_SIZE] = [0x36; BLOCK_SIZE];
    let mut opad: [u8; BLOCK_SIZE] = [0x5c; BLOCK_SIZE];
    if key.len() <= BLOCK_SIZE {
        xor_pads(&mut ipad, &mut opad, key);
    } else {
        xor_pads(&mut ipad, &mut opad, &H::hash(key));
    }

    let mut ihasher = H::new();
    ihasher.update(&ipad);
    ihasher.update(contents);
    let ihash = ihasher.finalize();

    let mut ohasher = H::new();
    ohasher.update(&opad);
    ohasher.update(&ihash);

    ohasher.finalize()
}

fn xor_pads(ipad: &mut [u8; BLOCK_SIZE], opad: &mut [u8; BLOCK_SIZE], key: &[u8]) {
    for (i, k) in key.iter().enumerate() {
        ipad[i] ^= k;
        opad[i] ^= k;
Repository: google/OpenSK
File: src/env/test/mod.rs (Rust)

Code before:
impl Env for TestEnv {
    type Rng = ThreadRng256;
    type UserPresence = TestUserPresence;
    type Storage = BufferStorage;
    type UpgradeStorage = BufferUpgradeStorage;
    type FirmwareProtection = Self;

    fn rng(&mut self) -> &mut Self::Rng {
        &mut self.rng
    }

    fn user_presence(&mut self) -> &mut Self::UserPresence {
        &mut self.user_presence
    }

    fn storage(&mut self) -> StorageResult<Self::Storage> {
        // Use the Nordic configuration.
        const PAGE_SIZE: usize = 0x1000;
        const NUM_PAGES: usize = 20;
        let store = vec![0xff; NUM_PAGES * PAGE_SIZE].into_boxed_slice();
        let options = BufferOptions {
            word_size: 4,
            page_size: PAGE_SIZE,
            max_word_writes: 2,
            max_page_erases: 10000,
            strict_mode: true,
        };
        Ok(BufferStorage::new(store, options))
    }

    fn upgrade_storage(&mut self) -> StorageResult<Self::UpgradeStorage> {
        BufferUpgradeStorage::new()
    }

    fn firmware_protection(&mut self) -> &mut Self::FirmwareProtection {
        self
    }
}


Review comment (suggestion):
I don't know off the top of my hat, but does
`crp::set_protection(crp::ProtectionLevel::FullyLocked).is_ok()`
return false the second time you call it?

In that case, we could give the Test version a bool to store its state, and replicate that behavior.
Since workflows are green, we
1. either don't have a test for that (not sure if we should care)
2. or TockEnv always returns true and you can ignore this comment.

Looking at the [only place I think we test](https://github.com/google/OpenSK/blob/91ba2c375eeb31dfebde7bbba6cf95dc97181c12/src/ctap/mod.rs#L2944), it's option (1).

Code after:
            user_presence,
            store,
            upgrade_storage,
        }
    }

    pub fn disable_upgrade_storage(&mut self) {
        self.upgrade_storage = None;
    }
}

impl TestUserPresence {
    pub fn set(&mut self, check: impl Fn(ChannelID) -> Result<(), Ctap2StatusCode> + 'static) {
        self.check = Box::new(check);
    }
}

impl UserPresence for TestUserPresence {
    fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> {
        (self.check)(cid)
    }
}

impl FirmwareProtection for TestEnv {
    fn lock(&mut self) -> bool {
        true
    }
}

impl Env for TestEnv {
    type Rng = ThreadRng256;
    type UserPresence = TestUserPresence;
    type Storage = BufferStorage;
    type UpgradeStorage = BufferUpgradeStorage;
    type FirmwareProtection = Self;

    fn rng(&mut self) -> &mut Self::Rng {
        &mut self.rng
    }

    fn user_presence(&mut self) -> &mut Self::UserPresence {
        &mut self.user_presence
    }

    fn store(&mut self) -> &mut Store<Self::Storage> {
        &mut self.store
    }

    fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> {
        self.upgrade_storage.as_mut()
    }
Repository: google/OpenSK
File: src/api/customization.rs (Rust)

Code before:
    /// this value.
    fn max_msg_size(&self) -> usize;
}

#[derive(Clone)]
pub struct CustomizationImpl {
    pub max_msg_size: usize,
}

pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl { max_msg_size: 7609 };

impl Customization for CustomizationImpl {
    fn max_msg_size(&self) -> usize {
        self.max_msg_size
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    #[allow(clippy::assertions_on_constants)]
    fn test_invariants() {
        assert!(DEFAULT_CUSTOMIZATION.max_msg_size >= 1024);
        assert!(DEFAULT_CUSTOMIZATION.max_msg_size <= 7609);
    }
}


Review comment (suggestion):
Let's instead have a validation function if those invariants should hold for all customizations.

```rust
fn is_valid(customization: &impl Customization) -> bool {
    // explanation
    if ... { return false; }
    // explanation
    if ... { return false; }
    true
}
```

Then the test would just `assert!(is_valid(&DEFAULT_CUSTOMIZATION))`.

Code after:
    /// this value.
    fn max_msg_size(&self) -> usize;
}

#[derive(Clone)]
pub struct CustomizationImpl {
    pub max_msg_size: usize,
}

pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl { max_msg_size: 7609 };

impl Customization for CustomizationImpl {
    fn max_msg_size(&self) -> usize {
        self.max_msg_size
    }
}

#[cfg(feature = "std")]
pub fn is_valid(customization: &impl Customization) -> bool {
    // Max message size must be between 1024 and 7609.
    if customization.max_msg_size() < 1024 || customization.max_msg_size() > 7609 {
        return false;
    }
    true
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    #[allow(clippy::assertions_on_constants)]
    fn test_invariants() {
        assert!(is_valid(&DEFAULT_CUSTOMIZATION));
    }
}
Repository: google/OpenSK
File: src/api/customization.rs (Rust)

Code before:
// Copyright 2022 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.

//! This file contains all customizable constants.
//!
//! If you adapt them, make sure to run the tests before flashing the firmware.
//! Our deploy script enforces the invariants.

pub trait Customization {
    /// Maximum message size send for CTAP commands.
    ///
    /// The maximum value is 7609, as HID packets can not encode longer messages.
    /// 1024 is the default mentioned in the authenticatorLargeBlobs commands.
    /// Larger values are preferred, as that allows more parameters in commands.
    /// If long commands are too unreliable on your hardware, consider decreasing
    /// this value.
    fn max_msg_size(&self) -> usize;
}

#[derive(Clone)]
pub struct CustomizationImpl {
    pub max_msg_size: usize,
}

pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl { max_msg_size: 7609 };

impl Customization for CustomizationImpl {
    fn max_msg_size(&self) -> usize {
        self.max_msg_size
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    #[allow(clippy::assertions_on_constants)]
    fn test_invariants() {
        assert!(DEFAULT_CUSTOMIZATION.max_msg_size >= 1024);
        assert!(DEFAULT_CUSTOMIZATION.max_msg_size <= 7609);
    }
}


Review comment (suggestion):
`deploy.py` runs the customization test to enforce that all constants are correctly set. Can you add this one as well?

https://github.com/google/OpenSK/blob/81996f650e7ddbb60461242e62b40cb0f467b183/deploy.py#L471

Code after:
}

pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl { max_msg_size: 7609 };

impl Customization for CustomizationImpl {
    fn max_msg_size(&self) -> usize {
        self.max_msg_size
    }
}

#[cfg(feature = "std")]
pub fn is_valid(customization: &impl Customization) -> bool {
    // Max message size must be between 1024 and 7609.
    if customization.max_msg_size() < 1024 || customization.max_msg_size() > 7609 {
        return false;
    }
    true
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    #[allow(clippy::assertions_on_constants)]
    fn test_invariants() {
        assert!(is_valid(&DEFAULT_CUSTOMIZATION));
    }
}
Repository: google/OpenSK
File: src/env/test/mod.rs (Rust)

Code before:
        };
        let user_presence = TestUserPresence {
            check: Box::new(|_| Ok(())),
        };
        let storage = new_storage();
        let store = Store::new(storage).ok().unwrap();
        let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap());
        let customization = DEFAULT_CUSTOMIZATION.into();
        TestEnv {
            rng,
            user_presence,
            store,
            upgrade_storage,
            customization,
        }
    }

    pub fn disable_upgrade_storage(&mut self) {
        self.upgrade_storage = None;
    }

    pub fn customization_mut(&mut self) -> &mut TestCustomization {
        &mut self.customization
    }

    pub fn rng(&mut self) -> &mut TestRng256 {
        &mut self.rng
    }

    pub fn set_enterprise_attestation(&mut self) {
        storage::init(self).ok().unwrap();
        let mut key_bytes = [0; 32];
        let private_key = crypto::ecdsa::SecKey::gensk(self.rng());
        private_key.to_bytes(array_mut_ref!(key_bytes, 0, 32));
        storage::set_attestation_certificate(self, &[0xCC]).unwrap();
        storage::set_attestation_private_key(self, &key_bytes).unwrap();
        storage::enable_enterprise_attestation(self).unwrap();
    }
}

impl TestUserPresence {
    pub fn set(&mut self, check: impl Fn(Channel) -> Result<(), Ctap2StatusCode> + 'static) {
        self.check = Box::new(check);
    }
}

impl UserPresence for TestUserPresence {
    fn check(&mut self, channel: Channel) -> Result<(), Ctap2StatusCode> {
        (self.check)(channel)
    }
}

Review comment (refactor):
Oops, I just realized this function should be deleted. It's a duplicate of the one in `Env`. I missed that in a prior review I guess.

Code after:
        };
        let user_presence = TestUserPresence {
            check: Box::new(|_| Ok(())),
        };
        let storage = new_storage();
        let store = Store::new(storage).ok().unwrap();
        let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap());
        let customization = DEFAULT_CUSTOMIZATION.into();
        TestEnv {
            rng,
            user_presence,
            store,
            upgrade_storage,
            customization,
        }
    }

    pub fn disable_upgrade_storage(&mut self) {
        self.upgrade_storage = None;
    }

    pub fn customization_mut(&mut self) -> &mut TestCustomization {
        &mut self.customization
    }

    pub fn rng(&mut self) -> &mut TestRng256 {
        &mut self.rng
    }
}

impl TestUserPresence {
    pub fn set(&mut self, check: impl Fn(Channel) -> Result<(), Ctap2StatusCode> + 'static) {
        self.check = Box::new(check);
    }
}

impl UserPresence for TestUserPresence {
    fn check(&mut self, channel: Channel) -> Result<(), Ctap2StatusCode> {
        (self.check)(channel)
    }
}

impl FirmwareProtection for TestEnv {
    fn lock(&mut self) -> bool {
        true
    }
}

impl Env for TestEnv {
    type Rng = TestRng256;
    type UserPresence = TestUserPresence;