diff --git a/reproducible/binaries.sha256sum b/reproducible/binaries.sha256sum
deleted file mode 100644
index 6858153d17e1a2491fa968c67cbef248393acce8..0000000000000000000000000000000000000000
--- a/reproducible/binaries.sha256sum
+++ /dev/null
@@ -1,9 +0,0 @@
-c182bb4902fff51b2f56810fc2a27df3646cd66ba21359162354d53445623ab8	third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin
-2c2879a0263ebaa6e841db4b352346cc5b4cef5084ce85525cf49669d3b0b41d	target/nrf52840dk_merged.hex
-0a9929ba8fa57e8a502a49fc7c53177397202e1b11f4c7c3cb6ed68b2b99dd46	third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin
-652824a90674dc3199070f2f46a791ab4951e367982ecae6f65fc41338a5a856	target/nrf52840_dongle_merged.hex
-cca9086c9149c607589b23ffa599a5e4c26db7c20bd3700b79528bd3a5df991d	third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin
-a030505f5576129954a3977f97957b8b4e023b2b51a29d45d4511566458666ac	target/nrf52840_dongle_dfu_merged.hex
-8857488ba6a69e366f0da229bbfc012a2ad291d3a88d9494247d600c10bb19b7	third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin
-82ac4290967ae67a78c986444fd6c2a2aa5668ac254a2af642c98be4a064f913	target/nrf52840_mdk_dfu_merged.hex
-c3e901a80fd779b15a6f266e48dcef5140b318f5f62b23a96547498572ac1666	target/tab/ctap2.tab
diff --git a/reproducible/reference_binaries_macos-10.15.sha256sum b/reproducible/reference_binaries_macos-10.15.sha256sum
index bb62ef986f45ea6808cba8e72debc1bda67b1561..0228740f016945649827a653187b83a37e147d99 100644
--- a/reproducible/reference_binaries_macos-10.15.sha256sum
+++ b/reproducible/reference_binaries_macos-10.15.sha256sum
@@ -1,9 +1,9 @@
 1003863864e06553e730eec6df4bf8d30c99f697ef9380efdc35eba679b4db78	third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin
-63dda5b708add5ac72db0c0757de451f95173b0fc7d71fc85c3b25460850e23c	target/nrf52840dk_merged.hex
+022268c93fa8bbd9e54e082982b87c10a0e7c0486704de8219d1bb374304636a	target/nrf52840dk_merged.hex
 88f00a5e1dae6ab3f7571c254ac75f5f3e29ebea7f3ca46c16cfdc3708e804fc	third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin
-2fca47df0053c6750d9f8fe61e8e6a0e9eee2457c908aa1c2225e2aab2690cb4	target/nrf52840_dongle_merged.hex
+8d68ecc700527789b8edf318f0872ca8fc3b72fa73236f4e06bec89a3682fcf8	target/nrf52840_dongle_merged.hex
 1bc69b48a2c48da55db8b322902e1fe3f2e095c0dd8517db28837d86e0addc85	third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin
-33b5a96bb427de653440cd8bfa31e518e1a2c269368a6cd103602d2fd6f3e127	target/nrf52840_dongle_dfu_merged.hex
+af5465e4209914aaf74ee878d03e883a717827119e47b9295aa279ee21f0c5f4	target/nrf52840_dongle_dfu_merged.hex
 f38ee31d3a09e7e11848e78b5318f95517b6dcd076afcb37e6e3d3e5e9995cc7	third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin
-dce8f1f02e2f7e93634b1edd02343547e139445d23c2d17ce6ccdc4895a67c8c	target/nrf52840_mdk_dfu_merged.hex
-1e216599d58e6c66845845a2ec709f72842e21d48a2185022223d1ffe006de07	target/tab/ctap2.tab
+23603386a615e4e8cb2173c5ce4762110e6cbb979efdbb6e8bef9bc1e3988de4	target/nrf52840_mdk_dfu_merged.hex
+c2cbcc28b835934be4c3d3e3c5bdaba642a5811d760c1d2cb73d26b6474e4219	target/tab/ctap2.tab
diff --git a/reproducible/reference_binaries_ubuntu-18.04.sha256sum b/reproducible/reference_binaries_ubuntu-18.04.sha256sum
index d57d20c51f43956b184aadded1a64dc5396e2a2e..666eecd0eb3f10daa9893fe13acb5ccb7bed6970 100644
--- a/reproducible/reference_binaries_ubuntu-18.04.sha256sum
+++ b/reproducible/reference_binaries_ubuntu-18.04.sha256sum
@@ -1,9 +1,9 @@
 c182bb4902fff51b2f56810fc2a27df3646cd66ba21359162354d53445623ab8	third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin
-d978bbb7d56e54f1d8dc43a8b73e01ede50f404f8640ed8969112acac9efa36e	target/nrf52840dk_merged.hex
+d8b62ece387a77cc21f2c10a5f5d65d0d57bf4739b47fd86d2c9ecdd90fbfd7e	target/nrf52840dk_merged.hex
 0a9929ba8fa57e8a502a49fc7c53177397202e1b11f4c7c3cb6ed68b2b99dd46	third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin
-53f3f390fce878d4d6108d34a0f4f305375ca125a02a4160839eeee3acd55e88	target/nrf52840_dongle_merged.hex
+380de1a910b4d9eeb0c814b11b074b2e66334968cc99a4bd34d52a1fce3c5a79	target/nrf52840_dongle_merged.hex
 cca9086c9149c607589b23ffa599a5e4c26db7c20bd3700b79528bd3a5df991d	third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin
-68ef4c5d2e5b53761c31e185fa6237eaaa13736f8e0657c68d235f27497efe85	target/nrf52840_dongle_dfu_merged.hex
+4edd988b3e37991f1e58fc520e41f7666f8ae3e8d3993e1bb2fb71657a71fa50	target/nrf52840_dongle_dfu_merged.hex
 8857488ba6a69e366f0da229bbfc012a2ad291d3a88d9494247d600c10bb19b7	third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin
-898842aeaf9a7b03f3f5828871fc8a41fff8fc550683c18f9cfd718f8da26ba4	target/nrf52840_mdk_dfu_merged.hex
-d7d8e13bd8a183a6868a463512a999e1c3843d9d5b13f1d35909bfa80d24619e	target/tab/ctap2.tab
+a51aba1cd12e55aa33fd9017af406583ebf14e1c690295b15cf147713dfe2561	target/nrf52840_mdk_dfu_merged.hex
+40b413a8b645b4b47fae62a4311acb12cb0c57faff2757e45c18d9e5d441e52d	target/tab/ctap2.tab
diff --git a/reproducible/reproduced.tar b/reproducible/reproduced.tar
deleted file mode 100644
index 96616b5374f432ffd0eec49b727128ee17c52d4b..0000000000000000000000000000000000000000
Binary files a/reproducible/reproduced.tar and /dev/null differ
diff --git a/src/ctap/command.rs b/src/ctap/command.rs
index 19132bc9d1363aea7ff2839da3a9f86b6726135e..d6dd0fab10085747eff73e306ff61e49c1192f8e 100644
--- a/src/ctap/command.rs
+++ b/src/ctap/command.rs
@@ -13,10 +13,10 @@
 // limitations under the License.
 
 use super::data_formats::{
-    ok_or_missing, read_array, read_byte_string, read_map, read_text_string, read_unsigned,
-    ClientPinSubCommand, CoseKey, Extensions, GetAssertionOptions, MakeCredentialOptions,
-    PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialRpEntity,
-    PublicKeyCredentialUserEntity,
+    extract_array, extract_byte_string, extract_map, extract_text_string, extract_unsigned,
+    ok_or_missing, ClientPinSubCommand, CoseKey, GetAssertionExtensions, GetAssertionOptions,
+    MakeCredentialExtensions, MakeCredentialOptions, PublicKeyCredentialDescriptor,
+    PublicKeyCredentialParameter, PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity,
 };
 use super::status_code::Ctap2StatusCode;
 use alloc::string::String;
