From 0287a0957334676e04a4de9bb6e38f4002f115d8 Mon Sep 17 00:00:00 2001 From: David Drysdale Date: Fri, 18 Jun 2021 17:39:54 +0000 Subject: [PATCH] cbor: allow user to control nesting (#329) * cbor: allow user to control nesting - Make the default read/write entrypoints allow infinite nesting. - Add {read,write}_nested() entrypoints that allow the crate user to control the depth of nesting that's allowed. - Along the way, convert the write[_nested] variants to return a `Result<(), EncoderError>` rather than a bool. This exposes more failure information (and forces the caller to take notice of those tailures), and allows use of the ? operator. * fixup: transmute error Co-authored-by: kaczmarczyck <43844792+kaczmarczyck@users.noreply.github.com> --- libraries/cbor/examples/cbor.rs | 4 +- .../fuzz/fuzz_targets/fuzz_target_cbor.rs | 2 +- libraries/cbor/src/reader.rs | 42 +++++----- libraries/cbor/src/values.rs | 4 +- libraries/cbor/src/writer.rs | 76 +++++++++++-------- src/ctap/command.rs | 21 ++--- src/ctap/config_command.rs | 5 +- src/ctap/credential_management.rs | 5 +- src/ctap/mod.rs | 32 +++++--- src/ctap/storage.rs | 19 ++--- 10 files changed, 111 insertions(+), 99 deletions(-) diff --git a/libraries/cbor/examples/cbor.rs b/libraries/cbor/examples/cbor.rs index 2e098c2..871be4e 100644 --- a/libraries/cbor/examples/cbor.rs +++ b/libraries/cbor/examples/cbor.rs @@ -58,10 +58,10 @@ fn main() { // Serialize to bytes. let mut manual_data = vec![]; - sk_cbor::writer::write(manual_object, &mut manual_data); + sk_cbor::writer::write(manual_object, &mut manual_data).unwrap(); let hex_manual_data = hexify(&manual_data); let mut macro_data = vec![]; - sk_cbor::writer::write(macro_object, &mut macro_data); + sk_cbor::writer::write(macro_object, &mut macro_data).unwrap(); let hex_macro_data = hexify(¯o_data); assert_eq!(hex_manual_data, hex_macro_data); diff --git a/libraries/cbor/fuzz/fuzz_targets/fuzz_target_cbor.rs b/libraries/cbor/fuzz/fuzz_targets/fuzz_target_cbor.rs index 2bacc55..6323968 100644 --- a/libraries/cbor/fuzz/fuzz_targets/fuzz_target_cbor.rs +++ b/libraries/cbor/fuzz/fuzz_targets/fuzz_target_cbor.rs @@ -8,7 +8,7 @@ use sk_cbor as cbor; fuzz_target!(|data: &[u8]| { if let Ok(value) = cbor::read(data) { let mut result = Vec::new(); - assert!(cbor::write(value, &mut result)); + assert!(cbor::write(value, &mut result).is_ok()); assert_eq!(result, data); }; }); diff --git a/libraries/cbor/src/reader.rs b/libraries/cbor/src/reader.rs index 17cc5b4..fa03521 100644 --- a/libraries/cbor/src/reader.rs +++ b/libraries/cbor/src/reader.rs @@ -37,11 +37,18 @@ pub enum DecoderError { OutOfRangeIntegerValue, } -/// Deserialize CBOR binary data to produce a single [`Value`], expecting that there -/// is no additional data. +/// Deserialize CBOR binary data to produce a single [`Value`], expecting that there is no additional data. +/// Supports arbitrarily nested CBOR (so the [`DecoderError::TooMuchNesting`] error is never emitted). pub fn read(encoded_cbor: &[u8]) -> Result { + read_nested(encoded_cbor, None) +} + +/// Deserialize CBOR binary data to produce a single [`Value`], expecting that there is no additional data. If +/// `max_nest` is `Some(max)`, then nested structures are only supported up to the given limit (returning +/// [`DecoderError::TooMuchNesting`] if the limit is hit). +pub fn read_nested(encoded_cbor: &[u8], max_nest: Option) -> Result { let mut reader = Reader::new(encoded_cbor); - let value = reader.decode_complete_data_item(Reader::MAX_NESTING_DEPTH)?; + let value = reader.decode_complete_data_item(max_nest)?; if !reader.remaining_cbor.is_empty() { return Err(DecoderError::ExtraneousData); } @@ -53,8 +60,6 @@ struct Reader<'a> { } impl<'a> Reader<'a> { - const MAX_NESTING_DEPTH: i8 = 4; - pub fn new(cbor: &'a [u8]) -> Reader<'a> { Reader { remaining_cbor: cbor, @@ -63,9 +68,9 @@ impl<'a> Reader<'a> { pub fn decode_complete_data_item( &mut self, - remaining_depth: i8, + remaining_depth: Option, ) -> Result { - if remaining_depth < 0 { + if remaining_depth.map_or(false, |d| d < 0) { return Err(DecoderError::TooMuchNesting); } @@ -162,12 +167,12 @@ impl<'a> Reader<'a> { fn read_array_content( &mut self, size_value: u64, - remaining_depth: i8, + remaining_depth: Option, ) -> Result { // Don't set the capacity already, it is an unsanitized input. let mut value_array = Vec::new(); for _ in 0..size_value { - value_array.push(self.decode_complete_data_item(remaining_depth - 1)?); + value_array.push(self.decode_complete_data_item(remaining_depth.map(|d| d - 1))?); } Ok(cbor_array_vec!(value_array)) } @@ -175,17 +180,20 @@ impl<'a> Reader<'a> { fn read_map_content( &mut self, size_value: u64, - remaining_depth: i8, + remaining_depth: Option, ) -> Result { let mut value_map = Vec::<(Value, Value)>::new(); for _ in 0..size_value { - let key = self.decode_complete_data_item(remaining_depth - 1)?; + let key = self.decode_complete_data_item(remaining_depth.map(|d| d - 1))?; if let Some(last_item) = value_map.last() { if last_item.0 >= key { return Err(DecoderError::OutOfOrderKey); } } - value_map.push((key, self.decode_complete_data_item(remaining_depth - 1)?)); + value_map.push(( + key, + self.decode_complete_data_item(remaining_depth.map(|d| d - 1))?, + )); } Ok(cbor_map_collection!(value_map)) } @@ -193,9 +201,9 @@ impl<'a> Reader<'a> { fn read_tagged_content( &mut self, tag_value: u64, - remaining_depth: i8, + remaining_depth: Option, ) -> Result { - let inner_value = self.decode_complete_data_item(remaining_depth - 1)?; + let inner_value = self.decode_complete_data_item(remaining_depth.map(|d| d - 1))?; Ok(cbor_tagged!(tag_value, inner_value)) } @@ -679,7 +687,7 @@ mod test { ]; for cbor in cases { let mut reader = Reader::new(&cbor); - assert!(reader.decode_complete_data_item(0).is_ok()); + assert!(reader.decode_complete_data_item(Some(0)).is_ok()); } let map_cbor = vec![ 0xa2, // map of 2 pairs @@ -690,11 +698,11 @@ mod test { ]; let mut reader = Reader::new(&map_cbor); assert_eq!( - reader.decode_complete_data_item(1), + reader.decode_complete_data_item(Some(1)), Err(DecoderError::TooMuchNesting) ); reader = Reader::new(&map_cbor); - assert!(reader.decode_complete_data_item(2).is_ok()); + assert!(reader.decode_complete_data_item(Some(2)).is_ok()); } #[test] diff --git a/libraries/cbor/src/values.rs b/libraries/cbor/src/values.rs index eee304c..a2e0cab 100644 --- a/libraries/cbor/src/values.rs +++ b/libraries/cbor/src/values.rs @@ -130,9 +130,9 @@ impl Ord for Value { (v1, v2) => { // This case could handle all of the above as well. Checking individually is faster. let mut encoding1 = Vec::new(); - write(v1.clone(), &mut encoding1); + let _ = write(v1.clone(), &mut encoding1); let mut encoding2 = Vec::new(); - write(v2.clone(), &mut encoding2); + let _ = write(v2.clone(), &mut encoding2); encoding1.cmp(&encoding2) } } diff --git a/libraries/cbor/src/writer.rs b/libraries/cbor/src/writer.rs index 4770cf7..f465010 100644 --- a/libraries/cbor/src/writer.rs +++ b/libraries/cbor/src/writer.rs @@ -17,11 +17,29 @@ use super::values::{Constants, Value}; use alloc::vec::Vec; +/// Possible errors from a serialization operation. +#[derive(Debug, PartialEq)] +pub enum EncoderError { + TooMuchNesting, + DuplicateMapKey, +} + /// Convert a [`Value`] to serialized CBOR data, consuming it along the way and appending to the provided vector. -/// Returns a `bool` indicating whether conversion succeeded. -pub fn write(value: Value, encoded_cbor: &mut Vec) -> bool { +/// Supports arbitrarily nested CBOR (so the [`EncoderError::TooMuchNesting`] error is never emitted). +pub fn write(value: Value, encoded_cbor: &mut Vec) -> Result<(), EncoderError> { + write_nested(value, encoded_cbor, None) +} + +/// Convert a [`Value`] to serialized CBOR data, consuming it along the way and appending to the provided vector. If +/// `max_nest` is `Some(max)`, then nested structures are only supported up to the given limit (returning +/// [`DecoderError::TooMuchNesting`] if the limit is hit). +pub fn write_nested( + value: Value, + encoded_cbor: &mut Vec, + max_nest: Option, +) -> Result<(), EncoderError> { let mut writer = Writer::new(encoded_cbor); - writer.encode_cbor(value, Writer::MAX_NESTING_DEPTH) + writer.encode_cbor(value, max_nest) } struct Writer<'a> { @@ -29,15 +47,17 @@ struct Writer<'a> { } impl<'a> Writer<'a> { - const MAX_NESTING_DEPTH: i8 = 4; - pub fn new(encoded_cbor: &mut Vec) -> Writer { Writer { encoded_cbor } } - fn encode_cbor(&mut self, value: Value, remaining_depth: i8) -> bool { - if remaining_depth < 0 { - return false; + fn encode_cbor( + &mut self, + value: Value, + remaining_depth: Option, + ) -> Result<(), EncoderError> { + if remaining_depth.map_or(false, |d| d < 0) { + return Err(EncoderError::TooMuchNesting); } let type_label = value.type_label(); match value { @@ -54,9 +74,7 @@ impl<'a> Writer<'a> { Value::Array(array) => { self.start_item(type_label, array.len() as u64); for el in array { - if !self.encode_cbor(el, remaining_depth - 1) { - return false; - } + self.encode_cbor(el, remaining_depth.map(|d| d - 1))?; } } Value::Map(mut map) => { @@ -64,27 +82,21 @@ impl<'a> Writer<'a> { let map_len = map.len(); map.dedup_by(|a, b| a.0.eq(&b.0)); if map_len != map.len() { - return false; + return Err(EncoderError::DuplicateMapKey); } self.start_item(type_label, map_len as u64); for (k, v) in map { - if !self.encode_cbor(k, remaining_depth - 1) { - return false; - } - if !self.encode_cbor(v, remaining_depth - 1) { - return false; - } + self.encode_cbor(k, remaining_depth.map(|d| d - 1))?; + self.encode_cbor(v, remaining_depth.map(|d| d - 1))?; } } Value::Tag(tag, inner_value) => { self.start_item(type_label, tag); - if !self.encode_cbor(*inner_value, remaining_depth - 1) { - return false; - } + self.encode_cbor(*inner_value, remaining_depth.map(|d| d - 1))?; } Value::Simple(simple_value) => self.start_item(type_label, simple_value as u64), } - true + Ok(()) } fn start_item(&mut self, type_label: u8, size: u64) { @@ -115,7 +127,7 @@ mod test { fn write_return(value: Value) -> Option> { let mut encoded_cbor = Vec::new(); - if write(value, &mut encoded_cbor) { + if write(value, &mut encoded_cbor).is_ok() { Some(encoded_cbor) } else { None @@ -459,12 +471,12 @@ mod test { for (value, level) in positive_cases { let mut buf = Vec::new(); let mut writer = Writer::new(&mut buf); - assert!(writer.encode_cbor(value, level)); + assert!(writer.encode_cbor(value, Some(level)).is_ok()); } for (value, level) in negative_cases { let mut buf = Vec::new(); let mut writer = Writer::new(&mut buf); - assert!(!writer.encode_cbor(value, level)); + assert!(!writer.encode_cbor(value, Some(level)).is_ok()); } } @@ -480,9 +492,10 @@ mod test { let mut buf = Vec::new(); let mut writer = Writer::new(&mut buf); - assert!(writer.encode_cbor(cbor_map.clone(), 2)); + assert!(writer.encode_cbor(cbor_map.clone(), Some(2)).is_ok()); + assert!(writer.encode_cbor(cbor_map.clone(), None).is_ok()); writer = Writer::new(&mut buf); - assert!(!writer.encode_cbor(cbor_map, 1)); + assert!(writer.encode_cbor(cbor_map, Some(1)).is_err()); } #[test] @@ -502,9 +515,9 @@ mod test { let mut buf = Vec::new(); let mut writer = Writer::new(&mut buf); - assert!(writer.encode_cbor(cbor_array.clone(), 3)); + assert!(writer.encode_cbor(cbor_array.clone(), Some(3)).is_ok()); writer = Writer::new(&mut buf); - assert!(!writer.encode_cbor(cbor_array, 2)); + assert!(writer.encode_cbor(cbor_array, Some(2)).is_err()); } #[test] @@ -530,8 +543,9 @@ mod test { let mut buf = Vec::new(); let mut writer = Writer::new(&mut buf); - assert!(writer.encode_cbor(cbor_map.clone(), 5)); + assert!(writer.encode_cbor(cbor_map.clone(), Some(5)).is_ok()); + assert!(writer.encode_cbor(cbor_map.clone(), None).is_ok()); writer = Writer::new(&mut buf); - assert!(!writer.encode_cbor(cbor_map, 4)); + assert!(writer.encode_cbor(cbor_map, Some(4)).is_err()); } } diff --git a/src/ctap/command.rs b/src/ctap/command.rs index db4b07c..4d7f965 100644 --- a/src/ctap/command.rs +++ b/src/ctap/command.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::cbor_read; use super::customization::{MAX_CREDENTIAL_COUNT_IN_LIST, MAX_LARGE_BLOB_ARRAY_SIZE}; use super::data_formats::{ extract_array, extract_bool, extract_byte_string, extract_map, extract_text_string, @@ -50,12 +51,6 @@ pub enum Command { AuthenticatorVendorConfigure(AuthenticatorVendorConfigureParameters), } -impl From for Ctap2StatusCode { - fn from(_: cbor::reader::DecoderError) -> Self { - Ctap2StatusCode::CTAP2_ERR_INVALID_CBOR - } -} - impl Command { const AUTHENTICATOR_MAKE_CREDENTIAL: u8 = 0x01; const AUTHENTICATOR_GET_ASSERTION: u8 = 0x02; @@ -82,13 +77,13 @@ impl Command { let command_value = bytes[0]; match command_value { Command::AUTHENTICATOR_MAKE_CREDENTIAL => { - let decoded_cbor = cbor::read(&bytes[1..])?; + let decoded_cbor = cbor_read(&bytes[1..])?; Ok(Command::AuthenticatorMakeCredential( AuthenticatorMakeCredentialParameters::try_from(decoded_cbor)?, )) } Command::AUTHENTICATOR_GET_ASSERTION => { - let decoded_cbor = cbor::read(&bytes[1..])?; + let decoded_cbor = cbor_read(&bytes[1..])?; Ok(Command::AuthenticatorGetAssertion( AuthenticatorGetAssertionParameters::try_from(decoded_cbor)?, )) @@ -98,7 +93,7 @@ impl Command { Ok(Command::AuthenticatorGetInfo) } Command::AUTHENTICATOR_CLIENT_PIN => { - let decoded_cbor = cbor::read(&bytes[1..])?; + let decoded_cbor = cbor_read(&bytes[1..])?; Ok(Command::AuthenticatorClientPin( AuthenticatorClientPinParameters::try_from(decoded_cbor)?, )) @@ -112,7 +107,7 @@ impl Command { Ok(Command::AuthenticatorGetNextAssertion) } Command::AUTHENTICATOR_CREDENTIAL_MANAGEMENT => { - let decoded_cbor = cbor::read(&bytes[1..])?; + let decoded_cbor = cbor_read(&bytes[1..])?; Ok(Command::AuthenticatorCredentialManagement( AuthenticatorCredentialManagementParameters::try_from(decoded_cbor)?, )) @@ -122,19 +117,19 @@ impl Command { Ok(Command::AuthenticatorSelection) } Command::AUTHENTICATOR_LARGE_BLOBS => { - let decoded_cbor = cbor::read(&bytes[1..])?; + let decoded_cbor = cbor_read(&bytes[1..])?; Ok(Command::AuthenticatorLargeBlobs( AuthenticatorLargeBlobsParameters::try_from(decoded_cbor)?, )) } Command::AUTHENTICATOR_CONFIG => { - let decoded_cbor = cbor::read(&bytes[1..])?; + let decoded_cbor = cbor_read(&bytes[1..])?; Ok(Command::AuthenticatorConfig( AuthenticatorConfigParameters::try_from(decoded_cbor)?, )) } Command::AUTHENTICATOR_VENDOR_CONFIGURE => { - let decoded_cbor = cbor::read(&bytes[1..])?; + let decoded_cbor = cbor_read(&bytes[1..])?; Ok(Command::AuthenticatorVendorConfigure( AuthenticatorVendorConfigureParameters::try_from(decoded_cbor)?, )) diff --git a/src/ctap/config_command.rs b/src/ctap/config_command.rs index 943366a..54daeba 100644 --- a/src/ctap/config_command.rs +++ b/src/ctap/config_command.rs @@ -20,7 +20,6 @@ use super::response::ResponseData; use super::status_code::Ctap2StatusCode; use super::storage::PersistentStore; use alloc::vec; -use sk_cbor as cbor; /// Processes the subcommand enableEnterpriseAttestation for AuthenticatorConfig. fn process_enable_enterprise_attestation( @@ -100,9 +99,7 @@ pub fn process_config( let mut config_data = vec![0xFF; 32]; config_data.extend(&[0x0D, sub_command as u8]); if let Some(sub_command_params) = sub_command_params.clone() { - if !cbor::write(sub_command_params.into(), &mut config_data) { - return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); - } + super::cbor_write(sub_command_params.into(), &mut config_data)?; } client_pin.verify_pin_uv_auth_token( &config_data, diff --git a/src/ctap/credential_management.rs b/src/ctap/credential_management.rs index e99e86e..f31ea43 100644 --- a/src/ctap/credential_management.rs +++ b/src/ctap/credential_management.rs @@ -30,7 +30,6 @@ use alloc::vec::Vec; use crypto::sha256::Sha256; use crypto::Hash256; use libtock_drivers::timer::ClockValue; -use sk_cbor as cbor; /// Generates a set with all existing RP IDs. fn get_stored_rp_ids( @@ -289,9 +288,7 @@ pub fn process_credential_management( pin_uv_auth_protocol.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?; let mut management_data = vec![sub_command as u8]; if let Some(sub_command_params) = sub_command_params.clone() { - if !cbor::write(sub_command_params.into(), &mut management_data) { - return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); - } + super::cbor_write(sub_command_params.into(), &mut management_data)?; } client_pin.verify_pin_uv_auth_token( &management_data, diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index c1f069b..47a16c2 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -98,6 +98,9 @@ const AT_FLAG: u8 = 0x40; // Set this bit when an extension is used. const ED_FLAG: u8 = 0x80; +// CTAP2 specification section 6 requires that the depth of nested CBOR structures be limited to at most four levels. +const MAX_CBOR_NESTING_DEPTH: i8 = 4; + pub const TOUCH_TIMEOUT_MS: isize = 30000; #[cfg(feature = "with_ctap1")] const U2F_UP_PROMPT_TIMEOUT: Duration = Duration::from_ms(10000); @@ -118,6 +121,17 @@ pub const ES256_CRED_PARAM: PublicKeyCredentialParameter = PublicKeyCredentialPa alg: SignatureAlgorithm::ES256, }; +// Helpers to perform CBOR read/write while respecting CTAP2 nesting limits. +fn cbor_read(encoded_cbor: &[u8]) -> Result { + cbor::reader::read_nested(encoded_cbor, Some(MAX_CBOR_NESTING_DEPTH)) + .map_err(|_e| Ctap2StatusCode::CTAP2_ERR_INVALID_CBOR) +} + +fn cbor_write(value: cbor::Value, mut encoded_cbor: &mut Vec) -> Result<(), Ctap2StatusCode> { + cbor::writer::write_nested(value, &mut encoded_cbor, Some(MAX_CBOR_NESTING_DEPTH)) + .map_err(|_e| Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR) +} + // This function is adapted from https://doc.rust-lang.org/nightly/src/core/str/mod.rs.html#2110 // (as of 2020-01-20) and truncates to "max" bytes, not breaking the encoding. // We change the return value, since we don't need the bool. @@ -474,7 +488,7 @@ where Ok(response_data) => { let mut response_vec = vec![0x00]; if let Some(value) = response_data.into() { - if !cbor::write(value, &mut response_vec) { + if cbor_write(value, &mut response_vec).is_err() { response_vec = vec![Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR as u8]; } @@ -690,9 +704,7 @@ where } auth_data.extend(vec![0x00, credential_id.len() as u8]); auth_data.extend(&credential_id); - if !cbor::write(cbor::Value::from(CoseKey::from(pk)), &mut auth_data) { - return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); - } + cbor_write(cbor::Value::from(CoseKey::from(pk)), &mut auth_data)?; if has_extension_output { let hmac_secret_output = if extensions.hmac_secret { Some(true) @@ -711,9 +723,7 @@ where "hmac-secret" => hmac_secret_output, "minPinLength" => min_pin_length_output, }; - if !cbor::write(extensions_output, &mut auth_data) { - return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); - } + cbor_write(extensions_output, &mut auth_data)?; } let mut signature_data = auth_data.clone(); @@ -808,9 +818,7 @@ where "credBlob" => cred_blob, "hmac-secret" => encrypted_output, }; - if !cbor::write(extensions_output, &mut auth_data) { - return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); - } + cbor_write(extensions_output, &mut auth_data)?; } let large_blob_key = match extensions.large_blob_key { Some(true) => credential.large_blob_key, @@ -1324,7 +1332,7 @@ mod test { }; let mut response_cbor = vec![0x00]; - assert!(cbor::write(expected_cbor, &mut response_cbor)); + assert!(cbor_write(expected_cbor, &mut response_cbor).is_ok()); assert_eq!(info_reponse, response_cbor); } @@ -2646,7 +2654,7 @@ mod test { }, 4 => cbor_array![ES256_CRED_PARAM], }; - assert!(cbor::write(cbor_value, &mut command_cbor)); + assert!(cbor_write(cbor_value, &mut command_cbor).is_ok()); ctap_state.process_command(&command_cbor, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE); diff --git a/src/ctap/storage.rs b/src/ctap/storage.rs index 77afb46..9e1127e 100644 --- a/src/ctap/storage.rs +++ b/src/ctap/storage.rs @@ -36,7 +36,6 @@ use core::cmp; use core::convert::TryInto; use crypto::rng256::Rng256; use persistent_store::{fragment, StoreUpdate}; -use sk_cbor as cbor; use sk_cbor::cbor_array_vec; /// Wrapper for master keys. @@ -721,23 +720,20 @@ impl<'a> Iterator for IterCredentials<'a> { /// Deserializes a credential from storage representation. fn deserialize_credential(data: &[u8]) -> Option { - let cbor = cbor::read(data).ok()?; + let cbor = super::cbor_read(data).ok()?; cbor.try_into().ok() } /// Serializes a credential to storage representation. fn serialize_credential(credential: PublicKeyCredentialSource) -> Result, Ctap2StatusCode> { let mut data = Vec::new(); - if cbor::write(credential.into(), &mut data) { - Ok(data) - } else { - Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR) - } + super::cbor_write(credential.into(), &mut data)?; + Ok(data) } /// Deserializes a list of RP IDs from storage representation. fn deserialize_min_pin_length_rp_ids(data: &[u8]) -> Option> { - let cbor = cbor::read(data).ok()?; + let cbor = super::cbor_read(data).ok()?; extract_array(cbor) .ok()? .into_iter() @@ -749,11 +745,8 @@ fn deserialize_min_pin_length_rp_ids(data: &[u8]) -> Option> { /// Serializes a list of RP IDs to storage representation. fn serialize_min_pin_length_rp_ids(rp_ids: Vec) -> Result, Ctap2StatusCode> { let mut data = Vec::new(); - if cbor::write(cbor_array_vec!(rp_ids), &mut data) { - Ok(data) - } else { - Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR) - } + super::cbor_write(cbor_array_vec!(rp_ids), &mut data)?; + Ok(data) } #[cfg(test)]