tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_token.rs (36763B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 use authenticator::authenticatorservice::{RegisterArgs, SignArgs};
      6 use authenticator::crypto::{ecdsa_p256_sha256_sign_raw, COSEAlgorithm, COSEKey, SharedSecret};
      7 use authenticator::ctap2::{
      8    attestation::{
      9        AAGuid, AttestationObject, AttestationStatement, AttestationStatementPacked,
     10        AttestedCredentialData, AuthenticatorData, AuthenticatorDataFlags, Extension,
     11        HmacSecretResponse,
     12    },
     13    client_data::ClientDataHash,
     14    commands::{
     15        client_pin::{ClientPIN, ClientPinResponse, PINSubcommand},
     16        get_assertion::{
     17            GetAssertion, GetAssertionResponse, GetAssertionResult, HmacGetSecretOrPrf,
     18            HmacSecretExtension,
     19        },
     20        get_info::{AuthenticatorInfo, AuthenticatorOptions, AuthenticatorVersion},
     21        get_version::{GetVersion, U2FInfo},
     22        make_credentials::{HmacCreateSecretOrPrf, MakeCredentials, MakeCredentialsResult},
     23        reset::Reset,
     24        selection::Selection,
     25        RequestCtap1, RequestCtap2, StatusCode,
     26    },
     27    preflight::CheckKeyHandle,
     28    server::{
     29        AuthenticatorAttachment, CredentialProtectionPolicy, PublicKeyCredentialDescriptor,
     30        PublicKeyCredentialUserEntity, RelyingParty,
     31    },
     32 };
     33 use authenticator::errors::{AuthenticatorError, CommandError, HIDError, U2FTokenError};
     34 use authenticator::{ctap2, statecallback::StateCallback};
     35 use authenticator::{FidoDevice, FidoDeviceIO, FidoProtocol, VirtualFidoDevice};
     36 use authenticator::{RegisterResult, SignResult, StatusUpdate};
     37 use base64::Engine;
     38 use moz_task::RunnableBuilder;
     39 use nserror::{nsresult, NS_ERROR_FAILURE, NS_ERROR_INVALID_ARG, NS_OK};
     40 use nsstring::{nsACString, nsAString, nsCString, nsString};
     41 use rand::{thread_rng, RngCore};
     42 use std::cell::{Ref, RefCell};
     43 use std::collections::{hash_map::Entry, HashMap};
     44 use std::ops::{Deref, DerefMut};
     45 use std::sync::atomic::{AtomicU32, Ordering};
     46 use std::sync::mpsc::Sender;
     47 use std::sync::{Arc, Mutex};
     48 use thin_vec::ThinVec;
     49 use xpcom::interfaces::{nsICredentialParameters, nsIWebAuthnAutoFillEntry};
     50 use xpcom::{xpcom_method, RefPtr};
     51 
     52 // All TestTokens use this fixed, randomly generated, AAGUID
     53 const VIRTUAL_TOKEN_AAGUID: AAGuid = AAGuid([
     54    0x68, 0xe1, 0x00, 0xa5, 0x0b, 0x47, 0x91, 0x04, 0xb8, 0x54, 0x97, 0xa9, 0xba, 0x51, 0x06, 0x38,
     55 ]);
     56 
     57 #[derive(Debug)]
     58 struct TestTokenCredential {
     59    id: Vec<u8>,
     60    privkey: Vec<u8>,
     61    user_handle: Vec<u8>,
     62    sign_count: AtomicU32,
     63    is_discoverable_credential: bool,
     64    rp: RelyingParty,
     65    credential_protection_policy: CredentialProtectionPolicy,
     66 }
     67 
     68 impl TestTokenCredential {
     69    fn assert(
     70        &self,
     71        client_data_hash: &ClientDataHash,
     72        flags: AuthenticatorDataFlags,
     73    ) -> Result<GetAssertionResponse, HIDError> {
     74        let credentials = Some(PublicKeyCredentialDescriptor {
     75            id: self.id.clone(),
     76            transports: vec![],
     77        });
     78 
     79        let auth_data = AuthenticatorData {
     80            rp_id_hash: self.rp.hash(),
     81            flags,
     82            counter: self.sign_count.fetch_add(1, Ordering::Relaxed),
     83            credential_data: None,
     84            extensions: Extension::default(),
     85        };
     86 
     87        let user = Some(PublicKeyCredentialUserEntity {
     88            id: self.user_handle.clone(),
     89            ..Default::default()
     90        });
     91 
     92        let mut data = auth_data.to_vec();
     93        data.extend_from_slice(client_data_hash.as_ref());
     94        let signature =
     95            ecdsa_p256_sha256_sign_raw(&self.privkey, &data).or(Err(HIDError::DeviceError))?;
     96 
     97        Ok(GetAssertionResponse {
     98            credentials,
     99            auth_data,
    100            signature,
    101            user,
    102            number_of_credentials: Some(1),
    103        })
    104    }
    105 }
    106 
    107 #[derive(Debug)]
    108 struct TestToken {
    109    protocol: FidoProtocol,
    110    transport: String,
    111    versions: Vec<AuthenticatorVersion>,
    112    has_resident_key: bool,
    113    has_user_verification: bool,
    114    is_user_consenting: bool,
    115    is_user_verified: bool,
    116    // This is modified in `make_credentials` which takes a &TestToken, but we only allow one transaction at a time.
    117    credentials: RefCell<Vec<TestTokenCredential>>,
    118    pin_token: [u8; 32],
    119    shared_secret: Option<SharedSecret>,
    120    authenticator_info: Option<AuthenticatorInfo>,
    121 }
    122 
    123 impl TestToken {
    124    fn new(
    125        versions: Vec<AuthenticatorVersion>,
    126        transport: String,
    127        has_resident_key: bool,
    128        has_user_verification: bool,
    129        is_user_consenting: bool,
    130        is_user_verified: bool,
    131    ) -> TestToken {
    132        let mut pin_token = [0u8; 32];
    133        thread_rng().fill_bytes(&mut pin_token);
    134        Self {
    135            protocol: FidoProtocol::CTAP2,
    136            transport,
    137            versions,
    138            has_resident_key,
    139            has_user_verification,
    140            is_user_consenting,
    141            is_user_verified,
    142            credentials: RefCell::new(vec![]),
    143            pin_token,
    144            shared_secret: None,
    145            authenticator_info: None,
    146        }
    147    }
    148 
    149    fn insert_credential(
    150        &self,
    151        id: &[u8],
    152        privkey: &[u8],
    153        rp: &RelyingParty,
    154        is_discoverable_credential: bool,
    155        user_handle: &[u8],
    156        sign_count: u32,
    157        credential_protection_policy: CredentialProtectionPolicy,
    158    ) {
    159        let c = TestTokenCredential {
    160            id: id.to_vec(),
    161            privkey: privkey.to_vec(),
    162            rp: rp.clone(),
    163            is_discoverable_credential,
    164            user_handle: user_handle.to_vec(),
    165            sign_count: AtomicU32::new(sign_count),
    166            credential_protection_policy,
    167        };
    168 
    169        let mut credlist = self.credentials.borrow_mut();
    170 
    171        match credlist.binary_search_by_key(&id, |probe| &probe.id) {
    172            Ok(_) => {}
    173            Err(idx) => credlist.insert(idx, c),
    174        }
    175    }
    176 
    177    fn get_credentials(&self) -> Ref<'_, Vec<TestTokenCredential>> {
    178        self.credentials.borrow()
    179    }
    180 
    181    fn delete_credential(&mut self, id: &[u8]) -> bool {
    182        let mut credlist = self.credentials.borrow_mut();
    183        if let Ok(idx) = credlist.binary_search_by_key(&id, |probe| &probe.id) {
    184            credlist.remove(idx);
    185            return true;
    186        }
    187 
    188        false
    189    }
    190 
    191    fn delete_all_credentials(&mut self) {
    192        self.credentials.borrow_mut().clear();
    193    }
    194 
    195    fn has_credential(&self, id: &[u8]) -> bool {
    196        self.credentials
    197            .borrow()
    198            .binary_search_by_key(&id, |probe| &probe.id)
    199            .is_ok()
    200    }
    201 
    202    fn max_supported_version(&self) -> AuthenticatorVersion {
    203        self.authenticator_info
    204            .as_ref()
    205            .map_or(AuthenticatorVersion::U2F_V2, |info| {
    206                info.max_supported_version()
    207            })
    208    }
    209 }
    210 
    211 impl FidoDevice for TestToken {
    212    fn pre_init(&mut self) -> Result<(), HIDError> {
    213        Ok(())
    214    }
    215 
    216    fn should_try_ctap2(&self) -> bool {
    217        true
    218    }
    219 
    220    fn initialized(&self) -> bool {
    221        true
    222    }
    223 
    224    fn is_u2f(&mut self) -> bool {
    225        true
    226    }
    227 
    228    fn get_shared_secret(&self) -> Option<&SharedSecret> {
    229        self.shared_secret.as_ref()
    230    }
    231 
    232    fn set_shared_secret(&mut self, shared_secret: SharedSecret) {
    233        self.shared_secret = Some(shared_secret);
    234    }
    235 
    236    fn get_authenticator_info(&self) -> Option<&AuthenticatorInfo> {
    237        self.authenticator_info.as_ref()
    238    }
    239 
    240    fn set_authenticator_info(&mut self, authenticator_info: AuthenticatorInfo) {
    241        self.authenticator_info = Some(authenticator_info);
    242    }
    243 
    244    fn get_protocol(&self) -> FidoProtocol {
    245        self.protocol
    246    }
    247 
    248    fn downgrade_to_ctap1(&mut self) {
    249        self.protocol = FidoProtocol::CTAP1
    250    }
    251 }
    252 
    253 impl FidoDeviceIO for TestToken {
    254    fn send_msg_cancellable<Out, Req: RequestCtap1<Output = Out> + RequestCtap2<Output = Out>>(
    255        &mut self,
    256        msg: &Req,
    257        keep_alive: &dyn Fn() -> bool,
    258    ) -> Result<Out, HIDError> {
    259        if !self.initialized() {
    260            return Err(HIDError::DeviceNotInitialized);
    261        }
    262 
    263        match self.get_protocol() {
    264            FidoProtocol::CTAP1 => self.send_ctap1_cancellable(msg, keep_alive),
    265            FidoProtocol::CTAP2 => self.send_cbor_cancellable(msg, keep_alive),
    266        }
    267    }
    268 
    269    fn send_cbor_cancellable<Req: RequestCtap2>(
    270        &mut self,
    271        msg: &Req,
    272        _keep_alive: &dyn Fn() -> bool,
    273    ) -> Result<Req::Output, HIDError> {
    274        msg.send_to_virtual_device(self)
    275    }
    276 
    277    fn send_ctap1_cancellable<Req: RequestCtap1>(
    278        &mut self,
    279        msg: &Req,
    280        _keep_alive: &dyn Fn() -> bool,
    281    ) -> Result<Req::Output, HIDError> {
    282        msg.send_to_virtual_device(self)
    283    }
    284 }
    285 
    286 impl VirtualFidoDevice for TestToken {
    287    fn check_key_handle(&self, req: &CheckKeyHandle) -> Result<(), HIDError> {
    288        let credlist = self.credentials.borrow();
    289        let req_rp_hash = req.rp.hash();
    290        let eligible_cred_iter = credlist.iter().filter(|x| x.rp.hash() == req_rp_hash);
    291        for credential in eligible_cred_iter {
    292            if req.key_handle == credential.id {
    293                return Ok(());
    294            }
    295        }
    296        Err(HIDError::DeviceError)
    297    }
    298 
    299    fn client_pin(&self, req: &ClientPIN) -> Result<ClientPinResponse, HIDError> {
    300        match req.subcommand {
    301            PINSubcommand::GetKeyAgreement => {
    302                // We don't need to save, or even know, the private key for the public key returned
    303                // here because we have access to the shared secret derived on the client side.
    304                let (_private, public) = COSEKey::generate(COSEAlgorithm::ECDH_ES_HKDF256)
    305                    .map_err(|_| HIDError::DeviceError)?;
    306                Ok(ClientPinResponse {
    307                    key_agreement: Some(public),
    308                    ..Default::default()
    309                })
    310            }
    311            PINSubcommand::GetPinUvAuthTokenUsingUvWithPermissions => {
    312                // TODO: permissions
    313                if !self.is_user_consenting || !self.is_user_verified {
    314                    return Err(HIDError::Command(CommandError::StatusCode(
    315                        StatusCode::OperationDenied,
    316                        None,
    317                    )));
    318                }
    319                let secret = match self.shared_secret.as_ref() {
    320                    Some(secret) => secret,
    321                    _ => return Err(HIDError::DeviceError),
    322                };
    323                let encrypted_pin_token = match secret.encrypt(&self.pin_token) {
    324                    Ok(token) => token,
    325                    _ => return Err(HIDError::DeviceError),
    326                };
    327                Ok(ClientPinResponse {
    328                    pin_token: Some(encrypted_pin_token),
    329                    ..Default::default()
    330                })
    331            }
    332            _ => Err(HIDError::UnsupportedCommand),
    333        }
    334    }
    335 
    336    fn get_assertion(&self, req: &GetAssertion) -> Result<Vec<GetAssertionResult>, HIDError> {
    337        // Algorithm 6.2.2 from CTAP 2.1
    338        // https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#sctn-makeCred-authnr-alg
    339 
    340        // 1. zero length pinUvAuthParam
    341        // (not implemented)
    342 
    343        // 2. Validate pinUvAuthParam
    344        // Handled by caller
    345 
    346        // 3. Initialize "uv" and "up" bits to false
    347        let mut flags = AuthenticatorDataFlags::empty();
    348 
    349        // 4. Handle all options
    350        // 4.1 and 4.2
    351        let effective_uv_opt =
    352            req.options.user_verification.unwrap_or(false) && req.pin_uv_auth_param.is_none();
    353 
    354        // 4.3
    355        if effective_uv_opt && !self.has_user_verification {
    356            return Err(HIDError::Command(CommandError::StatusCode(
    357                StatusCode::InvalidOption,
    358                None,
    359            )));
    360        }
    361 
    362        // 4.4 rk
    363        // (not implemented, we don't encode it)
    364 
    365        // 4.5
    366        let effective_up_opt = req.options.user_presence.unwrap_or(true);
    367 
    368        // 5. alwaysUv
    369        // (not implemented)
    370 
    371        // 6. User verification
    372        // TODO: Permissions, (maybe) validate pinUvAuthParam
    373        if self.is_user_verified && (effective_uv_opt || req.pin_uv_auth_param.is_some()) {
    374            flags |= AuthenticatorDataFlags::USER_VERIFIED;
    375        }
    376 
    377        // 7. Locate credentials
    378        let credlist = self.credentials.borrow();
    379        let req_rp_hash = req.rp.hash();
    380        let eligible_cred_iter = credlist.iter().filter(|x| x.rp.hash() == req_rp_hash);
    381 
    382        // 8. Set up=true if evidence of user interaction was provided in step 6.
    383        // (not applicable, we use pinUvAuthParam)
    384 
    385        // 9. User presence test
    386        if effective_up_opt {
    387            if self.is_user_consenting {
    388                flags |= AuthenticatorDataFlags::USER_PRESENT;
    389            } else {
    390                return Err(HIDError::Command(CommandError::StatusCode(
    391                    StatusCode::UpRequired,
    392                    None,
    393                )));
    394            }
    395        }
    396 
    397        // 10. Extensions
    398        let hmac_secret_response = match &req.extensions.hmac_secret {
    399            Some(HmacGetSecretOrPrf::Prf(HmacSecretExtension {
    400                salt1, salt2: None, ..
    401            })) => {
    402                // Not much point in using an actual PRF here, the identity function
    403                // will work since salt1 is guaranteed to be 32 bytes.
    404                let mut eval = vec![0u8; 32];
    405                eval[..].copy_from_slice(salt1);
    406                self.get_shared_secret()
    407                    .map(|secret| secret.encrypt(&eval).ok())
    408                    .flatten()
    409            }
    410            Some(HmacGetSecretOrPrf::Prf(HmacSecretExtension {
    411                salt1,
    412                salt2: Some(salt2),
    413                ..
    414            })) => {
    415                // Likewise, the identity function is fine for tests.
    416                let mut eval = vec![0u8; 64];
    417                eval[0..32].copy_from_slice(salt1);
    418                eval[32..64].copy_from_slice(salt2);
    419                self.get_shared_secret()
    420                    .map(|secret| secret.encrypt(&eval).ok())
    421                    .flatten()
    422            }
    423            _ => None,
    424        };
    425 
    426        let mut assertions: Vec<GetAssertionResult> = vec![];
    427        if !req.allow_list.is_empty() {
    428            // 11. Non-discoverable credential case
    429            // return at most one assertion matching an allowed credential ID
    430            for credential in eligible_cred_iter {
    431                if !self.is_user_verified
    432                    && credential.credential_protection_policy
    433                        == CredentialProtectionPolicy::UserVerificationRequired
    434                {
    435                    // Enforce the credential protection policy given that we have an allow list.
    436                    continue;
    437                }
    438                if req.allow_list.iter().any(|x| x.id == credential.id) {
    439                    let mut assertion: GetAssertionResponse =
    440                        credential.assert(&req.client_data_hash, flags)?;
    441                    if req.allow_list.len() == 1
    442                        && self.max_supported_version() == AuthenticatorVersion::FIDO_2_0
    443                    {
    444                        // CTAP 2.0 authenticators are allowed to omit the credential ID in the
    445                        // response if the allow list contains exactly one entry. This behavior is
    446                        // a common source of bugs, e.g. Bug 1864504, so we'll exercise it here.
    447                        assertion.credentials = None;
    448                    }
    449                    assertion.auth_data.extensions = Extension::default();
    450                    assertion.auth_data.extensions.hmac_secret = match &hmac_secret_response {
    451                        Some(resp) => Some(HmacSecretResponse::Secret(resp.clone())),
    452                        None => None,
    453                    };
    454                    assertions.push(GetAssertionResult {
    455                        assertion: assertion.into(),
    456                        attachment: AuthenticatorAttachment::Unknown,
    457                        extensions: Default::default(),
    458                    });
    459                    break;
    460                }
    461            }
    462        } else {
    463            // 12. Discoverable credential case
    464            // return any number of assertions from credentials bound to this RP ID
    465            for credential in eligible_cred_iter.filter(|x| x.is_discoverable_credential) {
    466                if !(self.is_user_verified
    467                    || credential.credential_protection_policy
    468                        == CredentialProtectionPolicy::UserVerificationOptional)
    469                {
    470                    // Enforce the credential protection policy given that we do not have an allow list.
    471                    continue;
    472                }
    473                let mut assertion: GetAssertionResponse =
    474                    credential.assert(&req.client_data_hash, flags)?.into();
    475                assertion.auth_data.extensions = Extension::default();
    476                assertion.auth_data.extensions.hmac_secret = match &hmac_secret_response {
    477                    Some(resp) => Some(HmacSecretResponse::Secret(resp.clone())),
    478                    None => None,
    479                };
    480                assertions.push(GetAssertionResult {
    481                    assertion: assertion.into(),
    482                    attachment: AuthenticatorAttachment::Unknown,
    483                    extensions: Default::default(),
    484                });
    485            }
    486        }
    487 
    488        if assertions.is_empty() {
    489            return Err(HIDError::Command(CommandError::StatusCode(
    490                StatusCode::NoCredentials,
    491                None,
    492            )));
    493        }
    494 
    495        Ok(assertions)
    496    }
    497 
    498    fn get_info(&self) -> Result<AuthenticatorInfo, HIDError> {
    499        // This is a CTAP2.1 device with internal user verification support
    500        Ok(AuthenticatorInfo {
    501            versions: self.versions.clone(),
    502            options: AuthenticatorOptions {
    503                platform_device: self.transport == "internal",
    504                resident_key: self.has_resident_key,
    505                pin_uv_auth_token: Some(self.has_user_verification),
    506                user_verification: Some(self.has_user_verification),
    507                ..Default::default()
    508            },
    509            ..Default::default()
    510        })
    511    }
    512 
    513    fn get_version(&self, _req: &GetVersion) -> Result<U2FInfo, HIDError> {
    514        Err(HIDError::UnsupportedCommand)
    515    }
    516 
    517    fn make_credentials(&self, req: &MakeCredentials) -> Result<MakeCredentialsResult, HIDError> {
    518        // Algorithm 6.1.2 from CTAP 2.1
    519        // https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#sctn-makeCred-authnr-alg
    520 
    521        // 1. zero length pinUvAuthParam
    522        // (not implemented)
    523 
    524        // 2. Validate pinUvAuthParam
    525        // Handled by caller
    526 
    527        // 3. Validate pubKeyCredParams
    528        if !req
    529            .pub_cred_params
    530            .iter()
    531            .any(|x| x.alg == COSEAlgorithm::ES256)
    532        {
    533            return Err(HIDError::Command(CommandError::StatusCode(
    534                StatusCode::UnsupportedAlgorithm,
    535                None,
    536            )));
    537        }
    538 
    539        // 4. initialize "uv" and "up" bits to false
    540        let mut flags = AuthenticatorDataFlags::empty();
    541 
    542        // 5. process all options
    543 
    544        // 5.1 and 5.2
    545        let effective_uv_opt =
    546            req.options.user_verification.unwrap_or(false) && req.pin_uv_auth_param.is_none();
    547 
    548        // 5.3
    549        if effective_uv_opt && !self.has_user_verification {
    550            return Err(HIDError::Command(CommandError::StatusCode(
    551                StatusCode::InvalidOption,
    552                None,
    553            )));
    554        }
    555 
    556        // 5.4
    557        if req.options.resident_key.unwrap_or(false) && !self.has_resident_key {
    558            return Err(HIDError::Command(CommandError::StatusCode(
    559                StatusCode::UnsupportedOption,
    560                None,
    561            )));
    562        }
    563 
    564        // 5.6 and 5.7
    565        // Nothing to do. We don't provide a way to set up=false.
    566 
    567        // 6. alwaysUv option ID
    568        // (not implemented)
    569 
    570        // 7. and 8. makeCredUvNotRqd option ID
    571        // (not implemented)
    572 
    573        // 9. enterprise attestation
    574        // (not implemented)
    575 
    576        // 11. User verification
    577        // TODO: Permissions, (maybe) validate pinUvAuthParam
    578        if self.is_user_verified {
    579            flags |= AuthenticatorDataFlags::USER_VERIFIED;
    580        }
    581 
    582        // 12. exclude list
    583        // TODO: credProtect
    584        if req.exclude_list.iter().any(|x| self.has_credential(&x.id)) {
    585            return Err(HIDError::Command(CommandError::StatusCode(
    586                StatusCode::CredentialExcluded,
    587                None,
    588            )));
    589        }
    590 
    591        // 13. Set up=true if evidence of user interaction was provided in step 11.
    592        // (not applicable, we use pinUvAuthParam)
    593 
    594        // 14. User presence test
    595        if self.is_user_consenting {
    596            flags |= AuthenticatorDataFlags::USER_PRESENT;
    597        } else {
    598            return Err(HIDError::Command(CommandError::StatusCode(
    599                StatusCode::UpRequired,
    600                None,
    601            )));
    602        }
    603 
    604        // 15. process extensions
    605        let mut extensions = Extension::default();
    606        if req.extensions.min_pin_length == Some(true) {
    607            // a real authenticator would
    608            //  1) return an actual minimum pin length, and
    609            //  2) check the RP ID against an allowlist before providing any data
    610            extensions.min_pin_length = Some(4);
    611        }
    612 
    613        extensions.cred_protect = req.extensions.cred_protect;
    614        if let Some(req_hmac_or_prf) = &req.extensions.hmac_secret {
    615            match req_hmac_or_prf {
    616                HmacCreateSecretOrPrf::HmacCreateSecret(true) | HmacCreateSecretOrPrf::Prf => {
    617                    extensions.hmac_secret = Some(HmacSecretResponse::Confirmed(true));
    618                }
    619                _ => (),
    620            }
    621        }
    622 
    623        if extensions.has_some() {
    624            flags |= AuthenticatorDataFlags::EXTENSION_DATA;
    625        }
    626 
    627        // 16. Generate a new credential.
    628        let (private, public) =
    629            COSEKey::generate(COSEAlgorithm::ES256).map_err(|_| HIDError::DeviceError)?;
    630        let counter = 0;
    631 
    632        // 17. and 18. Store credential
    633        //
    634        // All of the credentials that we create are "resident"---we store the private key locally,
    635        // and use a random value for the credential ID. The `req.options.resident_key` field
    636        // determines whether we make the credential "discoverable".
    637        let mut id = [0u8; 32];
    638        thread_rng().fill_bytes(&mut id);
    639        self.insert_credential(
    640            &id,
    641            &private,
    642            &req.rp,
    643            req.options.resident_key.unwrap_or(false),
    644            &req.user.clone().unwrap_or_default().id,
    645            counter,
    646            req.extensions
    647                .cred_protect
    648                .unwrap_or(CredentialProtectionPolicy::UserVerificationOptional),
    649        );
    650 
    651        // 19. Generate attestation statement
    652        flags |= AuthenticatorDataFlags::ATTESTED;
    653 
    654        let auth_data = AuthenticatorData {
    655            rp_id_hash: req.rp.hash(),
    656            flags,
    657            counter,
    658            credential_data: Some(AttestedCredentialData {
    659                aaguid: VIRTUAL_TOKEN_AAGUID,
    660                credential_id: id.to_vec(),
    661                credential_public_key: public,
    662            }),
    663            extensions,
    664        };
    665 
    666        let mut data = auth_data.to_vec();
    667        data.extend_from_slice(req.client_data_hash.as_ref());
    668 
    669        let sig = ecdsa_p256_sha256_sign_raw(&private, &data).or(Err(HIDError::DeviceError))?;
    670 
    671        let att_stmt = AttestationStatement::Packed(AttestationStatementPacked {
    672            alg: COSEAlgorithm::ES256,
    673            sig: sig.as_slice().into(),
    674            attestation_cert: vec![],
    675        });
    676 
    677        let result = MakeCredentialsResult {
    678            attachment: AuthenticatorAttachment::Unknown,
    679            att_obj: AttestationObject {
    680                att_stmt,
    681                auth_data,
    682            },
    683            extensions: Default::default(),
    684        };
    685        Ok(result)
    686    }
    687 
    688    fn reset(&self, _req: &Reset) -> Result<(), HIDError> {
    689        Err(HIDError::UnsupportedCommand)
    690    }
    691 
    692    fn selection(&self, _req: &Selection) -> Result<(), HIDError> {
    693        Err(HIDError::UnsupportedCommand)
    694    }
    695 }
    696 
    697 #[xpcom(implement(nsICredentialParameters), atomic)]
    698 struct CredentialParameters {
    699    credential_id: Vec<u8>,
    700    is_resident_credential: bool,
    701    rp_id: String,
    702    private_key: Vec<u8>,
    703    user_handle: Vec<u8>,
    704    sign_count: u32,
    705 }
    706 
    707 impl CredentialParameters {
    708    xpcom_method!(get_credential_id => GetCredentialId() -> nsACString);
    709    fn get_credential_id(&self) -> Result<nsCString, nsresult> {
    710        Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD
    711            .encode(&self.credential_id)
    712            .into())
    713    }
    714 
    715    xpcom_method!(get_is_resident_credential => GetIsResidentCredential() -> bool);
    716    fn get_is_resident_credential(&self) -> Result<bool, nsresult> {
    717        Ok(self.is_resident_credential)
    718    }
    719 
    720    xpcom_method!(get_rp_id => GetRpId() -> nsACString);
    721    fn get_rp_id(&self) -> Result<nsCString, nsresult> {
    722        Ok(nsCString::from(&self.rp_id))
    723    }
    724 
    725    xpcom_method!(get_private_key => GetPrivateKey() -> nsACString);
    726    fn get_private_key(&self) -> Result<nsCString, nsresult> {
    727        Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD
    728            .encode(&self.private_key)
    729            .into())
    730    }
    731 
    732    xpcom_method!(get_user_handle => GetUserHandle() -> nsACString);
    733    fn get_user_handle(&self) -> Result<nsCString, nsresult> {
    734        Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD
    735            .encode(&self.user_handle)
    736            .into())
    737    }
    738 
    739    xpcom_method!(get_sign_count => GetSignCount() -> u32);
    740    fn get_sign_count(&self) -> Result<u32, nsresult> {
    741        Ok(self.sign_count)
    742    }
    743 }
    744 
    745 #[xpcom(implement(nsIWebAuthnAutoFillEntry), atomic)]
    746 struct WebAuthnAutoFillEntry {
    747    rp: String,
    748    credential_id: Vec<u8>,
    749 }
    750 
    751 impl WebAuthnAutoFillEntry {
    752    xpcom_method!(get_provider => GetProvider() -> u8);
    753    fn get_provider(&self) -> Result<u8, nsresult> {
    754        Ok(nsIWebAuthnAutoFillEntry::PROVIDER_TEST_TOKEN)
    755    }
    756 
    757    xpcom_method!(get_user_name => GetUserName() -> nsAString);
    758    fn get_user_name(&self) -> Result<nsString, nsresult> {
    759        Ok(nsString::from("Test User"))
    760    }
    761 
    762    xpcom_method!(get_rp_id => GetRpId() -> nsAString);
    763    fn get_rp_id(&self) -> Result<nsString, nsresult> {
    764        Ok(nsString::from(&self.rp))
    765    }
    766 
    767    xpcom_method!(get_credential_id => GetCredentialId() -> ThinVec<u8>);
    768    fn get_credential_id(&self) -> Result<ThinVec<u8>, nsresult> {
    769        Ok(self.credential_id.as_slice().into())
    770    }
    771 }
    772 
    773 #[derive(Default)]
    774 pub(crate) struct TestTokenManager {
    775    state: Arc<Mutex<HashMap<String, TestToken>>>,
    776 }
    777 
    778 impl TestTokenManager {
    779    pub fn new() -> Self {
    780        Default::default()
    781    }
    782 
    783    pub fn add_virtual_authenticator(
    784        &self,
    785        protocol: AuthenticatorVersion,
    786        transport: String,
    787        has_resident_key: bool,
    788        has_user_verification: bool,
    789        is_user_consenting: bool,
    790        is_user_verified: bool,
    791    ) -> Result<String, nsresult> {
    792        let mut guard = self.state.lock().map_err(|_| NS_ERROR_FAILURE)?;
    793        let token = TestToken::new(
    794            vec![protocol],
    795            transport,
    796            has_resident_key,
    797            has_user_verification,
    798            is_user_consenting,
    799            is_user_verified,
    800        );
    801        loop {
    802            let mut id = [0u8; 32];
    803            thread_rng().fill_bytes(&mut id);
    804            let id = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&id);
    805            match guard.deref_mut().entry(id.clone()) {
    806                Entry::Occupied(_) => continue,
    807                Entry::Vacant(v) => {
    808                    v.insert(token);
    809                    return Ok(id);
    810                }
    811            };
    812        }
    813    }
    814 
    815    pub fn remove_virtual_authenticator(&self, authenticator_id: &str) -> Result<(), nsresult> {
    816        let mut guard = self.state.lock().map_err(|_| NS_ERROR_FAILURE)?;
    817        guard
    818            .deref_mut()
    819            .remove(authenticator_id)
    820            .ok_or(NS_ERROR_INVALID_ARG)?;
    821        Ok(())
    822    }
    823 
    824    pub fn add_credential(
    825        &self,
    826        authenticator_id: &str,
    827        id: &[u8],
    828        privkey: &[u8],
    829        user_handle: &[u8],
    830        sign_count: u32,
    831        rp_id: String,
    832        is_resident_credential: bool,
    833    ) -> Result<(), nsresult> {
    834        let mut guard = self.state.lock().map_err(|_| NS_ERROR_FAILURE)?;
    835        let token = guard
    836            .deref_mut()
    837            .get_mut(authenticator_id)
    838            .ok_or(NS_ERROR_INVALID_ARG)?;
    839        let rp = RelyingParty::from(rp_id);
    840        token.insert_credential(
    841            id,
    842            privkey,
    843            &rp,
    844            is_resident_credential,
    845            user_handle,
    846            sign_count,
    847            CredentialProtectionPolicy::UserVerificationOptional,
    848        );
    849        Ok(())
    850    }
    851 
    852    pub fn get_credentials(
    853        &self,
    854        authenticator_id: &str,
    855    ) -> Result<ThinVec<Option<RefPtr<nsICredentialParameters>>>, nsresult> {
    856        let mut guard = self.state.lock().map_err(|_| NS_ERROR_FAILURE)?;
    857        let token = guard
    858            .get_mut(authenticator_id)
    859            .ok_or(NS_ERROR_INVALID_ARG)?;
    860        let credentials = token.get_credentials();
    861        let mut credentials_parameters = ThinVec::with_capacity(credentials.len());
    862        for credential in credentials.deref() {
    863            // CTAP1 credentials are not currently supported here.
    864            let credential_parameters = CredentialParameters::allocate(InitCredentialParameters {
    865                credential_id: credential.id.clone(),
    866                is_resident_credential: credential.is_discoverable_credential,
    867                rp_id: credential.rp.id.clone(),
    868                private_key: credential.privkey.clone(),
    869                user_handle: credential.user_handle.clone(),
    870                sign_count: credential.sign_count.load(Ordering::Relaxed),
    871            })
    872            .query_interface::<nsICredentialParameters>()
    873            .ok_or(NS_ERROR_FAILURE)?;
    874            credentials_parameters.push(Some(credential_parameters));
    875        }
    876        Ok(credentials_parameters)
    877    }
    878 
    879    pub fn remove_credential(&self, authenticator_id: &str, id: &[u8]) -> Result<(), nsresult> {
    880        let mut guard = self.state.lock().map_err(|_| NS_ERROR_FAILURE)?;
    881        let token = guard
    882            .deref_mut()
    883            .get_mut(authenticator_id)
    884            .ok_or(NS_ERROR_INVALID_ARG)?;
    885        if token.delete_credential(id) {
    886            Ok(())
    887        } else {
    888            Err(NS_ERROR_INVALID_ARG)
    889        }
    890    }
    891 
    892    pub fn remove_all_credentials(&self, authenticator_id: &str) -> Result<(), nsresult> {
    893        let mut guard = self.state.lock().map_err(|_| NS_ERROR_FAILURE)?;
    894        let token = guard
    895            .deref_mut()
    896            .get_mut(authenticator_id)
    897            .ok_or(NS_ERROR_INVALID_ARG)?;
    898        token.delete_all_credentials();
    899        Ok(())
    900    }
    901 
    902    pub fn set_user_verified(
    903        &self,
    904        authenticator_id: &str,
    905        is_user_verified: bool,
    906    ) -> Result<(), nsresult> {
    907        let mut guard = self.state.lock().map_err(|_| NS_ERROR_FAILURE)?;
    908        let token = guard
    909            .deref_mut()
    910            .get_mut(authenticator_id)
    911            .ok_or(NS_ERROR_INVALID_ARG)?;
    912        token.is_user_verified = is_user_verified;
    913        Ok(())
    914    }
    915 
    916    pub fn register(
    917        &self,
    918        _timeout_ms: u64,
    919        ctap_args: RegisterArgs,
    920        status: Sender<StatusUpdate>,
    921        callback: StateCallback<Result<RegisterResult, AuthenticatorError>>,
    922    ) {
    923        if !static_prefs::pref!("security.webauth.webauthn_enable_softtoken") {
    924            return;
    925        }
    926 
    927        let state_obj = self.state.clone();
    928 
    929        // Registration doesn't currently block, but it might in a future version, so we run it on
    930        // a background thread.
    931        let _ = RunnableBuilder::new("TestTokenManager::register", move || {
    932            // TODO(Bug 1854278) We should actually run one thread per token here
    933            // and attempt to fulfill this request in parallel.
    934            for token in state_obj.lock().unwrap().values_mut() {
    935                let _ = token.init();
    936                if ctap2::register(
    937                    token,
    938                    ctap_args.clone(),
    939                    status.clone(),
    940                    callback.clone(),
    941                    &|| true,
    942                ) {
    943                    // callback was called
    944                    return;
    945                }
    946            }
    947 
    948            // Send an error, if the callback wasn't called already.
    949            callback.call(Err(AuthenticatorError::U2FToken(U2FTokenError::NotAllowed)));
    950        })
    951        .may_block(true)
    952        .dispatch_background_task();
    953    }
    954 
    955    pub fn sign(
    956        &self,
    957        _timeout_ms: u64,
    958        ctap_args: SignArgs,
    959        status: Sender<StatusUpdate>,
    960        callback: StateCallback<Result<SignResult, AuthenticatorError>>,
    961    ) {
    962        if !static_prefs::pref!("security.webauth.webauthn_enable_softtoken") {
    963            return;
    964        }
    965 
    966        let state_obj = self.state.clone();
    967 
    968        // Signing can block during signature selection, so we need to run it on a background thread.
    969        let _ = RunnableBuilder::new("TestTokenManager::sign", move || {
    970            // TODO(Bug 1854278) We should actually run one thread per token here
    971            // and attempt to fulfill this request in parallel.
    972            for token in state_obj.lock().unwrap().values_mut() {
    973                let _ = token.init();
    974                if ctap2::sign(
    975                    token,
    976                    ctap_args.clone(),
    977                    status.clone(),
    978                    callback.clone(),
    979                    &|| true,
    980                ) {
    981                    // callback was called
    982                    return;
    983                }
    984            }
    985 
    986            // Send an error, if the callback wasn't called already.
    987            callback.call(Err(AuthenticatorError::U2FToken(U2FTokenError::NotAllowed)));
    988        })
    989        .may_block(true)
    990        .dispatch_background_task();
    991    }
    992 
    993    pub fn has_user_verifying_platform_authenticator(&self) -> bool {
    994        if !static_prefs::pref!("security.webauth.webauthn_enable_softtoken") {
    995            return false;
    996        }
    997 
    998        for token in self.state.lock().unwrap().values_mut() {
    999            let _ = token.init();
   1000            if token.transport.as_str() == "internal" && token.has_user_verification {
   1001                return true;
   1002            }
   1003        }
   1004 
   1005        false
   1006    }
   1007 
   1008    pub fn get_autofill_entries(
   1009        &self,
   1010        rp_id: &str,
   1011        credential_filter: &Vec<PublicKeyCredentialDescriptor>,
   1012    ) -> Result<ThinVec<Option<RefPtr<nsIWebAuthnAutoFillEntry>>>, nsresult> {
   1013        let guard = self.state.lock().map_err(|_| NS_ERROR_FAILURE)?;
   1014        let mut entries = ThinVec::new();
   1015 
   1016        for token in guard.values() {
   1017            let credentials = token.get_credentials();
   1018            for credential in credentials.deref() {
   1019                // The relying party ID must match.
   1020                if !rp_id.eq(&credential.rp.id) {
   1021                    continue;
   1022                }
   1023                // Only discoverable credentials are admissible.
   1024                if !credential.is_discoverable_credential {
   1025                    continue;
   1026                }
   1027                // Only credentials listed in the credential filter (if it is
   1028                // non-empty) are admissible.
   1029                if credential_filter.len() > 0
   1030                    && credential_filter
   1031                        .iter()
   1032                        .find(|cred| cred.id == credential.id)
   1033                        .is_none()
   1034                {
   1035                    continue;
   1036                }
   1037                let entry = WebAuthnAutoFillEntry::allocate(InitWebAuthnAutoFillEntry {
   1038                    rp: credential.rp.id.clone(),
   1039                    credential_id: credential.id.clone(),
   1040                })
   1041                .query_interface::<nsIWebAuthnAutoFillEntry>()
   1042                .ok_or(NS_ERROR_FAILURE)?;
   1043                entries.push(Some(entry));
   1044            }
   1045        }
   1046        Ok(entries)
   1047    }
   1048 }