From 6fb7e194eb72407916627a510704168828e9f07d Mon Sep 17 00:00:00 2001 From: kaczmarczyck <43844792+kaczmarczyck@users.noreply.github.com> Date: Mon, 8 May 2023 15:45:32 +0200 Subject: [PATCH] Compile flag for AuthenticatorConfig (#628) * Adds a compile flag for AuthenticatorConfig The command can be disabled for authenticators that don't want users to change their configuration. * adds tool for calling Config * std now implies config_command * removes obsolete comment --- Cargo.toml | 3 +- deploy.py | 9 +++- libraries/opensk/Cargo.toml | 4 +- libraries/opensk/src/ctap/client_pin.rs | 1 + libraries/opensk/src/ctap/command.rs | 18 +++++--- libraries/opensk/src/ctap/mod.rs | 3 ++ libraries/opensk/src/ctap/response.rs | 3 ++ libraries/opensk/src/ctap/storage.rs | 10 +++++ tools/authenticator_config.py | 55 +++++++++++++++++++++++++ 9 files changed, 98 insertions(+), 8 deletions(-) create mode 100755 tools/authenticator_config.py diff --git a/Cargo.toml b/Cargo.toml index 6663961..f4c451d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ libtock_drivers = { path = "third_party/libtock-drivers" } libtock_console = { path = "third_party/libtock-rs/apis/console" } libtock_leds = { path = "third_party/libtock-rs/apis/leds" } lang_items = { path = "third_party/lang-items" } -opensk = { path = "libraries/opensk" } +opensk = { path = "libraries/opensk", default-features = false } sk-cbor = { path = "libraries/cbor" } crypto = { path = "libraries/crypto" } persistent_store = { path = "libraries/persistent_store" } @@ -33,6 +33,7 @@ rand_core = "0.6.4" ed25519-compact = { version = "1", default-features = false, optional = true } [features] +config_command = ["opensk/config_command"] debug_allocations = ["lang_items/debug_allocations"] debug_ctap = ["libtock_drivers/debug_ctap", "opensk/debug_ctap"] panic_console = ["lang_items/panic_console"] diff --git a/deploy.py b/deploy.py index 68665cb..e198c49 100755 --- a/deploy.py +++ b/deploy.py @@ -1091,6 +1091,13 @@ if __name__ == "__main__": help=("Compiles the OpenSK application without backward compatible " "support for U2F/CTAP1 protocol."), ) + main_parser.add_argument( + "--no-config-command", + action=RemoveConstAction, + const="config_command", + dest="features", + help=("Removes the AuthenticatorConfig command."), + ) main_parser.add_argument( "--rust-crypto", action="append_const", @@ -1174,7 +1181,7 @@ if __name__ == "__main__": help=("Firmware version that is built."), ) - main_parser.set_defaults(features=["with_ctap1"]) + main_parser.set_defaults(features=["with_ctap1", "config_command"]) # Start parsing to know if we're going to list things or not. partial_args, _ = main_parser.parse_known_args() diff --git a/libraries/opensk/Cargo.toml b/libraries/opensk/Cargo.toml index 60a9d2a..707af94 100644 --- a/libraries/opensk/Cargo.toml +++ b/libraries/opensk/Cargo.toml @@ -36,8 +36,10 @@ features = ["alloc", "ecdh", "ecdsa"] optional = true [features] +default = ["config_command", "with_ctap1"] +config_command = [] debug_ctap = [] -std = ["crypto/std", "persistent_store/std", "rand/std_rng"] +std = ["crypto/std", "persistent_store/std", "rand/std_rng", "config_command"] with_ctap1 = ["crypto/with_ctap1"] vendor_hid = [] fuzz = ["arbitrary", "std"] diff --git a/libraries/opensk/src/ctap/client_pin.rs b/libraries/opensk/src/ctap/client_pin.rs index f3ccff9..4b19d83 100644 --- a/libraries/opensk/src/ctap/client_pin.rs +++ b/libraries/opensk/src/ctap/client_pin.rs @@ -119,6 +119,7 @@ pub enum PinPermission { CredentialManagement = 0x04, _BioEnrollment = 0x08, LargeBlobWrite = 0x10, + #[cfg(feature = "config_command")] AuthenticatorConfiguration = 0x20, } diff --git a/libraries/opensk/src/ctap/command.rs b/libraries/opensk/src/ctap/command.rs index b3a0e9d..9bc6fbc 100644 --- a/libraries/opensk/src/ctap/command.rs +++ b/libraries/opensk/src/ctap/command.rs @@ -15,12 +15,14 @@ use super::cbor_read; use super::data_formats::{ extract_array, extract_byte_string, extract_map, extract_text_string, extract_unsigned, - ok_or_missing, ClientPinSubCommand, ConfigSubCommand, ConfigSubCommandParams, CoseKey, - CredentialManagementSubCommand, CredentialManagementSubCommandParameters, - GetAssertionExtensions, GetAssertionOptions, MakeCredentialExtensions, MakeCredentialOptions, - PinUvAuthProtocol, PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, - PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity, SetMinPinLengthParams, + ok_or_missing, ClientPinSubCommand, CoseKey, CredentialManagementSubCommand, + CredentialManagementSubCommandParameters, GetAssertionExtensions, GetAssertionOptions, + MakeCredentialExtensions, MakeCredentialOptions, PinUvAuthProtocol, + PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialRpEntity, + PublicKeyCredentialUserEntity, }; +#[cfg(feature = "config_command")] +use super::data_formats::{ConfigSubCommand, ConfigSubCommandParams, SetMinPinLengthParams}; use super::status_code::Ctap2StatusCode; use alloc::string::String; use alloc::vec::Vec; @@ -46,6 +48,7 @@ pub enum Command { AuthenticatorCredentialManagement(AuthenticatorCredentialManagementParameters), AuthenticatorSelection, AuthenticatorLargeBlobs(AuthenticatorLargeBlobsParameters), + #[cfg(feature = "config_command")] AuthenticatorConfig(AuthenticatorConfigParameters), } @@ -61,6 +64,7 @@ impl Command { const AUTHENTICATOR_CREDENTIAL_MANAGEMENT: u8 = 0x0A; const AUTHENTICATOR_SELECTION: u8 = 0x0B; const AUTHENTICATOR_LARGE_BLOBS: u8 = 0x0C; + #[cfg(feature = "config_command")] const AUTHENTICATOR_CONFIG: u8 = 0x0D; const _AUTHENTICATOR_VENDOR_FIRST: u8 = 0x40; // This commands is the same as AUTHENTICATOR_CREDENTIAL_MANAGEMENT but is duplicated as a @@ -124,6 +128,7 @@ impl Command { AuthenticatorLargeBlobsParameters::try_from(decoded_cbor)?, )) } + #[cfg(feature = "config_command")] Command::AUTHENTICATOR_CONFIG => { let decoded_cbor = cbor_read(&bytes[1..])?; Ok(Command::AuthenticatorConfig( @@ -429,6 +434,7 @@ impl TryFrom for AuthenticatorLargeBlobsParameters { } } +#[cfg(feature = "config_command")] #[derive(Debug, PartialEq, Eq)] pub struct AuthenticatorConfigParameters { pub sub_command: ConfigSubCommand, @@ -437,6 +443,7 @@ pub struct AuthenticatorConfigParameters { pub pin_uv_auth_param: Option>, } +#[cfg(feature = "config_command")] impl TryFrom for AuthenticatorConfigParameters { type Error = Ctap2StatusCode; @@ -684,6 +691,7 @@ mod test { } #[test] + #[cfg(feature = "config_command")] fn test_from_cbor_config_parameters() { let cbor_value = cbor_map! { 0x01 => ConfigSubCommand::SetMinPinLength as u64, diff --git a/libraries/opensk/src/ctap/mod.rs b/libraries/opensk/src/ctap/mod.rs index 177f5d7..a1a5ab0 100644 --- a/libraries/opensk/src/ctap/mod.rs +++ b/libraries/opensk/src/ctap/mod.rs @@ -15,6 +15,7 @@ pub mod apdu; mod client_pin; pub mod command; +#[cfg(feature = "config_command")] mod config_command; mod credential_management; pub mod crypto_wrapper; @@ -39,6 +40,7 @@ use self::client_pin::{ClientPin, PinPermission}; use self::command::{ AuthenticatorGetAssertionParameters, AuthenticatorMakeCredentialParameters, Command, }; +#[cfg(feature = "config_command")] use self::config_command::process_config; use self::credential_management::process_credential_management; use self::data_formats::{ @@ -655,6 +657,7 @@ impl CtapState { self.large_blobs .process_command(env, &mut self.client_pin, params) } + #[cfg(feature = "config_command")] Command::AuthenticatorConfig(params) => { process_config(env, &mut self.client_pin, params) } diff --git a/libraries/opensk/src/ctap/response.rs b/libraries/opensk/src/ctap/response.rs index ef7e225..3ee1adc 100644 --- a/libraries/opensk/src/ctap/response.rs +++ b/libraries/opensk/src/ctap/response.rs @@ -36,6 +36,7 @@ pub enum ResponseData { AuthenticatorCredentialManagement(Option), AuthenticatorSelection, AuthenticatorLargeBlobs(Option), + #[cfg(feature = "config_command")] AuthenticatorConfig, } @@ -51,6 +52,7 @@ impl From for Option { ResponseData::AuthenticatorCredentialManagement(data) => data.map(|d| d.into()), ResponseData::AuthenticatorSelection => None, ResponseData::AuthenticatorLargeBlobs(data) => data.map(|d| d.into()), + #[cfg(feature = "config_command")] ResponseData::AuthenticatorConfig => None, } } @@ -586,6 +588,7 @@ mod test { } #[test] + #[cfg(feature = "config_command")] fn test_config_into_cbor() { let response_cbor: Option = ResponseData::AuthenticatorConfig.into(); assert_eq!(response_cbor, None); diff --git a/libraries/opensk/src/ctap/storage.rs b/libraries/opensk/src/ctap/storage.rs index a781f79..da9ea60 100644 --- a/libraries/opensk/src/ctap/storage.rs +++ b/libraries/opensk/src/ctap/storage.rs @@ -14,6 +14,7 @@ mod key; +#[cfg(feature = "config_command")] use crate::api::attestation_store::{self, AttestationStore}; use crate::api::customization::Customization; use crate::api::key_store::KeyStore; @@ -31,6 +32,7 @@ use arrayref::array_ref; use core::cmp; use core::convert::TryInto; use persistent_store::{fragment, StoreUpdate}; +#[cfg(feature = "config_command")] use sk_cbor::cbor_array_vec; /// Wrapper for PIN properties. @@ -39,6 +41,7 @@ struct PinProperties { hash: [u8; PIN_AUTH_LENGTH], /// Length of the current PIN in code points. + #[cfg_attr(not(feature = "config_command"), allow(dead_code))] code_point_length: u8, } @@ -268,6 +271,7 @@ pub fn pin_hash(env: &mut impl Env) -> Result, Cta } /// Returns the length of the currently set PIN if defined. +#[cfg(feature = "config_command")] pub fn pin_code_point_length(env: &mut impl Env) -> Result, Ctap2StatusCode> { Ok(pin_properties(env)?.map(|p| p.code_point_length)) } @@ -328,6 +332,7 @@ pub fn min_pin_length(env: &mut impl Env) -> Result { } /// Sets the minimum PIN length. +#[cfg(feature = "config_command")] pub fn set_min_pin_length(env: &mut impl Env, min_pin_length: u8) -> Result<(), Ctap2StatusCode> { Ok(env.store().insert(key::MIN_PIN_LENGTH, &[min_pin_length])?) } @@ -344,6 +349,7 @@ pub fn min_pin_length_rp_ids(env: &mut impl Env) -> Result, Ctap2Sta } /// Sets the list of RP IDs that are used to check if reading the minimum PIN length is allowed. +#[cfg(feature = "config_command")] pub fn set_min_pin_length_rp_ids( env: &mut impl Env, min_pin_length_rp_ids: Vec, @@ -428,6 +434,7 @@ pub fn has_force_pin_change(env: &mut impl Env) -> Result } /// Marks the PIN as outdated with respect to the new PIN policy. +#[cfg(feature = "config_command")] pub fn force_pin_change(env: &mut impl Env) -> Result<(), Ctap2StatusCode> { Ok(env.store().insert(key::FORCE_PIN_CHANGE, &[])?) } @@ -442,6 +449,7 @@ pub fn enterprise_attestation(env: &mut impl Env) -> Result Result<(), Ctap2StatusCode> { if env .attestation_store() @@ -469,6 +477,7 @@ pub fn has_always_uv(env: &mut impl Env) -> Result { } /// Enables alwaysUv, when disabled, and vice versa. +#[cfg(feature = "config_command")] pub fn toggle_always_uv(env: &mut impl Env) -> Result<(), Ctap2StatusCode> { if env.customization().enforce_always_uv() { return Err(Ctap2StatusCode::CTAP2_ERR_OPERATION_DENIED); @@ -588,6 +597,7 @@ fn deserialize_min_pin_length_rp_ids(data: &[u8]) -> Option> { } /// Serializes a list of RP IDs to storage representation. +#[cfg(feature = "config_command")] fn serialize_min_pin_length_rp_ids(rp_ids: Vec) -> Result, Ctap2StatusCode> { let mut data = Vec::new(); super::cbor_write(cbor_array_vec!(rp_ids), &mut data)?; diff --git a/tools/authenticator_config.py b/tools/authenticator_config.py new file mode 100755 index 0000000..ea137fe --- /dev/null +++ b/tools/authenticator_config.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# Copyright 2023 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. +# Lint as: python3 +"""Tool that sends a config command to OpenSK.""" + +from __future__ import absolute_import, division, print_function + +import argparse +import colorama +from fido2 import hid +from fido2.ctap2 import Config +import uuid + +from tools.configure import fatal, get_opensk_devices, info + + +def main(args): + colorama.init() + + devices = get_opensk_devices(False) + if not devices: + fatal("No devices found.") + + for authenticator in devices: + if authenticator.device.capabilities & hid.CAPABILITY.WINK: + authenticator.device.wink() + aaguid = uuid.UUID(bytes=authenticator.get_info().aaguid) + info(f"Config of device AAGUID {aaguid} ({authenticator.device}).") + + config = Config(authenticator) + if args.ep: + info("Enable EP...") + config.enable_enterprise_attestation() + if args.always_uv: + info("Toggle AlwaysUv...") + config.toggle_always_uv() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--ep", action=argparse.BooleanOptionalAction) + parser.add_argument("--always-uv", action=argparse.BooleanOptionalAction) + main(parser.parse_args())