Add a read_cbor_map macro to avoid the overhead of removing values on-by-one in BTreeMap.

This commit is contained in:
Guillaume Endignoux
2020-06-09 14:36:16 +02:00
parent e18a26317f
commit 97fb222455
3 changed files with 306 additions and 130 deletions

View File

@@ -124,26 +124,30 @@ impl TryFrom<cbor::Value> for AuthenticatorMakeCredentialParameters {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
let mut param_map = extract_map(cbor_value)?;
read_cbor_map! {
extract_map(cbor_value)?,
client_data_hash @ cbor_unsigned!(1),
rp @ cbor_unsigned!(2),
user @ cbor_unsigned!(3),
cred_param_vec @ cbor_unsigned!(4),
exclude_list @ cbor_unsigned!(5),
extensions @ cbor_unsigned!(6),
options @ cbor_unsigned!(7),
pin_uv_auth_param @ cbor_unsigned!(8),
pin_uv_auth_protocol @ cbor_unsigned!(9),
};
let client_data_hash =
extract_byte_string(ok_or_missing(param_map.remove(&cbor_unsigned!(1)))?)?;
let client_data_hash = extract_byte_string(ok_or_missing(client_data_hash)?)?;
let rp = PublicKeyCredentialRpEntity::try_from(ok_or_missing(rp)?)?;
let user = PublicKeyCredentialUserEntity::try_from(ok_or_missing(user)?)?;
let rp = PublicKeyCredentialRpEntity::try_from(ok_or_missing(
param_map.remove(&cbor_unsigned!(2)),
)?)?;
let user = PublicKeyCredentialUserEntity::try_from(ok_or_missing(
param_map.remove(&cbor_unsigned!(3)),
)?)?;
let cred_param_vec = extract_array(ok_or_missing(param_map.remove(&cbor_unsigned!(4)))?)?;
let cred_param_vec = extract_array(ok_or_missing(cred_param_vec)?)?;
let pub_key_cred_params = cred_param_vec
.into_iter()
.map(PublicKeyCredentialParameter::try_from)
.collect::<Result<Vec<PublicKeyCredentialParameter>, Ctap2StatusCode>>()?;
let exclude_list = match param_map.remove(&cbor_unsigned!(5)) {
let exclude_list = match exclude_list {
Some(entry) => {
let exclude_list_vec = extract_array(entry)?;
let list_len = MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(exclude_list_vec.len());
@@ -157,12 +161,11 @@ impl TryFrom<cbor::Value> for AuthenticatorMakeCredentialParameters {
None => None,
};
let extensions = param_map
.remove(&cbor_unsigned!(6))
let extensions = extensions
.map(MakeCredentialExtensions::try_from)
.transpose()?;
let options = match param_map.remove(&cbor_unsigned!(7)) {
let options = match options {
Some(entry) => MakeCredentialOptions::try_from(entry)?,
None => MakeCredentialOptions {
rk: false,
@@ -170,15 +173,8 @@ impl TryFrom<cbor::Value> for AuthenticatorMakeCredentialParameters {
},
};
let pin_uv_auth_param = param_map
.remove(&cbor_unsigned!(8))
.map(extract_byte_string)
.transpose()?;
let pin_uv_auth_protocol = param_map
.remove(&cbor_unsigned!(9))
.map(extract_unsigned)
.transpose()?;
let pin_uv_auth_param = pin_uv_auth_param.map(extract_byte_string).transpose()?;
let pin_uv_auth_protocol = pin_uv_auth_protocol.map(extract_unsigned).transpose()?;
Ok(AuthenticatorMakeCredentialParameters {
client_data_hash,
@@ -210,14 +206,21 @@ impl TryFrom<cbor::Value> for AuthenticatorGetAssertionParameters {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
let mut param_map = extract_map(cbor_value)?;
read_cbor_map! {
extract_map(cbor_value)?,
rp_id @ cbor_unsigned!(1),
client_data_hash @ cbor_unsigned!(2),
allow_list @ cbor_unsigned!(3),
extensions @ cbor_unsigned!(4),
options @ cbor_unsigned!(5),
pin_uv_auth_param @ cbor_unsigned!(6),
pin_uv_auth_protocol @ cbor_unsigned!(7),
};
let rp_id = extract_text_string(ok_or_missing(param_map.remove(&cbor_unsigned!(1)))?)?;
let rp_id = extract_text_string(ok_or_missing(rp_id)?)?;
let client_data_hash = extract_byte_string(ok_or_missing(client_data_hash)?)?;
let client_data_hash =
extract_byte_string(ok_or_missing(param_map.remove(&cbor_unsigned!(2)))?)?;
let allow_list = match param_map.remove(&cbor_unsigned!(3)) {
let allow_list = match allow_list {
Some(entry) => {
let allow_list_vec = extract_array(entry)?;
let list_len = MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(allow_list_vec.len());
@@ -231,12 +234,11 @@ impl TryFrom<cbor::Value> for AuthenticatorGetAssertionParameters {
None => None,
};
let extensions = param_map
.remove(&cbor_unsigned!(4))
let extensions = extensions
.map(GetAssertionExtensions::try_from)
.transpose()?;
let options = match param_map.remove(&cbor_unsigned!(5)) {
let options = match options {
Some(entry) => GetAssertionOptions::try_from(entry)?,
None => GetAssertionOptions {
up: true,
@@ -244,15 +246,8 @@ impl TryFrom<cbor::Value> for AuthenticatorGetAssertionParameters {
},
};
let pin_uv_auth_param = param_map
.remove(&cbor_unsigned!(6))
.map(extract_byte_string)
.transpose()?;
let pin_uv_auth_protocol = param_map
.remove(&cbor_unsigned!(7))
.map(extract_unsigned)
.transpose()?;
let pin_uv_auth_param = pin_uv_auth_param.map(extract_byte_string).transpose()?;
let pin_uv_auth_protocol = pin_uv_auth_protocol.map(extract_unsigned).transpose()?;
Ok(AuthenticatorGetAssertionParameters {
rp_id,
@@ -280,33 +275,25 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
let mut param_map = extract_map(cbor_value)?;
read_cbor_map! {
extract_map(cbor_value)?,
pin_protocol @ cbor_unsigned!(1),
sub_command @ cbor_unsigned!(2),
key_agreement @ cbor_unsigned!(3),
pin_auth @ cbor_unsigned!(4),
new_pin_enc @ cbor_unsigned!(5),
pin_hash_enc @ cbor_unsigned!(6),
};
let pin_protocol = extract_unsigned(ok_or_missing(param_map.remove(&cbor_unsigned!(1)))?)?;
let sub_command =
ClientPinSubCommand::try_from(ok_or_missing(param_map.remove(&cbor_unsigned!(2)))?)?;
let key_agreement = param_map
.remove(&cbor_unsigned!(3))
let pin_protocol = extract_unsigned(ok_or_missing(pin_protocol)?)?;
let sub_command = ClientPinSubCommand::try_from(ok_or_missing(sub_command)?)?;
let key_agreement = key_agreement
.map(extract_map)
.transpose()?
.map(|x| CoseKey(x));
let pin_auth = param_map
.remove(&cbor_unsigned!(4))
.map(extract_byte_string)
.transpose()?;
let new_pin_enc = param_map
.remove(&cbor_unsigned!(5))
.map(extract_byte_string)
.transpose()?;
let pin_hash_enc = param_map
.remove(&cbor_unsigned!(6))
.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 pin_hash_enc = pin_hash_enc.map(extract_byte_string).transpose()?;
Ok(AuthenticatorClientPinParameters {
pin_protocol,

View File

@@ -31,16 +31,17 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialRpEntity {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
let mut rp_map = extract_map(cbor_value)?;
let rp_id = extract_text_string(ok_or_missing(rp_map.remove(&cbor_text!("id")))?)?;
let rp_name = rp_map
.remove(&cbor_text!("name"))
.map(extract_text_string)
.transpose()?;
let rp_icon = rp_map
.remove(&cbor_text!("icon"))
.map(extract_text_string)
.transpose()?;
read_cbor_map! {
extract_map(cbor_value)?,
rp_id @ cbor_text!("id"),
rp_icon @ cbor_text!("icon"),
rp_name @ cbor_text!("name"),
};
let rp_id = extract_text_string(ok_or_missing(rp_id)?)?;
let rp_name = rp_name.map(extract_text_string).transpose()?;
let rp_icon = rp_icon.map(extract_text_string).transpose()?;
Ok(Self {
rp_id,
rp_name,
@@ -62,20 +63,19 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialUserEntity {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
let mut user_map = extract_map(cbor_value)?;
let user_id = extract_byte_string(ok_or_missing(user_map.remove(&cbor_text!("id")))?)?;
let user_name = user_map
.remove(&cbor_text!("name"))
.map(extract_text_string)
.transpose()?;
let user_display_name = user_map
.remove(&cbor_text!("displayName"))
.map(extract_text_string)
.transpose()?;
let user_icon = user_map
.remove(&cbor_text!("icon"))
.map(extract_text_string)
.transpose()?;
read_cbor_map! {
extract_map(cbor_value)?,
user_id @ cbor_text!("id"),
user_icon @ cbor_text!("icon"),
user_name @ cbor_text!("name"),
user_display_name @ cbor_text!("displayName"),
};
let user_id = extract_byte_string(ok_or_missing(user_id)?)?;
let user_name = user_name.map(extract_text_string).transpose()?;
let user_display_name = user_display_name.map(extract_text_string).transpose()?;
let user_icon = user_icon.map(extract_text_string).transpose()?;
Ok(Self {
user_id,
user_name,
@@ -141,13 +141,14 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialParameter {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
let mut cred_param_map = extract_map(cbor_value)?;
let cred_type = PublicKeyCredentialType::try_from(ok_or_missing(
cred_param_map.remove(&cbor_text!("type")),
)?)?;
let alg = SignatureAlgorithm::try_from(ok_or_missing(
cred_param_map.remove(&cbor_text!("alg")),
)?)?;
read_cbor_map! {
extract_map(cbor_value)?,
alg @ cbor_text!("alg"),
cred_type @ cbor_text!("type"),
};
let cred_type = PublicKeyCredentialType::try_from(ok_or_missing(cred_type)?)?;
let alg = SignatureAlgorithm::try_from(ok_or_missing(alg)?)?;
Ok(Self { cred_type, alg })
}
}
@@ -209,12 +210,16 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialDescriptor {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
let mut cred_desc_map = extract_map(cbor_value)?;
let key_type = PublicKeyCredentialType::try_from(ok_or_missing(
cred_desc_map.remove(&cbor_text!("type")),
)?)?;
let key_id = extract_byte_string(ok_or_missing(cred_desc_map.remove(&cbor_text!("id")))?)?;
let transports = match cred_desc_map.remove(&cbor_text!("transports")) {
read_cbor_map! {
extract_map(cbor_value)?,
key_id @ cbor_text!("id"),
key_type @ cbor_text!("type"),
transports @ cbor_text!("transports"),
};
let key_type = PublicKeyCredentialType::try_from(ok_or_missing(key_type)?)?;
let key_id = extract_byte_string(ok_or_missing(key_id)?)?;
let transports = match transports {
Some(exclude_entry) => {
let transport_vec = extract_array(exclude_entry)?;
let transports = transport_vec
@@ -225,6 +230,7 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialDescriptor {
}
None => None,
};
Ok(Self {
key_type,
key_id,
@@ -253,12 +259,14 @@ impl TryFrom<cbor::Value> for MakeCredentialExtensions {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
let mut extensions_map = extract_map(cbor_value)?;
let hmac_secret = extensions_map
.remove(&cbor_text!("hmac-secret"))
.map_or(Ok(false), extract_bool)?;
let cred_protect = extensions_map
.remove(&cbor_text!("credProtect"))
read_cbor_map! {
extract_map(cbor_value)?,
cred_protect @ cbor_text!("credProtect"),
hmac_secret @ cbor_text!("hmac-secret"),
};
let hmac_secret = hmac_secret.map_or(Ok(false), extract_bool)?;
let cred_protect = cred_protect
.map(CredentialProtectionPolicy::try_from)
.transpose()?;
Ok(Self {
@@ -277,9 +285,12 @@ impl TryFrom<cbor::Value> for GetAssertionExtensions {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
let mut extensions_map = extract_map(cbor_value)?;
let hmac_secret = extensions_map
.remove(&cbor_text!("hmac-secret"))
read_cbor_map! {
extract_map(cbor_value)?,
hmac_secret @ cbor_text!("hmac-secret"),
};
let hmac_secret = hmac_secret
.map(GetAssertionHmacSecretInput::try_from)
.transpose()?;
Ok(Self { hmac_secret })
@@ -297,10 +308,16 @@ impl TryFrom<cbor::Value> for GetAssertionHmacSecretInput {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
let mut input_map = extract_map(cbor_value)?;
let cose_key = extract_map(ok_or_missing(input_map.remove(&cbor_unsigned!(1)))?)?;
let salt_enc = extract_byte_string(ok_or_missing(input_map.remove(&cbor_unsigned!(2)))?)?;
let salt_auth = extract_byte_string(ok_or_missing(input_map.remove(&cbor_unsigned!(3)))?)?;
read_cbor_map! {
extract_map(cbor_value)?,
cose_key @ cbor_unsigned!(1),
salt_enc @ cbor_unsigned!(2),
salt_auth @ cbor_unsigned!(3),
};
let cose_key = extract_map(ok_or_missing(cose_key)?)?;
let salt_enc = extract_byte_string(ok_or_missing(salt_enc)?)?;
let salt_auth = extract_byte_string(ok_or_missing(salt_auth)?)?;
Ok(Self {
key_agreement: CoseKey(cose_key),
salt_enc,
@@ -320,17 +337,23 @@ impl TryFrom<cbor::Value> for MakeCredentialOptions {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
let mut options_map = extract_map(cbor_value)?;
let rk = match options_map.remove(&cbor_text!("rk")) {
read_cbor_map! {
extract_map(cbor_value)?,
rk @ cbor_text!("rk"),
up @ cbor_text!("up"),
uv @ cbor_text!("uv"),
};
let rk = match rk {
Some(options_entry) => extract_bool(options_entry)?,
None => false,
};
if let Some(options_entry) = options_map.remove(&cbor_text!("up")) {
if let Some(options_entry) = up {
if !extract_bool(options_entry)? {
return Err(Ctap2StatusCode::CTAP2_ERR_INVALID_OPTION);
}
}
let uv = match options_map.remove(&cbor_text!("uv")) {
let uv = match uv {
Some(options_entry) => extract_bool(options_entry)?,
None => false,
};
@@ -348,17 +371,23 @@ impl TryFrom<cbor::Value> for GetAssertionOptions {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
let mut options_map = extract_map(cbor_value)?;
if let Some(options_entry) = options_map.remove(&cbor_text!("rk")) {
read_cbor_map! {
extract_map(cbor_value)?,
rk @ cbor_text!("rk"),
up @ cbor_text!("up"),
uv @ cbor_text!("uv"),
};
if let Some(options_entry) = rk {
// This is only for returning the correct status code.
extract_bool(options_entry)?;
return Err(Ctap2StatusCode::CTAP2_ERR_INVALID_OPTION);
}
let up = match options_map.remove(&cbor_text!("up")) {
let up = match up {
Some(options_entry) => extract_bool(options_entry)?,
None => true,
};
let uv = match options_map.remove(&cbor_text!("uv")) {
let uv = match uv {
Some(options_entry) => extract_bool(options_entry)?,
None => false,
};
@@ -599,27 +628,36 @@ impl TryFrom<CoseKey> for ecdh::PubKey {
type Error = Ctap2StatusCode;
fn try_from(cose_key: CoseKey) -> Result<Self, Ctap2StatusCode> {
let mut cose_map = cose_key.0;
let key_type = extract_integer(ok_or_missing(cose_map.remove(&cbor_int!(1)))?)?;
read_cbor_map! {
cose_key.0,
key_type @ cbor_int!(1),
algorithm @ cbor_int!(3),
curve @ cbor_int!(-1),
x_bytes @ cbor_int!(-2),
y_bytes @ cbor_int!(-3),
};
let key_type = extract_integer(ok_or_missing(key_type)?)?;
if key_type != EC2_KEY_TYPE {
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
}
let algorithm = extract_integer(ok_or_missing(cose_map.remove(&cbor_int!(3)))?)?;
let algorithm = extract_integer(ok_or_missing(algorithm)?)?;
if algorithm != ECDH_ALGORITHM && algorithm != ES256_ALGORITHM {
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
}
let curve = extract_integer(ok_or_missing(cose_map.remove(&cbor_int!(-1)))?)?;
let curve = extract_integer(ok_or_missing(curve)?)?;
if curve != P_256_CURVE {
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
}
let x_bytes = extract_byte_string(ok_or_missing(cose_map.remove(&cbor_int!(-2)))?)?;
let x_bytes = extract_byte_string(ok_or_missing(x_bytes)?)?;
if x_bytes.len() != ecdh::NBYTES {
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
}
let y_bytes = extract_byte_string(ok_or_missing(cose_map.remove(&cbor_int!(-3)))?)?;
let y_bytes = extract_byte_string(ok_or_missing(y_bytes)?)?;
if y_bytes.len() != ecdh::NBYTES {
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
}
let x_array_ref = array_ref![x_bytes.as_slice(), 0, ecdh::NBYTES];
let y_array_ref = array_ref![y_bytes.as_slice(), 0, ecdh::NBYTES];
ecdh::PubKey::from_coordinates(x_array_ref, y_array_ref)