Merge pull request #252 from kaczmarczyck/remove-flag-2-1
Remove CTAP 2.1 flag
This commit is contained in:
@@ -27,7 +27,6 @@ panic_console = ["lang_items/panic_console"]
|
|||||||
std = ["cbor/std", "crypto/std", "crypto/derive_debug", "lang_items/std", "persistent_store/std"]
|
std = ["cbor/std", "crypto/std", "crypto/derive_debug", "lang_items/std", "persistent_store/std"]
|
||||||
verbose = ["debug_ctap", "libtock_drivers/verbose_usb"]
|
verbose = ["debug_ctap", "libtock_drivers/verbose_usb"]
|
||||||
with_ctap1 = ["crypto/with_ctap1"]
|
with_ctap1 = ["crypto/with_ctap1"]
|
||||||
with_ctap2_1 = []
|
|
||||||
with_nfc = ["libtock_drivers/with_nfc"]
|
with_nfc = ["libtock_drivers/with_nfc"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -24,15 +24,14 @@ few limitations:
|
|||||||
|
|
||||||
### FIDO2
|
### FIDO2
|
||||||
|
|
||||||
Although we tested and implemented our firmware based on the published
|
The stable branch implements the published
|
||||||
[CTAP2.0 specifications](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html),
|
[CTAP2.0 specifications](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html),
|
||||||
our implementation was not reviewed nor officially tested and doesn't claim to
|
but our implementation was not reviewed nor officially tested and doesn't claim
|
||||||
be FIDO Certified.
|
to be FIDO Certified. It already contains some preview features of 2.1, that you
|
||||||
We started adding features of the upcoming next version of the
|
can try by adding the flag `--ctap2.1` to the deploy command.
|
||||||
[CTAP2.1 specifications](https://fidoalliance.org/specs/fido2/fido-client-to-authenticator-protocol-v2.1-rd-20191217.html).
|
The develop branch offers only the
|
||||||
The development is currently between 2.0 and 2.1, with updates hidden behind
|
[CTAP2.1 specifications](https://fidoalliance.org/specs/fido-v2.1-rd-20201208/fido-client-to-authenticator-protocol-v2.1-rd-20201208.html).
|
||||||
a feature flag.
|
The new features of 2.1 are currently work in progress.
|
||||||
Please add the flag `--ctap2.1` to the deploy command to include them.
|
|
||||||
|
|
||||||
### Cryptography
|
### Cryptography
|
||||||
|
|
||||||
|
|||||||
10
deploy.py
10
deploy.py
@@ -881,14 +881,6 @@ if __name__ == "__main__":
|
|||||||
help=("Compiles the OpenSK application without backward compatible "
|
help=("Compiles the OpenSK application without backward compatible "
|
||||||
"support for U2F/CTAP1 protocol."),
|
"support for U2F/CTAP1 protocol."),
|
||||||
)
|
)
|
||||||
main_parser.add_argument(
|
|
||||||
"--ctap2.1",
|
|
||||||
action="append_const",
|
|
||||||
const="with_ctap2_1",
|
|
||||||
dest="features",
|
|
||||||
help=("Compiles the OpenSK application with backward compatible "
|
|
||||||
"support for CTAP2.1 protocol."),
|
|
||||||
)
|
|
||||||
main_parser.add_argument(
|
main_parser.add_argument(
|
||||||
"--nfc",
|
"--nfc",
|
||||||
action="append_const",
|
action="append_const",
|
||||||
@@ -947,7 +939,7 @@ if __name__ == "__main__":
|
|||||||
dest="application",
|
dest="application",
|
||||||
action="store_const",
|
action="store_const",
|
||||||
const="store_latency",
|
const="store_latency",
|
||||||
help=("Compiles and installs the store_latency example which print "
|
help=("Compiles and installs the store_latency example which prints "
|
||||||
"latency statistics of the persistent store library."))
|
"latency statistics of the persistent store library."))
|
||||||
apps_group.add_argument(
|
apps_group.add_argument(
|
||||||
"--erase_storage",
|
"--erase_storage",
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ pub enum Command {
|
|||||||
AuthenticatorClientPin(AuthenticatorClientPinParameters),
|
AuthenticatorClientPin(AuthenticatorClientPinParameters),
|
||||||
AuthenticatorReset,
|
AuthenticatorReset,
|
||||||
AuthenticatorGetNextAssertion,
|
AuthenticatorGetNextAssertion,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
AuthenticatorSelection,
|
AuthenticatorSelection,
|
||||||
// TODO(kaczmarczyck) implement FIDO 2.1 commands (see below consts)
|
// TODO(kaczmarczyck) implement FIDO 2.1 commands (see below consts)
|
||||||
// Vendor specific commands
|
// Vendor specific commands
|
||||||
@@ -111,7 +110,6 @@ impl Command {
|
|||||||
// Parameters are ignored.
|
// Parameters are ignored.
|
||||||
Ok(Command::AuthenticatorGetNextAssertion)
|
Ok(Command::AuthenticatorGetNextAssertion)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
Command::AUTHENTICATOR_SELECTION => {
|
Command::AUTHENTICATOR_SELECTION => {
|
||||||
// Parameters are ignored.
|
// Parameters are ignored.
|
||||||
Ok(Command::AuthenticatorSelection)
|
Ok(Command::AuthenticatorSelection)
|
||||||
@@ -292,13 +290,9 @@ pub struct AuthenticatorClientPinParameters {
|
|||||||
pub pin_auth: Option<Vec<u8>>,
|
pub pin_auth: Option<Vec<u8>>,
|
||||||
pub new_pin_enc: Option<Vec<u8>>,
|
pub new_pin_enc: Option<Vec<u8>>,
|
||||||
pub pin_hash_enc: Option<Vec<u8>>,
|
pub pin_hash_enc: Option<Vec<u8>>,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub min_pin_length: Option<u8>,
|
pub min_pin_length: Option<u8>,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub min_pin_length_rp_ids: Option<Vec<String>>,
|
pub min_pin_length_rp_ids: Option<Vec<String>>,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub permissions: Option<u8>,
|
pub permissions: Option<u8>,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub permissions_rp_id: Option<String>,
|
pub permissions_rp_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,18 +300,6 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
|
|||||||
type Error = Ctap2StatusCode;
|
type Error = Ctap2StatusCode;
|
||||||
|
|
||||||
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
|
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
|
||||||
#[cfg(not(feature = "with_ctap2_1"))]
|
|
||||||
destructure_cbor_map! {
|
|
||||||
let {
|
|
||||||
1 => pin_protocol,
|
|
||||||
2 => sub_command,
|
|
||||||
3 => key_agreement,
|
|
||||||
4 => pin_auth,
|
|
||||||
5 => new_pin_enc,
|
|
||||||
6 => pin_hash_enc,
|
|
||||||
} = extract_map(cbor_value)?;
|
|
||||||
}
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
destructure_cbor_map! {
|
destructure_cbor_map! {
|
||||||
let {
|
let {
|
||||||
1 => pin_protocol,
|
1 => pin_protocol,
|
||||||
@@ -339,14 +321,12 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
|
|||||||
let pin_auth = pin_auth.map(extract_byte_string).transpose()?;
|
let pin_auth = pin_auth.map(extract_byte_string).transpose()?;
|
||||||
let new_pin_enc = new_pin_enc.map(extract_byte_string).transpose()?;
|
let new_pin_enc = new_pin_enc.map(extract_byte_string).transpose()?;
|
||||||
let pin_hash_enc = pin_hash_enc.map(extract_byte_string).transpose()?;
|
let pin_hash_enc = pin_hash_enc.map(extract_byte_string).transpose()?;
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
let min_pin_length = min_pin_length
|
let min_pin_length = min_pin_length
|
||||||
.map(extract_unsigned)
|
.map(extract_unsigned)
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.map(u8::try_from)
|
.map(u8::try_from)
|
||||||
.transpose()
|
.transpose()
|
||||||
.map_err(|_| Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION)?;
|
.map_err(|_| Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION)?;
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
let min_pin_length_rp_ids = match min_pin_length_rp_ids {
|
let min_pin_length_rp_ids = match min_pin_length_rp_ids {
|
||||||
Some(entry) => Some(
|
Some(entry) => Some(
|
||||||
extract_array(entry)?
|
extract_array(entry)?
|
||||||
@@ -356,14 +336,12 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
|
|||||||
),
|
),
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
// We expect a bit field of 8 bits, and drop everything else.
|
// We expect a bit field of 8 bits, and drop everything else.
|
||||||
// This means we ignore extensions in future versions.
|
// This means we ignore extensions in future versions.
|
||||||
let permissions = permissions
|
let permissions = permissions
|
||||||
.map(extract_unsigned)
|
.map(extract_unsigned)
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.map(|p| p as u8);
|
.map(|p| p as u8);
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
let permissions_rp_id = permissions_rp_id.map(extract_text_string).transpose()?;
|
let permissions_rp_id = permissions_rp_id.map(extract_text_string).transpose()?;
|
||||||
|
|
||||||
Ok(AuthenticatorClientPinParameters {
|
Ok(AuthenticatorClientPinParameters {
|
||||||
@@ -373,13 +351,9 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
|
|||||||
pin_auth,
|
pin_auth,
|
||||||
new_pin_enc,
|
new_pin_enc,
|
||||||
pin_hash_enc,
|
pin_hash_enc,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
min_pin_length,
|
min_pin_length,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
min_pin_length_rp_ids,
|
min_pin_length_rp_ids,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions,
|
permissions,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions_rp_id,
|
permissions_rp_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -560,18 +534,6 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_cbor_client_pin_parameters() {
|
fn test_from_cbor_client_pin_parameters() {
|
||||||
// TODO(kaczmarczyck) inline the #cfg when #128 is resolved:
|
|
||||||
// https://github.com/google/OpenSK/issues/128
|
|
||||||
#[cfg(not(feature = "with_ctap2_1"))]
|
|
||||||
let cbor_value = cbor_map! {
|
|
||||||
1 => 1,
|
|
||||||
2 => ClientPinSubCommand::GetPinRetries,
|
|
||||||
3 => cbor_map!{},
|
|
||||||
4 => vec! [0xBB],
|
|
||||||
5 => vec! [0xCC],
|
|
||||||
6 => vec! [0xDD],
|
|
||||||
};
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
let cbor_value = cbor_map! {
|
let cbor_value = cbor_map! {
|
||||||
1 => 1,
|
1 => 1,
|
||||||
2 => ClientPinSubCommand::GetPinRetries,
|
2 => ClientPinSubCommand::GetPinRetries,
|
||||||
@@ -594,13 +556,9 @@ mod test {
|
|||||||
pin_auth: Some(vec![0xBB]),
|
pin_auth: Some(vec![0xBB]),
|
||||||
new_pin_enc: Some(vec![0xCC]),
|
new_pin_enc: Some(vec![0xCC]),
|
||||||
pin_hash_enc: Some(vec![0xDD]),
|
pin_hash_enc: Some(vec![0xDD]),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
min_pin_length: Some(4),
|
min_pin_length: Some(4),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
min_pin_length_rp_ids: Some(vec!["example.com".to_string()]),
|
min_pin_length_rp_ids: Some(vec!["example.com".to_string()]),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions: Some(0x03),
|
permissions: Some(0x03),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions_rp_id: Some("example.com".to_string()),
|
permissions_rp_id: Some("example.com".to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -632,7 +590,6 @@ mod test {
|
|||||||
assert_eq!(command, Ok(Command::AuthenticatorGetNextAssertion));
|
assert_eq!(command, Ok(Command::AuthenticatorGetNextAssertion));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_deserialize_selection() {
|
fn test_deserialize_selection() {
|
||||||
let cbor_bytes = [Command::AUTHENTICATOR_SELECTION];
|
let cbor_bytes = [Command::AUTHENTICATOR_SELECTION];
|
||||||
|
|||||||
@@ -704,13 +704,9 @@ pub enum ClientPinSubCommand {
|
|||||||
SetPin = 0x03,
|
SetPin = 0x03,
|
||||||
ChangePin = 0x04,
|
ChangePin = 0x04,
|
||||||
GetPinToken = 0x05,
|
GetPinToken = 0x05,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
GetPinUvAuthTokenUsingUvWithPermissions = 0x06,
|
GetPinUvAuthTokenUsingUvWithPermissions = 0x06,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
GetUvRetries = 0x07,
|
GetUvRetries = 0x07,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
SetMinPinLength = 0x08,
|
SetMinPinLength = 0x08,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
GetPinUvAuthTokenUsingPinWithPermissions = 0x09,
|
GetPinUvAuthTokenUsingPinWithPermissions = 0x09,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -731,18 +727,11 @@ impl TryFrom<cbor::Value> for ClientPinSubCommand {
|
|||||||
0x03 => Ok(ClientPinSubCommand::SetPin),
|
0x03 => Ok(ClientPinSubCommand::SetPin),
|
||||||
0x04 => Ok(ClientPinSubCommand::ChangePin),
|
0x04 => Ok(ClientPinSubCommand::ChangePin),
|
||||||
0x05 => Ok(ClientPinSubCommand::GetPinToken),
|
0x05 => Ok(ClientPinSubCommand::GetPinToken),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
0x06 => Ok(ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions),
|
0x06 => Ok(ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
0x07 => Ok(ClientPinSubCommand::GetUvRetries),
|
0x07 => Ok(ClientPinSubCommand::GetUvRetries),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
0x08 => Ok(ClientPinSubCommand::SetMinPinLength),
|
0x08 => Ok(ClientPinSubCommand::SetMinPinLength),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
0x09 => Ok(ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions),
|
0x09 => Ok(ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
_ => Err(Ctap2StatusCode::CTAP2_ERR_INVALID_SUBCOMMAND),
|
_ => Err(Ctap2StatusCode::CTAP2_ERR_INVALID_SUBCOMMAND),
|
||||||
#[cfg(not(feature = "with_ctap2_1"))]
|
|
||||||
_ => Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,23 +25,19 @@ pub mod status_code;
|
|||||||
mod storage;
|
mod storage;
|
||||||
mod timed_permission;
|
mod timed_permission;
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
use self::command::MAX_CREDENTIAL_COUNT_IN_LIST;
|
|
||||||
use self::command::{
|
use self::command::{
|
||||||
AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters,
|
AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters,
|
||||||
AuthenticatorMakeCredentialParameters, AuthenticatorVendorConfigureParameters, Command,
|
AuthenticatorMakeCredentialParameters, AuthenticatorVendorConfigureParameters, Command,
|
||||||
|
MAX_CREDENTIAL_COUNT_IN_LIST,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
use self::data_formats::AuthenticatorTransport;
|
|
||||||
use self::data_formats::{
|
use self::data_formats::{
|
||||||
CredentialProtectionPolicy, GetAssertionHmacSecretInput, PackedAttestationStatement,
|
AuthenticatorTransport, CredentialProtectionPolicy, GetAssertionHmacSecretInput,
|
||||||
PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialSource,
|
PackedAttestationStatement, PublicKeyCredentialDescriptor, PublicKeyCredentialParameter,
|
||||||
PublicKeyCredentialType, PublicKeyCredentialUserEntity, SignatureAlgorithm,
|
PublicKeyCredentialSource, PublicKeyCredentialType, PublicKeyCredentialUserEntity,
|
||||||
|
SignatureAlgorithm,
|
||||||
};
|
};
|
||||||
use self::hid::ChannelID;
|
use self::hid::ChannelID;
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
use self::pin_protocol_v1::{PinPermission, PinProtocolV1};
|
||||||
use self::pin_protocol_v1::PinPermission;
|
|
||||||
use self::pin_protocol_v1::PinProtocolV1;
|
|
||||||
use self::response::{
|
use self::response::{
|
||||||
AuthenticatorGetAssertionResponse, AuthenticatorGetInfoResponse,
|
AuthenticatorGetAssertionResponse, AuthenticatorGetInfoResponse,
|
||||||
AuthenticatorMakeCredentialResponse, AuthenticatorVendorResponse, ResponseData,
|
AuthenticatorMakeCredentialResponse, AuthenticatorVendorResponse, ResponseData,
|
||||||
@@ -108,7 +104,6 @@ pub const FIDO2_VERSION_STRING: &str = "FIDO_2_0";
|
|||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
pub const U2F_VERSION_STRING: &str = "U2F_V2";
|
pub const U2F_VERSION_STRING: &str = "U2F_V2";
|
||||||
// TODO(#106) change to final string when ready
|
// TODO(#106) change to final string when ready
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub const FIDO2_1_VERSION_STRING: &str = "FIDO_2_1_PRE";
|
pub const FIDO2_1_VERSION_STRING: &str = "FIDO_2_1_PRE";
|
||||||
|
|
||||||
// We currently only support one algorithm for signatures: ES256.
|
// We currently only support one algorithm for signatures: ES256.
|
||||||
@@ -339,7 +334,6 @@ where
|
|||||||
// GetInfo does not reset stateful commands.
|
// GetInfo does not reset stateful commands.
|
||||||
(Command::AuthenticatorGetInfo, _) => (),
|
(Command::AuthenticatorGetInfo, _) => (),
|
||||||
// AuthenticatorSelection does not reset stateful commands.
|
// AuthenticatorSelection does not reset stateful commands.
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
(Command::AuthenticatorSelection, _) => (),
|
(Command::AuthenticatorSelection, _) => (),
|
||||||
(_, _) => {
|
(_, _) => {
|
||||||
self.stateful_command_type = None;
|
self.stateful_command_type = None;
|
||||||
@@ -356,7 +350,6 @@ where
|
|||||||
Command::AuthenticatorGetInfo => self.process_get_info(),
|
Command::AuthenticatorGetInfo => self.process_get_info(),
|
||||||
Command::AuthenticatorClientPin(params) => self.process_client_pin(params),
|
Command::AuthenticatorClientPin(params) => self.process_client_pin(params),
|
||||||
Command::AuthenticatorReset => self.process_reset(cid, now),
|
Command::AuthenticatorReset => self.process_reset(cid, now),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
Command::AuthenticatorSelection => self.process_selection(cid),
|
Command::AuthenticatorSelection => self.process_selection(cid),
|
||||||
// TODO(kaczmarczyck) implement FIDO 2.1 commands
|
// TODO(kaczmarczyck) implement FIDO 2.1 commands
|
||||||
// Vendor specific commands
|
// Vendor specific commands
|
||||||
@@ -484,12 +477,9 @@ where
|
|||||||
{
|
{
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
{
|
|
||||||
self.pin_protocol_v1
|
self.pin_protocol_v1
|
||||||
.has_permission(PinPermission::MakeCredential)?;
|
.has_permission(PinPermission::MakeCredential)?;
|
||||||
self.pin_protocol_v1.has_permission_for_rp_id(&rp_id)?;
|
self.pin_protocol_v1.has_permission_for_rp_id(&rp_id)?;
|
||||||
}
|
|
||||||
UP_FLAG | UV_FLAG | AT_FLAG | ed_flag
|
UP_FLAG | UV_FLAG | AT_FLAG | ed_flag
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@@ -738,12 +728,9 @@ where
|
|||||||
{
|
{
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
{
|
|
||||||
self.pin_protocol_v1
|
self.pin_protocol_v1
|
||||||
.has_permission(PinPermission::GetAssertion)?;
|
.has_permission(PinPermission::GetAssertion)?;
|
||||||
self.pin_protocol_v1.has_permission_for_rp_id(&rp_id)?;
|
self.pin_protocol_v1.has_permission_for_rp_id(&rp_id)?;
|
||||||
}
|
|
||||||
UV_FLAG
|
UV_FLAG
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@@ -851,7 +838,6 @@ where
|
|||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
String::from(U2F_VERSION_STRING),
|
String::from(U2F_VERSION_STRING),
|
||||||
String::from(FIDO2_VERSION_STRING),
|
String::from(FIDO2_VERSION_STRING),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
String::from(FIDO2_1_VERSION_STRING),
|
String::from(FIDO2_1_VERSION_STRING),
|
||||||
],
|
],
|
||||||
extensions: Some(vec![String::from("hmac-secret")]),
|
extensions: Some(vec![String::from("hmac-secret")]),
|
||||||
@@ -861,19 +847,13 @@ where
|
|||||||
pin_protocols: Some(vec![
|
pin_protocols: Some(vec![
|
||||||
CtapState::<R, CheckUserPresence>::PIN_PROTOCOL_VERSION,
|
CtapState::<R, CheckUserPresence>::PIN_PROTOCOL_VERSION,
|
||||||
]),
|
]),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
max_credential_count_in_list: MAX_CREDENTIAL_COUNT_IN_LIST.map(|c| c as u64),
|
max_credential_count_in_list: MAX_CREDENTIAL_COUNT_IN_LIST.map(|c| c as u64),
|
||||||
// #TODO(106) update with version 2.1 of HMAC-secret
|
// #TODO(106) update with version 2.1 of HMAC-secret
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
max_credential_id_length: Some(CREDENTIAL_ID_SIZE as u64),
|
max_credential_id_length: Some(CREDENTIAL_ID_SIZE as u64),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
transports: Some(vec![AuthenticatorTransport::Usb]),
|
transports: Some(vec![AuthenticatorTransport::Usb]),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
algorithms: Some(vec![ES256_CRED_PARAM]),
|
algorithms: Some(vec![ES256_CRED_PARAM]),
|
||||||
default_cred_protect: DEFAULT_CRED_PROTECT,
|
default_cred_protect: DEFAULT_CRED_PROTECT,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
min_pin_length: self.persistent_store.min_pin_length()?,
|
min_pin_length: self.persistent_store.min_pin_length()?,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
firmware_version: None,
|
firmware_version: None,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -916,7 +896,6 @@ where
|
|||||||
Ok(ResponseData::AuthenticatorReset)
|
Ok(ResponseData::AuthenticatorReset)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
fn process_selection(&self, cid: ChannelID) -> Result<ResponseData, Ctap2StatusCode> {
|
fn process_selection(&self, cid: ChannelID) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
(self.check_user_presence)(cid)?;
|
(self.check_user_presence)(cid)?;
|
||||||
Ok(ResponseData::AuthenticatorSelection)
|
Ok(ResponseData::AuthenticatorSelection)
|
||||||
@@ -1036,42 +1015,28 @@ mod test {
|
|||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
let info_reponse = ctap_state.process_command(&[0x04], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
|
let info_reponse = ctap_state.process_command(&[0x04], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
let mut expected_response = vec![0x00, 0xAA, 0x01];
|
let mut expected_response = vec![0x00, 0xAA, 0x01];
|
||||||
#[cfg(not(feature = "with_ctap2_1"))]
|
// The version array differs with CTAP1, always including 2.0 and 2.1.
|
||||||
let mut expected_response = vec![0x00, 0xA6, 0x01];
|
#[cfg(not(feature = "with_ctap1"))]
|
||||||
// The difference here is a longer array of supported versions.
|
let version_count = 2;
|
||||||
let mut version_count = 0;
|
|
||||||
// CTAP 2 is always supported
|
|
||||||
version_count += 1;
|
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
{
|
let version_count = 3;
|
||||||
version_count += 1;
|
|
||||||
}
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
{
|
|
||||||
version_count += 1;
|
|
||||||
}
|
|
||||||
expected_response.push(0x80 + version_count);
|
expected_response.push(0x80 + version_count);
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
expected_response.extend(&[0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32]);
|
expected_response.extend(&[0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32]);
|
||||||
expected_response.extend(&[0x68, 0x46, 0x49, 0x44, 0x4F, 0x5F, 0x32, 0x5F, 0x30]);
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
expected_response.extend(&[
|
|
||||||
0x6C, 0x46, 0x49, 0x44, 0x4F, 0x5F, 0x32, 0x5F, 0x31, 0x5F, 0x50, 0x52, 0x45,
|
|
||||||
]);
|
|
||||||
expected_response.extend(&[
|
|
||||||
0x02, 0x81, 0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74,
|
|
||||||
0x03, 0x50,
|
|
||||||
]);
|
|
||||||
expected_response.extend(&ctap_state.persistent_store.aaguid().unwrap());
|
|
||||||
expected_response.extend(&[
|
|
||||||
0x04, 0xA3, 0x62, 0x72, 0x6B, 0xF5, 0x62, 0x75, 0x70, 0xF5, 0x69, 0x63, 0x6C, 0x69,
|
|
||||||
0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E, 0xF4, 0x05, 0x19, 0x04, 0x00, 0x06, 0x81, 0x01,
|
|
||||||
]);
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
expected_response.extend(
|
expected_response.extend(
|
||||||
[
|
[
|
||||||
|
0x68, 0x46, 0x49, 0x44, 0x4F, 0x5F, 0x32, 0x5F, 0x30, 0x6C, 0x46, 0x49, 0x44, 0x4F,
|
||||||
|
0x5F, 0x32, 0x5F, 0x31, 0x5F, 0x50, 0x52, 0x45, 0x02, 0x81, 0x6B, 0x68, 0x6D, 0x61,
|
||||||
|
0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x03, 0x50,
|
||||||
|
]
|
||||||
|
.iter(),
|
||||||
|
);
|
||||||
|
expected_response.extend(&ctap_state.persistent_store.aaguid().unwrap());
|
||||||
|
expected_response.extend(
|
||||||
|
[
|
||||||
|
0x04, 0xA3, 0x62, 0x72, 0x6B, 0xF5, 0x62, 0x75, 0x70, 0xF5, 0x69, 0x63, 0x6C, 0x69,
|
||||||
|
0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E, 0xF4, 0x05, 0x19, 0x04, 0x00, 0x06, 0x81, 0x01,
|
||||||
0x08, 0x18, 0x70, 0x09, 0x81, 0x63, 0x75, 0x73, 0x62, 0x0A, 0x81, 0xA2, 0x63, 0x61,
|
0x08, 0x18, 0x70, 0x09, 0x81, 0x63, 0x75, 0x73, 0x62, 0x0A, 0x81, 0xA2, 0x63, 0x61,
|
||||||
0x6C, 0x67, 0x26, 0x64, 0x74, 0x79, 0x70, 0x65, 0x6A, 0x70, 0x75, 0x62, 0x6C, 0x69,
|
0x6C, 0x67, 0x26, 0x64, 0x74, 0x79, 0x70, 0x65, 0x6A, 0x70, 0x75, 0x62, 0x6C, 0x69,
|
||||||
0x63, 0x2D, 0x6B, 0x65, 0x79, 0x0D, 0x04,
|
0x63, 0x2D, 0x6B, 0x65, 0x79, 0x0D, 0x04,
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ use super::data_formats::{ClientPinSubCommand, CoseKey, GetAssertionHmacSecretIn
|
|||||||
use super::response::{AuthenticatorClientPinResponse, ResponseData};
|
use super::response::{AuthenticatorClientPinResponse, ResponseData};
|
||||||
use super::status_code::Ctap2StatusCode;
|
use super::status_code::Ctap2StatusCode;
|
||||||
use super::storage::PersistentStore;
|
use super::storage::PersistentStore;
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@@ -28,7 +27,7 @@ use crypto::hmac::{hmac_256, verify_hmac_256_first_128bits};
|
|||||||
use crypto::rng256::Rng256;
|
use crypto::rng256::Rng256;
|
||||||
use crypto::sha256::Sha256;
|
use crypto::sha256::Sha256;
|
||||||
use crypto::Hash256;
|
use crypto::Hash256;
|
||||||
#[cfg(all(test, feature = "with_ctap2_1"))]
|
#[cfg(test)]
|
||||||
use enum_iterator::IntoEnumIterator;
|
use enum_iterator::IntoEnumIterator;
|
||||||
use subtle::ConstantTimeEq;
|
use subtle::ConstantTimeEq;
|
||||||
|
|
||||||
@@ -141,10 +140,7 @@ fn check_and_store_new_pin(
|
|||||||
let pin = decrypt_pin(aes_dec_key, new_pin_enc)
|
let pin = decrypt_pin(aes_dec_key, new_pin_enc)
|
||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION)?;
|
.ok_or(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION)?;
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
let min_pin_length = persistent_store.min_pin_length()? as usize;
|
let min_pin_length = persistent_store.min_pin_length()? as usize;
|
||||||
#[cfg(not(feature = "with_ctap2_1"))]
|
|
||||||
let min_pin_length = 4;
|
|
||||||
if pin.len() < min_pin_length || pin.len() == PIN_PADDED_LENGTH {
|
if pin.len() < min_pin_length || pin.len() == PIN_PADDED_LENGTH {
|
||||||
// TODO(kaczmarczyck) check 4 code point minimum instead
|
// TODO(kaczmarczyck) check 4 code point minimum instead
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||||
@@ -155,7 +151,6 @@ fn check_and_store_new_pin(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
#[cfg_attr(test, derive(IntoEnumIterator))]
|
#[cfg_attr(test, derive(IntoEnumIterator))]
|
||||||
// TODO remove when all variants are used
|
// TODO remove when all variants are used
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -173,9 +168,7 @@ pub struct PinProtocolV1 {
|
|||||||
key_agreement_key: crypto::ecdh::SecKey,
|
key_agreement_key: crypto::ecdh::SecKey,
|
||||||
pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH],
|
pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH],
|
||||||
consecutive_pin_mismatches: u8,
|
consecutive_pin_mismatches: u8,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions: u8,
|
permissions: u8,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions_rp_id: Option<String>,
|
permissions_rp_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,9 +180,7 @@ impl PinProtocolV1 {
|
|||||||
key_agreement_key,
|
key_agreement_key,
|
||||||
pin_uv_auth_token,
|
pin_uv_auth_token,
|
||||||
consecutive_pin_mismatches: 0,
|
consecutive_pin_mismatches: 0,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions: 0,
|
permissions: 0,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions_rp_id: None,
|
permissions_rp_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,11 +336,8 @@ impl PinProtocolV1 {
|
|||||||
cbc_encrypt(&token_encryption_key, iv, &mut blocks);
|
cbc_encrypt(&token_encryption_key, iv, &mut blocks);
|
||||||
let pin_token: Vec<u8> = blocks.iter().flatten().cloned().collect();
|
let pin_token: Vec<u8> = blocks.iter().flatten().cloned().collect();
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
{
|
|
||||||
self.permissions = 0x03;
|
self.permissions = 0x03;
|
||||||
self.permissions_rp_id = None;
|
self.permissions_rp_id = None;
|
||||||
}
|
|
||||||
|
|
||||||
Ok(AuthenticatorClientPinResponse {
|
Ok(AuthenticatorClientPinResponse {
|
||||||
key_agreement: None,
|
key_agreement: None,
|
||||||
@@ -358,7 +346,6 @@ impl PinProtocolV1 {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
fn process_get_pin_uv_auth_token_using_uv_with_permissions(
|
fn process_get_pin_uv_auth_token_using_uv_with_permissions(
|
||||||
&self,
|
&self,
|
||||||
// If you want to support local user verification, implement this function.
|
// If you want to support local user verification, implement this function.
|
||||||
@@ -368,30 +355,14 @@ impl PinProtocolV1 {
|
|||||||
_permissions_rp_id: Option<String>,
|
_permissions_rp_id: Option<String>,
|
||||||
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||||
// User verifications is only supported through PIN currently.
|
// User verifications is only supported through PIN currently.
|
||||||
#[cfg(not(feature = "with_ctap2_1"))]
|
|
||||||
{
|
|
||||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND)
|
|
||||||
}
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
{
|
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_INVALID_SUBCOMMAND)
|
Err(Ctap2StatusCode::CTAP2_ERR_INVALID_SUBCOMMAND)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
fn process_get_uv_retries(&self) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
fn process_get_uv_retries(&self) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||||
// User verifications is only supported through PIN currently.
|
// User verifications is only supported through PIN currently.
|
||||||
#[cfg(not(feature = "with_ctap2_1"))]
|
|
||||||
{
|
|
||||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND)
|
|
||||||
}
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
{
|
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_INVALID_SUBCOMMAND)
|
Err(Ctap2StatusCode::CTAP2_ERR_INVALID_SUBCOMMAND)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
fn process_set_min_pin_length(
|
fn process_set_min_pin_length(
|
||||||
&mut self,
|
&mut self,
|
||||||
persistent_store: &mut PersistentStore,
|
persistent_store: &mut PersistentStore,
|
||||||
@@ -440,7 +411,6 @@ impl PinProtocolV1 {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
fn process_get_pin_uv_auth_token_using_pin_with_permissions(
|
fn process_get_pin_uv_auth_token_using_pin_with_permissions(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut impl Rng256,
|
rng: &mut impl Rng256,
|
||||||
@@ -480,20 +450,13 @@ impl PinProtocolV1 {
|
|||||||
pin_auth,
|
pin_auth,
|
||||||
new_pin_enc,
|
new_pin_enc,
|
||||||
pin_hash_enc,
|
pin_hash_enc,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
min_pin_length,
|
min_pin_length,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
min_pin_length_rp_ids,
|
min_pin_length_rp_ids,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions,
|
permissions,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions_rp_id,
|
permissions_rp_id,
|
||||||
} = client_pin_params;
|
} = client_pin_params;
|
||||||
|
|
||||||
if pin_protocol != 1 {
|
if pin_protocol != 1 {
|
||||||
#[cfg(not(feature = "with_ctap2_1"))]
|
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID);
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -528,7 +491,6 @@ impl PinProtocolV1 {
|
|||||||
key_agreement.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
key_agreement.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
pin_hash_enc.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
pin_hash_enc.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
)?),
|
)?),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions => Some(
|
ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions => Some(
|
||||||
self.process_get_pin_uv_auth_token_using_uv_with_permissions(
|
self.process_get_pin_uv_auth_token_using_uv_with_permissions(
|
||||||
key_agreement.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
key_agreement.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
@@ -536,9 +498,7 @@ impl PinProtocolV1 {
|
|||||||
permissions_rp_id,
|
permissions_rp_id,
|
||||||
)?,
|
)?,
|
||||||
),
|
),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
ClientPinSubCommand::GetUvRetries => Some(self.process_get_uv_retries()?),
|
ClientPinSubCommand::GetUvRetries => Some(self.process_get_uv_retries()?),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
ClientPinSubCommand::SetMinPinLength => {
|
ClientPinSubCommand::SetMinPinLength => {
|
||||||
self.process_set_min_pin_length(
|
self.process_set_min_pin_length(
|
||||||
persistent_store,
|
persistent_store,
|
||||||
@@ -548,7 +508,6 @@ impl PinProtocolV1 {
|
|||||||
)?;
|
)?;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions => Some(
|
ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions => Some(
|
||||||
self.process_get_pin_uv_auth_token_using_pin_with_permissions(
|
self.process_get_pin_uv_auth_token_using_pin_with_permissions(
|
||||||
rng,
|
rng,
|
||||||
@@ -571,12 +530,9 @@ impl PinProtocolV1 {
|
|||||||
self.key_agreement_key = crypto::ecdh::SecKey::gensk(rng);
|
self.key_agreement_key = crypto::ecdh::SecKey::gensk(rng);
|
||||||
self.pin_uv_auth_token = rng.gen_uniform_u8x32();
|
self.pin_uv_auth_token = rng.gen_uniform_u8x32();
|
||||||
self.consecutive_pin_mismatches = 0;
|
self.consecutive_pin_mismatches = 0;
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
{
|
|
||||||
self.permissions = 0;
|
self.permissions = 0;
|
||||||
self.permissions_rp_id = None;
|
self.permissions_rp_id = None;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_hmac_secret(
|
pub fn process_hmac_secret(
|
||||||
&self,
|
&self,
|
||||||
@@ -598,7 +554,6 @@ impl PinProtocolV1 {
|
|||||||
encrypt_hmac_secret_output(&shared_secret, &salt_enc[..], cred_random)
|
encrypt_hmac_secret_output(&shared_secret, &salt_enc[..], cred_random)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub fn has_permission(&self, permission: PinPermission) -> Result<(), Ctap2StatusCode> {
|
pub fn has_permission(&self, permission: PinPermission) -> Result<(), Ctap2StatusCode> {
|
||||||
// Relies on the fact that all permissions are represented by powers of two.
|
// Relies on the fact that all permissions are represented by powers of two.
|
||||||
if permission as u8 & self.permissions != 0 {
|
if permission as u8 & self.permissions != 0 {
|
||||||
@@ -608,7 +563,6 @@ impl PinProtocolV1 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub fn has_permission_for_rp_id(&mut self, rp_id: &str) -> Result<(), Ctap2StatusCode> {
|
pub fn has_permission_for_rp_id(&mut self, rp_id: &str) -> Result<(), Ctap2StatusCode> {
|
||||||
if let Some(permissions_rp_id) = &self.permissions_rp_id {
|
if let Some(permissions_rp_id) = &self.permissions_rp_id {
|
||||||
if rp_id != permissions_rp_id {
|
if rp_id != permissions_rp_id {
|
||||||
@@ -629,9 +583,7 @@ impl PinProtocolV1 {
|
|||||||
key_agreement_key,
|
key_agreement_key,
|
||||||
pin_uv_auth_token,
|
pin_uv_auth_token,
|
||||||
consecutive_pin_mismatches: 0,
|
consecutive_pin_mismatches: 0,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions: 0xFF,
|
permissions: 0xFF,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions_rp_id: None,
|
permissions_rp_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -919,7 +871,6 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_get_pin_uv_auth_token_using_pin_with_permissions() {
|
fn test_process_get_pin_uv_auth_token_using_pin_with_permissions() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
@@ -963,7 +914,7 @@ mod test {
|
|||||||
&mut rng,
|
&mut rng,
|
||||||
&mut persistent_store,
|
&mut persistent_store,
|
||||||
key_agreement.clone(),
|
key_agreement.clone(),
|
||||||
pin_hash_enc.clone(),
|
pin_hash_enc,
|
||||||
0x03,
|
0x03,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
@@ -984,7 +935,6 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_set_min_pin_length() {
|
fn test_process_set_min_pin_length() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
@@ -1031,13 +981,9 @@ mod test {
|
|||||||
pin_auth: None,
|
pin_auth: None,
|
||||||
new_pin_enc: None,
|
new_pin_enc: None,
|
||||||
pin_hash_enc: None,
|
pin_hash_enc: None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
min_pin_length: None,
|
min_pin_length: None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
min_pin_length_rp_ids: None,
|
min_pin_length_rp_ids: None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions: None,
|
permissions: None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions_rp_id: None,
|
permissions_rp_id: None,
|
||||||
};
|
};
|
||||||
assert!(pin_protocol_v1
|
assert!(pin_protocol_v1
|
||||||
@@ -1051,18 +997,11 @@ mod test {
|
|||||||
pin_auth: None,
|
pin_auth: None,
|
||||||
new_pin_enc: None,
|
new_pin_enc: None,
|
||||||
pin_hash_enc: None,
|
pin_hash_enc: None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
min_pin_length: None,
|
min_pin_length: None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
min_pin_length_rp_ids: None,
|
min_pin_length_rp_ids: None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions: None,
|
permissions: None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
permissions_rp_id: None,
|
permissions_rp_id: None,
|
||||||
};
|
};
|
||||||
#[cfg(not(feature = "with_ctap2_1"))]
|
|
||||||
let error_code = Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID;
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
let error_code = Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER;
|
let error_code = Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pin_protocol_v1.process_subcommand(&mut rng, &mut persistent_store, client_pin_params),
|
pin_protocol_v1.process_subcommand(&mut rng, &mut persistent_store, client_pin_params),
|
||||||
@@ -1231,7 +1170,6 @@ mod test {
|
|||||||
assert_eq!(&output_dec[..32], &expected_output1);
|
assert_eq!(&output_dec[..32], &expected_output1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_has_permission() {
|
fn test_has_permission() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
@@ -1249,7 +1187,6 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_has_permission_for_rp_id() {
|
fn test_has_permission_for_rp_id() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
|
|||||||
@@ -12,11 +12,9 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
use super::data_formats::{AuthenticatorTransport, PublicKeyCredentialParameter};
|
|
||||||
use super::data_formats::{
|
use super::data_formats::{
|
||||||
CoseKey, CredentialProtectionPolicy, PackedAttestationStatement, PublicKeyCredentialDescriptor,
|
AuthenticatorTransport, CoseKey, CredentialProtectionPolicy, PackedAttestationStatement,
|
||||||
PublicKeyCredentialUserEntity,
|
PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialUserEntity,
|
||||||
};
|
};
|
||||||
use alloc::collections::BTreeMap;
|
use alloc::collections::BTreeMap;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
@@ -32,7 +30,6 @@ pub enum ResponseData {
|
|||||||
AuthenticatorGetInfo(AuthenticatorGetInfoResponse),
|
AuthenticatorGetInfo(AuthenticatorGetInfoResponse),
|
||||||
AuthenticatorClientPin(Option<AuthenticatorClientPinResponse>),
|
AuthenticatorClientPin(Option<AuthenticatorClientPinResponse>),
|
||||||
AuthenticatorReset,
|
AuthenticatorReset,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
AuthenticatorSelection,
|
AuthenticatorSelection,
|
||||||
AuthenticatorVendor(AuthenticatorVendorResponse),
|
AuthenticatorVendor(AuthenticatorVendorResponse),
|
||||||
}
|
}
|
||||||
@@ -47,7 +44,6 @@ impl From<ResponseData> for Option<cbor::Value> {
|
|||||||
ResponseData::AuthenticatorClientPin(Some(data)) => Some(data.into()),
|
ResponseData::AuthenticatorClientPin(Some(data)) => Some(data.into()),
|
||||||
ResponseData::AuthenticatorClientPin(None) => None,
|
ResponseData::AuthenticatorClientPin(None) => None,
|
||||||
ResponseData::AuthenticatorReset => None,
|
ResponseData::AuthenticatorReset => None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
ResponseData::AuthenticatorSelection => None,
|
ResponseData::AuthenticatorSelection => None,
|
||||||
ResponseData::AuthenticatorVendor(data) => Some(data.into()),
|
ResponseData::AuthenticatorVendor(data) => Some(data.into()),
|
||||||
}
|
}
|
||||||
@@ -118,23 +114,16 @@ pub struct AuthenticatorGetInfoResponse {
|
|||||||
pub options: Option<BTreeMap<String, bool>>,
|
pub options: Option<BTreeMap<String, bool>>,
|
||||||
pub max_msg_size: Option<u64>,
|
pub max_msg_size: Option<u64>,
|
||||||
pub pin_protocols: Option<Vec<u64>>,
|
pub pin_protocols: Option<Vec<u64>>,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub max_credential_count_in_list: Option<u64>,
|
pub max_credential_count_in_list: Option<u64>,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub max_credential_id_length: Option<u64>,
|
pub max_credential_id_length: Option<u64>,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub transports: Option<Vec<AuthenticatorTransport>>,
|
pub transports: Option<Vec<AuthenticatorTransport>>,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub algorithms: Option<Vec<PublicKeyCredentialParameter>>,
|
pub algorithms: Option<Vec<PublicKeyCredentialParameter>>,
|
||||||
pub default_cred_protect: Option<CredentialProtectionPolicy>,
|
pub default_cred_protect: Option<CredentialProtectionPolicy>,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub min_pin_length: u8,
|
pub min_pin_length: u8,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub firmware_version: Option<u64>,
|
pub firmware_version: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AuthenticatorGetInfoResponse> for cbor::Value {
|
impl From<AuthenticatorGetInfoResponse> for cbor::Value {
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
fn from(get_info_response: AuthenticatorGetInfoResponse) -> Self {
|
fn from(get_info_response: AuthenticatorGetInfoResponse) -> Self {
|
||||||
let AuthenticatorGetInfoResponse {
|
let AuthenticatorGetInfoResponse {
|
||||||
versions,
|
versions,
|
||||||
@@ -176,37 +165,6 @@ impl From<AuthenticatorGetInfoResponse> for cbor::Value {
|
|||||||
0x0E => firmware_version,
|
0x0E => firmware_version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "with_ctap2_1"))]
|
|
||||||
fn from(get_info_response: AuthenticatorGetInfoResponse) -> Self {
|
|
||||||
let AuthenticatorGetInfoResponse {
|
|
||||||
versions,
|
|
||||||
extensions,
|
|
||||||
aaguid,
|
|
||||||
options,
|
|
||||||
max_msg_size,
|
|
||||||
pin_protocols,
|
|
||||||
default_cred_protect,
|
|
||||||
} = get_info_response;
|
|
||||||
|
|
||||||
let options_cbor: Option<cbor::Value> = options.map(|options| {
|
|
||||||
let option_map: BTreeMap<_, _> = options
|
|
||||||
.into_iter()
|
|
||||||
.map(|(key, value)| (cbor_text!(key), cbor_bool!(value)))
|
|
||||||
.collect();
|
|
||||||
cbor_map_btree!(option_map)
|
|
||||||
});
|
|
||||||
|
|
||||||
cbor_map_options! {
|
|
||||||
0x01 => cbor_array_vec!(versions),
|
|
||||||
0x02 => extensions.map(|vec| cbor_array_vec!(vec)),
|
|
||||||
0x03 => &aaguid,
|
|
||||||
0x04 => options_cbor,
|
|
||||||
0x05 => max_msg_size,
|
|
||||||
0x06 => pin_protocols.map(|vec| cbor_array_vec!(vec)),
|
|
||||||
0x0C => default_cred_protect.map(|p| p as u64),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
@@ -257,7 +215,6 @@ impl From<AuthenticatorVendorResponse> for cbor::Value {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::super::data_formats::PackedAttestationStatement;
|
use super::super::data_formats::PackedAttestationStatement;
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
use super::super::ES256_CRED_PARAM;
|
use super::super::ES256_CRED_PARAM;
|
||||||
use super::*;
|
use super::*;
|
||||||
use cbor::{cbor_bytes, cbor_map};
|
use cbor::{cbor_bytes, cbor_map};
|
||||||
@@ -321,28 +278,16 @@ mod test {
|
|||||||
options: None,
|
options: None,
|
||||||
max_msg_size: None,
|
max_msg_size: None,
|
||||||
pin_protocols: None,
|
pin_protocols: None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
max_credential_count_in_list: None,
|
max_credential_count_in_list: None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
max_credential_id_length: None,
|
max_credential_id_length: None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
transports: None,
|
transports: None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
algorithms: None,
|
algorithms: None,
|
||||||
default_cred_protect: None,
|
default_cred_protect: None,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
min_pin_length: 4,
|
min_pin_length: 4,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
firmware_version: None,
|
firmware_version: None,
|
||||||
};
|
};
|
||||||
let response_cbor: Option<cbor::Value> =
|
let response_cbor: Option<cbor::Value> =
|
||||||
ResponseData::AuthenticatorGetInfo(get_info_response).into();
|
ResponseData::AuthenticatorGetInfo(get_info_response).into();
|
||||||
#[cfg(not(feature = "with_ctap2_1"))]
|
|
||||||
let expected_cbor = cbor_map_options! {
|
|
||||||
0x01 => cbor_array_vec![versions],
|
|
||||||
0x03 => vec![0x00; 16],
|
|
||||||
};
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
let expected_cbor = cbor_map_options! {
|
let expected_cbor = cbor_map_options! {
|
||||||
0x01 => cbor_array_vec![versions],
|
0x01 => cbor_array_vec![versions],
|
||||||
0x03 => vec![0x00; 16],
|
0x03 => vec![0x00; 16],
|
||||||
@@ -352,7 +297,6 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
fn test_get_info_optionals_into_cbor() {
|
fn test_get_info_optionals_into_cbor() {
|
||||||
let mut options_map = BTreeMap::new();
|
let mut options_map = BTreeMap::new();
|
||||||
options_map.insert(String::from("rk"), true);
|
options_map.insert(String::from("rk"), true);
|
||||||
@@ -418,7 +362,6 @@ mod test {
|
|||||||
assert_eq!(response_cbor, None);
|
assert_eq!(response_cbor, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_selection_into_cbor() {
|
fn test_selection_into_cbor() {
|
||||||
let response_cbor: Option<cbor::Value> = ResponseData::AuthenticatorSelection.into();
|
let response_cbor: Option<cbor::Value> = ResponseData::AuthenticatorSelection.into();
|
||||||
|
|||||||
@@ -31,9 +31,7 @@ pub enum Ctap2StatusCode {
|
|||||||
CTAP2_ERR_INVALID_CBOR = 0x12,
|
CTAP2_ERR_INVALID_CBOR = 0x12,
|
||||||
CTAP2_ERR_MISSING_PARAMETER = 0x14,
|
CTAP2_ERR_MISSING_PARAMETER = 0x14,
|
||||||
CTAP2_ERR_LIMIT_EXCEEDED = 0x15,
|
CTAP2_ERR_LIMIT_EXCEEDED = 0x15,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
CTAP2_ERR_FP_DATABASE_FULL = 0x17,
|
CTAP2_ERR_FP_DATABASE_FULL = 0x17,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
CTAP2_ERR_LARGE_BLOB_STORAGE_FULL = 0x18,
|
CTAP2_ERR_LARGE_BLOB_STORAGE_FULL = 0x18,
|
||||||
CTAP2_ERR_CREDENTIAL_EXCLUDED = 0x19,
|
CTAP2_ERR_CREDENTIAL_EXCLUDED = 0x19,
|
||||||
CTAP2_ERR_PROCESSING = 0x21,
|
CTAP2_ERR_PROCESSING = 0x21,
|
||||||
@@ -63,13 +61,9 @@ pub enum Ctap2StatusCode {
|
|||||||
CTAP2_ERR_ACTION_TIMEOUT = 0x3A,
|
CTAP2_ERR_ACTION_TIMEOUT = 0x3A,
|
||||||
CTAP2_ERR_UP_REQUIRED = 0x3B,
|
CTAP2_ERR_UP_REQUIRED = 0x3B,
|
||||||
CTAP2_ERR_UV_BLOCKED = 0x3C,
|
CTAP2_ERR_UV_BLOCKED = 0x3C,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
CTAP2_ERR_INTEGRITY_FAILURE = 0x3D,
|
CTAP2_ERR_INTEGRITY_FAILURE = 0x3D,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
CTAP2_ERR_INVALID_SUBCOMMAND = 0x3E,
|
CTAP2_ERR_INVALID_SUBCOMMAND = 0x3E,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
CTAP2_ERR_UV_INVALID = 0x3F,
|
CTAP2_ERR_UV_INVALID = 0x3F,
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
CTAP2_ERR_UNAUTHORIZED_PERMISSION = 0x40,
|
CTAP2_ERR_UNAUTHORIZED_PERMISSION = 0x40,
|
||||||
CTAP1_ERR_OTHER = 0x7F,
|
CTAP1_ERR_OTHER = 0x7F,
|
||||||
_CTAP2_ERR_SPEC_LAST = 0xDF,
|
_CTAP2_ERR_SPEC_LAST = 0xDF,
|
||||||
|
|||||||
@@ -14,20 +14,18 @@
|
|||||||
|
|
||||||
mod key;
|
mod key;
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
use crate::ctap::data_formats::{
|
||||||
use crate::ctap::data_formats::{extract_array, extract_text_string};
|
extract_array, extract_text_string, CredentialProtectionPolicy, PublicKeyCredentialSource,
|
||||||
use crate::ctap::data_formats::{CredentialProtectionPolicy, PublicKeyCredentialSource};
|
};
|
||||||
use crate::ctap::key_material;
|
use crate::ctap::key_material;
|
||||||
use crate::ctap::pin_protocol_v1::PIN_AUTH_LENGTH;
|
use crate::ctap::pin_protocol_v1::PIN_AUTH_LENGTH;
|
||||||
use crate::ctap::status_code::Ctap2StatusCode;
|
use crate::ctap::status_code::Ctap2StatusCode;
|
||||||
use crate::ctap::INITIAL_SIGNATURE_COUNTER;
|
use crate::ctap::INITIAL_SIGNATURE_COUNTER;
|
||||||
use crate::embedded_flash::{new_storage, Storage};
|
use crate::embedded_flash::{new_storage, Storage};
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use arrayref::array_ref;
|
use arrayref::array_ref;
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
use cbor::cbor_array_vec;
|
use cbor::cbor_array_vec;
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use crypto::rng256::Rng256;
|
use crypto::rng256::Rng256;
|
||||||
@@ -54,14 +52,11 @@ const NUM_PAGES: usize = 20;
|
|||||||
const MAX_SUPPORTED_RESIDENTIAL_KEYS: usize = 150;
|
const MAX_SUPPORTED_RESIDENTIAL_KEYS: usize = 150;
|
||||||
|
|
||||||
const MAX_PIN_RETRIES: u8 = 8;
|
const MAX_PIN_RETRIES: u8 = 8;
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
const DEFAULT_MIN_PIN_LENGTH: u8 = 4;
|
const DEFAULT_MIN_PIN_LENGTH: u8 = 4;
|
||||||
// TODO(kaczmarczyck) use this for the minPinLength extension
|
// TODO(kaczmarczyck) use this for the minPinLength extension
|
||||||
// https://github.com/google/OpenSK/issues/129
|
// https://github.com/google/OpenSK/issues/129
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
const _DEFAULT_MIN_PIN_LENGTH_RP_IDS: Vec<String> = Vec::new();
|
const _DEFAULT_MIN_PIN_LENGTH_RP_IDS: Vec<String> = Vec::new();
|
||||||
// TODO(kaczmarczyck) Check whether this constant is necessary, or replace it accordingly.
|
// TODO(kaczmarczyck) Check whether this constant is necessary, or replace it accordingly.
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
const _MAX_RP_IDS_LENGTH: usize = 8;
|
const _MAX_RP_IDS_LENGTH: usize = 8;
|
||||||
|
|
||||||
/// Wrapper for master keys.
|
/// Wrapper for master keys.
|
||||||
@@ -348,7 +343,6 @@ impl PersistentStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the minimum PIN length.
|
/// Returns the minimum PIN length.
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub fn min_pin_length(&self) -> Result<u8, Ctap2StatusCode> {
|
pub fn min_pin_length(&self) -> Result<u8, Ctap2StatusCode> {
|
||||||
match self.store.find(key::MIN_PIN_LENGTH)? {
|
match self.store.find(key::MIN_PIN_LENGTH)? {
|
||||||
None => Ok(DEFAULT_MIN_PIN_LENGTH),
|
None => Ok(DEFAULT_MIN_PIN_LENGTH),
|
||||||
@@ -358,14 +352,12 @@ impl PersistentStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the minimum PIN length.
|
/// Sets the minimum PIN length.
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub fn set_min_pin_length(&mut self, min_pin_length: u8) -> Result<(), Ctap2StatusCode> {
|
pub fn set_min_pin_length(&mut self, min_pin_length: u8) -> Result<(), Ctap2StatusCode> {
|
||||||
Ok(self.store.insert(key::MIN_PIN_LENGTH, &[min_pin_length])?)
|
Ok(self.store.insert(key::MIN_PIN_LENGTH, &[min_pin_length])?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the list of RP IDs that are used to check if reading the minimum PIN length is
|
/// Returns the list of RP IDs that are used to check if reading the minimum PIN length is
|
||||||
/// allowed.
|
/// allowed.
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub fn _min_pin_length_rp_ids(&self) -> Result<Vec<String>, Ctap2StatusCode> {
|
pub fn _min_pin_length_rp_ids(&self) -> Result<Vec<String>, Ctap2StatusCode> {
|
||||||
let rp_ids = self
|
let rp_ids = self
|
||||||
.store
|
.store
|
||||||
@@ -374,11 +366,10 @@ impl PersistentStore {
|
|||||||
_deserialize_min_pin_length_rp_ids(&value)
|
_deserialize_min_pin_length_rp_ids(&value)
|
||||||
});
|
});
|
||||||
debug_assert!(rp_ids.is_some());
|
debug_assert!(rp_ids.is_some());
|
||||||
Ok(rp_ids.unwrap_or(vec![]))
|
Ok(rp_ids.unwrap_or_default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the list of RP IDs that are used to check if reading the minimum PIN length is allowed.
|
/// Sets the list of RP IDs that are used to check if reading the minimum PIN length is allowed.
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
pub fn _set_min_pin_length_rp_ids(
|
pub fn _set_min_pin_length_rp_ids(
|
||||||
&mut self,
|
&mut self,
|
||||||
min_pin_length_rp_ids: Vec<String>,
|
min_pin_length_rp_ids: Vec<String>,
|
||||||
@@ -582,7 +573,6 @@ fn serialize_credential(credential: PublicKeyCredentialSource) -> Result<Vec<u8>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Deserializes a list of RP IDs from storage representation.
|
/// Deserializes a list of RP IDs from storage representation.
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
fn _deserialize_min_pin_length_rp_ids(data: &[u8]) -> Option<Vec<String>> {
|
fn _deserialize_min_pin_length_rp_ids(data: &[u8]) -> Option<Vec<String>> {
|
||||||
let cbor = cbor::read(data).ok()?;
|
let cbor = cbor::read(data).ok()?;
|
||||||
extract_array(cbor)
|
extract_array(cbor)
|
||||||
@@ -594,7 +584,6 @@ fn _deserialize_min_pin_length_rp_ids(data: &[u8]) -> Option<Vec<String>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes a list of RP IDs to storage representation.
|
/// Serializes a list of RP IDs to storage representation.
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
fn _serialize_min_pin_length_rp_ids(rp_ids: Vec<String>) -> Result<Vec<u8>, Ctap2StatusCode> {
|
fn _serialize_min_pin_length_rp_ids(rp_ids: Vec<String>) -> Result<Vec<u8>, Ctap2StatusCode> {
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
if cbor::write(cbor_array_vec!(rp_ids), &mut data) {
|
if cbor::write(cbor_array_vec!(rp_ids), &mut data) {
|
||||||
@@ -988,7 +977,6 @@ mod test {
|
|||||||
assert_eq!(&persistent_store.aaguid().unwrap(), key_material::AAGUID);
|
assert_eq!(&persistent_store.aaguid().unwrap(), key_material::AAGUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_min_pin_length() {
|
fn test_min_pin_length() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
@@ -1011,7 +999,6 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_min_pin_length_rp_ids() {
|
fn test_min_pin_length_rp_ids() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
@@ -1080,7 +1067,6 @@ mod test {
|
|||||||
assert_eq!(credential, reconstructed);
|
assert_eq!(credential, reconstructed);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialize_deserialize_min_pin_length_rp_ids() {
|
fn test_serialize_deserialize_min_pin_length_rp_ids() {
|
||||||
let rp_ids = vec![String::from("example.com")];
|
let rp_ids = vec![String::from("example.com")];
|
||||||
|
|||||||
@@ -92,13 +92,11 @@ make_partition! {
|
|||||||
CRED_RANDOM_SECRET = 2041;
|
CRED_RANDOM_SECRET = 2041;
|
||||||
|
|
||||||
/// List of RP IDs allowed to read the minimum PIN length.
|
/// List of RP IDs allowed to read the minimum PIN length.
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
_MIN_PIN_LENGTH_RP_IDS = 2042;
|
_MIN_PIN_LENGTH_RP_IDS = 2042;
|
||||||
|
|
||||||
/// The minimum PIN length.
|
/// The minimum PIN length.
|
||||||
///
|
///
|
||||||
/// If the entry is absent, the minimum PIN length is `DEFAULT_MIN_PIN_LENGTH`.
|
/// If the entry is absent, the minimum PIN length is `DEFAULT_MIN_PIN_LENGTH`.
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
|
||||||
MIN_PIN_LENGTH = 2043;
|
MIN_PIN_LENGTH = 2043;
|
||||||
|
|
||||||
/// The number of PIN retries.
|
/// The number of PIN retries.
|
||||||
|
|||||||
Reference in New Issue
Block a user