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
This commit is contained in:
@@ -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"]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -119,6 +119,7 @@ pub enum PinPermission {
|
||||
CredentialManagement = 0x04,
|
||||
_BioEnrollment = 0x08,
|
||||
LargeBlobWrite = 0x10,
|
||||
#[cfg(feature = "config_command")]
|
||||
AuthenticatorConfiguration = 0x20,
|
||||
}
|
||||
|
||||
|
||||
@@ -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<cbor::Value> 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<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "config_command")]
|
||||
impl TryFrom<cbor::Value> 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,
|
||||
|
||||
@@ -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<E: Env> CtapState<E> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ pub enum ResponseData {
|
||||
AuthenticatorCredentialManagement(Option<AuthenticatorCredentialManagementResponse>),
|
||||
AuthenticatorSelection,
|
||||
AuthenticatorLargeBlobs(Option<AuthenticatorLargeBlobsResponse>),
|
||||
#[cfg(feature = "config_command")]
|
||||
AuthenticatorConfig,
|
||||
}
|
||||
|
||||
@@ -51,6 +52,7 @@ impl From<ResponseData> for Option<cbor::Value> {
|
||||
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<cbor::Value> = ResponseData::AuthenticatorConfig.into();
|
||||
assert_eq!(response_cbor, None);
|
||||
|
||||
@@ -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<Option<[u8; PIN_AUTH_LENGTH]>, 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<Option<u8>, 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<u8, Ctap2StatusCode> {
|
||||
}
|
||||
|
||||
/// 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<Vec<String>, 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<String>,
|
||||
@@ -428,6 +434,7 @@ pub fn has_force_pin_change(env: &mut impl Env) -> Result<bool, Ctap2StatusCode>
|
||||
}
|
||||
|
||||
/// 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<bool, Ctap2StatusCod
|
||||
}
|
||||
|
||||
/// Marks enterprise attestation as enabled.
|
||||
#[cfg(feature = "config_command")]
|
||||
pub fn enable_enterprise_attestation(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
|
||||
if env
|
||||
.attestation_store()
|
||||
@@ -469,6 +477,7 @@ pub fn has_always_uv(env: &mut impl Env) -> Result<bool, Ctap2StatusCode> {
|
||||
}
|
||||
|
||||
/// 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<Vec<String>> {
|
||||
}
|
||||
|
||||
/// Serializes a list of RP IDs to storage representation.
|
||||
#[cfg(feature = "config_command")]
|
||||
fn serialize_min_pin_length_rp_ids(rp_ids: Vec<String>) -> Result<Vec<u8>, Ctap2StatusCode> {
|
||||
let mut data = Vec::new();
|
||||
super::cbor_write(cbor_array_vec!(rp_ids), &mut data)?;
|
||||
|
||||
55
tools/authenticator_config.py
Executable file
55
tools/authenticator_config.py
Executable file
@@ -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())
|
||||
Reference in New Issue
Block a user