Repository: google/OpenSK
File: src/ctap/mod.rs (Rust)

Code before:
            ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
        assert_eq!(
            make_credential_response,
            Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
        );
    }

    fn check_ep(make_credential_response: Result<ResponseData, Ctap2StatusCode>, has_ep: bool) {
        let ep_att = if has_ep { Some(true) } else { None };
        match make_credential_response.unwrap() {
            ResponseData::AuthenticatorMakeCredential(make_credential_response) => {
                assert_eq!(make_credential_response.ep_att, ep_att);
            }
            _ => panic!("Invalid response type"),
        }
    }

    #[test]
    fn test_process_make_credential_with_enterprise_attestation_vendor_facilitated() {
        let mut env = TestEnv::new();
        env.customization_mut().enterprise_attestation_mode =
            Some(EnterpriseAttestationMode::VendorFacilitated);
        env.customization_mut().enterprise_rp_id_list = vec!["example.com".to_string()];
        let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));

        env.set_enterprise_attestation();

        let mut make_credential_params = create_minimal_make_credential_parameters();
        make_credential_params.enterprise_attestation = Some(1);
        make_credential_params.rp = PublicKeyCredentialRpEntity {
            rp_id: "counter-example.com".to_string(),
            rp_name: None,
            rp_icon: None,
        };
        let make_credential_response =
            ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
        check_ep(make_credential_response, false);

        let mut make_credential_params = create_minimal_make_credential_parameters();
        make_credential_params.enterprise_attestation = Some(2);
        make_credential_params.rp = PublicKeyCredentialRpEntity {
            rp_id: "counter-example.com".to_string(),
            rp_name: None,
            rp_icon: None,
        };
        let make_credential_response =
            ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
        check_ep(make_credential_response, false);

        let mut make_credential_params = create_minimal_make_credential_parameters();
        make_credential_params.enterprise_attestation = Some(1);

Review comment (suggestion):
I would argue that this should be done before calling `CtapState::new`. That would be a change with current code (so not related to this PR), but I think it makes more sense for tests with non-empty storage to not let `CtapState::new` initialize the storage. Since this cannot happen in practice that `CtapState` is created and the storage is modified under its feet.

At least something like that would make sense:

```rust
let _ = CtapState::new(&mut env, ...);  // initial boot of device
env.foo();  // simulate some modification without calling the actual functions
let mut ctap_state = CtapState::new(&mut env, ...);  // boot device for test
```

But ideally we want (in particular for fuzzing sequences):

```rust
let mut ctap = CtapState::new(&mut env, ...);  // first boot
ctap.foo(); // some commands (including configure)
let mut ctap = CtapState::new(&mut env, ...);  // second boot
ctap.bar(); // some other commands
```

Modifying directly the environment without going through the ctap library, should only be done for things that may be modified outside the library, e.g. customization (before any calls to the library), rng (the library can't guess the future), user presence (the library can't guess the user behavior), etc. The storage is not supposed to be modified outside the library.

Code after:
        make_credential_params.pin_uv_auth_protocol = Some(PinUvAuthProtocol::V1);
        let make_credential_response =
            ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
        assert_eq!(
            make_credential_response,
            Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
        );
    }

    fn check_ep(make_credential_response: Result<ResponseData, Ctap2StatusCode>, has_ep: bool) {
        let ep_att = if has_ep { Some(true) } else { None };
        match make_credential_response.unwrap() {
            ResponseData::AuthenticatorMakeCredential(make_credential_response) => {
                assert_eq!(make_credential_response.ep_att, ep_att);
            }
            _ => panic!("Invalid response type"),
        }
    }

    #[test]
    fn test_process_make_credential_with_enterprise_attestation_vendor_facilitated() {
        let mut env = TestEnv::new();
        env.customization_mut().setup_enterprise_attestation(
            Some(EnterpriseAttestationMode::VendorFacilitated),
            Some(vec!["example.com".to_string()]),
        );

        let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
        test_helpers::enable_enterprise_attestation(&mut ctap_state, &mut env).unwrap();

        let mut make_credential_params = create_minimal_make_credential_parameters();
        make_credential_params.enterprise_attestation = Some(1);
        make_credential_params.rp = PublicKeyCredentialRpEntity {
            rp_id: "counter-example.com".to_string(),
            rp_name: None,
            rp_icon: None,
        };
        let make_credential_response =
            ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
        check_ep(make_credential_response, false);

        let mut make_credential_params = create_minimal_make_credential_parameters();
        make_credential_params.enterprise_attestation = Some(2);
        make_credential_params.rp = PublicKeyCredentialRpEntity {
            rp_id: "counter-example.com".to_string(),
            rp_name: None,
            rp_icon: None,
        };
        let make_credential_response =
            ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
        check_ep(make_credential_response, false);