@@ -113,7 +113,7 @@ pub struct AuthenticatorMakeCredentialParameters {
     pub user: PublicKeyCredentialUserEntity,
     pub pub_key_cred_params: Vec<PublicKeyCredentialParameter>,
     pub exclude_list: Option<Vec<PublicKeyCredentialDescriptor>>,
-    pub extensions: Option<Extensions>,
+    pub extensions: Option<MakeCredentialExtensions>,
     // Even though options are optional, we can use the default if not present.
     pub options: MakeCredentialOptions,
     pub pin_uv_auth_param: Option<Vec<u8>>,
@@ -124,30 +124,32 @@ impl TryFrom<cbor::Value> for AuthenticatorMakeCredentialParameters {
     type Error = Ctap2StatusCode;
 
     fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let param_map = read_map(&cbor_value)?;
+        let mut param_map = extract_map(cbor_value)?;
 
-        let client_data_hash = read_byte_string(ok_or_missing(param_map.get(&cbor_unsigned!(1)))?)?;
+        let client_data_hash =
+            extract_byte_string(ok_or_missing(param_map.remove(&cbor_unsigned!(1)))?)?;
 
         let rp = PublicKeyCredentialRpEntity::try_from(ok_or_missing(
-            param_map.get(&cbor_unsigned!(2)),
+            param_map.remove(&cbor_unsigned!(2)),
         )?)?;
 
         let user = PublicKeyCredentialUserEntity::try_from(ok_or_missing(
-            param_map.get(&cbor_unsigned!(3)),
+            param_map.remove(&cbor_unsigned!(3)),
         )?)?;
 
-        let cred_param_vec = read_array(ok_or_missing(param_map.get(&cbor_unsigned!(4)))?)?;
+        let cred_param_vec = extract_array(ok_or_missing(param_map.remove(&cbor_unsigned!(4)))?)?;
         let pub_key_cred_params = cred_param_vec
-            .iter()
+            .into_iter()
             .map(PublicKeyCredentialParameter::try_from)
             .collect::<Result<Vec<PublicKeyCredentialParameter>, Ctap2StatusCode>>()?;
 
-        let exclude_list = match param_map.get(&cbor_unsigned!(5)) {
+        let exclude_list = match param_map.remove(&cbor_unsigned!(5)) {
             Some(entry) => {
-                let exclude_list_vec = read_array(entry)?;
+                let exclude_list_vec = extract_array(entry)?;
+                let list_len = MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(exclude_list_vec.len());
                 let exclude_list = exclude_list_vec
-                    .iter()
-                    .take(MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(exclude_list_vec.len()))
+                    .into_iter()
+                    .take(list_len)
                     .map(PublicKeyCredentialDescriptor::try_from)
                     .collect::<Result<Vec<PublicKeyCredentialDescriptor>, Ctap2StatusCode>>()?;
                 Some(exclude_list)
@@ -156,11 +158,11 @@ impl TryFrom<cbor::Value> for AuthenticatorMakeCredentialParameters {
         };
 
         let extensions = param_map
-            .get(&cbor_unsigned!(6))
-            .map(Extensions::try_from)
+            .remove(&cbor_unsigned!(6))
+            .map(MakeCredentialExtensions::try_from)
             .transpose()?;
 
-        let options = match param_map.get(&cbor_unsigned!(7)) {
+        let options = match param_map.remove(&cbor_unsigned!(7)) {
             Some(entry) => MakeCredentialOptions::try_from(entry)?,
             None => MakeCredentialOptions {
                 rk: false,
@@ -169,13 +171,13 @@ impl TryFrom<cbor::Value> for AuthenticatorMakeCredentialParameters {
         };
 
         let pin_uv_auth_param = param_map
-            .get(&cbor_unsigned!(8))
-            .map(read_byte_string)
+            .remove(&cbor_unsigned!(8))
+            .map(extract_byte_string)
             .transpose()?;
 
         let pin_uv_auth_protocol = param_map
-            .get(&cbor_unsigned!(9))
-            .map(read_unsigned)
+            .remove(&cbor_unsigned!(9))
+            .map(extract_unsigned)
             .transpose()?;
 
         Ok(AuthenticatorMakeCredentialParameters {
@@ -197,7 +199,7 @@ pub struct AuthenticatorGetAssertionParameters {
     pub rp_id: String,
     pub client_data_hash: Vec<u8>,
     pub allow_list: Option<Vec<PublicKeyCredentialDescriptor>>,
-    pub extensions: Option<Extensions>,
+    pub extensions: Option<GetAssertionExtensions>,
     // Even though options are optional, we can use the default if not present.
     pub options: GetAssertionOptions,
     pub pin_uv_auth_param: Option<Vec<u8>>,
@@ -208,18 +210,20 @@ impl TryFrom<cbor::Value> for AuthenticatorGetAssertionParameters {
     type Error = Ctap2StatusCode;
 
     fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let param_map = read_map(&cbor_value)?;
+        let mut param_map = extract_map(cbor_value)?;
 
-        let rp_id = read_text_string(ok_or_missing(param_map.get(&cbor_unsigned!(1)))?)?;
+        let rp_id = extract_text_string(ok_or_missing(param_map.remove(&cbor_unsigned!(1)))?)?;
 
-        let client_data_hash = read_byte_string(ok_or_missing(param_map.get(&cbor_unsigned!(2)))?)?;
+        let client_data_hash =
+            extract_byte_string(ok_or_missing(param_map.remove(&cbor_unsigned!(2)))?)?;
 
-        let allow_list = match param_map.get(&cbor_unsigned!(3)) {
+        let allow_list = match param_map.remove(&cbor_unsigned!(3)) {
             Some(entry) => {
-                let allow_list_vec = read_array(entry)?;
+                let allow_list_vec = extract_array(entry)?;
+                let list_len = MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(allow_list_vec.len());
                 let allow_list = allow_list_vec
-                    .iter()
-                    .take(MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(allow_list_vec.len()))
+                    .into_iter()
+                    .take(list_len)
                     .map(PublicKeyCredentialDescriptor::try_from)
                     .collect::<Result<Vec<PublicKeyCredentialDescriptor>, Ctap2StatusCode>>()?;
                 Some(allow_list)
@@ -228,11 +232,11 @@ impl TryFrom<cbor::Value> for AuthenticatorGetAssertionParameters {
         };
 
         let extensions = param_map
-            .get(&cbor_unsigned!(4))
-            .map(Extensions::try_from)
+            .remove(&cbor_unsigned!(4))
+            .map(GetAssertionExtensions::try_from)
             .transpose()?;
 
-        let options = match param_map.get(&cbor_unsigned!(5)) {
+        let options = match param_map.remove(&cbor_unsigned!(5)) {
             Some(entry) => GetAssertionOptions::try_from(entry)?,
             None => GetAssertionOptions {
                 up: true,
@@ -241,13 +245,13 @@ impl TryFrom<cbor::Value> for AuthenticatorGetAssertionParameters {
         };
 
         let pin_uv_auth_param = param_map
-            .get(&cbor_unsigned!(6))
-            .map(read_byte_string)
+            .remove(&cbor_unsigned!(6))
+            .map(extract_byte_string)
             .transpose()?;
 
         let pin_uv_auth_protocol = param_map
-            .get(&cbor_unsigned!(7))
-            .map(read_unsigned)
+            .remove(&cbor_unsigned!(7))
+            .map(extract_unsigned)
             .transpose()?;
 
         Ok(AuthenticatorGetAssertionParameters {
@@ -276,32 +280,32 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
     type Error = Ctap2StatusCode;
 
     fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let param_map = read_map(&cbor_value)?;
+        let mut param_map = extract_map(cbor_value)?;
 
-        let pin_protocol = read_unsigned(ok_or_missing(param_map.get(&cbor_unsigned!(1)))?)?;
+        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.get(&cbor_unsigned!(2)))?)?;
+            ClientPinSubCommand::try_from(ok_or_missing(param_map.remove(&cbor_unsigned!(2)))?)?;
 
         let key_agreement = param_map
-            .get(&cbor_unsigned!(3))
-            .map(read_map)
+            .remove(&cbor_unsigned!(3))
+            .map(extract_map)
             .transpose()?
-            .map(|x| CoseKey(x.clone()));
+            .map(|x| CoseKey(x));
 
         let pin_auth = param_map
-            .get(&cbor_unsigned!(4))
-            .map(read_byte_string)
+            .remove(&cbor_unsigned!(4))
+            .map(extract_byte_string)
             .transpose()?;
 
         let new_pin_enc = param_map
-            .get(&cbor_unsigned!(5))
-            .map(read_byte_string)
+            .remove(&cbor_unsigned!(5))
+            .map(extract_byte_string)
             .transpose()?;
 
         let pin_hash_enc = param_map
-            .get(&cbor_unsigned!(6))
-            .map(read_byte_string)
+            .remove(&cbor_unsigned!(6))
+            .map(extract_byte_string)
             .transpose()?;
 
         Ok(AuthenticatorClientPinParameters {
diff --git a/src/ctap/data_formats.rs b/src/ctap/data_formats.rs
index d226fca34941c0f0c3a1f485f3d79f14e4c45f9f..33dec12ec62b5d2f4afa4f071997624a880b0df5 100644
--- a/src/ctap/data_formats.rs
+++ b/src/ctap/data_formats.rs
@@ -14,7 +14,7 @@
 
 use super::status_code::Ctap2StatusCode;
 use alloc::collections::BTreeMap;
-use alloc::string::{String, ToString};
+use alloc::string::String;
 use alloc::vec::Vec;
 use core::convert::TryFrom;
 use crypto::{ecdh, ecdsa};
@@ -27,19 +27,19 @@ pub struct PublicKeyCredentialRpEntity {
     pub rp_icon: Option<String>,
 }
 
-impl TryFrom<&cbor::Value> for PublicKeyCredentialRpEntity {
+impl TryFrom<cbor::Value> for PublicKeyCredentialRpEntity {
     type Error = Ctap2StatusCode;
 
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let rp_map = read_map(cbor_value)?;
-        let rp_id = read_text_string(ok_or_missing(rp_map.get(&cbor_text!("id")))?)?;
+    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
-            .get(&cbor_text!("name"))
-            .map(read_text_string)
+            .remove(&cbor_text!("name"))
+            .map(extract_text_string)
             .transpose()?;
         let rp_icon = rp_map
-            .get(&cbor_text!("icon"))
-            .map(read_text_string)
+            .remove(&cbor_text!("icon"))
+            .map(extract_text_string)
             .transpose()?;
         Ok(Self {
             rp_id,
@@ -58,23 +58,23 @@ pub struct PublicKeyCredentialUserEntity {
     pub user_icon: Option<String>,
 }
 
-impl TryFrom<&cbor::Value> for PublicKeyCredentialUserEntity {
+impl TryFrom<cbor::Value> for PublicKeyCredentialUserEntity {
     type Error = Ctap2StatusCode;
 
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let user_map = read_map(cbor_value)?;
-        let user_id = read_byte_string(ok_or_missing(user_map.get(&cbor_text!("id")))?)?;
+    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
-            .get(&cbor_text!("name"))
-            .map(read_text_string)
+            .remove(&cbor_text!("name"))
+            .map(extract_text_string)
             .transpose()?;
         let user_display_name = user_map
-            .get(&cbor_text!("displayName"))
-            .map(read_text_string)
+            .remove(&cbor_text!("displayName"))
+            .map(extract_text_string)
             .transpose()?;
         let user_icon = user_map
-            .get(&cbor_text!("icon"))
-            .map(read_text_string)
+            .remove(&cbor_text!("icon"))
+            .map(extract_text_string)
             .transpose()?;
         Ok(Self {
             user_id,
@@ -117,11 +117,11 @@ impl From<PublicKeyCredentialType> for cbor::Value {
     }
 }
 
-impl TryFrom<&cbor::Value> for PublicKeyCredentialType {
+impl TryFrom<cbor::Value> for PublicKeyCredentialType {
     type Error = Ctap2StatusCode;
 
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let cred_type_string = read_text_string(cbor_value)?;
+    fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
+        let cred_type_string = extract_text_string(cbor_value)?;
         match &cred_type_string[..] {
             "public-key" => Ok(PublicKeyCredentialType::PublicKey),
             _ => Ok(PublicKeyCredentialType::Unknown),
@@ -137,16 +137,17 @@ pub struct PublicKeyCredentialParameter {
     pub alg: SignatureAlgorithm,
 }
 
-impl TryFrom<&cbor::Value> for PublicKeyCredentialParameter {
+impl TryFrom<cbor::Value> for PublicKeyCredentialParameter {
     type Error = Ctap2StatusCode;
 
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let cred_param_map = read_map(cbor_value)?;
+    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.get(&cbor_text!("type")),
+            cred_param_map.remove(&cbor_text!("type")),
+        )?)?;
+        let alg = SignatureAlgorithm::try_from(ok_or_missing(
+            cred_param_map.remove(&cbor_text!("alg")),
         )?)?;
-        let alg =
-            SignatureAlgorithm::try_from(ok_or_missing(cred_param_map.get(&cbor_text!("alg")))?)?;
         Ok(Self { cred_type, alg })
     }
 }
@@ -181,11 +182,11 @@ impl From<AuthenticatorTransport> for cbor::Value {
     }
 }
 
-impl TryFrom<&cbor::Value> for AuthenticatorTransport {
+impl TryFrom<cbor::Value> for AuthenticatorTransport {
     type Error = Ctap2StatusCode;
 
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let transport_string = read_text_string(cbor_value)?;
+    fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
+        let transport_string = extract_text_string(cbor_value)?;
         match &transport_string[..] {
             "usb" => Ok(AuthenticatorTransport::Usb),
             "nfc" => Ok(AuthenticatorTransport::Nfc),
@@ -204,23 +205,22 @@ pub struct PublicKeyCredentialDescriptor {
     pub transports: Option<Vec<AuthenticatorTransport>>,
 }
 
-impl TryFrom<&cbor::Value> for PublicKeyCredentialDescriptor {
+impl TryFrom<cbor::Value> for PublicKeyCredentialDescriptor {
     type Error = Ctap2StatusCode;
 
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let cred_desc_map = read_map(cbor_value)?;
+    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.get(&cbor_text!("type")),
+            cred_desc_map.remove(&cbor_text!("type")),
         )?)?;
-        let key_id = read_byte_string(ok_or_missing(cred_desc_map.get(&cbor_text!("id")))?)?;
-        let transports = match cred_desc_map.get(&cbor_text!("transports")) {
+        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")) {
             Some(exclude_entry) => {
-                let transport_vec = read_array(exclude_entry)?;
+                let transport_vec = extract_array(exclude_entry)?;
                 let transports = transport_vec
-                    .iter()
+                    .into_iter()
                     .map(AuthenticatorTransport::try_from)
-                    .collect::<Result<Vec<AuthenticatorTransport>, Ctap2StatusCode>>(
-                )?;
+                    .collect::<Result<Vec<AuthenticatorTransport>, Ctap2StatusCode>>()?;
                 Some(transports)
             }
             None => None,
@@ -243,105 +243,72 @@ impl From<PublicKeyCredentialDescriptor> for cbor::Value {
     }
 }
 
-#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
-pub struct Extensions(BTreeMap<String, cbor::Value>);
+#[cfg_attr(any(test, feature = "debug_ctap"), derive(Clone, Debug, PartialEq))]
+pub struct MakeCredentialExtensions {
+    pub hmac_secret: bool,
+    pub cred_protect: Option<CredentialProtectionPolicy>,
+}
 
-impl TryFrom<&cbor::Value> for Extensions {
+impl TryFrom<cbor::Value> for MakeCredentialExtensions {
     type Error = Ctap2StatusCode;
 
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let mut extensions = BTreeMap::new();
-        for (extension_key, extension_value) in read_map(cbor_value)? {
-            if let cbor::KeyType::TextString(extension_key_string) = extension_key {
-                extensions.insert(extension_key_string.to_string(), extension_value.clone());
-            } else {
-                return Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
-            }
-        }
-        Ok(Extensions(extensions))
+    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"))
+            .map(CredentialProtectionPolicy::try_from)
+            .transpose()?;
+        Ok(Self {
+            hmac_secret,
+            cred_protect,
+        })
     }
 }
 
-impl From<Extensions> for cbor::Value {
-    fn from(extensions: Extensions) -> Self {
-        cbor_map_btree!(extensions
-            .0
-            .into_iter()
-            .map(|(key, value)| (cbor_text!(key), value))
-            .collect())
-    }
+#[cfg_attr(any(test, feature = "debug_ctap"), derive(Clone, Debug, PartialEq))]
+pub struct GetAssertionExtensions {
+    pub hmac_secret: Option<GetAssertionHmacSecretInput>,
 }
 
-impl Extensions {
-    #[cfg(test)]
-    pub fn new(extension_map: BTreeMap<String, cbor::Value>) -> Self {
-        Extensions(extension_map)
-    }
-
-    pub fn has_make_credential_hmac_secret(&self) -> Result<bool, Ctap2StatusCode> {
-        self.0
-            .get("hmac-secret")
-            .map(read_bool)
-            .unwrap_or(Ok(false))
-    }
+impl TryFrom<cbor::Value> for GetAssertionExtensions {
+    type Error = Ctap2StatusCode;
 
-    pub fn get_assertion_hmac_secret(
-        &self,
-    ) -> Option<Result<GetAssertionHmacSecretInput, Ctap2StatusCode>> {
-        self.0
-            .get("hmac-secret")
+    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(GetAssertionHmacSecretInput::try_from)
-    }
-
-    pub fn make_credential_cred_protect_policy(
-        &self,
-    ) -> Option<Result<CredentialProtectionPolicy, Ctap2StatusCode>> {
-        self.0
-            .get("credProtect")
-            .map(CredentialProtectionPolicy::try_from)
+            .transpose()?;
+        Ok(Self { hmac_secret })
     }
 }
 
-#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
+#[cfg_attr(any(test, feature = "debug_ctap"), derive(Clone, Debug, PartialEq))]
 pub struct GetAssertionHmacSecretInput {
     pub key_agreement: CoseKey,
     pub salt_enc: Vec<u8>,
     pub salt_auth: Vec<u8>,
 }
 
-impl TryFrom<&cbor::Value> for GetAssertionHmacSecretInput {
+impl TryFrom<cbor::Value> for GetAssertionHmacSecretInput {
     type Error = Ctap2StatusCode;
 
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let input_map = read_map(cbor_value)?;
-        let cose_key = read_map(ok_or_missing(input_map.get(&cbor_unsigned!(1)))?)?;
-        let salt_enc = read_byte_string(ok_or_missing(input_map.get(&cbor_unsigned!(2)))?)?;
-        let salt_auth = read_byte_string(ok_or_missing(input_map.get(&cbor_unsigned!(3)))?)?;
+    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)))?)?;
         Ok(Self {
-            key_agreement: CoseKey(cose_key.clone()),
+            key_agreement: CoseKey(cose_key),
             salt_enc,
             salt_auth,
         })
     }
 }
 
-#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
-pub struct GetAssertionHmacSecretOutput(Vec<u8>);
-
-impl From<GetAssertionHmacSecretOutput> for cbor::Value {
-    fn from(message: GetAssertionHmacSecretOutput) -> cbor::Value {
-        cbor_bytes!(message.0)
-    }
-}
-
-impl TryFrom<&cbor::Value> for GetAssertionHmacSecretOutput {
-    type Error = Ctap2StatusCode;
-
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        Ok(GetAssertionHmacSecretOutput(read_byte_string(cbor_value)?))
-    }
-}
-
 // Even though options are optional, we can use the default if not present.
 #[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
 pub struct MakeCredentialOptions {
@@ -349,22 +316,22 @@ pub struct MakeCredentialOptions {
     pub uv: bool,
 }
 
-impl TryFrom<&cbor::Value> for MakeCredentialOptions {
+impl TryFrom<cbor::Value> for MakeCredentialOptions {
     type Error = Ctap2StatusCode;
 
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let options_map = read_map(cbor_value)?;
-        let rk = match options_map.get(&cbor_text!("rk")) {
-            Some(options_entry) => read_bool(options_entry)?,
+    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")) {
+            Some(options_entry) => extract_bool(options_entry)?,
             None => false,
         };
-        if let Some(options_entry) = options_map.get(&cbor_text!("up")) {
-            if !read_bool(options_entry)? {
+        if let Some(options_entry) = options_map.remove(&cbor_text!("up")) {
+            if !extract_bool(options_entry)? {
                 return Err(Ctap2StatusCode::CTAP2_ERR_INVALID_OPTION);
             }
         }
-        let uv = match options_map.get(&cbor_text!("uv")) {
-            Some(options_entry) => read_bool(options_entry)?,
+        let uv = match options_map.remove(&cbor_text!("uv")) {
+            Some(options_entry) => extract_bool(options_entry)?,
             None => false,
         };
         Ok(Self { rk, uv })
@@ -377,22 +344,22 @@ pub struct GetAssertionOptions {
     pub uv: bool,
 }
 
-impl TryFrom<&cbor::Value> for GetAssertionOptions {
+impl TryFrom<cbor::Value> for GetAssertionOptions {
     type Error = Ctap2StatusCode;
 
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let options_map = read_map(cbor_value)?;
-        if let Some(options_entry) = options_map.get(&cbor_text!("rk")) {
+    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")) {
             // This is only for returning the correct status code.
-            read_bool(options_entry)?;
+            extract_bool(options_entry)?;
             return Err(Ctap2StatusCode::CTAP2_ERR_INVALID_OPTION);
         }
-        let up = match options_map.get(&cbor_text!("up")) {
-            Some(options_entry) => read_bool(options_entry)?,
+        let up = match options_map.remove(&cbor_text!("up")) {
+            Some(options_entry) => extract_bool(options_entry)?,
             None => true,
         };
-        let uv = match options_map.get(&cbor_text!("uv")) {
-            Some(options_entry) => read_bool(options_entry)?,
+        let uv = match options_map.remove(&cbor_text!("uv")) {
+            Some(options_entry) => extract_bool(options_entry)?,
             None => false,
         };
         Ok(Self { up, uv })
@@ -435,11 +402,11 @@ impl From<SignatureAlgorithm> for cbor::Value {
     }
 }
 
-impl TryFrom<&cbor::Value> for SignatureAlgorithm {
+impl TryFrom<cbor::Value> for SignatureAlgorithm {
     type Error = Ctap2StatusCode;
 
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        match read_integer(cbor_value)? {
+    fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
+        match extract_integer(cbor_value)? {
             ecdsa::PubKey::ES256_ALGORITHM => Ok(SignatureAlgorithm::ES256),
             _ => Ok(SignatureAlgorithm::Unknown),
         }
@@ -473,19 +440,6 @@ impl TryFrom<cbor::Value> for CredentialProtectionPolicy {
     }
 }
 
-impl TryFrom<&cbor::Value> for CredentialProtectionPolicy {
-    type Error = Ctap2StatusCode;
-
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        match read_integer(cbor_value)? {
-            0x01 => Ok(CredentialProtectionPolicy::UserVerificationOptional),
-            0x02 => Ok(CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList),
-            0x03 => Ok(CredentialProtectionPolicy::UserVerificationRequired),
-            _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE),
-        }
-    }
-}
-
 // https://www.w3.org/TR/webauthn/#public-key-credential-source
 //
 // Note that we only use the WebAuthn definition as an example. This data-structure is not specified
@@ -605,7 +559,7 @@ impl PublicKeyCredentialSource {
 // TODO(kaczmarczyck) we could decide to split this data type up
 // It depends on the algorithm though, I think.
 // So before creating a mess, this is my workaround.
-#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
+#[cfg_attr(any(test, feature = "debug_ctap"), derive(Clone, Debug, PartialEq))]
 pub struct CoseKey(pub BTreeMap<cbor::KeyType, cbor::Value>);
 
 // This is the algorithm specifier that is supposed to be used in a COSE key
@@ -645,23 +599,24 @@ impl TryFrom<CoseKey> for ecdh::PubKey {
     type Error = Ctap2StatusCode;
 
     fn try_from(cose_key: CoseKey) -> Result<Self, Ctap2StatusCode> {
-        let key_type = read_integer(ok_or_missing(cose_key.0.get(&cbor_int!(1)))?)?;
+        let mut cose_map = cose_key.0;
+        let key_type = extract_integer(ok_or_missing(cose_map.remove(&cbor_int!(1)))?)?;
         if key_type != EC2_KEY_TYPE {
             return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
         }
-        let algorithm = read_integer(ok_or_missing(cose_key.0.get(&cbor_int!(3)))?)?;
+        let algorithm = extract_integer(ok_or_missing(cose_map.remove(&cbor_int!(3)))?)?;
         if algorithm != ECDH_ALGORITHM && algorithm != ES256_ALGORITHM {
             return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
         }
-        let curve = read_integer(ok_or_missing(cose_key.0.get(&cbor_int!(-1)))?)?;
+        let curve = extract_integer(ok_or_missing(cose_map.remove(&cbor_int!(-1)))?)?;
         if curve != P_256_CURVE {
             return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
         }
-        let x_bytes = read_byte_string(ok_or_missing(cose_key.0.get(&cbor_int!(-2)))?)?;
+        let x_bytes = extract_byte_string(ok_or_missing(cose_map.remove(&cbor_int!(-2)))?)?;
         if x_bytes.len() != ecdh::NBYTES {
             return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
         }
-        let y_bytes = read_byte_string(ok_or_missing(cose_key.0.get(&cbor_int!(-3)))?)?;
+        let y_bytes = extract_byte_string(ok_or_missing(cose_map.remove(&cbor_int!(-3)))?)?;
         if y_bytes.len() != ecdh::NBYTES {
             return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
         }
@@ -698,11 +653,11 @@ impl From<ClientPinSubCommand> for cbor::Value {
     }
 }
 
-impl TryFrom<&cbor::Value> for ClientPinSubCommand {
+impl TryFrom<cbor::Value> for ClientPinSubCommand {
     type Error = Ctap2StatusCode;
 
-    fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
-        let subcommand_int = read_unsigned(cbor_value)?;
+    fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
+        let subcommand_int = extract_unsigned(cbor_value)?;
         match subcommand_int {
             0x01 => Ok(ClientPinSubCommand::GetPinRetries),
             0x02 => Ok(ClientPinSubCommand::GetKeyAgreement),
@@ -717,28 +672,14 @@ impl TryFrom<&cbor::Value> for ClientPinSubCommand {
     }
 }
 
-pub(super) fn read_unsigned(cbor_value: &cbor::Value) -> Result<u64, Ctap2StatusCode> {
+pub(super) fn extract_unsigned(cbor_value: cbor::Value) -> Result<u64, Ctap2StatusCode> {
     match cbor_value {
-        cbor::Value::KeyValue(cbor::KeyType::Unsigned(unsigned)) => Ok(*unsigned),
-        _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE),
-    }
-}
-
-pub(super) fn read_integer(cbor_value: &cbor::Value) -> Result<i64, Ctap2StatusCode> {
-    match cbor_value {
-        cbor::Value::KeyValue(cbor::KeyType::Unsigned(unsigned)) => {
-            if *unsigned <= core::i64::MAX as u64 {
-                Ok(*unsigned as i64)
-            } else {
-                Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
-            }
-        }
-        cbor::Value::KeyValue(cbor::KeyType::Negative(signed)) => Ok(*signed),
+        cbor::Value::KeyValue(cbor::KeyType::Unsigned(unsigned)) => Ok(unsigned),
         _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE),
     }
 }
 
-fn extract_integer(cbor_value: cbor::Value) -> Result<i64, Ctap2StatusCode> {
+pub(super) fn extract_integer(cbor_value: cbor::Value) -> Result<i64, Ctap2StatusCode> {
     match cbor_value {
         cbor::Value::KeyValue(cbor::KeyType::Unsigned(unsigned)) => {
             if unsigned <= core::i64::MAX as u64 {
@@ -752,53 +693,28 @@ fn extract_integer(cbor_value: cbor::Value) -> Result<i64, Ctap2StatusCode> {
     }
 }
 
-pub fn read_byte_string(cbor_value: &cbor::Value) -> Result<Vec<u8>, Ctap2StatusCode> {
-    match cbor_value {
-        cbor::Value::KeyValue(cbor::KeyType::ByteString(byte_string)) => Ok(byte_string.to_vec()),
-        _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE),
-    }
-}
-
-fn extract_byte_string(cbor_value: cbor::Value) -> Result<Vec<u8>, Ctap2StatusCode> {
+pub fn extract_byte_string(cbor_value: cbor::Value) -> Result<Vec<u8>, Ctap2StatusCode> {
     match cbor_value {
         cbor::Value::KeyValue(cbor::KeyType::ByteString(byte_string)) => Ok(byte_string),
         _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE),
     }
 }
 
-pub(super) fn read_text_string(cbor_value: &cbor::Value) -> Result<String, Ctap2StatusCode> {
-    match cbor_value {
-        cbor::Value::KeyValue(cbor::KeyType::TextString(text_string)) => {
-            Ok(text_string.to_string())
-        }
-        _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE),
-    }
-}
-
-fn extract_text_string(cbor_value: cbor::Value) -> Result<String, Ctap2StatusCode> {
+pub(super) fn extract_text_string(cbor_value: cbor::Value) -> Result<String, Ctap2StatusCode> {
     match cbor_value {
         cbor::Value::KeyValue(cbor::KeyType::TextString(text_string)) => Ok(text_string),
         _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE),
     }
 }
 
-pub(super) fn read_array(cbor_value: &cbor::Value) -> Result<&Vec<cbor::Value>, Ctap2StatusCode> {
+pub(super) fn extract_array(cbor_value: cbor::Value) -> Result<Vec<cbor::Value>, Ctap2StatusCode> {
     match cbor_value {
         cbor::Value::Array(array) => Ok(array),
         _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE),
     }
 }
 
-pub(super) fn read_map(
-    cbor_value: &cbor::Value,
-) -> Result<&BTreeMap<cbor::KeyType, cbor::Value>, Ctap2StatusCode> {
-    match cbor_value {
-        cbor::Value::Map(map) => Ok(map),
-        _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE),
-    }
-}
-
-fn extract_map(
+pub(super) fn extract_map(
     cbor_value: cbor::Value,
 ) -> Result<BTreeMap<cbor::KeyType, cbor::Value>, Ctap2StatusCode> {
     match cbor_value {
@@ -807,7 +723,7 @@ fn extract_map(
     }
 }
 
-pub(super) fn read_bool(cbor_value: &cbor::Value) -> Result<bool, Ctap2StatusCode> {
+pub(super) fn extract_bool(cbor_value: cbor::Value) -> Result<bool, Ctap2StatusCode> {
     match cbor_value {
         cbor::Value::Simple(cbor::SimpleValue::FalseValue) => Ok(false),
         cbor::Value::Simple(cbor::SimpleValue::TrueValue) => Ok(true),
@@ -827,192 +743,192 @@ mod test {
     use crypto::rng256::{Rng256, ThreadRng256};
 
     #[test]
-    fn test_read_unsigned() {
-        assert_eq!(read_unsigned(&cbor_int!(123)), Ok(123));
+    fn test_extract_unsigned() {
+        assert_eq!(extract_unsigned(cbor_int!(123)), Ok(123));
         assert_eq!(
-            read_unsigned(&cbor_bool!(true)),
+            extract_unsigned(cbor_bool!(true)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_unsigned(&cbor_text!("foo")),
+            extract_unsigned(cbor_text!("foo")),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_unsigned(&cbor_bytes_lit!(b"bar")),
+            extract_unsigned(cbor_bytes_lit!(b"bar")),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_unsigned(&cbor_array![]),
+            extract_unsigned(cbor_array![]),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_unsigned(&cbor_map! {}),
+            extract_unsigned(cbor_map! {}),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
     }
 
     #[test]
-    fn test_read_unsigned_limits() {
+    fn test_extract_unsigned_limits() {
         assert_eq!(
-            read_unsigned(&cbor_unsigned!(std::u64::MAX)),
+            extract_unsigned(cbor_unsigned!(std::u64::MAX)),
             Ok(std::u64::MAX)
         );
         assert_eq!(
-            read_unsigned(&cbor_unsigned!((std::i64::MAX as u64) + 1)),
+            extract_unsigned(cbor_unsigned!((std::i64::MAX as u64) + 1)),
             Ok((std::i64::MAX as u64) + 1)
         );
         assert_eq!(
-            read_unsigned(&cbor_int!(std::i64::MAX)),
+            extract_unsigned(cbor_int!(std::i64::MAX)),
             Ok(std::i64::MAX as u64)
         );
-        assert_eq!(read_unsigned(&cbor_int!(123)), Ok(123));
-        assert_eq!(read_unsigned(&cbor_int!(1)), Ok(1));
-        assert_eq!(read_unsigned(&cbor_int!(0)), Ok(0));
+        assert_eq!(extract_unsigned(cbor_int!(123)), Ok(123));
+        assert_eq!(extract_unsigned(cbor_int!(1)), Ok(1));
+        assert_eq!(extract_unsigned(cbor_int!(0)), Ok(0));
         assert_eq!(
-            read_unsigned(&cbor_int!(-1)),
+            extract_unsigned(cbor_int!(-1)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_unsigned(&cbor_int!(-123)),
+            extract_unsigned(cbor_int!(-123)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_unsigned(&cbor_int!(std::i64::MIN)),
+            extract_unsigned(cbor_int!(std::i64::MIN)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
     }
 
     #[test]
-    fn test_read_integer() {
-        assert_eq!(read_integer(&cbor_int!(123)), Ok(123));
-        assert_eq!(read_integer(&cbor_int!(-123)), Ok(-123));
+    fn test_extract_integer() {
+        assert_eq!(extract_integer(cbor_int!(123)), Ok(123));
+        assert_eq!(extract_integer(cbor_int!(-123)), Ok(-123));
         assert_eq!(
-            read_integer(&cbor_bool!(true)),
+            extract_integer(cbor_bool!(true)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_integer(&cbor_text!("foo")),
+            extract_integer(cbor_text!("foo")),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_integer(&cbor_bytes_lit!(b"bar")),
+            extract_integer(cbor_bytes_lit!(b"bar")),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_integer(&cbor_array![]),
+            extract_integer(cbor_array![]),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_integer(&cbor_map! {}),
+            extract_integer(cbor_map! {}),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
     }
 
     #[test]
-    fn test_read_integer_limits() {
+    fn test_extract_integer_limits() {
         assert_eq!(
-            read_integer(&cbor_unsigned!(std::u64::MAX)),
+            extract_integer(cbor_unsigned!(std::u64::MAX)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_integer(&cbor_unsigned!((std::i64::MAX as u64) + 1)),
+            extract_integer(cbor_unsigned!((std::i64::MAX as u64) + 1)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
-        assert_eq!(read_integer(&cbor_int!(std::i64::MAX)), Ok(std::i64::MAX));
-        assert_eq!(read_integer(&cbor_int!(123)), Ok(123));
-        assert_eq!(read_integer(&cbor_int!(1)), Ok(1));
-        assert_eq!(read_integer(&cbor_int!(0)), Ok(0));
-        assert_eq!(read_integer(&cbor_int!(-1)), Ok(-1));
-        assert_eq!(read_integer(&cbor_int!(-123)), Ok(-123));
-        assert_eq!(read_integer(&cbor_int!(std::i64::MIN)), Ok(std::i64::MIN));
+        assert_eq!(extract_integer(cbor_int!(std::i64::MAX)), Ok(std::i64::MAX));
+        assert_eq!(extract_integer(cbor_int!(123)), Ok(123));
+        assert_eq!(extract_integer(cbor_int!(1)), Ok(1));
+        assert_eq!(extract_integer(cbor_int!(0)), Ok(0));
+        assert_eq!(extract_integer(cbor_int!(-1)), Ok(-1));
+        assert_eq!(extract_integer(cbor_int!(-123)), Ok(-123));
+        assert_eq!(extract_integer(cbor_int!(std::i64::MIN)), Ok(std::i64::MIN));
     }
 
     #[test]
-    fn test_read_byte_string() {
+    fn test_extract_byte_string() {
         assert_eq!(
-            read_byte_string(&cbor_int!(123)),
+            extract_byte_string(cbor_int!(123)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_byte_string(&cbor_bool!(true)),
+            extract_byte_string(cbor_bool!(true)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_byte_string(&cbor_text!("foo")),
+            extract_byte_string(cbor_text!("foo")),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
-        assert_eq!(read_byte_string(&cbor_bytes_lit!(b"")), Ok(Vec::new()));
+        assert_eq!(extract_byte_string(cbor_bytes_lit!(b"")), Ok(Vec::new()));
         assert_eq!(
-            read_byte_string(&cbor_bytes_lit!(b"bar")),
+            extract_byte_string(cbor_bytes_lit!(b"bar")),
             Ok(b"bar".to_vec())
         );
         assert_eq!(
-            read_byte_string(&cbor_array![]),
+            extract_byte_string(cbor_array![]),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_byte_string(&cbor_map! {}),
+            extract_byte_string(cbor_map! {}),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
     }
 
     #[test]
-    fn test_read_text_string() {
+    fn test_extract_text_string() {
         assert_eq!(
-            read_text_string(&cbor_int!(123)),
+            extract_text_string(cbor_int!(123)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_text_string(&cbor_bool!(true)),
+            extract_text_string(cbor_bool!(true)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
-        assert_eq!(read_text_string(&cbor_text!("")), Ok(String::new()));
+        assert_eq!(extract_text_string(cbor_text!("")), Ok(String::new()));
         assert_eq!(
-            read_text_string(&cbor_text!("foo")),
+            extract_text_string(cbor_text!("foo")),
             Ok(String::from("foo"))
         );
         assert_eq!(
-            read_text_string(&cbor_bytes_lit!(b"bar")),
+            extract_text_string(cbor_bytes_lit!(b"bar")),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_text_string(&cbor_array![]),
+            extract_text_string(cbor_array![]),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_text_string(&cbor_map! {}),
+            extract_text_string(cbor_map! {}),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
     }
 
     #[test]
-    fn test_read_array() {
+    fn test_extract_array() {
         assert_eq!(
-            read_array(&cbor_int!(123)),
+            extract_array(cbor_int!(123)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_array(&cbor_bool!(true)),
+            extract_array(cbor_bool!(true)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_array(&cbor_text!("foo")),
+            extract_array(cbor_text!("foo")),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_array(&cbor_bytes_lit!(b"bar")),
+            extract_array(cbor_bytes_lit!(b"bar")),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
-        assert_eq!(read_array(&cbor_array![]), Ok(&Vec::new()));
+        assert_eq!(extract_array(cbor_array![]), Ok(Vec::new()));
         assert_eq!(
-            read_array(&cbor_array![
+            extract_array(cbor_array![
                 123,
                 cbor_null!(),
                 "foo",
                 cbor_array![],
                 cbor_map! {},
             ]),
-            Ok(&vec![
+            Ok(vec![
                 cbor_int!(123),
                 cbor_null!(),
                 cbor_text!("foo"),
@@ -1021,41 +937,41 @@ mod test {
             ])
         );
         assert_eq!(
-            read_array(&cbor_map! {}),
+            extract_array(cbor_map! {}),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
     }
 
     #[test]
-    fn test_read_map() {
+    fn test_extract_map() {
         assert_eq!(
-            read_map(&cbor_int!(123)),
+            extract_map(cbor_int!(123)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_map(&cbor_bool!(true)),
+            extract_map(cbor_bool!(true)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_map(&cbor_text!("foo")),
+            extract_map(cbor_text!("foo")),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_map(&cbor_bytes_lit!(b"bar")),
+            extract_map(cbor_bytes_lit!(b"bar")),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_map(&cbor_array![]),
+            extract_map(cbor_array![]),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
-        assert_eq!(read_map(&cbor_map! {}), Ok(&BTreeMap::new()));
+        assert_eq!(extract_map(cbor_map! {}), Ok(BTreeMap::new()));
         assert_eq!(
-            read_map(&cbor_map! {
+            extract_map(cbor_map! {
                 1 => cbor_false!(),
                 "foo" => b"bar",
                 b"bin" => -42,
             }),
-            Ok(&[
+            Ok([
                 (cbor_unsigned!(1), cbor_false!()),
                 (cbor_text!("foo"), cbor_bytes_lit!(b"bar")),
                 (cbor_bytes_lit!(b"bin"), cbor_int!(-42)),
@@ -1067,27 +983,27 @@ mod test {
     }
 
     #[test]
-    fn test_read_bool() {
+    fn test_extract_bool() {
         assert_eq!(
-            read_bool(&cbor_int!(123)),
+            extract_bool(cbor_int!(123)),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
-        assert_eq!(read_bool(&cbor_bool!(true)), Ok(true));
-        assert_eq!(read_bool(&cbor_bool!(false)), Ok(false));
+        assert_eq!(extract_bool(cbor_bool!(true)), Ok(true));
+        assert_eq!(extract_bool(cbor_bool!(false)), Ok(false));
         assert_eq!(
-            read_bool(&cbor_text!("foo")),
+            extract_bool(cbor_text!("foo")),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_bool(&cbor_bytes_lit!(b"bar")),
+            extract_bool(cbor_bytes_lit!(b"bar")),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_bool(&cbor_array![]),
+            extract_bool(cbor_array![]),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
         assert_eq!(
-            read_bool(&cbor_map! {}),
+            extract_bool(cbor_map! {}),
             Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
         );
     }
@@ -1099,7 +1015,7 @@ mod test {
             "name" => "Example",
             "icon" => "example.com/icon.png",
         };
-        let rp_entity = PublicKeyCredentialRpEntity::try_from(&cbor_rp_entity);
+        let rp_entity = PublicKeyCredentialRpEntity::try_from(cbor_rp_entity);
         let expected_rp_entity = PublicKeyCredentialRpEntity {
             rp_id: "example.com".to_string(),
             rp_name: Some("Example".to_string()),
@@ -1116,7 +1032,7 @@ mod test {
             "displayName" => "bar",
             "icon" => "example.com/foo/icon.png",
         };
-        let user_entity = PublicKeyCredentialUserEntity::try_from(&cbor_user_entity);
+        let user_entity = PublicKeyCredentialUserEntity::try_from(cbor_user_entity.clone());
         let expected_user_entity = PublicKeyCredentialUserEntity {
             user_id: vec![0x1D, 0x1D, 0x1D, 0x1D],
             user_name: Some("foo".to_string()),
@@ -1130,30 +1046,30 @@ mod test {
 
     #[test]
     fn test_from_into_public_key_credential_type() {
-        let cbor_credential_type = cbor_text!("public-key");
-        let credential_type = PublicKeyCredentialType::try_from(&cbor_credential_type);
+        let cbor_credential_type: cbor::Value = cbor_text!("public-key");
+        let credential_type = PublicKeyCredentialType::try_from(cbor_credential_type.clone());
         let expected_credential_type = PublicKeyCredentialType::PublicKey;
         assert_eq!(credential_type, Ok(expected_credential_type));
         let created_cbor: cbor::Value = credential_type.unwrap().into();
         assert_eq!(created_cbor, cbor_credential_type);
 
-        let cbor_unknown_type = cbor_text!("unknown-type");
-        let unknown_type = PublicKeyCredentialType::try_from(&cbor_unknown_type);
+        let cbor_unknown_type: cbor::Value = cbor_text!("unknown-type");
+        let unknown_type = PublicKeyCredentialType::try_from(cbor_unknown_type);
         let expected_unknown_type = PublicKeyCredentialType::Unknown;
         assert_eq!(unknown_type, Ok(expected_unknown_type));
     }
 
     #[test]
     fn test_from_into_signature_algorithm() {
-        let cbor_signature_algorithm = cbor_int!(ecdsa::PubKey::ES256_ALGORITHM);
-        let signature_algorithm = SignatureAlgorithm::try_from(&cbor_signature_algorithm);
+        let cbor_signature_algorithm: cbor::Value = cbor_int!(ecdsa::PubKey::ES256_ALGORITHM);
+        let signature_algorithm = SignatureAlgorithm::try_from(cbor_signature_algorithm.clone());
         let expected_signature_algorithm = SignatureAlgorithm::ES256;
         assert_eq!(signature_algorithm, Ok(expected_signature_algorithm));
-        let created_cbor = cbor::Value::from(signature_algorithm.unwrap());
+        let created_cbor: cbor::Value = signature_algorithm.unwrap().into();
         assert_eq!(created_cbor, cbor_signature_algorithm);
 
-        let cbor_unknown_algorithm = cbor_int!(-1);
-        let unknown_algorithm = SignatureAlgorithm::try_from(&cbor_unknown_algorithm);
+        let cbor_unknown_algorithm: cbor::Value = cbor_int!(-1);
+        let unknown_algorithm = SignatureAlgorithm::try_from(cbor_unknown_algorithm);
         let expected_unknown_algorithm = SignatureAlgorithm::Unknown;
         assert_eq!(unknown_algorithm, Ok(expected_unknown_algorithm));
     }
@@ -1176,24 +1092,24 @@ mod test {
 
     #[test]
     fn test_from_into_cred_protection_policy() {
-        let cbor_policy = cbor::Value::from(CredentialProtectionPolicy::UserVerificationOptional);
-        let policy = CredentialProtectionPolicy::try_from(&cbor_policy);
+        let cbor_policy: cbor::Value = CredentialProtectionPolicy::UserVerificationOptional.into();
+        let policy = CredentialProtectionPolicy::try_from(cbor_policy.clone());
         let expected_policy = CredentialProtectionPolicy::UserVerificationOptional;
         assert_eq!(policy, Ok(expected_policy));
-        let created_cbor = cbor::Value::from(policy.unwrap());
+        let created_cbor: cbor::Value = policy.unwrap().into();
         assert_eq!(created_cbor, cbor_policy);
 
-        let cbor_policy_error = cbor_int!(-1);
-        let policy_error = CredentialProtectionPolicy::try_from(&cbor_policy_error);
+        let cbor_policy_error: cbor::Value = cbor_int!(-1);
+        let policy_error = CredentialProtectionPolicy::try_from(cbor_policy_error);
         let expected_error = Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
         assert_eq!(policy_error, expected_error);
     }
 
     #[test]
     fn test_from_into_authenticator_transport() {
-        let cbor_authenticator_transport = cbor_text!("usb");
+        let cbor_authenticator_transport: cbor::Value = cbor_text!("usb");
         let authenticator_transport =
-            AuthenticatorTransport::try_from(&cbor_authenticator_transport);
+            AuthenticatorTransport::try_from(cbor_authenticator_transport.clone());
         let expected_authenticator_transport = AuthenticatorTransport::Usb;
         assert_eq!(
             authenticator_transport,
@@ -1210,7 +1126,7 @@ mod test {
             "alg" => ecdsa::PubKey::ES256_ALGORITHM,
         };
         let credential_parameter =
-            PublicKeyCredentialParameter::try_from(&cbor_credential_parameter);
+            PublicKeyCredentialParameter::try_from(cbor_credential_parameter.clone());
         let expected_credential_parameter = PublicKeyCredentialParameter {
             cred_type: PublicKeyCredentialType::PublicKey,
             alg: SignatureAlgorithm::ES256,
@@ -1228,7 +1144,7 @@ mod test {
             "transports" => cbor_array!["usb"],
         };
         let credential_descriptor =
-            PublicKeyCredentialDescriptor::try_from(&cbor_credential_descriptor);
+            PublicKeyCredentialDescriptor::try_from(cbor_credential_descriptor.clone());
         let expected_credential_descriptor = PublicKeyCredentialDescriptor {
             key_type: PublicKeyCredentialType::PublicKey,
             key_id: vec![0x2D, 0x2D, 0x2D, 0x2D],
@@ -1240,44 +1156,21 @@ mod test {
     }
 
     #[test]
-    fn test_from_into_extensions() {
+    fn test_from_make_credential_extensions() {
         let cbor_extensions = cbor_map! {
-            "the_answer" => 42,
+            "hmac-secret" => true,
+            "credProtect" => CredentialProtectionPolicy::UserVerificationRequired,
+        };
+        let extensions = MakeCredentialExtensions::try_from(cbor_extensions);
+        let expected_extensions = MakeCredentialExtensions {
+            hmac_secret: true,
+            cred_protect: Some(CredentialProtectionPolicy::UserVerificationRequired),
         };
-        let extensions = Extensions::try_from(&cbor_extensions);
-        let mut expected_extensions = Extensions(BTreeMap::new());
-        expected_extensions
-            .0
-            .insert("the_answer".to_string(), cbor_int!(42));
         assert_eq!(extensions, Ok(expected_extensions));
-        let created_cbor: cbor::Value = extensions.unwrap().into();
-        assert_eq!(created_cbor, cbor_extensions);
     }
 
     #[test]
-    fn test_from_into_get_assertion_hmac_secret_output() {
-        let cbor_output = cbor_bytes![vec![0xC0; 32]];
-        let output = GetAssertionHmacSecretOutput::try_from(&cbor_output);
-        let expected_output = GetAssertionHmacSecretOutput(vec![0xC0; 32]);
-        assert_eq!(output, Ok(expected_output));
-        let created_cbor: cbor::Value = output.unwrap().into();
-        assert_eq!(created_cbor, cbor_output);
-    }
-
-    #[test]
-    fn test_hmac_secret_extension() {
-        let cbor_extensions = cbor_map! {
-            "hmac-secret" => true,
-        };
-        let extensions = Extensions::try_from(&cbor_extensions).unwrap();
-        assert!(extensions.has_make_credential_hmac_secret().unwrap());
-
-        let cbor_extensions = cbor_map! {
-            "hmac-secret" => false,
-        };
-        let extensions = Extensions::try_from(&cbor_extensions).unwrap();
-        assert!(!extensions.has_make_credential_hmac_secret().unwrap());
-
+    fn test_from_get_assertion_extensions() {
         let mut rng = ThreadRng256 {};
         let sk = crypto::ecdh::SecKey::gensk(&mut rng);
         let pk = sk.genpk();
@@ -1286,29 +1179,19 @@ mod test {
             "hmac-secret" => cbor_map! {
                 1 => cbor::Value::Map(cose_key.0.clone()),
                 2 => vec![0x02; 32],
-                3 => vec![0x03; 32],
+                3 => vec![0x03; 16],
             },
         };
-        let extensions = Extensions::try_from(&cbor_extensions).unwrap();
-        let get_assertion_input = extensions.get_assertion_hmac_secret();
+        let extensions = GetAssertionExtensions::try_from(cbor_extensions);
         let expected_input = GetAssertionHmacSecretInput {
             key_agreement: cose_key,
             salt_enc: vec![0x02; 32],
-            salt_auth: vec![0x03; 32],
+            salt_auth: vec![0x03; 16],
         };
-        assert_eq!(get_assertion_input, Some(Ok(expected_input)));
-    }
-
-    #[test]
-    fn test_cred_protect_extension() {
-        let cbor_extensions = cbor_map! {
-            "credProtect" => CredentialProtectionPolicy::UserVerificationRequired,
+        let expected_extensions = GetAssertionExtensions {
+            hmac_secret: Some(expected_input),
         };
-        let extensions = Extensions::try_from(&cbor_extensions).unwrap();
-        assert_eq!(
-            extensions.make_credential_cred_protect_policy(),
-            Some(Ok(CredentialProtectionPolicy::UserVerificationRequired))
-        );
+        assert_eq!(extensions, Ok(expected_extensions));
     }
 
     #[test]
@@ -1317,7 +1200,7 @@ mod test {
             "rk" => true,
             "uv" => false,
         };
-        let make_options = MakeCredentialOptions::try_from(&cbor_make_options);
+        let make_options = MakeCredentialOptions::try_from(cbor_make_options);
         let expected_make_options = MakeCredentialOptions {
             rk: true,
             uv: false,
@@ -1331,7 +1214,7 @@ mod test {
             "up" => true,
             "uv" => false,
         };
-        let get_assertion = GetAssertionOptions::try_from(&cbor_get_assertion);
+        let get_assertion = GetAssertionOptions::try_from(cbor_get_assertion);
         let expected_get_assertion = GetAssertionOptions {
             up: true,
             uv: false,
@@ -1370,8 +1253,8 @@ mod test {
 
     #[test]
     fn test_from_into_client_pin_sub_command() {
-        let cbor_sub_command = cbor_int!(0x01);
-        let sub_command = ClientPinSubCommand::try_from(&cbor_sub_command);
+        let cbor_sub_command: cbor::Value = cbor_int!(0x01);
+        let sub_command = ClientPinSubCommand::try_from(cbor_sub_command.clone());
         let expected_sub_command = ClientPinSubCommand::GetPinRetries;
         assert_eq!(sub_command, Ok(expected_sub_command));
         let created_cbor: cbor::Value = sub_command.unwrap().into();
diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs
index 5514fea2402558f071dcb5427175f37367ee7344..a52798b2a3ff1019c7a34b8852ac21a490bfbe61 100644
--- a/src/ctap/mod.rs
+++ b/src/ctap/mod.rs
@@ -435,16 +435,14 @@ where
         }
 
         let (use_hmac_extension, cred_protect_policy) = if let Some(extensions) = extensions {
-            let mut cred_protect = extensions
-                .make_credential_cred_protect_policy()
-                .transpose()?;
+            let mut cred_protect = extensions.cred_protect;
             if cred_protect.unwrap_or(CredentialProtectionPolicy::UserVerificationOptional)
                 < DEFAULT_CRED_PROTECT
                     .unwrap_or(CredentialProtectionPolicy::UserVerificationOptional)
             {
                 cred_protect = DEFAULT_CRED_PROTECT;
             }
-            (extensions.has_make_credential_hmac_secret()?, cred_protect)
+            (extensions.hmac_secret, cred_protect)
         } else {
             (false, None)
         };
@@ -545,11 +543,11 @@ where
         auth_data.extend(cose_key);
         if has_extension_output {
             let hmac_secret_output = if use_hmac_extension { Some(true) } else { None };
-            let extensions = cbor_map_options! {
+            let extensions_output = cbor_map_options! {
                 "hmac-secret" => hmac_secret_output,
                 "credProtect" => cred_protect_policy,
             };
-            if !cbor::write(extensions, &mut auth_data) {
+            if !cbor::write(extensions_output, &mut auth_data) {
                 return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_RESPONSE_CANNOT_WRITE_CBOR);
             }
         }
@@ -639,11 +637,8 @@ where
             }
         }
 
-        let get_assertion_hmac_secret_input = match extensions {
-            Some(extensions) => extensions.get_assertion_hmac_secret().transpose()?,
-            None => None,
-        };
-        if get_assertion_hmac_secret_input.is_some() && !options.up {
+        let hmac_secret_input = extensions.map(|e| e.hmac_secret).flatten();
+        if hmac_secret_input.is_some() && !options.up {
             // The extension is actually supported, but we need user presence.
             return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_EXTENSION);
         }
@@ -673,7 +668,7 @@ where
         if options.up {
             flags |= UP_FLAG;
         }
-        if get_assertion_hmac_secret_input.is_some() {
+        if hmac_secret_input.is_some() {
             flags |= ED_FLAG;
         }
 
@@ -720,12 +715,12 @@ where
 
         let mut auth_data = self.generate_auth_data(&rp_id_hash, flags);
         // Process extensions.
-        if let Some(get_assertion_hmac_secret_input) = get_assertion_hmac_secret_input {
+        if let Some(hmac_secret_input) = hmac_secret_input {
             let GetAssertionHmacSecretInput {
                 key_agreement,
                 salt_enc,
                 salt_auth,
-            } = get_assertion_hmac_secret_input;
+            } = hmac_secret_input;
             let pk: crypto::ecdh::PubKey = CoseKey::try_into(key_agreement)?;
             let shared_secret = self.key_agreement_key.exchange_x_sha256(&pk);
             // HMAC-secret does the same 16 byte truncated check.
@@ -740,10 +735,10 @@ where
                 None => return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_EXTENSION),
             };
 
-            let extensions = cbor_map! {
+            let extensions_output = cbor_map! {
                 "hmac-secret" => encrypted_output,
             };
-            if !cbor::write(extensions, &mut auth_data) {
+            if !cbor::write(extensions_output, &mut auth_data) {
                 return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_RESPONSE_CANNOT_WRITE_CBOR);
             }
         }
@@ -1118,8 +1113,8 @@ where
 #[cfg(test)]
 mod test {
     use super::data_formats::{
-        Extensions, GetAssertionOptions, MakeCredentialOptions, PublicKeyCredentialRpEntity,
-        PublicKeyCredentialUserEntity,
+        GetAssertionExtensions, GetAssertionOptions, MakeCredentialExtensions,
+        MakeCredentialOptions, PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity,
     };
     use super::*;
     use crypto::rng256::ThreadRng256;
@@ -1219,9 +1214,10 @@ mod test {
     fn create_make_credential_parameters_with_cred_protect_policy(
         policy: CredentialProtectionPolicy,
     ) -> AuthenticatorMakeCredentialParameters {
-        let mut extension_map = BTreeMap::new();
-        extension_map.insert("credProtect".to_string(), cbor::Value::from(policy));
-        let extensions = Some(Extensions::new(extension_map));
+        let extensions = Some(MakeCredentialExtensions {
+            hmac_secret: false,
+            cred_protect: Some(policy),
+        });
         let mut make_credential_params = create_minimal_make_credential_parameters();
         make_credential_params.extensions = extensions;
         make_credential_params
@@ -1408,9 +1404,10 @@ mod test {
         let user_immediately_present = |_| Ok(());
         let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
 
-        let mut extension_map = BTreeMap::new();
-        extension_map.insert("hmac-secret".to_string(), cbor_bool!(true));
-        let extensions = Some(Extensions::new(extension_map));
+        let extensions = Some(MakeCredentialExtensions {
+            hmac_secret: true,
+            cred_protect: None,
+        });
         let mut make_credential_params = create_minimal_make_credential_parameters();
         make_credential_params.extensions = extensions;
         let make_credential_response =
@@ -1520,9 +1517,10 @@ mod test {
         let user_immediately_present = |_| Ok(());
         let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
 
-        let mut extension_map = BTreeMap::new();
-        extension_map.insert("hmac-secret".to_string(), cbor_bool!(true));
-        let make_extensions = Some(Extensions::new(extension_map));
+        let make_extensions = Some(MakeCredentialExtensions {
+            hmac_secret: true,
+            cred_protect: None,
+        });
         let mut make_credential_params = create_minimal_make_credential_parameters();
         make_credential_params.extensions = make_extensions;
         assert!(ctap_state
@@ -1530,15 +1528,15 @@ mod test {
             .is_ok());
 
         let pk = sk.genpk();
-        let hmac_secret_parameters = cbor_map! {
-            1 => cbor::Value::Map(CoseKey::from(pk).0),
-            2 => vec![0; 32],
-            3 => vec![0; 16],
+        let hmac_secret_input = GetAssertionHmacSecretInput {
+            key_agreement: CoseKey::from(pk),
+            salt_enc: vec![0x02; 32],
+            salt_auth: vec![0x03; 16],
         };
-        let mut extension_map = BTreeMap::new();
-        extension_map.insert("hmac-secret".to_string(), hmac_secret_parameters);
+        let get_extensions = Some(GetAssertionExtensions {
+            hmac_secret: Some(hmac_secret_input),
+        });
 
-        let get_extensions = Some(Extensions::new(extension_map));
         let get_assertion_params = AuthenticatorGetAssertionParameters {
             rp_id: String::from("example.com"),
             client_data_hash: vec![0xCD],