Repository: openwsn-berkeley/lakers (language: python)

Issue:
Allow applications to pick their C_x
Applications may need to pick their own C_x (as opposed to the EDHOC library picking one at random, as it was before) for three reasons:

* The library doesn't manage the set of contexts, so it can't know which C_x are already in use and which are not.
* The C_x may be used for other purposes (eg. OSCORE contexts), and might be namespaced there, which also only the application may know. (Silly example, but the application may use the OSCORE context identifiers that correspond to negative C_x for use with ACE-OSCORE profile, and only those with positive C_x for EDHOC).
* When processing of message 1 and creation of message 2 are split over time (as is necessary when implementing an EDHOC server on the coap-handler interface), it is practical to already decide on a C_R at processing time, store the responder, and dig it up when creating the response -- but that requires the C_R to be decided already at storing time.

This will likely need follow-ups for similarly delegating the choice of {ID_,}CRED_peer to the callers (resolving #99 issues from), but those are separate enough that I'd do them in a separate PR.

These are API breaking changes. They're split in two because I did C_R first, and kept them split to ease review especially on the CI side; when ACK'ed, I'd like to squash them.


Discussion / hints:

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index c548161b..37212ef4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,7 +2,6 @@
 
 members = [
   "lib",
-  "hacspec",
   "ead",
   "ead/edhoc-ead-none",
   "ead/edhoc-ead-zeroconf",
@@ -20,7 +19,6 @@ members = [
 # tested on the host architecture
 default-members = [
   "lib",
-  "hacspec",
   "ead",
   "crypto",
   "crypto/edhoc-crypto-hacspec",
diff --git a/README.md b/README.md
index bb5f6f02..797f44ab 100644
--- a/README.md
+++ b/README.md
@@ -46,7 +46,7 @@ cd ./examples/edhoc-rs-no_std
 cargo build --target="thumbv7em-none-eabihf" --no-default-features --features="hacspec-psa, rtt" --release
 
 # build using the rust version of the lib, and hardware-accelerated crypto
-cargo build --target="thumbv7em-none-eabihf" --no-default-features --features="rust-cryptocell310, rtt"
+cargo build --target="thumbv7em-none-eabihf" --no-default-features --features="crypto-cryptocell310, rtt"
 ```
 
 To build **and** flash to the board, replace the word `build` with `embed` in the commands above (you may need to `cargo install cargo-embed`).
diff --git a/build_for_c.sh b/build_for_c.sh
index 006dacb9..aadf0a53 100755
--- a/build_for_c.sh
+++ b/build_for_c.sh
@@ -11,9 +11,9 @@
 
 cargo_features=$1
 
-if [[ $cargo_features != "rust-cryptocell310" && $cargo_features != "rust-psa-baremetal" ]]; then
-    echo "Select one of: rust-cryptocell310, rust-psa-baremetal"
-    echo "Example: ./build_static_lib.sh rust-cryptocell310"
+if [[ $cargo_features != "crypto-cryptocell310" && $cargo_features != "crypto-psa-baremetal" ]]; then
+    echo "Select one of: crypto-cryptocell310, crypto-psa-baremetal"
+    echo "Example: ./build_static_lib.sh crypto-cryptocell310"
     exit 1
 fi
 
diff --git a/consts/Cargo.toml b/consts/Cargo.toml
index d5a82d2e..9d1b8538 100644
--- a/consts/Cargo.toml
+++ b/consts/Cargo.toml
@@ -6,13 +6,5 @@ authors = ["Mališa Vučinić <malisa.vucinic@inria.fr>"]
 license = "BSD"
 description = "EDHOC crypto library constants crate"
 
-[dependencies]
-hacspec-lib = { version = "0.1.0-beta.1", default-features = false, optional = true }
-
 [build-dependencies]
 cbindgen = "0.24.5"
-
-[features]
-default = [ ]
-hacspec = [ "hacspec-lib/alloc" ]
-rust = [ ]
diff --git a/consts/src/lib.rs b/consts/src/lib.rs
index fb48fc74..3eb85b28 100644
--- a/consts/src/lib.rs
+++ b/consts/src/lib.rs
@@ -1,112 +1,10 @@
 #![no_std]
 
-pub use common::*;
+pub use consts::*;
+pub use structs::*;
 
-#[cfg(feature = "hacspec")]
-pub use hacspec::*;
-
-#[cfg(feature = "rust")]
-pub use rust::*;
-
-mod common {
-
-    #[repr(C)]
-    #[derive(Default, PartialEq, Copy, Clone, Debug)]
-    pub enum EDHOCState {
-        #[default]
-        Start = 0, // initiator and responder
-        WaitMessage2 = 1,      // initiator
-        ProcessedMessage2 = 2, // initiator
-        ProcessedMessage1 = 3, // responder
-        WaitMessage3 = 4,      // responder
-        Completed = 5,         // initiator and responder
-    }
-
-    #[repr(C)]
-    #[derive(PartialEq, Debug)]
-    pub enum EDHOCError {
-        Success = 0,
-        UnknownPeer = 1,
-        MacVerificationFailed = 2,
-        UnsupportedMethod = 3,
-        UnsupportedCipherSuite = 4,
-        ParsingError = 5,
-        WrongState = 6,
-        EADError = 7,
-        UnknownError = 8,
-    }
-
-    #[repr(C)]
-    #[derive(PartialEq, Debug)]
-    pub struct EdhocMessageBuffer {
-        pub content: [u8; MAX_MESSAGE_SIZE_LEN],
-        pub len: usize,
-    }
-
-    pub trait MessageBufferTrait {
-        fn new() -> Self;
-        fn from_hex(hex: &str) -> Self;
-    }
-
-    impl MessageBufferTrait for EdhocMessageBuffer {
-        fn new() -> Self {
-            EdhocMessageBuffer {
-                content: [0u8; MAX_MESSAGE_SIZE_LEN],
-                len: 0,
-            }
-        }
-        fn from_hex(hex: &str) -> Self {
-            let mut buffer = EdhocMessageBuffer::new();
-            buffer.len = hex.len() / 2;
-            for (i, chunk) in hex.as_bytes().chunks(2).enumerate() {
-                let chunk_str = core::str::from_utf8(chunk).unwrap();
-                buffer.content[i] = u8::from_str_radix(chunk_str, 16).unwrap();
-            }
-            buffer
-        }
-    }
-
-    impl TryInto<EdhocMessageBuffer> for &[u8] {
-        type Error = ();
-
-        fn try_into(self) -> Result<EdhocMessageBuffer, Self::Error> {
-            if self.len() <= MAX_MESSAGE_SIZE_LEN {
-                let mut buffer = [0u8; MAX_MESSAGE_SIZE_LEN];
-                for i in 0..self.len() {
-                    buffer[i] = self[i];
-                }
-
-                Ok(EdhocMessageBuffer {
-                    content: buffer,
-                    len: self.len(),
-                })
-            } else {
-                Err(())
-            }
-        }
-    }
-
-    #[derive(Debug)]
-    pub struct EADItem {
-        pub label: u8,
-        pub is_critical: bool,
-        // TODO[ead]: have adjustable (smaller) length for this buffer
-        pub value: Option<EdhocMessageBuffer>,
-    }
-
-    pub trait EADTrait {
-        fn new() -> Self;
-    }
-
-    impl EADTrait for EADItem {
-        fn new() -> Self {
-            EADItem {
-                label: 0,
-                is_critical: false,
-                value: None,
-            }
-        }
-    }
+mod consts {
+    use super::structs::*;
 
     pub const MAX_MESSAGE_SIZE_LEN: usize = 64;
     pub const MAX_EAD_SIZE_LEN: usize = 64;
@@ -146,13 +44,14 @@ mod common {
 						    1; // length as u8
 
     pub const ENC_STRUCTURE_LEN: usize = 8 + 5 + SHA256_DIGEST_LEN; // 8 for ENCRYPT0
+
+    pub const EDHOC_SUITES: BytesSuites = [0, 1, 2, 3, 4, 5, 6, 24, 25]; // all but private cipher suites
+    pub const EDHOC_SUPPORTED_SUITES: BytesSupportedSuites = [0x2u8];
 }
 
-#[cfg(feature = "rust")]
-mod rust {
-    use super::common::*;
+mod structs {
+    use super::consts::*;
 
-    pub type U8 = u8;
     pub type BytesEad2 = [u8; 0];
     pub type BytesIdCred = [u8; ID_CRED_LEN];
     pub type BytesSuites = [u8; SUITES_LEN];
@@ -177,8 +76,31 @@ mod rust {
     pub type BytesMaxLabelBuffeer = [u8; MAX_KDF_LABEL_LEN];
     pub type BytesEncStructureLen = [u8; ENC_STRUCTURE_LEN];
 
-    pub const EDHOC_SUITES: BytesSuites = [0, 1, 2, 3, 4, 5, 6, 24, 25]; // all but private cipher suites
-    pub const EDHOC_SUPPORTED_SUITES: BytesSupportedSuites = [0x2u8];
+    #[repr(C)]
+    #[derive(Default, PartialEq, Copy, Clone, Debug)]
+    pub enum EDHOCState {
+        #[default]
+        Start = 0, // initiator and responder
+        WaitMessage2 = 1,      // initiator
+        ProcessedMessage2 = 2, // initiator
+        ProcessedMessage1 = 3, // responder
+        WaitMessage3 = 4,      // responder
+        Completed = 5,         // initiator and responder
+    }
+
+    #[repr(C)]
+    #[derive(PartialEq, Debug)]
+    pub enum EDHOCError {
+        Success = 0,
+        UnknownPeer = 1,
+        MacVerificationFailed = 2,
+        UnsupportedMethod = 3,
+        UnsupportedCipherSuite = 4,
+        ParsingError = 5,
+        WrongState = 6,
+        EADError = 7,
+        UnknownError = 8,
+    }
 
     #[repr(C)]
     #[derive(Default, Copy, Clone, Debug)]
@@ -194,170 +116,76 @@ mod rust {
         pub BytesHashLen,     // h_message_1
         pub BytesHashLen,     // th_3
     );
-}
-
-#[cfg(feature = "hacspec")]
-mod hacspec {
-    use super::common::*;
-    use hacspec_lib::*;
 
-    array!(BytesMessageBuffer, MAX_MESSAGE_SIZE_LEN, U8);
-
-    #[derive(Debug)]
-    pub struct EdhocMessageBufferHacspec {
-        pub content: BytesMessageBuffer,
+    #[repr(C)]
+    #[derive(PartialEq, Debug)]
+    pub struct EdhocMessageBuffer {
+        pub content: [u8; MAX_MESSAGE_SIZE_LEN],
         pub len: usize,
     }
 
-    pub trait MessageBufferHacspecTrait {
+    pub trait MessageBufferTrait {
         fn new() -> Self;
         fn from_hex(hex: &str) -> Self;
-        fn from_public_buffer(buffer: &EdhocMessageBuffer) -> Self;
-        fn from_slice<A>(slice: &A, start: usize, len: usize) -> Self
-        where
-            A: SeqTrait<U8>;
-        fn from_seq(buffer: &Seq<U8>) -> Self;
-        fn to_public_buffer(&self) -> EdhocMessageBuffer;
     }
 
-    impl MessageBufferHacspecTrait for EdhocMessageBufferHacspec {
+    impl MessageBufferTrait for EdhocMessageBuffer {
         fn new() -> Self {
-            EdhocMessageBufferHacspec {
-                content: BytesMessageBuffer::new(),
+            EdhocMessageBuffer {
+                content: [0u8; MAX_MESSAGE_SIZE_LEN],
                 len: 0,
             }
         }
         fn from_hex(hex: &str) -> Self {
-            let mut buffer = EdhocMessageBufferHacspec::new();
+            let mut buffer = EdhocMessageBuffer::new();
             buffer.len = hex.len() / 2;
             for (i, chunk) in hex.as_bytes().chunks(2).enumerate() {
                 let chunk_str = core::str::from_utf8(chunk).unwrap();
-                buffer.content[i] = U8(u8::from_str_radix(chunk_str, 16).unwrap());
+                buffer.content[i] = u8::from_str_radix(chunk_str, 16).unwrap();
             }
             buffer
         }
-        fn from_public_buffer(buffer: &EdhocMessageBuffer) -> Self {
-            let mut hacspec_buffer = EdhocMessageBufferHacspec::new();
-            hacspec_buffer.len = buffer.len;
-            hacspec_buffer.content = BytesMessageBuffer::from_public_slice(&buffer.content[..]);
-            hacspec_buffer
-        }
-        fn from_slice<A>(slice: &A, start: usize, len: usize) -> Self
-        where
-            A: SeqTrait<U8>,
-        {
-            let mut hacspec_buffer = EdhocMessageBufferHacspec::new();
-            hacspec_buffer.len = len;
-            hacspec_buffer.content = BytesMessageBuffer::from_slice(slice, start, len);
-            hacspec_buffer
-        }
-        fn from_seq(buffer: &Seq<U8>) -> Self {
-            EdhocMessageBufferHacspec {
-                content: BytesMessageBuffer::from_slice(buffer, 0, buffer.len()),
-                len: buffer.len(),
+    }
+
+    impl TryInto<EdhocMessageBuffer> for &[u8] {
+        type Error = ();
+
+        fn try_into(self) -> Result<EdhocMessageBuffer, Self::Error> {
+            if self.len() <= MAX_MESSAGE_SIZE_LEN {
+                let mut buffer = [0u8; MAX_MESSAGE_SIZE_LEN];
+                for i in 0..self.len() {
+                    buffer[i] = self[i];
+                }
+
+                Ok(EdhocMessageBuffer {
+                    content: buffer,
+                    len: self.len(),
+                })
+            } else {
+                Err(())
             }
         }
-        fn to_public_buffer(&self) -> EdhocMessageBuffer {
-            let mut buffer = EdhocMessageBuffer::new();
-            buffer.content = self.content.to_public_array();
-            buffer.len = self.len;
-            buffer
-        }
     }
 
     #[derive(Debug)]
-    pub struct EADItemHacspec {
-        pub label: U8,
+    pub struct EADItem {
+        pub label: u8,
         pub is_critical: bool,
         // TODO[ead]: have adjustable (smaller) length for this buffer
-        pub value: Option<EdhocMessageBufferHacspec>,
+        pub value: Option<EdhocMessageBuffer>,
     }
 
-    pub trait EADItemHacspecTrait {
+    pub trait EADTrait {
         fn new() -> Self;
-        fn from_public_item(item: &EADItem) -> Self;
-        fn to_public_item(&self) -> EADItem;
     }
 
-    impl EADItemHacspecTrait for EADItemHacspec {
+    impl EADTrait for EADItem {
         fn new() -> Self {
-            EADItemHacspec {
-                label: U8(0),
+            EADItem {
+                label: 0,
                 is_critical: false,
                 value: None,
             }
         }
-        fn from_public_item(item: &EADItem) -> Self {
-            EADItemHacspec {
-                label: U8(item.label),
-                is_critical: item.is_critical,
-                value: match &item.value {
-                    Some(value) => Some(EdhocMessageBufferHacspec::from_public_buffer(value)),
-                    None => None,
-                },
-            }
-        }
-        fn to_public_item(&self) -> EADItem {
-            EADItem {
-                label: self.label.declassify(),
-                is_critical: self.is_critical,
-                value: match &self.value {
-                    Some(value) => Some(value.to_public_buffer()),
-                    None => None,
-                },
-            }
-        }
     }
-
-    array!(BytesIdCred, ID_CRED_LEN, U8);
-    array!(BytesSuites, SUITES_LEN, U8);
-    array!(BytesSupportedSuites, SUPPORTED_SUITES_LEN, U8);
-    array!(Bytes8, 8, U8);
-    array!(BytesCcmKeyLen, AES_CCM_KEY_LEN, U8);
-    array!(BytesCcmIvLen, AES_CCM_IV_LEN, U8);
-    pub type BufferPlaintext2 = EdhocMessageBufferHacspec;
-    pub type BufferPlaintext3 = EdhocMessageBufferHacspec;
-    array!(BytesMac2, MAC_LENGTH_2, U8);
-    array!(BytesMac3, MAC_LENGTH_3, U8);
-    pub type BufferMessage1 = EdhocMessageBufferHacspec;
-    pub type BufferMessage3 = EdhocMessageBufferHacspec;
-    pub type BufferCiphertext2 = EdhocMessageBufferHacspec;
-    pub type BufferCiphertext3 = EdhocMessageBufferHacspec;
-    array!(BytesHashLen, SHA256_DIGEST_LEN, U8);
-    array!(BytesP256ElemLen, P256_ELEM_LEN, U8);
-    pub type BufferMessage2 = EdhocMessageBufferHacspec;
-    array!(BytesMaxBuffer, MAX_BUFFER_LEN, U8);
-    array!(BytesMaxContextBuffer, MAX_KDF_CONTEXT_LEN, U8);
-    array!(BytesMaxInfoBuffer, MAX_INFO_LEN, U8);
-    array!(BytesMaxLabelBuffer, MAX_KDF_LABEL_LEN, U8);
-    array!(BytesEncStructureLen, ENC_STRUCTURE_LEN, U8);
-
-    // Currently only suite number 2 is supported,
-    // which corresponds to the array 10, -16, 8, 1, -7, 10, -16,
-    // which in turn corresponds to the following:
-    // - AES-CCM-16-64-128 | EDHOC AEAD algorithm
-    // - SHA-256 | EDHOC hash algorithm
-    // - 8 | MAC length in bytes
-    // - P-256 | key exchange algorithm
-    // - ES256 | signature algorithm
-    // - AES-CCM-16-64-128 | Application AEAD algorithm
-    // - SHA-256 | Application hash algorithm
-    pub const EDHOC_SUPPORTED_SUITES: BytesSupportedSuites =
-        BytesSupportedSuites(secret_bytes!([0x2u8]));
-    pub const EDHOC_SUITES: BytesSuites = BytesSuites(secret_bytes!([0, 1, 2, 3, 4, 5, 6, 24, 25])); // all but private cipher suites
-
-    #[repr(C)]
-    #[derive(Default, Copy, Clone, Debug)]
-    pub struct State(
-        pub EDHOCState,
-        pub BytesP256ElemLen, // x or y, ephemeral private key of myself
-        pub U8,               // c_i, connection identifier chosen by the initiator
-        pub BytesP256ElemLen, // g_y or g_x, ephemeral public key of the peer
-        pub BytesHashLen,     // prk_3e2m
-        pub BytesHashLen,     // prk_4e3m
-        pub BytesHashLen,     // prk_out
-        pub BytesHashLen,     // prk_exporter
-        pub BytesHashLen,     // h_message_1
-        pub BytesHashLen,     // th_3
-    );
 }
diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml
index 5c625a66..7a27aa8c 100644
--- a/crypto/Cargo.toml
+++ b/crypto/Cargo.toml
@@ -25,9 +25,6 @@ edhoc-crypto-cryptocell310 = { path = "./edhoc-crypto-cryptocell310-sys", option
 default = [  ]
 hacspec = [ "edhoc-crypto-hacspec" ]
 cc2538 = [ "edhoc-crypto-cc2538" ]
-psa = [ "edhoc-crypto-psa/hacspec" ]
+psa = [ "edhoc-crypto-psa" ]
 psa-baremetal = [ "psa", "edhoc-crypto-psa/baremetal" ]
-psa-rust = [ "edhoc-crypto-psa/rust" ]
-psa-rust-baremetal = [ "psa-rust", "edhoc-crypto-psa/baremetal" ]
-cryptocell310 = [ "edhoc-crypto-cryptocell310/hacspec" ]
-cryptocell310-rust = [ "edhoc-crypto-cryptocell310/rust" ]
+cryptocell310 = [ "edhoc-crypto-cryptocell310" ]
diff --git a/crypto/edhoc-crypto-cryptocell310-sys/Cargo.toml b/crypto/edhoc-crypto-cryptocell310-sys/Cargo.toml
index 4b5e9480..03739a11 100644
--- a/crypto/edhoc-crypto-cryptocell310-sys/Cargo.toml
+++ b/crypto/edhoc-crypto-cryptocell310-sys/Cargo.toml
@@ -9,11 +9,6 @@ links = "nrf_cc310_0.9.13"
 
 [dependencies]
 edhoc-consts = { path = "../../consts" }
-hacspec-lib = { version = "0.1.0-beta.1", default-features = false, features = [ "alloc" ], optional = true }
 
 [build-dependencies]
 bindgen = "0.63.0"
-
-[features]
-rust = [ ]
-hacspec = [ "hacspec-lib" ]
diff --git a/crypto/edhoc-crypto-cryptocell310-sys/src/lib.rs b/crypto/edhoc-crypto-cryptocell310-sys/src/lib.rs
index 794b9b24..be73d460 100644
--- a/crypto/edhoc-crypto-cryptocell310-sys/src/lib.rs
+++ b/crypto/edhoc-crypto-cryptocell310-sys/src/lib.rs
@@ -18,594 +18,278 @@ fn convert_array(input: &[u32]) -> [u8; SHA256_DIGEST_LEN] {
     output
 }
 
-#[cfg(feature = "hacspec")]
-pub use hacspec::*;
-
-#[cfg(feature = "rust")]
-pub use rust::*;
-
-#[cfg(feature = "hacspec")]
-mod hacspec {
-    use super::*;
-    use hacspec_lib::*;
-
-    pub fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen {
-        let mut buffer: [u32; 64 / 4] = [0x00; 64 / 4];
-
-        unsafe {
-            CRYS_HASH(
-                CRYS_HASH_OperationMode_t_CRYS_HASH_SHA256_mode,
-                message.to_public_array().as_mut_ptr(),
-                message_len,
-                buffer.as_mut_ptr(),
-            );
-        }
-
-        BytesHashLen::from_public_slice(&convert_array(&buffer[0..SHA256_DIGEST_LEN / 4]))
+pub fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen {
+    let mut buffer: [u32; 64 / 4] = [0x00; 64 / 4];
+
+    unsafe {
+        CRYS_HASH(
+            CRYS_HASH_OperationMode_t_CRYS_HASH_SHA256_mode,
+            message.clone().as_mut_ptr(),
+            message_len,
+            buffer.as_mut_ptr(),
+        );
     }
 
-    pub fn hkdf_expand(
-        prk: &BytesHashLen,
-        info: &BytesMaxInfoBuffer,
-        info_len: usize,
-        length: usize,
-    ) -> BytesMaxBuffer {
-        let mut buffer = [0x00u8; MAX_BUFFER_LEN];
-        unsafe {
-            CRYS_HKDF_KeyDerivFunc(
-                CRYS_HKDF_HASH_OpMode_t_CRYS_HKDF_HASH_SHA256_mode,
-                core::ptr::null_mut(),
-                0 as usize,
-                prk.to_public_array().as_mut_ptr(),
-                prk.len() as u32,
-                info.to_public_array().as_mut_ptr(),
-                info_len as u32,
-                buffer.as_mut_ptr(),
-                length as u32,
-                SaSiBool_SASI_TRUE,
-            );
-        }
-
-        BytesMaxBuffer::from_public_slice(&buffer)
-    }
-
-    pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen {
-        // Implementation of HKDF-Extract as per RFC 5869
-
-        // TODO generalize if salt is not provided
-        let output = hmac_sha256(&mut ikm.to_public_array(), salt.to_public_array());
+    convert_array(&buffer[0..SHA256_DIGEST_LEN / 4])
+}
 
-        output
+pub fn hkdf_expand(
+    prk: &BytesHashLen,
+    info: &BytesMaxInfoBuffer,
+    info_len: usize,
+    length: usize,
+) -> BytesMaxBuffer {
+    let mut buffer = [0x00u8; MAX_BUFFER_LEN];
+    unsafe {
+        CRYS_HKDF_KeyDerivFunc(
+            CRYS_HKDF_HASH_OpMode_t_CRYS_HKDF_HASH_SHA256_mode,
+            core::ptr::null_mut(),
+            0 as usize,
+            prk.clone().as_mut_ptr(),
+            prk.len() as u32,
+            info.clone().as_mut_ptr(),
+            info_len as u32,
+            buffer.as_mut_ptr(),
+            length as u32,
+            SaSiBool_SASI_TRUE,
+        );
     }
 
-    pub fn aes_ccm_encrypt_tag_8(
-        key: &BytesCcmKeyLen,
-        iv: &BytesCcmIvLen,
-        ad: &BytesEncStructureLen,
-        plaintext: &BufferPlaintext3,
-    ) -> BufferCiphertext3 {
-        let mut output = EdhocMessageBuffer::new();
-        let mut tag: CRYS_AESCCM_Mac_Res_t = Default::default();
-        let mut aesccm_key: CRYS_AESCCM_Key_t = Default::default();
-
-        aesccm_key[0..AES_CCM_KEY_LEN].copy_from_slice(&key.to_public_array());
-
-        let err = unsafe {
-            CC_AESCCM(
-                SaSiAesEncryptMode_t_SASI_AES_ENCRYPT,
-                aesccm_key.as_mut_ptr(),
-                CRYS_AESCCM_KeySize_t_CRYS_AES_Key128BitSize,
-                iv.to_public_array().as_mut_ptr(),
-                iv.len() as u8,
-                ad.to_public_array().as_mut_ptr(),
-                ad.len() as u32,
-                plaintext.content.to_public_array()[..plaintext.len].as_mut_ptr(),
-                plaintext.len as u32,
-                output.content.as_mut_ptr(),
-                AES_CCM_TAG_LEN as u8, // authentication tag length
-                tag.as_mut_ptr(),
-                0 as u32, // CCM
-            )
-        };
-
-        output.content[plaintext.len..plaintext.len + AES_CCM_TAG_LEN]
-            .copy_from_slice(&tag[..AES_CCM_TAG_LEN]);
-        output.len = plaintext.len + AES_CCM_TAG_LEN;
-
-        BufferCiphertext3::from_public_buffer(&output)
-    }
+    buffer
+}
 
-    pub fn aes_ccm_decrypt_tag_8(
-        key: &BytesCcmKeyLen,
-        iv: &BytesCcmIvLen,
-        ad: &BytesEncStructureLen,
-        ciphertext: &BufferCiphertext3,
-    ) -> Result<BufferPlaintext3, EDHOCError> {
-        let mut output = EdhocMessageBuffer::new();
-        let mut aesccm_key: CRYS_AESCCM_Key_t = Default::default();
-
-        aesccm_key[0..AES_CCM_KEY_LEN].copy_from_slice(&key.to_public_array());
-
-        let mut err = EDHOCError::MacVerificationFailed;
-        let mut plaintext = BufferPlaintext3::new();
-
-        unsafe {
-            match CC_AESCCM(
-                SaSiAesEncryptMode_t_SASI_AES_DECRYPT,
-                aesccm_key.as_mut_ptr(),
-                CRYS_AESCCM_KeySize_t_CRYS_AES_Key128BitSize,
-                iv.to_public_array().as_mut_ptr(),
-                iv.len() as u8,
-                ad.to_public_array().as_mut_ptr(),
-                ad.len() as u32,
-                ciphertext.content.to_public_array().as_mut_ptr(),
-                (ciphertext.len - AES_CCM_TAG_LEN) as u32,
-                output.content.as_mut_ptr(),
-                AES_CCM_TAG_LEN as u8, // authentication tag length
-                ciphertext.content.to_public_array()[ciphertext.len - AES_CCM_TAG_LEN..]
-                    .as_mut_ptr(),
-                0 as u32, // CCM
-            ) {
-                CRYS_OK => {
-                    output.len = ciphertext.len - AES_CCM_TAG_LEN;
-                    Ok(BufferPlaintext3::from_public_buffer(&output))
-                }
-                _ => Err(EDHOCError::MacVerificationFailed),
-            }
-        }
-    }
-    pub fn p256_ecdh(
-        private_key: &BytesP256ElemLen,
-        public_key: &BytesP256ElemLen,
-    ) -> BytesP256ElemLen {
-        let mut output = [0x0u8; P256_ELEM_LEN];
-        let mut output_len: u32 = output.len() as u32;
-
-        let mut tmp: CRYS_ECDH_TempData_t = Default::default();
-
-        let mut public_key_compressed = [0x0u8; P256_ELEM_LEN + 1];
-        public_key_compressed[0] = 0x02;
-        public_key_compressed[1..].copy_from_slice(&public_key.to_public_array());
-
-        let mut public_key_cc310: CRYS_ECPKI_UserPublKey_t = Default::default();
-
-        let mut domain =
-            unsafe { CRYS_ECPKI_GetEcDomain(CRYS_ECPKI_DomainID_t_CRYS_ECPKI_DomainID_secp256r1) };
-
-        unsafe {
-            _DX_ECPKI_BuildPublKey(
-                domain,
-                public_key_compressed.as_mut_ptr(),
-                (P256_ELEM_LEN + 1) as u32,
-                EC_PublKeyCheckMode_t_CheckPointersAndSizesOnly,
-                &mut public_key_cc310,
-                core::ptr::null_mut(),
-            );
-        }
+pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen {
+    // Implementation of HKDF-Extract as per RFC 5869
 
-        let mut private_key_cc310: CRYS_ECPKI_UserPrivKey_t = Default::default();
+    // TODO generalize if salt is not provided
+    let output = hmac_sha256(&mut ikm.clone()[..], *salt);
 
-        unsafe {
-            CRYS_ECPKI_BuildPrivKey(
-                domain,
-                private_key.to_public_array().as_mut_ptr(),
-                P256_ELEM_LEN as u32,
-                &mut private_key_cc310,
-            );
-        }
+    output
+}
 
-        unsafe {
-            CRYS_ECDH_SVDP_DH(
-                &mut public_key_cc310,
-                &mut private_key_cc310,
-                output.as_mut_ptr(),
-                &mut output_len,
-                &mut tmp,
-            );
-        }
+pub fn aes_ccm_encrypt_tag_8(
+    key: &BytesCcmKeyLen,
+    iv: &BytesCcmIvLen,
+    ad: &BytesEncStructureLen,
+    plaintext: &BufferPlaintext3,
+) -> BufferCiphertext3 {
+    let mut output: BufferCiphertext3 = BufferCiphertext3::new();
+    let mut tag: CRYS_AESCCM_Mac_Res_t = Default::default();
+    let mut aesccm_key: CRYS_AESCCM_Key_t = Default::default();
+
+    aesccm_key[0..AES_CCM_KEY_LEN].copy_from_slice(&key[..]);
+
+    let err = unsafe {
+        CC_AESCCM(
+            SaSiAesEncryptMode_t_SASI_AES_ENCRYPT,
+            aesccm_key.as_mut_ptr(),
+            CRYS_AESCCM_KeySize_t_CRYS_AES_Key128BitSize,
+            iv.clone().as_mut_ptr(),
+            iv.len() as u8,
+            ad.clone().as_mut_ptr(),
+            ad.len() as u32,
+            plaintext.content.clone().as_mut_ptr(),
+            plaintext.len as u32,
+            output.content.as_mut_ptr(),
+            AES_CCM_TAG_LEN as u8, // authentication tag length
+            tag.as_mut_ptr(),
+            0 as u32, // CCM
+        )
+    };
+
+    output.content[plaintext.len..plaintext.len + AES_CCM_TAG_LEN]
+        .copy_from_slice(&tag[..AES_CCM_TAG_LEN]);
+    output.len = plaintext.len + AES_CCM_TAG_LEN;
 
-        BytesP256ElemLen::from_public_slice(&output)
-    }
+    output
+}
 
-    fn hmac_sha256(message: &mut [u8], mut key: [u8; SHA256_DIGEST_LEN]) -> BytesHashLen {
-        let mut buffer: [u32; 64 / 4] = [0x00; 64 / 4];
-
-        unsafe {
-            CRYS_HMAC(
-                CRYS_HASH_OperationMode_t_CRYS_HASH_SHA256_mode,
-                key.as_mut_ptr(),
-                key.len() as u16,
-                message.as_mut_ptr(),
-                message.len(),
-                buffer.as_mut_ptr(),
-            );
+pub fn aes_ccm_decrypt_tag_8(
+    key: &BytesCcmKeyLen,
+    iv: &BytesCcmIvLen,
+    ad: &BytesEncStructureLen,
+    ciphertext: &BufferCiphertext3,
+) -> Result<BufferPlaintext3, EDHOCError> {
+    let mut output: BufferPlaintext3 = BufferPlaintext3::new();
+    let mut aesccm_key: CRYS_AESCCM_Key_t = Default::default();
+
+    aesccm_key[0..AES_CCM_KEY_LEN].copy_from_slice(&key[..]);
+
+    let mut err = EDHOCError::MacVerificationFailed;
+
+    unsafe {
+        match CC_AESCCM(
+            SaSiAesEncryptMode_t_SASI_AES_DECRYPT,
+            aesccm_key.as_mut_ptr(),
+            CRYS_AESCCM_KeySize_t_CRYS_AES_Key128BitSize,
+            iv.clone().as_mut_ptr(),
+            iv.len() as u8,
+            ad.clone().as_mut_ptr(),
+            ad.len() as u32,
+            ciphertext.content.clone().as_mut_ptr(),
+            (ciphertext.len - AES_CCM_TAG_LEN) as u32,
+            output.content.as_mut_ptr(),
+            AES_CCM_TAG_LEN as u8, // authentication tag length
+            ciphertext.content.clone()[ciphertext.len - AES_CCM_TAG_LEN..].as_mut_ptr(),
+            0 as u32, // CCM
+        ) {
+            CRYS_OK => {
+                output.len = ciphertext.len - AES_CCM_TAG_LEN;
+                Ok(output)
+            }
+            _ => Err(EDHOCError::MacVerificationFailed),
         }
-
-        BytesHashLen::from_public_slice(&convert_array(&buffer[..SHA256_DIGEST_LEN / 4]))
     }
+}
 
-    pub fn get_random_byte() -> U8 {
-        let mut rnd_context = CRYS_RND_State_t::default();
-        let mut rnd_work_buffer = CRYS_RND_WorkBuff_t::default();
-        unsafe {
-            SaSi_LibInit();
-            CRYS_RndInit(
-                &mut rnd_context as *mut _ as *mut c_void,
-                &mut rnd_work_buffer as *mut _,
-            );
-        }
-        let mut buffer = [0u8; 1];
-        unsafe {
-            CRYS_RND_GenerateVector(
-                &mut rnd_context as *mut _ as *mut c_void,
-                1,
-                buffer.as_mut_ptr(),
-            );
-        }
-        U8(buffer[0])
+pub fn p256_ecdh(
+    private_key: &BytesP256ElemLen,
+    public_key: &BytesP256ElemLen,
+) -> BytesP256ElemLen {
+    let mut output = [0x0u8; P256_ELEM_LEN];
+    let mut output_len: u32 = output.len() as u32;
+
+    let mut tmp: CRYS_ECDH_TempData_t = Default::default();
+
+    let mut public_key_compressed = [0x0u8; P256_ELEM_LEN + 1];
+    public_key_compressed[0] = 0x02;
+    public_key_compressed[1..].copy_from_slice(&public_key[..]);
+
+    let mut public_key_cc310: CRYS_ECPKI_UserPublKey_t = Default::default();
+
+    let mut domain =
+        unsafe { CRYS_ECPKI_GetEcDomain(CRYS_ECPKI_DomainID_t_CRYS_ECPKI_DomainID_secp256r1) };
+
+    unsafe {
+        _DX_ECPKI_BuildPublKey(
+            domain,
+            public_key_compressed.as_mut_ptr(),
+            (P256_ELEM_LEN + 1) as u32,
+            EC_PublKeyCheckMode_t_CheckPointersAndSizesOnly,
+            &mut public_key_cc310,
+            core::ptr::null_mut(),
+        );
     }
 
-    pub fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen) {
-        let mut rnd_context = CRYS_RND_State_t::default();
-        let mut rnd_work_buffer = CRYS_RND_WorkBuff_t::default();
-        unsafe {
-            SaSi_LibInit();
-            CRYS_RndInit(
-                &mut rnd_context as *mut _ as *mut c_void,
-                &mut rnd_work_buffer as *mut _,
-            );
-        }
-        let rnd_generate_vect_func: SaSiRndGenerateVectWorkFunc_t = Some(CRYS_RND_GenerateVector);
-        let mut curve_256 =
-            unsafe { CRYS_ECPKI_GetEcDomain(CRYS_ECPKI_DomainID_t_CRYS_ECPKI_DomainID_secp256r1) };
-        let mut crys_private_key: *mut CRYS_ECPKI_UserPrivKey_t =
-            &mut CRYS_ECPKI_UserPrivKey_t::default();
-        let mut crys_public_key: *mut CRYS_ECPKI_UserPublKey_t =
-            &mut CRYS_ECPKI_UserPublKey_t::default();
-        let mut temp_data: *mut CRYS_ECPKI_KG_TempData_t = &mut CRYS_ECPKI_KG_TempData_t::default();
-        let mut temp_fips_buffer: *mut CRYS_ECPKI_KG_FipsContext_t =
-            &mut CRYS_ECPKI_KG_FipsContext_t::default();
-
-        unsafe {
-            CRYS_ECPKI_GenKeyPair(
-                &mut rnd_context as *mut _ as *mut c_void,
-                rnd_generate_vect_func,
-                curve_256,
-                crys_private_key,
-                crys_public_key,
-                temp_data,
-                temp_fips_buffer,
-            );
-        }
-
-        let mut private_key: [u8; P256_ELEM_LEN] = [0x0; P256_ELEM_LEN];
-        let mut key_size: u32 = P256_ELEM_LEN.try_into().unwrap();
-
-        unsafe {
-            CRYS_ECPKI_ExportPrivKey(crys_private_key, private_key.as_mut_ptr(), &mut key_size);
-        }
-
-        let private_key = BytesP256ElemLen::from_public_slice(&private_key[..]);
-
-        let mut public_key: [u8; P256_ELEM_LEN + 1] = [0x0; P256_ELEM_LEN + 1];
-        let mut key_size: u32 = (P256_ELEM_LEN as u32) + 1;
-        let compressed_flag: CRYS_ECPKI_PointCompression_t =
-            CRYS_ECPKI_PointCompression_t_CRYS_EC_PointCompressed;
-
-        unsafe {
-            CRYS_ECPKI_ExportPublKey(
-                crys_public_key,
-                compressed_flag,
-                public_key.as_mut_ptr(),
-                &mut key_size,
-            );
-        }
-
-        let public_key = BytesP256ElemLen::from_public_slice(&public_key[1..]); // discard sign byte
+    let mut private_key_cc310: CRYS_ECPKI_UserPrivKey_t = Default::default();
 
-        (private_key, public_key)
+    unsafe {
+        CRYS_ECPKI_BuildPrivKey(
+            domain,
+            private_key.clone().as_mut_ptr(),
+            P256_ELEM_LEN as u32,
+            &mut private_key_cc310,
+        );
     }
 
-    pub fn test_hmac_sha256() {
-        let mut KEY: [u8; 32] = [0x0b; 32];
-        let mut MESSAGE_1: [u8; 0] = [];
-        const RESULT_1_TV: [u8; 32] = [
-            0x51, 0x77, 0xe6, 0x37, 0xaa, 0xac, 0x0b, 0x50, 0xe5, 0xdc, 0xa8, 0xbb, 0x05, 0xb0,
-            0xb5, 0x71, 0x44, 0x4b, 0xd5, 0x9b, 0x9b, 0x0d, 0x83, 0x4d, 0x50, 0x68, 0x1a, 0xf2,
-            0x1f, 0xc1, 0x4b, 0x1e,
-        ];
-        let mut MESSAGE_2: [u8; 1] = [0x0a];
-        const RESULT_2_TV: [u8; 32] = [
-            0x30, 0x50, 0x86, 0x79, 0x39, 0x85, 0x02, 0xd9, 0xdd, 0x70, 0x7e, 0xff, 0x6c, 0x84,
-            0x08, 0x9d, 0x83, 0x12, 0xcc, 0xea, 0x25, 0x36, 0x4d, 0x9c, 0xb8, 0xb0, 0xbd, 0x94,
-            0xd0, 0xe6, 0x55, 0xa3,
-        ];
-
-        let result_1 = hmac_sha256(&mut MESSAGE_1, KEY).to_public_array();
-        assert_eq!(result_1, RESULT_1_TV);
-
-        let result_2 = hmac_sha256(&mut MESSAGE_2, KEY).to_public_array();
-        assert_eq!(result_2, RESULT_2_TV);
+    unsafe {
+        CRYS_ECDH_SVDP_DH(
+            &mut public_key_cc310,
+            &mut private_key_cc310,
+            output.as_mut_ptr(),
+            &mut output_len,
+            &mut tmp,
+        );
     }
-}
-
-#[cfg(feature = "rust")]
-mod rust {
-    use super::*;
-
-    pub fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen {
-        let mut buffer: [u32; 64 / 4] = [0x00; 64 / 4];
 
-        unsafe {
-            CRYS_HASH(
-                CRYS_HASH_OperationMode_t_CRYS_HASH_SHA256_mode,
-                message.clone().as_mut_ptr(),
-                message_len,
-                buffer.as_mut_ptr(),
-            );
-        }
+    output
+}
 
-        convert_array(&buffer[0..SHA256_DIGEST_LEN / 4])
+fn hmac_sha256(message: &mut [u8], mut key: [u8; SHA256_DIGEST_LEN]) -> BytesHashLen {
+    let mut buffer: [u32; 64 / 4] = [0x00; 64 / 4];
+
+    unsafe {
+        CRYS_HMAC(
+            CRYS_HASH_OperationMode_t_CRYS_HASH_SHA256_mode,
+            key.as_mut_ptr(),
+            key.len() as u16,
+            message.as_mut_ptr(),
+            message.len(),
+            buffer.as_mut_ptr(),
+        );
     }
 
-    pub fn hkdf_expand(
-        prk: &BytesHashLen,
-        info: &BytesMaxInfoBuffer,
-        info_len: usize,
-        length: usize,
-    ) -> BytesMaxBuffer {
-        let mut buffer = [0x00u8; MAX_BUFFER_LEN];
-        unsafe {
-            CRYS_HKDF_KeyDerivFunc(
-                CRYS_HKDF_HASH_OpMode_t_CRYS_HKDF_HASH_SHA256_mode,
-                core::ptr::null_mut(),
-                0 as usize,
-                prk.clone().as_mut_ptr(),
-                prk.len() as u32,
-                info.clone().as_mut_ptr(),
-                info_len as u32,
-                buffer.as_mut_ptr(),
-                length as u32,
-                SaSiBool_SASI_TRUE,
-            );
-        }
+    convert_array(&buffer[..SHA256_DIGEST_LEN / 4])
+}
 
-        buffer
+pub fn get_random_byte() -> u8 {
+    let mut rnd_context = CRYS_RND_State_t::default();
+    let mut rnd_work_buffer = CRYS_RND_WorkBuff_t::default();
+    unsafe {
+        SaSi_LibInit();
+        CRYS_RndInit(
+            &mut rnd_context as *mut _ as *mut c_void,
+            &mut rnd_work_buffer as *mut _,
+        );
     }
-
-    pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen {
-        // Implementation of HKDF-Extract as per RFC 5869
-
-        // TODO generalize if salt is not provided
-        let output = hmac_sha256(&mut ikm.clone()[..], *salt);
-
-        output
+    let mut buffer = [0u8; 1];
+    unsafe {
+        CRYS_RND_GenerateVector(
+            &mut rnd_context as *mut _ as *mut c_void,
+            1,
+            buffer.as_mut_ptr(),
+        );
     }
+    buffer[0]
+}
 
-    pub fn aes_ccm_encrypt_tag_8(
-        key: &BytesCcmKeyLen,
-        iv: &BytesCcmIvLen,
-        ad: &BytesEncStructureLen,
-        plaintext: &BufferPlaintext3,
-    ) -> BufferCiphertext3 {
-        let mut output: BufferCiphertext3 = BufferCiphertext3::new();
-        let mut tag: CRYS_AESCCM_Mac_Res_t = Default::default();
-        let mut aesccm_key: CRYS_AESCCM_Key_t = Default::default();
-
-        aesccm_key[0..AES_CCM_KEY_LEN].copy_from_slice(&key[..]);
-
-        let err = unsafe {
-            CC_AESCCM(
-                SaSiAesEncryptMode_t_SASI_AES_ENCRYPT,
-                aesccm_key.as_mut_ptr(),
-                CRYS_AESCCM_KeySize_t_CRYS_AES_Key128BitSize,
-                iv.clone().as_mut_ptr(),
-                iv.len() as u8,
-                ad.clone().as_mut_ptr(),
-                ad.len() as u32,
-                plaintext.content.clone().as_mut_ptr(),
-                plaintext.len as u32,
-                output.content.as_mut_ptr(),
-                AES_CCM_TAG_LEN as u8, // authentication tag length
-                tag.as_mut_ptr(),
-                0 as u32, // CCM
-            )
-        };
-
-        output.content[plaintext.len..plaintext.len + AES_CCM_TAG_LEN]
-            .copy_from_slice(&tag[..AES_CCM_TAG_LEN]);
-        output.len = plaintext.len + AES_CCM_TAG_LEN;
-
-        output
+pub fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen) {
+    let mut rnd_context = CRYS_RND_State_t::default();
+    let mut rnd_work_buffer = CRYS_RND_WorkBuff_t::default();
+    unsafe {
+        SaSi_LibInit();
+        CRYS_RndInit(
+            &mut rnd_context as *mut _ as *mut c_void,
+            &mut rnd_work_buffer as *mut _,
+        );
     }
-
-    pub fn aes_ccm_decrypt_tag_8(
-        key: &BytesCcmKeyLen,
-        iv: &BytesCcmIvLen,
-        ad: &BytesEncStructureLen,
-        ciphertext: &BufferCiphertext3,
-    ) -> Result<BufferPlaintext3, EDHOCError> {
-        let mut output: BufferPlaintext3 = BufferPlaintext3::new();
-        let mut aesccm_key: CRYS_AESCCM_Key_t = Default::default();
-
-        aesccm_key[0..AES_CCM_KEY_LEN].copy_from_slice(&key[..]);
-
-        let mut err = EDHOCError::MacVerificationFailed;
-
-        unsafe {
-            match CC_AESCCM(
-                SaSiAesEncryptMode_t_SASI_AES_DECRYPT,
-                aesccm_key.as_mut_ptr(),
-                CRYS_AESCCM_KeySize_t_CRYS_AES_Key128BitSize,
-                iv.clone().as_mut_ptr(),
-                iv.len() as u8,
-                ad.clone().as_mut_ptr(),
-                ad.len() as u32,
-                ciphertext.content.clone().as_mut_ptr(),
-                (ciphertext.len - AES_CCM_TAG_LEN) as u32,
-                output.content.as_mut_ptr(),
-                AES_CCM_TAG_LEN as u8, // authentication tag length
-                ciphertext.content.clone()[ciphertext.len - AES_CCM_TAG_LEN..].as_mut_ptr(),
-                0 as u32, // CCM
-            ) {
-                CRYS_OK => {
-                    output.len = ciphertext.len - AES_CCM_TAG_LEN;
-                    Ok(output)
-                }
-                _ => Err(EDHOCError::MacVerificationFailed),
-            }
-        }
+    let rnd_generate_vect_func: SaSiRndGenerateVectWorkFunc_t = Some(CRYS_RND_GenerateVector);
+    let mut curve_256 =
+        unsafe { CRYS_ECPKI_GetEcDomain(CRYS_ECPKI_DomainID_t_CRYS_ECPKI_DomainID_secp256r1) };
+    let mut crys_private_key: *mut CRYS_ECPKI_UserPrivKey_t =
+        &mut CRYS_ECPKI_UserPrivKey_t::default();
+    let mut crys_public_key: *mut CRYS_ECPKI_UserPublKey_t =
+        &mut CRYS_ECPKI_UserPublKey_t::default();
+    let mut temp_data: *mut CRYS_ECPKI_KG_TempData_t = &mut CRYS_ECPKI_KG_TempData_t::default();
+    let mut temp_fips_buffer: *mut CRYS_ECPKI_KG_FipsContext_t =
+        &mut CRYS_ECPKI_KG_FipsContext_t::default();
+
+    unsafe {
+        CRYS_ECPKI_GenKeyPair(
+            &mut rnd_context as *mut _ as *mut c_void,
+            rnd_generate_vect_func,
+            curve_256,
+            crys_private_key,
+            crys_public_key,
+            temp_data,
+            temp_fips_buffer,
+        );
     }
 
-    pub fn p256_ecdh(
-        private_key: &BytesP256ElemLen,
-        public_key: &BytesP256ElemLen,
-    ) -> BytesP256ElemLen {
-        let mut output = [0x0u8; P256_ELEM_LEN];
-        let mut output_len: u32 = output.len() as u32;
-
-        let mut tmp: CRYS_ECDH_TempData_t = Default::default();
-
-        let mut public_key_compressed = [0x0u8; P256_ELEM_LEN + 1];
-        public_key_compressed[0] = 0x02;
-        public_key_compressed[1..].copy_from_slice(&public_key[..]);
-
-        let mut public_key_cc310: CRYS_ECPKI_UserPublKey_t = Default::default();
-
-        let mut domain =
-            unsafe { CRYS_ECPKI_GetEcDomain(CRYS_ECPKI_DomainID_t_CRYS_ECPKI_DomainID_secp256r1) };
-
-        unsafe {
-            _DX_ECPKI_BuildPublKey(
-                domain,
-                public_key_compressed.as_mut_ptr(),
-                (P256_ELEM_LEN + 1) as u32,
-                EC_PublKeyCheckMode_t_CheckPointersAndSizesOnly,
-                &mut public_key_cc310,
-                core::ptr::null_mut(),
-            );
-        }
-
-        let mut private_key_cc310: CRYS_ECPKI_UserPrivKey_t = Default::default();
-
-        unsafe {
-            CRYS_ECPKI_BuildPrivKey(
-                domain,
-                private_key.clone().as_mut_ptr(),
-                P256_ELEM_LEN as u32,
-                &mut private_key_cc310,
-            );
-        }
-
-        unsafe {
-            CRYS_ECDH_SVDP_DH(
-                &mut public_key_cc310,
-                &mut private_key_cc310,
-                output.as_mut_ptr(),
-                &mut output_len,
-                &mut tmp,
-            );
-        }
+    let mut private_key: [u8; P256_ELEM_LEN] = [0x0; P256_ELEM_LEN];
+    let mut key_size: u32 = P256_ELEM_LEN.try_into().unwrap();
 
-        output
+    unsafe {
+        CRYS_ECPKI_ExportPrivKey(crys_private_key, private_key.as_mut_ptr(), &mut key_size);
     }
 
-    fn hmac_sha256(message: &mut [u8], mut key: [u8; SHA256_DIGEST_LEN]) -> BytesHashLen {
-        let mut buffer: [u32; 64 / 4] = [0x00; 64 / 4];
-
-        unsafe {
-            CRYS_HMAC(
-                CRYS_HASH_OperationMode_t_CRYS_HASH_SHA256_mode,
-                key.as_mut_ptr(),
-                key.len() as u16,
-                message.as_mut_ptr(),
-                message.len(),
-                buffer.as_mut_ptr(),
-            );
-        }
+    // let private_key = BytesP256ElemLen::from_public_slice(&private_key[..]);
 
-        convert_array(&buffer[..SHA256_DIGEST_LEN / 4])
-    }
+    let mut public_key: [u8; P256_ELEM_LEN + 1] = [0x0; P256_ELEM_LEN + 1];
+    let mut key_size: u32 = (P256_ELEM_LEN as u32) + 1;
+    let compressed_flag: CRYS_ECPKI_PointCompression_t =
+        CRYS_ECPKI_PointCompression_t_CRYS_EC_PointCompressed;
 
-    pub fn get_random_byte() -> u8 {
-        let mut rnd_context = CRYS_RND_State_t::default();
-        let mut rnd_work_buffer = CRYS_RND_WorkBuff_t::default();
-        unsafe {
-            SaSi_LibInit();
-            CRYS_RndInit(
-                &mut rnd_context as *mut _ as *mut c_void,
-                &mut rnd_work_buffer as *mut _,
-            );
-        }
-        let mut buffer = [0u8; 1];
-        unsafe {
-            CRYS_RND_GenerateVector(
-                &mut rnd_context as *mut _ as *mut c_void,
-                1,
-                buffer.as_mut_ptr(),
-            );
-        }
-        buffer[0]
+    unsafe {
+        CRYS_ECPKI_ExportPublKey(
+            crys_public_key,
+            compressed_flag,
+            public_key.as_mut_ptr(),
+            &mut key_size,
+        );
     }
 
-    pub fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen) {
-        let mut rnd_context = CRYS_RND_State_t::default();
-        let mut rnd_work_buffer = CRYS_RND_WorkBuff_t::default();
-        unsafe {
-            SaSi_LibInit();
-            CRYS_RndInit(
-                &mut rnd_context as *mut _ as *mut c_void,
-                &mut rnd_work_buffer as *mut _,
-            );
-        }
-        let rnd_generate_vect_func: SaSiRndGenerateVectWorkFunc_t = Some(CRYS_RND_GenerateVector);
-        let mut curve_256 =
-            unsafe { CRYS_ECPKI_GetEcDomain(CRYS_ECPKI_DomainID_t_CRYS_ECPKI_DomainID_secp256r1) };
-        let mut crys_private_key: *mut CRYS_ECPKI_UserPrivKey_t =
-            &mut CRYS_ECPKI_UserPrivKey_t::default();
-        let mut crys_public_key: *mut CRYS_ECPKI_UserPublKey_t =
-            &mut CRYS_ECPKI_UserPublKey_t::default();
-        let mut temp_data: *mut CRYS_ECPKI_KG_TempData_t = &mut CRYS_ECPKI_KG_TempData_t::default();
-        let mut temp_fips_buffer: *mut CRYS_ECPKI_KG_FipsContext_t =
-            &mut CRYS_ECPKI_KG_FipsContext_t::default();
-
-        unsafe {
-            CRYS_ECPKI_GenKeyPair(
-                &mut rnd_context as *mut _ as *mut c_void,
-                rnd_generate_vect_func,
-                curve_256,
-                crys_private_key,
-                crys_public_key,
-                temp_data,
-                temp_fips_buffer,
-            );
-        }
-
-        let mut private_key: [u8; P256_ELEM_LEN] = [0x0; P256_ELEM_LEN];
-        let mut key_size: u32 = P256_ELEM_LEN.try_into().unwrap();
-
-        unsafe {
-            CRYS_ECPKI_ExportPrivKey(crys_private_key, private_key.as_mut_ptr(), &mut key_size);
-        }
-
-        // let private_key = BytesP256ElemLen::from_public_slice(&private_key[..]);
-
-        let mut public_key: [u8; P256_ELEM_LEN + 1] = [0x0; P256_ELEM_LEN + 1];
-        let mut key_size: u32 = (P256_ELEM_LEN as u32) + 1;
-        let compressed_flag: CRYS_ECPKI_PointCompression_t =
-            CRYS_ECPKI_PointCompression_t_CRYS_EC_PointCompressed;
+    let public_key: [u8; P256_ELEM_LEN] = public_key[1..33].try_into().unwrap(); // discard sign byte
 
-        unsafe {
-            CRYS_ECPKI_ExportPublKey(
-                crys_public_key,
-                compressed_flag,
-                public_key.as_mut_ptr(),
-                &mut key_size,
-            );
-        }
-
-        let public_key: [u8; P256_ELEM_LEN] = public_key[1..33].try_into().unwrap(); // discard sign byte
-
-        (private_key, public_key)
-    }
+    (private_key, public_key)
 }
diff --git a/crypto/edhoc-crypto-hacspec/Cargo.toml b/crypto/edhoc-crypto-hacspec/Cargo.toml
index e59a361c..df741064 100644
--- a/crypto/edhoc-crypto-hacspec/Cargo.toml
+++ b/crypto/edhoc-crypto-hacspec/Cargo.toml
@@ -7,7 +7,7 @@ license = "BSD"
 description = "EDHOC crypto library hacspec backend"
 
 [dependencies]
-edhoc-consts = { path = "../../consts", default-features = false, features = ["hacspec"] }
+edhoc-consts = { path = "../../consts", default-features = false }
 hacspec-lib = { version = "0.1.0-beta.1", default-features = false }
 hacspec-p256 = { version = "0.1.0" }
 hacspec-hkdf = { version = "0.1.0" }
diff --git a/crypto/edhoc-crypto-hacspec/src/lib.rs b/crypto/edhoc-crypto-hacspec/src/lib.rs
index 13c312b5..350fe741 100644
--- a/crypto/edhoc-crypto-hacspec/src/lib.rs
+++ b/crypto/edhoc-crypto-hacspec/src/lib.rs
@@ -9,9 +9,74 @@ use hacspec_p256::*;
 use hacspec_sha256::*;
 use rand::Rng;
 
+// Types and functions to aid in translation between the hacspec and non-hacspec world
+
+// TODO: the `array!` construct is not needed anymore.
+// Ideally this should be: `type BytesCcmKeyLen = [u8; AES_CCM_KEY_LEN];`
+// However, it is not clear how to implement the equivalents to `from_public_slice` and friends
+//  when using the normal array construct.
+array!(BytesCcmKeyLenHacspec, AES_CCM_KEY_LEN, U8);
+array!(BytesCcmIvLenHacspec, AES_CCM_IV_LEN, U8);
+array!(BytesHashLenHacspec, SHA256_DIGEST_LEN, U8);
+array!(BytesP256ElemLenHacspec, P256_ELEM_LEN, U8);
+array!(BytesMaxBufferHacspec, MAX_BUFFER_LEN, U8);
+array!(BytesMaxInfoBufferHacspec, MAX_INFO_LEN, U8);
+array!(BytesEncStructureLenHacspec, ENC_STRUCTURE_LEN, U8);
+
+array!(BytesMessageBuffer, MAX_MESSAGE_SIZE_LEN, U8);
+
+#[derive(Debug)]
+pub struct EdhocMessageBufferHacspec {
+    pub content: BytesMessageBuffer,
+    pub len: usize,
+}
+
+pub trait MessageBufferHacspecTrait {
+    fn new() -> Self;
+    fn from_public_buffer(buffer: &EdhocMessageBuffer) -> Self;
+    fn from_seq(buffer: &Seq<U8>) -> Self;
+    fn to_public_buffer(&self) -> EdhocMessageBuffer;
+}
+
+impl MessageBufferHacspecTrait for EdhocMessageBufferHacspec {
+    fn new() -> Self {
+        EdhocMessageBufferHacspec {
+            content: BytesMessageBuffer::new(),
+            len: 0,
+        }
+    }
+    fn from_public_buffer(buffer: &EdhocMessageBuffer) -> Self {
+        let mut hacspec_buffer = EdhocMessageBufferHacspec::new();
+        hacspec_buffer.len = buffer.len;
+        hacspec_buffer.content = BytesMessageBuffer::from_public_slice(&buffer.content[..]);
+        hacspec_buffer
+    }
+    fn from_seq(buffer: &Seq<U8>) -> Self {
+        EdhocMessageBufferHacspec {
+            content: BytesMessageBuffer::from_slice(buffer, 0, buffer.len()),
+            len: buffer.len(),
+        }
+    }
+    fn to_public_buffer(&self) -> EdhocMessageBuffer {
+        let mut buffer = EdhocMessageBuffer::new();
+        buffer.content = self.content.to_public_array();
+        buffer.len = self.len;
+        buffer
+    }
+}
+
+type BufferCiphertext3Hacspec = EdhocMessageBufferHacspec;
+type BufferPlaintext3Hacspec = EdhocMessageBufferHacspec;
+
+// Public functions
+
 pub fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen {
-    let output = BytesHashLen::from_seq(&hash(&ByteSeq::from_slice(message, 0, message_len)));
-    output
+    let message: BytesMaxBufferHacspec = BytesMaxBufferHacspec::from_public_slice(message);
+
+    let output =
+        BytesHashLenHacspec::from_seq(&hash(&ByteSeq::from_slice(&message, 0, message_len)));
+
+    output.to_public_array()
 }
 
 pub fn hkdf_expand(
@@ -20,25 +85,33 @@ pub fn hkdf_expand(
     info_len: usize,
     length: usize,
 ) -> BytesMaxBuffer {
-    let mut output = BytesMaxBuffer::new();
+    let mut output = BytesMaxBufferHacspec::new();
     output = output.update(
         0,
         &expand(
-            &ByteSeq::from_slice(prk, 0, prk.len()),
-            &ByteSeq::from_slice(info, 0, info_len),
+            &ByteSeq::from_slice(&BytesHashLenHacspec::from_public_slice(prk), 0, prk.len()),
+            &ByteSeq::from_slice(
+                &BytesMaxInfoBufferHacspec::from_public_slice(info),
+                0,
+                info_len,
+            ),
             length,
         )
         .unwrap(),
     );
-    output
+    output.to_public_array()
 }
 
 pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen {
-    let output = BytesHashLen::from_seq(&extract(
-        &ByteSeq::from_slice(salt, 0, salt.len()),
-        &ByteSeq::from_slice(ikm, 0, ikm.len()),
+    let output = BytesHashLenHacspec::from_seq(&extract(
+        &ByteSeq::from_slice(&BytesHashLenHacspec::from_public_slice(salt), 0, salt.len()),
+        &ByteSeq::from_slice(
+            &BytesP256ElemLenHacspec::from_public_slice(ikm),
+            0,
+            ikm.len(),
+        ),
     ));
-    output
+    output.to_public_array()
 }
 
 pub fn aes_ccm_encrypt_tag_8(
@@ -47,15 +120,21 @@ pub fn aes_ccm_encrypt_tag_8(
     ad: &BytesEncStructureLen,
     plaintext: &BufferPlaintext3,
 ) -> BufferCiphertext3 {
-    let output = BufferCiphertext3::from_seq(&encrypt_ccm(
-        ByteSeq::from_slice(ad, 0, ad.len()),
-        ByteSeq::from_slice(iv, 0, iv.len()),
+    let plaintext = BufferPlaintext3Hacspec::from_public_buffer(plaintext);
+
+    let output = BufferCiphertext3Hacspec::from_seq(&encrypt_ccm(
+        ByteSeq::from_slice(
+            &BytesEncStructureLenHacspec::from_public_slice(ad),
+            0,
+            ad.len(),
+        ),
+        ByteSeq::from_slice(&BytesCcmIvLenHacspec::from_public_slice(iv), 0, iv.len()),
         ByteSeq::from_slice(&plaintext.content, 0, plaintext.len),
-        Key128::from_slice(key, 0, key.len()),
+        Key128::from_slice(&BytesCcmKeyLenHacspec::from_public_slice(key), 0, key.len()),
         AES_CCM_TAG_LEN,
     ));
 
-    output
+    output.to_public_buffer()
 }
 
 pub fn aes_ccm_decrypt_tag_8(
@@ -64,15 +143,21 @@ pub fn aes_ccm_decrypt_tag_8(
     ad: &BytesEncStructureLen,
     ciphertext: &BufferCiphertext3,
 ) -> Result<BufferPlaintext3, EDHOCError> {
+    let ciphertext = BufferCiphertext3Hacspec::from_public_buffer(ciphertext);
+
     match decrypt_ccm(
-        ByteSeq::from_slice(ad, 0, ad.len()),
-        ByteSeq::from_slice(iv, 0, iv.len()),
-        Key128::from_slice(key, 0, key.len()),
+        ByteSeq::from_slice(
+            &BytesEncStructureLenHacspec::from_public_slice(ad),
+            0,
+            ad.len(),
+        ),
+        ByteSeq::from_slice(&BytesCcmIvLenHacspec::from_public_slice(iv), 0, iv.len()),
+        Key128::from_slice(&BytesCcmKeyLenHacspec::from_public_slice(key), 0, key.len()),
         ByteSeq::from_slice(&ciphertext.content, 0, ciphertext.len),
         ciphertext.len,
         AES_CCM_TAG_LEN,
     ) {
-        Ok(p) => Ok(BufferPlaintext3::from_seq(&p)),
+        Ok(p) => Ok(BufferPlaintext3Hacspec::from_seq(&p).to_public_buffer()),
         Err(_) => Err(EDHOCError::MacVerificationFailed),
     }
 }
@@ -81,28 +166,32 @@ pub fn p256_ecdh(
     private_key: &BytesP256ElemLen,
     public_key: &BytesP256ElemLen,
 ) -> BytesP256ElemLen {
-    let scalar = P256Scalar::from_byte_seq_be(private_key);
+    let private_key = BytesP256ElemLenHacspec::from_public_slice(private_key);
+    let public_key = BytesP256ElemLenHacspec::from_public_slice(public_key);
+
+    let scalar = P256Scalar::from_byte_seq_be(&private_key);
     let point = (
-        P256FieldElement::from_byte_seq_be(public_key),
-        p256_calculate_w(P256FieldElement::from_byte_seq_be(public_key)),
+        P256FieldElement::from_byte_seq_be(&public_key),
+        p256_calculate_w(P256FieldElement::from_byte_seq_be(&public_key)),
     );
 
     // we only care about the x coordinate
     let (x, _y) = p256_point_mul(scalar, point).unwrap();
 
-    let secret = BytesP256ElemLen::from_seq(&x.to_byte_seq_be());
-    secret
+    let secret = BytesP256ElemLenHacspec::from_seq(&x.to_byte_seq_be());
+
+    secret.to_public_array()
 }
 
 #[cfg(not(feature = "hacspec-pure"))]
-pub fn get_random_byte() -> U8 {
-    U8(rand::thread_rng().gen::<u8>())
+pub fn get_random_byte() -> u8 {
+    rand::thread_rng().gen::<u8>()
 }
 
 #[cfg(not(feature = "hacspec-pure"))]
 pub fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen) {
     // generate a private key
-    let mut private_key = BytesP256ElemLen::new();
+    let mut private_key = BytesP256ElemLenHacspec::new();
     loop {
         for i in 0..private_key.len() {
             private_key[i] = U8(rand::thread_rng().gen::<u8>());
@@ -115,9 +204,9 @@ pub fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen) {
     // obtain the corresponding public key
     let scalar = P256Scalar::from_byte_seq_be(&private_key);
     let public_key_point = p256_point_mul_base(scalar).unwrap();
-    let public_key = BytesP256ElemLen::from_seq(&public_key_point.0.to_byte_seq_be());
+    let public_key = BytesP256ElemLenHacspec::from_seq(&public_key_point.0.to_byte_seq_be());
 
-    (private_key, public_key)
+    (private_key.to_public_array(), public_key.to_public_array())
 }
 
 #[cfg(test)]
@@ -135,6 +224,6 @@ mod tests {
         let g_xy = p256_ecdh(&x, &g_y);
         let g_yx = p256_ecdh(&y, &g_x);
 
-        assert_bytes_eq!(g_xy, g_yx);
+        assert_eq!(g_xy, g_yx);
     }
 }
diff --git a/crypto/edhoc-crypto-psa/Cargo.toml b/crypto/edhoc-crypto-psa/Cargo.toml
index 1716cdc2..d3df8a5e 100644
--- a/crypto/edhoc-crypto-psa/Cargo.toml
+++ b/crypto/edhoc-crypto-psa/Cargo.toml
@@ -8,10 +8,7 @@ description = "EDHOC crypto library PSA backend"
 
 [dependencies]
 edhoc-consts = { path = "../../consts" }
-hacspec-lib = { version = "0.1.0-beta.1", default-features = false, features = [ "alloc" ], optional = true }
 psa-crypto = { version = "0.9.2" }
 
 [features]
 baremetal = [ "psa-crypto/baremetal" ]
-hacspec = [ "hacspec-lib" ]
-rust = [ ]
diff --git a/crypto/edhoc-crypto-psa/src/lib.rs b/crypto/edhoc-crypto-psa/src/lib.rs
index 94b92d30..938ba1dc 100644
--- a/crypto/edhoc-crypto-psa/src/lib.rs
+++ b/crypto/edhoc-crypto-psa/src/lib.rs
@@ -20,547 +20,257 @@ pub extern "C" fn mbedtls_hardware_poll(
     0i32
 }
 
-#[cfg(feature = "hacspec")]
-pub use hacspec::*;
+pub fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen {
+    let hash_alg = Hash::Sha256;
+    let mut hash: [u8; SHA256_DIGEST_LEN] = [0; SHA256_DIGEST_LEN];
+    psa_crypto::init().unwrap();
+    hash_compute(hash_alg, &message[..message_len], &mut hash).unwrap();
 
-#[cfg(feature = "rust")]
-pub use rust::*;
-
-#[cfg(feature = "hacspec")]
-mod hacspec {
-    use super::*;
-    use hacspec_lib::*;
-
-    pub fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen {
-        let hash_alg = Hash::Sha256;
-        let mut hash: [u8; SHA256_DIGEST_LEN] = [0; SHA256_DIGEST_LEN];
-        let message = message.to_public_array();
-        psa_crypto::init().unwrap();
-        hash_compute(hash_alg, &message[..message_len], &mut hash).unwrap();
-        let output = BytesHashLen::from_public_slice(&hash);
-
-        output
-    }
-
-    pub fn hkdf_expand(
-        prk: &BytesHashLen,
-        info: &BytesMaxInfoBuffer,
-        info_len: usize,
-        length: usize,
-    ) -> BytesMaxBuffer {
-        // Implementation of HKDF-Expand as per RFC5869
-
-        let mut output: [u8; MAX_BUFFER_LEN] = [0; MAX_BUFFER_LEN];
-
-        let mut n = 0;
-
-        // N = ceil(L/HashLen)
-        if length % SHA256_DIGEST_LEN == 0 {
-            n = length / SHA256_DIGEST_LEN;
-        } else {
-            n = length / SHA256_DIGEST_LEN + 1;
-        }
-
-        let info_buf = info.to_public_array();
-
-        let mut message: [u8; MAX_INFO_LEN + SHA256_DIGEST_LEN + 1] =
-            [0; MAX_INFO_LEN + SHA256_DIGEST_LEN + 1];
-        message[..info_len].copy_from_slice(&info_buf[..info_len]);
-        message[info_len] = 0x01;
-        let mut t_i =
-            hmac_sha256(&message[..info_len + 1], prk.to_public_array()).to_public_array();
-        output[..SHA256_DIGEST_LEN].copy_from_slice(&t_i);
-
-        for i in 2..n {
-            message[..SHA256_DIGEST_LEN].copy_from_slice(&t_i);
-            message[SHA256_DIGEST_LEN..SHA256_DIGEST_LEN + info_len]
-                .copy_from_slice(&info_buf[..info_len]);
-            message[SHA256_DIGEST_LEN + info_len] = i as u8;
-            t_i = hmac_sha256(
-                &message[..SHA256_DIGEST_LEN + info_len + 1],
-                prk.to_public_array(),
-            )
-            .to_public_array();
-            output[i * SHA256_DIGEST_LEN..(i + 1) * SHA256_DIGEST_LEN].copy_from_slice(&t_i);
-        }
-
-        output[length..].fill(0x00);
-
-        BytesMaxBuffer::from_public_slice(&output)
-    }
+    hash
+}
 
-    pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen {
-        // Implementation of HKDF-Extract as per RFC 5869
+pub fn hkdf_expand(
+    prk: &BytesHashLen,
+    info: &BytesMaxInfoBuffer,
+    info_len: usize,
+    length: usize,
+) -> BytesMaxBuffer {
+    // Implementation of HKDF-Expand as per RFC5869
 
-        // TODO generalize if salt is not provided
-        let output = hmac_sha256(&ikm.to_public_array(), salt.to_public_array());
+    let mut output: [u8; MAX_BUFFER_LEN] = [0; MAX_BUFFER_LEN];
 
-        output
-    }
+    let mut n = 0;
 
-    pub fn aes_ccm_encrypt_tag_8(
-        key: &BytesCcmKeyLen,
-        iv: &BytesCcmIvLen,
-        ad: &BytesEncStructureLen,
-        plaintext: &BufferPlaintext3,
-    ) -> BufferCiphertext3 {
-        psa_crypto::init().unwrap();
-
-        let alg = Aead::AeadWithShortenedTag {
-            aead_alg: AeadWithDefaultLengthTag::Ccm,
-            tag_length: 8,
-        };
-        let mut usage_flags: UsageFlags = Default::default();
-        usage_flags.set_encrypt();
-
-        let attributes = Attributes {
-            key_type: Type::Aes,
-            bits: 128,
-            lifetime: Lifetime::Volatile,
-            policy: Policy {
-                usage_flags,
-                permitted_algorithms: alg.into(),
-            },
-        };
-        let my_key = key_management::import(attributes, None, &key.to_public_array()).unwrap();
-        let mut output_buffer = EdhocMessageBuffer::new();
-
-        aead::encrypt(
-            my_key,
-            alg,
-            &iv.to_public_array(),
-            &ad.to_public_array(),
-            &plaintext.content.to_public_array()[..plaintext.len],
-            &mut output_buffer.content,
-        )
-        .unwrap();
-
-        output_buffer.len = plaintext.len + AES_CCM_TAG_LEN;
-        let output = BufferCiphertext3::from_public_buffer(&output_buffer);
-        output
+    // N = ceil(L/HashLen)
+    if length % SHA256_DIGEST_LEN == 0 {
+        n = length / SHA256_DIGEST_LEN;
+    } else {
+        n = length / SHA256_DIGEST_LEN + 1;
     }
 
-    pub fn aes_ccm_decrypt_tag_8(
-        key: &BytesCcmKeyLen,
-        iv: &BytesCcmIvLen,
-        ad: &BytesEncStructureLen,
-        ciphertext: &BufferCiphertext3,
-    ) -> Result<BufferPlaintext3, EDHOCError> {
-        psa_crypto::init().unwrap();
-
-        let alg = Aead::AeadWithShortenedTag {
-            aead_alg: AeadWithDefaultLengthTag::Ccm,
-            tag_length: 8,
-        };
-        let mut usage_flags: UsageFlags = Default::default();
-        usage_flags.set_decrypt();
-
-        let attributes = Attributes {
-            key_type: Type::Aes,
-            bits: 128,
-            lifetime: Lifetime::Volatile,
-            policy: Policy {
-                usage_flags,
-                permitted_algorithms: alg.into(),
-            },
-        };
-        let my_key = key_management::import(attributes, None, &key.to_public_array()).unwrap();
-        let mut output_buffer = EdhocMessageBuffer::new();
-
-        match aead::decrypt(
-            my_key,
-            alg,
-            &iv.to_public_array(),
-            &ad.to_public_array(),
-            &ciphertext.content.to_public_array()[..ciphertext.len],
-            &mut output_buffer.content,
-        ) {
-            Ok(_) => {
-                output_buffer.len = ciphertext.len - AES_CCM_TAG_LEN;
-                Ok(BufferPlaintext3::from_public_buffer(&output_buffer))
-            }
-            Err(_) => Err(EDHOCError::MacVerificationFailed),
-        }
-    }
-    pub fn p256_ecdh(
-        private_key: &BytesP256ElemLen,
-        public_key: &BytesP256ElemLen,
-    ) -> BytesP256ElemLen {
-        let mut peer_public_key: [u8; 33] = [0; 33];
-        peer_public_key[0] = 0x02; // sign does not matter for ECDH operation
-        peer_public_key[1..33].copy_from_slice(&public_key.to_public_array());
-
-        let alg = RawKeyAgreement::Ecdh;
-        let mut usage_flags: UsageFlags = Default::default();
-        usage_flags.set_derive();
-        let attributes = Attributes {
-            key_type: Type::EccKeyPair {
-                curve_family: EccFamily::SecpR1,
-            },
-            bits: 256,
-            lifetime: Lifetime::Volatile,
-            policy: Policy {
-                usage_flags,
-                permitted_algorithms: KeyAgreement::Raw(alg).into(),
-            },
-        };
-
-        psa_crypto::init().unwrap();
-        let my_key =
-            key_management::import(attributes, None, &private_key.to_public_array()).unwrap();
-        let mut output_buffer: [u8; P256_ELEM_LEN] = [0; P256_ELEM_LEN];
-
-        key_agreement::raw_key_agreement(alg, my_key, &peer_public_key, &mut output_buffer)
-            .unwrap();
-
-        let output = BytesP256ElemLen::from_public_slice(&output_buffer[..]);
-
-        output
+    let mut message: [u8; MAX_INFO_LEN + SHA256_DIGEST_LEN + 1] =
+        [0; MAX_INFO_LEN + SHA256_DIGEST_LEN + 1];
+    message[..info_len].copy_from_slice(&info[..info_len]);
+    message[info_len] = 0x01;
+    let mut t_i = hmac_sha256(&message[..info_len + 1], prk);
+    output[..SHA256_DIGEST_LEN].copy_from_slice(&t_i);
+
+    for i in 2..n {
+        message[..SHA256_DIGEST_LEN].copy_from_slice(&t_i);
+        message[SHA256_DIGEST_LEN..SHA256_DIGEST_LEN + info_len].copy_from_slice(&info[..info_len]);
+        message[SHA256_DIGEST_LEN + info_len] = i as u8;
+        t_i = hmac_sha256(&message[..SHA256_DIGEST_LEN + info_len + 1], prk);
+        output[i * SHA256_DIGEST_LEN..(i + 1) * SHA256_DIGEST_LEN].copy_from_slice(&t_i);
     }
 
-    pub fn hmac_sha256(message: &[u8], key: [u8; SHA256_DIGEST_LEN]) -> BytesHashLen {
-        // implementation of HMAC as per RFC2104
+    output[length..].fill(0x00);
 
-        const IPAD: [u8; 64] = [0x36; 64];
-        const OPAD: [u8; 64] = [0x5C; 64];
+    output
+}
 
-        //    (1) append zeros to the end of K to create a B byte string
-        //        (e.g., if K is of length 20 bytes and B=64, then K will be
-        //         appended with 44 zero bytes 0x00)
-        let mut b: [u8; MAX_BUFFER_LEN] = [0; MAX_BUFFER_LEN];
-        b[0..SHA256_DIGEST_LEN].copy_from_slice(&key);
+pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen {
+    // Implementation of HKDF-Extract as per RFC 5869
 
-        //    (2) XOR (bitwise exclusive-OR) the B byte string computed in step
-        //        (1) with ipad
-        let mut s2: [u8; MAX_BUFFER_LEN] = [0; MAX_BUFFER_LEN];
-        for i in 0..64 {
-            s2[i] = b[i] ^ IPAD[i];
-        }
+    // TODO generalize if salt is not provided
+    let output = hmac_sha256(ikm, salt);
 
-        //    (3) append the stream of data 'text' to the B byte string resulting
-        //        from step (2)
-        s2[64..64 + message.len()].copy_from_slice(message);
+    output
+}
 
-        //    (4) apply H to the stream generated in step (3)
-        let ih = sha256_digest(&BytesMaxBuffer::from_public_slice(&s2), 64 + message.len());
+pub fn aes_ccm_encrypt_tag_8(
+    key: &BytesCcmKeyLen,
+    iv: &BytesCcmIvLen,
+    ad: &BytesEncStructureLen,
+    plaintext: &BufferPlaintext3,
+) -> BufferCiphertext3 {
+    psa_crypto::init().unwrap();
+
+    let alg = Aead::AeadWithShortenedTag {
+        aead_alg: AeadWithDefaultLengthTag::Ccm,
+        tag_length: 8,
+    };
+    let mut usage_flags: UsageFlags = Default::default();
+    usage_flags.set_encrypt();
+
+    let attributes = Attributes {
+        key_type: Type::Aes,
+        bits: 128,
+        lifetime: Lifetime::Volatile,
+        policy: Policy {
+            usage_flags,
+            permitted_algorithms: alg.into(),
+        },
+    };
+    let my_key = key_management::import(attributes, None, &key[..]).unwrap();
+    let mut output_buffer: BufferCiphertext3 = BufferCiphertext3::new();
+
+    aead::encrypt(
+        my_key,
+        alg,
+        iv,
+        ad,
+        &plaintext.content[..plaintext.len],
+        &mut output_buffer.content,
+    )
+    .unwrap();
+
+    output_buffer.len = plaintext.len + AES_CCM_TAG_LEN;
+    output_buffer
+}
 
-        //    (5) XOR (bitwise exclusive-OR) the B byte string computed in
-        //        step (1) with opad
-        let mut s5: [u8; MAX_BUFFER_LEN] = [0; MAX_BUFFER_LEN];
-        for i in 0..64 {
-            s5[i] = b[i] ^ OPAD[i];
+pub fn aes_ccm_decrypt_tag_8(
+    key: &BytesCcmKeyLen,
+    iv: &BytesCcmIvLen,
+    ad: &BytesEncStructureLen,
+    ciphertext: &BufferCiphertext3,
+) -> Result<BufferPlaintext3, EDHOCError> {
+    psa_crypto::init().unwrap();
+
+    let alg = Aead::AeadWithShortenedTag {
+        aead_alg: AeadWithDefaultLengthTag::Ccm,
+        tag_length: 8,
+    };
+    let mut usage_flags: UsageFlags = Default::default();
+    usage_flags.set_decrypt();
+
+    let attributes = Attributes {
+        key_type: Type::Aes,
+        bits: 128,
+        lifetime: Lifetime::Volatile,
+        policy: Policy {
+            usage_flags,
+            permitted_algorithms: alg.into(),
+        },
+    };
+    let my_key = key_management::import(attributes, None, &key[..]).unwrap();
+    let mut output_buffer: BufferPlaintext3 = BufferPlaintext3::new();
+
+    match aead::decrypt(
+        my_key,
+        alg,
+        iv,
+        ad,
+        &ciphertext.content[..ciphertext.len],
+        &mut output_buffer.content,
+    ) {
+        Ok(_) => {
+            output_buffer.len = ciphertext.len - AES_CCM_TAG_LEN;
+            Ok(output_buffer)
         }
-        //    (6) append the H result from step (4) to the B byte string
-        //        resulting from step (5)
-        s5[64..64 + SHA256_DIGEST_LEN].copy_from_slice(&ih.to_public_array());
-
-        //    (7) apply H to the stream generated in step (6) and output
-        //        the result
-        let oh = sha256_digest(
-            &BytesMaxBuffer::from_public_slice(&s5),
-            3 * SHA256_DIGEST_LEN,
-        );
-
-        oh
-    }
-
-    pub fn get_random_byte() -> U8 {
-        psa_crypto::init().unwrap();
-        let mut buffer = [0u8; 1];
-        generate_random(&mut buffer); // TODO: check return value
-        U8(buffer[0])
-    }
-
-    pub fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen) {
-        let alg = RawKeyAgreement::Ecdh;
-        let mut usage_flags: UsageFlags = UsageFlags::default();
-        usage_flags.set_export();
-        usage_flags.set_derive();
-        let attributes = Attributes {
-            key_type: Type::EccKeyPair {
-                curve_family: EccFamily::SecpR1,
-            },
-            bits: 256,
-            lifetime: Lifetime::Volatile,
-            policy: Policy {
-                usage_flags,
-                permitted_algorithms: KeyAgreement::Raw(alg).into(),
-            },
-        };
-
-        psa_crypto::init().unwrap();
-
-        let key_id = key_management::generate(attributes, None).unwrap();
-        let mut private_key: [u8; P256_ELEM_LEN] = [0; P256_ELEM_LEN];
-        key_management::export(key_id, &mut private_key).unwrap();
-        let private_key = BytesP256ElemLen::from_public_slice(&private_key[..]);
-
-        let mut public_key: [u8; P256_ELEM_LEN * 2 + 1] = [0; P256_ELEM_LEN * 2 + 1]; // allocate buffer for: sign, x, and y coordinates
-        key_management::export_public(key_id, &mut public_key).unwrap();
-        let public_key = BytesP256ElemLen::from_public_slice(&public_key[1..33]); // return only the x coordinate
-
-        (private_key, public_key)
+        Err(_) => Err(EDHOCError::MacVerificationFailed),
     }
 }
 
-#[cfg(feature = "rust")]
-mod rust {
-    use super::*;
-
-    pub fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen {
-        let hash_alg = Hash::Sha256;
-        let mut hash: [u8; SHA256_DIGEST_LEN] = [0; SHA256_DIGEST_LEN];
-        psa_crypto::init().unwrap();
-        hash_compute(hash_alg, &message[..message_len], &mut hash).unwrap();
-
-        hash
-    }
-
-    pub fn hkdf_expand(
-        prk: &BytesHashLen,
-        info: &BytesMaxInfoBuffer,
-        info_len: usize,
-        length: usize,
-    ) -> BytesMaxBuffer {
-        // Implementation of HKDF-Expand as per RFC5869
-
-        let mut output: [u8; MAX_BUFFER_LEN] = [0; MAX_BUFFER_LEN];
-
-        let mut n = 0;
-
-        // N = ceil(L/HashLen)
-        if length % SHA256_DIGEST_LEN == 0 {
-            n = length / SHA256_DIGEST_LEN;
-        } else {
-            n = length / SHA256_DIGEST_LEN + 1;
-        }
-
-        let mut message: [u8; MAX_INFO_LEN + SHA256_DIGEST_LEN + 1] =
-            [0; MAX_INFO_LEN + SHA256_DIGEST_LEN + 1];
-        message[..info_len].copy_from_slice(&info[..info_len]);
-        message[info_len] = 0x01;
-        let mut t_i = hmac_sha256(&message[..info_len + 1], prk);
-        output[..SHA256_DIGEST_LEN].copy_from_slice(&t_i);
-
-        for i in 2..n {
-            message[..SHA256_DIGEST_LEN].copy_from_slice(&t_i);
-            message[SHA256_DIGEST_LEN..SHA256_DIGEST_LEN + info_len]
-                .copy_from_slice(&info[..info_len]);
-            message[SHA256_DIGEST_LEN + info_len] = i as u8;
-            t_i = hmac_sha256(&message[..SHA256_DIGEST_LEN + info_len + 1], prk);
-            output[i * SHA256_DIGEST_LEN..(i + 1) * SHA256_DIGEST_LEN].copy_from_slice(&t_i);
-        }
-
-        output[length..].fill(0x00);
+pub fn p256_ecdh(
+    private_key: &BytesP256ElemLen,
+    public_key: &BytesP256ElemLen,
+) -> BytesP256ElemLen {
+    let mut peer_public_key: [u8; 33] = [0; 33];
+    peer_public_key[0] = 0x02; // sign does not matter for ECDH operation
+    peer_public_key[1..33].copy_from_slice(&public_key[..]);
+
+    let alg = RawKeyAgreement::Ecdh;
+    let mut usage_flags: UsageFlags = Default::default();
+    usage_flags.set_derive();
+    let attributes = Attributes {
+        key_type: Type::EccKeyPair {
+            curve_family: EccFamily::SecpR1,
+        },
+        bits: 256,
+        lifetime: Lifetime::Volatile,
+        policy: Policy {
+            usage_flags,
+            permitted_algorithms: KeyAgreement::Raw(alg).into(),
+        },
+    };
+
+    psa_crypto::init().unwrap();
+    let my_key = key_management::import(attributes, None, private_key).unwrap();
+    let mut output_buffer: [u8; P256_ELEM_LEN] = [0; P256_ELEM_LEN];
+
+    key_agreement::raw_key_agreement(alg, my_key, &peer_public_key, &mut output_buffer).unwrap();
+
+    output_buffer
+}
 
-        output
-    }
+pub fn hmac_sha256(message: &[u8], key: &[u8; SHA256_DIGEST_LEN]) -> BytesHashLen {
+    // implementation of HMAC as per RFC2104
 
-    pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen {
-        // Implementation of HKDF-Extract as per RFC 5869
+    const IPAD: [u8; 64] = [0x36; 64];
+    const OPAD: [u8; 64] = [0x5C; 64];
 
-        // TODO generalize if salt is not provided
-        let output = hmac_sha256(ikm, salt);
+    //    (1) append zeros to the end of K to create a B byte string
+    //        (e.g., if K is of length 20 bytes and B=64, then K will be
+    //         appended with 44 zero bytes 0x00)
+    let mut b: [u8; MAX_BUFFER_LEN] = [0; MAX_BUFFER_LEN];
+    b[0..SHA256_DIGEST_LEN].copy_from_slice(&key[..]);
 
-        output
+    //    (2) XOR (bitwise exclusive-OR) the B byte string computed in step
+    //        (1) with ipad
+    let mut s2: [u8; MAX_BUFFER_LEN] = [0; MAX_BUFFER_LEN];
+    for i in 0..64 {
+        s2[i] = b[i] ^ IPAD[i];
     }
 
-    pub fn aes_ccm_encrypt_tag_8(
-        key: &BytesCcmKeyLen,
-        iv: &BytesCcmIvLen,
-        ad: &BytesEncStructureLen,
-        plaintext: &BufferPlaintext3,
-    ) -> BufferCiphertext3 {
-        psa_crypto::init().unwrap();
-
-        let alg = Aead::AeadWithShortenedTag {
-            aead_alg: AeadWithDefaultLengthTag::Ccm,
-            tag_length: 8,
-        };
-        let mut usage_flags: UsageFlags = Default::default();
-        usage_flags.set_encrypt();
-
-        let attributes = Attributes {
-            key_type: Type::Aes,
-            bits: 128,
-            lifetime: Lifetime::Volatile,
-            policy: Policy {
-                usage_flags,
-                permitted_algorithms: alg.into(),
-            },
-        };
-        let my_key = key_management::import(attributes, None, &key[..]).unwrap();
-        let mut output_buffer: BufferCiphertext3 = BufferCiphertext3::new();
-
-        aead::encrypt(
-            my_key,
-            alg,
-            iv,
-            ad,
-            &plaintext.content[..plaintext.len],
-            &mut output_buffer.content,
-        )
-        .unwrap();
-
-        output_buffer.len = plaintext.len + AES_CCM_TAG_LEN;
-        output_buffer
-    }
+    //    (3) append the stream of data 'text' to the B byte string resulting
+    //        from step (2)
+    s2[64..64 + message.len()].copy_from_slice(message);
 
-    pub fn aes_ccm_decrypt_tag_8(
-        key: &BytesCcmKeyLen,
-        iv: &BytesCcmIvLen,
-        ad: &BytesEncStructureLen,
-        ciphertext: &BufferCiphertext3,
-    ) -> Result<BufferPlaintext3, EDHOCError> {
-        psa_crypto::init().unwrap();
-
-        let alg = Aead::AeadWithShortenedTag {
-            aead_alg: AeadWithDefaultLengthTag::Ccm,
-            tag_length: 8,
-        };
-        let mut usage_flags: UsageFlags = Default::default();
-        usage_flags.set_decrypt();
-
-        let attributes = Attributes {
-            key_type: Type::Aes,
-            bits: 128,
-            lifetime: Lifetime::Volatile,
-            policy: Policy {
-                usage_flags,
-                permitted_algorithms: alg.into(),
-            },
-        };
-        let my_key = key_management::import(attributes, None, &key[..]).unwrap();
-        let mut output_buffer: BufferPlaintext3 = BufferPlaintext3::new();
-
-        match aead::decrypt(
-            my_key,
-            alg,
-            iv,
-            ad,
-            &ciphertext.content[..ciphertext.len],
-            &mut output_buffer.content,
-        ) {
-            Ok(_) => {
-                output_buffer.len = ciphertext.len - AES_CCM_TAG_LEN;
-                Ok(output_buffer)
-            }
-            Err(_) => Err(EDHOCError::MacVerificationFailed),
-        }
-    }
+    //    (4) apply H to the stream generated in step (3)
+    let ih = sha256_digest(&s2, 64 + message.len());
 
-    pub fn p256_ecdh(
-        private_key: &BytesP256ElemLen,
-        public_key: &BytesP256ElemLen,
-    ) -> BytesP256ElemLen {
-        let mut peer_public_key: [u8; 33] = [0; 33];
-        peer_public_key[0] = 0x02; // sign does not matter for ECDH operation
-        peer_public_key[1..33].copy_from_slice(&public_key[..]);
-
-        let alg = RawKeyAgreement::Ecdh;
-        let mut usage_flags: UsageFlags = Default::default();
-        usage_flags.set_derive();
-        let attributes = Attributes {
-            key_type: Type::EccKeyPair {
-                curve_family: EccFamily::SecpR1,
-            },
-            bits: 256,
-            lifetime: Lifetime::Volatile,
-            policy: Policy {
-                usage_flags,
-                permitted_algorithms: KeyAgreement::Raw(alg).into(),
-            },
-        };
-
-        psa_crypto::init().unwrap();
-        let my_key = key_management::import(attributes, None, private_key).unwrap();
-        let mut output_buffer: [u8; P256_ELEM_LEN] = [0; P256_ELEM_LEN];
-
-        key_agreement::raw_key_agreement(alg, my_key, &peer_public_key, &mut output_buffer)
-            .unwrap();
-
-        output_buffer
+    //    (5) XOR (bitwise exclusive-OR) the B byte string computed in
+    //        step (1) with opad
+    let mut s5: [u8; MAX_BUFFER_LEN] = [0; MAX_BUFFER_LEN];
+    for i in 0..64 {
+        s5[i] = b[i] ^ OPAD[i];
     }
+    //    (6) append the H result from step (4) to the B byte string
+    //        resulting from step (5)
+    s5[64..64 + SHA256_DIGEST_LEN].copy_from_slice(&ih);
 
-    pub fn hmac_sha256(message: &[u8], key: &[u8; SHA256_DIGEST_LEN]) -> BytesHashLen {
-        // implementation of HMAC as per RFC2104
+    //    (7) apply H to the stream generated in step (6) and output
+    //        the result
+    let oh = sha256_digest(&s5, 3 * SHA256_DIGEST_LEN);
 
-        const IPAD: [u8; 64] = [0x36; 64];
-        const OPAD: [u8; 64] = [0x5C; 64];
-
-        //    (1) append zeros to the end of K to create a B byte string
-        //        (e.g., if K is of length 20 bytes and B=64, then K will be
-        //         appended with 44 zero bytes 0x00)
-        let mut b: [u8; MAX_BUFFER_LEN] = [0; MAX_BUFFER_LEN];
-        b[0..SHA256_DIGEST_LEN].copy_from_slice(&key[..]);
-
-        //    (2) XOR (bitwise exclusive-OR) the B byte string computed in step
-        //        (1) with ipad
-        let mut s2: [u8; MAX_BUFFER_LEN] = [0; MAX_BUFFER_LEN];
-        for i in 0..64 {
-            s2[i] = b[i] ^ IPAD[i];
-        }
-
-        //    (3) append the stream of data 'text' to the B byte string resulting
-        //        from step (2)
-        s2[64..64 + message.len()].copy_from_slice(message);
-
-        //    (4) apply H to the stream generated in step (3)
-        let ih = sha256_digest(&s2, 64 + message.len());
-
-        //    (5) XOR (bitwise exclusive-OR) the B byte string computed in
-        //        step (1) with opad
-        let mut s5: [u8; MAX_BUFFER_LEN] = [0; MAX_BUFFER_LEN];
-        for i in 0..64 {
-            s5[i] = b[i] ^ OPAD[i];
-        }
-        //    (6) append the H result from step (4) to the B byte string
-        //        resulting from step (5)
-        s5[64..64 + SHA256_DIGEST_LEN].copy_from_slice(&ih);
-
-        //    (7) apply H to the stream generated in step (6) and output
-        //        the result
-        let oh = sha256_digest(&s5, 3 * SHA256_DIGEST_LEN);
-
-        oh
-    }
+    oh
+}
 
-    pub fn get_random_byte() -> u8 {
-        psa_crypto::init().unwrap();
-        let mut buffer = [0u8; 1];
-        generate_random(&mut buffer); // TODO: check return value
-        buffer[0]
-    }
+pub fn get_random_byte() -> u8 {
+    psa_crypto::init().unwrap();
+    let mut buffer = [0u8; 1];
+    generate_random(&mut buffer); // TODO: check return value
+    buffer[0]
+}
 
-    pub fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen) {
-        let alg = RawKeyAgreement::Ecdh;
-        let mut usage_flags: UsageFlags = UsageFlags::default();
-        usage_flags.set_export();
-        usage_flags.set_derive();
-        let attributes = Attributes {
-            key_type: Type::EccKeyPair {
-                curve_family: EccFamily::SecpR1,
-            },
-            bits: 256,
-            lifetime: Lifetime::Volatile,
-            policy: Policy {
-                usage_flags,
-                permitted_algorithms: KeyAgreement::Raw(alg).into(),
-            },
-        };
-
-        psa_crypto::init().unwrap();
-
-        let key_id = key_management::generate(attributes, None).unwrap();
-        let mut private_key: [u8; P256_ELEM_LEN] = [0; P256_ELEM_LEN];
-        key_management::export(key_id, &mut private_key).unwrap();
-
-        let mut public_key: [u8; P256_ELEM_LEN * 2 + 1] = [0; P256_ELEM_LEN * 2 + 1]; // allocate buffer for: sign, x, and y coordinates
-        key_management::export_public(key_id, &mut public_key).unwrap();
-        let public_key: [u8; P256_ELEM_LEN] = public_key[1..33].try_into().unwrap(); // return only the x coordinate
-
-        (private_key, public_key)
-    }
+pub fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen) {
+    let alg = RawKeyAgreement::Ecdh;
+    let mut usage_flags: UsageFlags = UsageFlags::default();
+    usage_flags.set_export();
+    usage_flags.set_derive();
+    let attributes = Attributes {
+        key_type: Type::EccKeyPair {
+            curve_family: EccFamily::SecpR1,
+        },
+        bits: 256,
+        lifetime: Lifetime::Volatile,
+        policy: Policy {
+            usage_flags,
+            permitted_algorithms: KeyAgreement::Raw(alg).into(),
+        },
+    };
+
+    psa_crypto::init().unwrap();
+
+    let key_id = key_management::generate(attributes, None).unwrap();
+    let mut private_key: [u8; P256_ELEM_LEN] = [0; P256_ELEM_LEN];
+    key_management::export(key_id, &mut private_key).unwrap();
+
+    let mut public_key: [u8; P256_ELEM_LEN * 2 + 1] = [0; P256_ELEM_LEN * 2 + 1]; // allocate buffer for: sign, x, and y coordinates
+    key_management::export_public(key_id, &mut public_key).unwrap();
+    let public_key: [u8; P256_ELEM_LEN] = public_key[1..33].try_into().unwrap(); // return only the x coordinate
+
+    (private_key, public_key)
 }
 
 #[cfg(test)]
@@ -583,10 +293,10 @@ mod tests {
             0xd0, 0xe6, 0x55, 0xa3,
         ];
 
-        let result_1 = hmac_sha256(&MESSAGE_1, KEY).to_public_array();
+        let result_1 = hmac_sha256(&MESSAGE_1, &KEY);
         assert_eq!(result_1, RESULT_1_TV);
 
-        let result_2 = hmac_sha256(&MESSAGE_2, KEY).to_public_array();
+        let result_2 = hmac_sha256(&MESSAGE_2, &KEY);
         assert_eq!(result_2, RESULT_2_TV);
     }
 }
diff --git a/examples/c-wrapper-riot/Makefile b/examples/c-wrapper-riot/Makefile
index 44e00f6e..e4f6a241 100644
--- a/examples/c-wrapper-riot/Makefile
+++ b/examples/c-wrapper-riot/Makefile
@@ -7,13 +7,13 @@ BOARD ?= nrf52840dk
 INCLUDES += -I$(CURDIR)/../../target/include
 ARCHIVES += $(CURDIR)/../../target/thumbv7em-none-eabihf/release/libedhoc_rs.a
 
-ifeq ($(EDHOC_CRYPTO), RUST_PSA)
+ifeq ($(EDHOC_CRYPTO), CRYPTO_PSA)
 	CFLAGS += -D$(EDHOC_CRYPTO)
 else
-	CFLAGS += -DRUST_CRYPTOCELL310
+	CFLAGS += -DCRYPTO_CRYPTOCELL310
 endif
 
-# This is actually only needed in the RUST_CRYPTOCELL310 configuration
+# This is actually only needed in the CRYPTO_CRYPTOCELL310 configuration
 CFLAGS += -DTHREAD_STACKSIZE_DEFAULT=16384 -DISR_STACKSIZE=16384
 
 USEMODULE += od
diff --git a/examples/c-wrapper-riot/README.md b/examples/c-wrapper-riot/README.md
index f61e5276..da5bf9c5 100644
--- a/examples/c-wrapper-riot/README.md
+++ b/examples/c-wrapper-riot/README.md
@@ -8,21 +8,21 @@ See [Requirements](#requirements) below.
 First, go to the top level directory and generate the headers and static library:
 
 ```bash
-./build_for_c.sh rust-cryptocell310 # or rust-psa-baremetal
+./build_for_c.sh crypto-cryptocell310 # or crypto-psa-baremetal
 ```
 
 Then, compile and flash to the board (default is nRF52840), as shown below.
 
-With `rust-cryptocell310`:
+With `crypto-cryptocell310`:
 
 ```bash
 make flash term
 ```
 
-With `rust-psa-baremetal`:
+With `crypto-psa-baremetal`:
 
 ```bash
-make flash term EDHOC_CRYPTO=RUST_PSA
+make flash term EDHOC_CRYPTO=CRYPTO_PSA
 ```
 
 # Requirements
diff --git a/examples/c-wrapper-riot/main.c b/examples/c-wrapper-riot/main.c
index 3c030232..2b479f6a 100644
--- a/examples/c-wrapper-riot/main.c
+++ b/examples/c-wrapper-riot/main.c
@@ -3,7 +3,7 @@
 #include "od.h"
 #include "edhoc_rs.h"
 
-#ifdef RUST_PSA
+#ifdef CRYPTO_PSA
 extern void mbedtls_memory_buffer_alloc_init(uint8_t *buf, size_t len);
 #endif
 
@@ -20,7 +20,7 @@ int main(void)
  {
     puts("Calling edhoc-rs from C!");
 
-#ifdef RUST_PSA
+#ifdef CRYPTO_PSA
     // Memory buffer for mbedtls
     uint8_t buffer[4096 * 2] = {0};
     mbedtls_memory_buffer_alloc_init(buffer, 4096 * 2);
diff --git a/examples/coap/Cargo.toml b/examples/coap/Cargo.toml
index f94fd75a..0fe1785e 100644
--- a/examples/coap/Cargo.toml
+++ b/examples/coap/Cargo.toml
@@ -4,6 +4,6 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-edhoc-rs = { path = "../../lib", features = [ "hacspec-hacspec" ] }
+edhoc-rs = { path = "../../lib", features = [ "crypto-psa" ] }
 coap = { version = "0.13" }
 coap-lite = { version = "0.11.3" }
diff --git a/examples/coap/src/bin/coapclient.rs b/examples/coap/src/bin/coapclient.rs
index 275074d8..353a01e4 100644
--- a/examples/coap/src/bin/coapclient.rs
+++ b/examples/coap/src/bin/coapclient.rs
@@ -23,7 +23,8 @@ fn main() {
 
     // Send Message 1 over CoAP and convert the response to byte
     let mut msg_1_buf = Vec::from([0xf5u8]); // EDHOC message_1 when transported over CoAP is prepended with CBOR true
-    let message_1 = initiator.prepare_message_1().unwrap();
+    let c_i = generate_connection_identifier_cbor();
+    let message_1 = initiator.prepare_message_1(c_i).unwrap();
     msg_1_buf.extend_from_slice(&message_1.content[..message_1.len]);
     println!("message_1 len = {}", msg_1_buf.len());
 
diff --git a/examples/coap/src/bin/coapserver.rs b/examples/coap/src/bin/coapserver.rs
index 08344ef8..28337b18 100644
--- a/examples/coap/src/bin/coapserver.rs
+++ b/examples/coap/src/bin/coapserver.rs
@@ -40,7 +40,8 @@ fn main() {
                 );
 
                 if error.is_ok() {
-                    let (message_2, c_r) = responder.prepare_message_2().unwrap();
+                    let c_r = generate_connection_identifier_cbor();
+                    let message_2 = responder.prepare_message_2(c_r).unwrap();
                     response.message.payload = Vec::from(&message_2.content[..message_2.len]);
                     // save edhoc connection
                     edhoc_connections.push((c_r, responder));
diff --git a/examples/edhoc-rs-cc2538/Cargo.toml b/examples/edhoc-rs-cc2538/Cargo.toml
index 2fb5d035..7a967f41 100644
--- a/examples/edhoc-rs-cc2538/Cargo.toml
+++ b/examples/edhoc-rs-cc2538/Cargo.toml
@@ -19,7 +19,6 @@ panic-rtt-target = { version = "0.1.2", features = ["cortex-m"] }
 rtt-target = { version = "0.3.1", features = ["cortex-m"] }
 
 [features]
-default = [ "cc2538" ]
-cc2538 = [ "edhoc-rs/hacspec-cc2538" ]
-psa = [ "edhoc-rs/hacspec-psa-baremetal" ]
+default = [ "psa" ]
+psa = [ "edhoc-rs/crypto-psa-baremetal" ]
 
diff --git a/examples/edhoc-rs-no_std/Cargo.toml b/examples/edhoc-rs-no_std/Cargo.toml
index 05772a7f..ee4245b6 100644
--- a/examples/edhoc-rs-no_std/Cargo.toml
+++ b/examples/edhoc-rs-no_std/Cargo.toml
@@ -21,11 +21,8 @@ rtt-target = { version = "0.3.1", features = ["cortex-m"] }
 
 [features]
 default = [ ]
-hacspec-cc2538 = [ "edhoc-rs/hacspec-cc2538" ]
-hacspec-psa = [ "edhoc-rs/hacspec-psa-baremetal" ]
-rust-psa = [ "edhoc-rs/rust-psa-baremetal" ]
 rtt = [ ]
-hacspec-cryptocell310 = [ "edhoc-rs/hacspec-cryptocell310" ]
-rust-cryptocell310 = [ "edhoc-rs/rust-cryptocell310" ]
+crypto-psa = [ "edhoc-rs/crypto-psa-baremetal" ]
+crypto-cryptocell310 = [ "edhoc-rs/crypto-cryptocell310" ]
 ead-none = [ "edhoc-rs/ead-none" ]
 ead-zeroconf = [ "edhoc-rs/ead-zeroconf" ]
diff --git a/examples/edhoc-rs-no_std/src/main.rs b/examples/edhoc-rs-no_std/src/main.rs
index 23d913fb..c86b6d33 100644
--- a/examples/edhoc-rs-no_std/src/main.rs
+++ b/examples/edhoc-rs-no_std/src/main.rs
@@ -32,8 +32,9 @@ fn main() -> ! {
     rtt_init_print!();
 
     // Initialize the allocator BEFORE you use it
-    // The hacspec version does some allocations in the heap
-    #[cfg(any(feature = "hacspec-psa", feature = "hacspec-cryptocell310",))]
+    // The hacspec version does some heap allocations
+    // TODO: we still don't have a baremetal version with hacspec as crypto backend, so maybe remove `HEAP`.
+    #[cfg(any(feature = "crypto-hacspec"))]
     {
         use core::mem::MaybeUninit;
         const HEAP_SIZE: usize = 1 << 10;
@@ -42,9 +43,9 @@ fn main() -> ! {
     }
 
     // Memory buffer for mbedtls
-    #[cfg(any(feature = "hacspec-psa", feature = "rust-psa",))]
+    #[cfg(feature = "crypto-psa")]
     let mut buffer: [c_char; 4096 * 2] = [0; 4096 * 2];
-    #[cfg(any(feature = "hacspec-psa", feature = "rust-psa",))]
+    #[cfg(feature = "crypto-psa")]
     unsafe {
         mbedtls_memory_buffer_alloc_init(buffer.as_mut_ptr(), buffer.len());
     }
@@ -84,12 +85,7 @@ fn main() -> ! {
         let g_xy = p256_ecdh(&x, &g_y);
         let g_yx = p256_ecdh(&y, &g_x);
 
-        // NOTE: application code is not supposed to distinguish between the rust and hacspec implementations
-        // however, it is valuable to test that the results are correct, so we distinguish it here as an exception.
-        #[cfg(any(feature = "rust-psa", feature = "rust-cryptocell310",))]
         assert_eq!(g_xy, g_yx);
-        #[cfg(any(feature = "hacspec-psa", feature = "hacspec-cryptocell310",))]
-        assert_eq!(g_xy.to_public_array(), g_yx.to_public_array());
     }
     test_p256_keys();
     println!("Test test_p256_keys passed.");
@@ -99,7 +95,8 @@ fn main() -> ! {
         let mut initiator =
             EdhocInitiator::new(state, I, G_R, ID_CRED_I, CRED_I, ID_CRED_R, CRED_R);
 
-        let message_1 = initiator.prepare_message_1();
+        let c_i: u8 = generate_connection_identifier_cbor().into();
+        let message_1 = initiator.prepare_message_1(c_i);
         assert!(message_1.is_ok());
     }
 
@@ -128,16 +125,18 @@ fn main() -> ! {
             CRED_R,
         );
 
-        let ret = initiator.prepare_message_1(); // to update the state
+        let c_i: u8 = generate_connection_identifier_cbor().into();
+        let ret = initiator.prepare_message_1(c_i); // to update the state
         assert!(ret.is_ok());
         let message_1 = ret.unwrap();
 
         let ret = responder.process_message_1(&message_1);
         assert!(ret.is_ok());
 
-        let ret = responder.prepare_message_2();
+        let c_r: u8 = generate_connection_identifier_cbor().into();
+        let ret = responder.prepare_message_2(c_r);
         assert!(ret.is_ok());
-        let (message_2, c_r) = ret.unwrap();
+        let message_2 = ret.unwrap();
         assert!(c_r != 0xff);
 
         let _c_r = initiator.process_message_2(&message_2);
diff --git a/hacspec/Cargo.toml b/hacspec/Cargo.toml
deleted file mode 100644
index 94b80aeb..00000000
--- a/hacspec/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[package]
-name = "edhoc-hacspec"
-version = "0.1.0"
-edition = "2021"
-authors = ["Mališa Vučinić <malisa.vucinic@inria.fr>"]
-license = "BSD"
-description = "EDHOC implementation"
-
-[dependencies]
-hacspec-lib = { version = "0.1.0-beta.1", default-features = false, features = [ "alloc" ] }
-edhoc-crypto = { path = "../crypto", default-features = false }
-edhoc-ead = { path = "../ead", default-features = false }
-edhoc-consts = { path = "../consts" }
-
-[features]
-default = [ "edhoc-ead/edhoc-ead-none" ]
-ead-none = [ "edhoc-ead/edhoc-ead-none" ]
-ead-zeroconf = [ "edhoc-ead/edhoc-ead-zeroconf" ]
diff --git a/hacspec/adjust_features_for_hax.patch b/hacspec/adjust_features_for_hax.patch
deleted file mode 100644
index 3f4c9456..00000000
--- a/hacspec/adjust_features_for_hax.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml
-index 5c625a6..9dc22c3 100644
---- a/crypto/Cargo.toml
-+++ b/crypto/Cargo.toml
-@@ -10,7 +10,7 @@ description = "EDHOC crypto library dispatch crate"
- edhoc-consts = { path = "../consts", default-features = false }
- 
- # hacspec
--edhoc-crypto-hacspec = { path = "./edhoc-crypto-hacspec", optional = true }
-+edhoc-crypto-hacspec = { path = "./edhoc-crypto-hacspec", optional = true, features = ["hacspec-pure"] }
- 
- # cc2538 hardware accelerated
- edhoc-crypto-cc2538 = { path = "./edhoc-crypto-cc2538", optional = true }
-@@ -22,7 +22,7 @@ edhoc-crypto-psa = { path = "./edhoc-crypto-psa", default-features = false, opti
- edhoc-crypto-cryptocell310 = { path = "./edhoc-crypto-cryptocell310-sys", optional = true }
- 
- [features]
--default = [  ]
-+default = [ "hacspec" ]
- hacspec = [ "edhoc-crypto-hacspec" ]
- cc2538 = [ "edhoc-crypto-cc2538" ]
- psa = [ "edhoc-crypto-psa/hacspec" ]
-diff --git a/crypto/edhoc-crypto-hacspec/Cargo.toml b/crypto/edhoc-crypto-hacspec/Cargo.toml
-index e59a361..14f606e 100644
---- a/crypto/edhoc-crypto-hacspec/Cargo.toml
-+++ b/crypto/edhoc-crypto-hacspec/Cargo.toml
-@@ -15,3 +15,6 @@ hacspec-sha256 = { version = "0.1.0" }
- hacspec-aes = { version = "0.1.0" }
- hacspec-aes-ccm = { version = "0.1.0" }
- rand = "0.8.5"
-+
-+[features]
-+hacspec-pure = []
-\ No newline at end of file
-diff --git a/hacspec/Cargo.toml b/hacspec/Cargo.toml
-index 94b80ae..51dbeb2 100644
---- a/hacspec/Cargo.toml
-+++ b/hacspec/Cargo.toml
-@@ -8,8 +8,8 @@ description = "EDHOC implementation"
- 
- [dependencies]
- hacspec-lib = { version = "0.1.0-beta.1", default-features = false, features = [ "alloc" ] }
--edhoc-crypto = { path = "../crypto", default-features = false }
--edhoc-ead = { path = "../ead", default-features = false }
-+edhoc-crypto = { path = "../crypto" }
-+edhoc-ead = { path = "../ead" }
- edhoc-consts = { path = "../consts" }
- 
- [features]
diff --git a/hacspec/src/lib.rs b/hacspec/src/lib.rs
deleted file mode 100644
index 7e8f49e3..00000000
--- a/hacspec/src/lib.rs
+++ /dev/null
@@ -1,2293 +0,0 @@
-#![no_std]
-
-use edhoc_consts::*;
-use edhoc_crypto::*;
-use edhoc_ead::*;
-use hacspec_lib::*;
-
-pub fn edhoc_exporter(
-    state: State,
-    label: U8,
-    context: &BytesMaxContextBuffer,
-    context_len: usize,
-    length: usize,
-) -> Result<(State, BytesMaxBuffer), EDHOCError> {
-    let State(
-        current_state,
-        _x_or_y,
-        _c_i,
-        _gy_or_gx,
-        _prk_3e2m,
-        _prk_4e3m,
-        _prk_out,
-        prk_exporter,
-        _h_message_1,
-        _th_3,
-    ) = state;
-
-    let mut output = BytesMaxBuffer::new();
-    let mut error = EDHOCError::UnknownError;
-
-    if current_state == EDHOCState::Completed {
-        output = edhoc_kdf(&prk_exporter, label, context, context_len, length);
-        Ok((state, output))
-    } else {
-        Err(EDHOCError::WrongState)
-    }
-}
-
-pub fn edhoc_key_update(
-    mut state: State,
-    context: &BytesMaxContextBuffer,
-    context_len: usize,
-) -> Result<(State, BytesHashLen), EDHOCError> {
-    let State(
-        current_state,
-        _x_or_y,
-        _c_i,
-        _gy_or_gx,
-        _prk_3e2m,
-        _prk_4e3m,
-        mut prk_out,
-        mut prk_exporter,
-        _h_message_1,
-        _th_3,
-    ) = state;
-
-    let mut prk_new_buf = BytesMaxBuffer::new();
-    let mut error = EDHOCError::UnknownError;
-
-    if current_state == EDHOCState::Completed {
-        // new PRK_out
-        prk_new_buf = edhoc_kdf(
-            &prk_out,
-            U8(11 as u8),
-            context,
-            context_len,
-            SHA256_DIGEST_LEN,
-        );
-        prk_out = prk_out.update_slice(0, &prk_new_buf, 0, SHA256_DIGEST_LEN);
-
-        // new PRK_exporter
-        prk_new_buf = edhoc_kdf(
-            &prk_out,
-            U8(10 as u8),
-            &BytesMaxContextBuffer::new(),
-            0,
-            SHA256_DIGEST_LEN,
-        );
-        prk_exporter = prk_exporter.update_slice(0, &prk_new_buf, 0, SHA256_DIGEST_LEN);
-
-        state = construct_state(
-            current_state,
-            _x_or_y,
-            _c_i,
-            _gy_or_gx,
-            _prk_3e2m,
-            _prk_4e3m,
-            prk_out,
-            prk_exporter,
-            _h_message_1,
-            _th_3,
-        );
-
-        Ok((state, prk_out))
-    } else {
-        Err(EDHOCError::WrongState)
-    }
-}
-
-/// process message_1: parse, check method, check cipher suite
-pub fn r_process_message_1(
-    mut state: State,
-    message_1: &BufferMessage1,
-) -> Result<State, EDHOCError> {
-    let State(
-        mut current_state,
-        _y,
-        mut c_i,
-        g_x,
-        _prk_3e2m,
-        _prk_4e3m,
-        _prk_out,
-        _prk_exporter,
-        mut h_message_1,
-        _th_3,
-    ) = state;
-
-    let mut error = EDHOCError::UnknownError;
-
-    if current_state == EDHOCState::Start {
-        // Step 1: decode message_1
-        // g_x will be saved to the state
-        let res = parse_message_1(message_1);
-
-        if res.is_ok() {
-            let (method, suites_i, suites_i_len, g_x, c_i, ead_1) = res.unwrap();
-            // verify that the method is supported
-            if method.declassify() == EDHOC_METHOD {
-                // Step 2: verify that the selected cipher suite is supported
-                if suites_i[suites_i_len - 1].declassify()
-                    == EDHOC_SUPPORTED_SUITES[0u8].declassify()
-                {
-                    // Step 3: If EAD is present make it available to the application
-                    let ead_success = if let Some(ead_1) = ead_1 {
-                        r_process_ead_1(ead_1.to_public_item()).is_ok()
-                    } else {
-                        true
-                    };
-                    if ead_success {
-                        // hash message_1 and save the hash to the state to avoid saving the whole message
-                        h_message_1 = sha256_digest(
-                            &BytesMaxBuffer::from_slice(&message_1.content, 0, message_1.len),
-                            message_1.len,
-                        );
-
-                        error = EDHOCError::Success;
-                        current_state = EDHOCState::ProcessedMessage1;
-
-                        state = construct_state(
-                            current_state,
-                            _y,
-                            c_i,
-                            g_x,
-                            _prk_3e2m,
-                            _prk_4e3m,
-                            _prk_out,
-                            _prk_exporter,
-                            h_message_1,
-                            _th_3,
-                        );
-                    } else {
-                        error = EDHOCError::EADError;
-                    }
-                } else {
-                    error = EDHOCError::UnsupportedCipherSuite;
-                }
-            } else {
-                error = EDHOCError::UnsupportedMethod;
-            }
-        } else {
-            error = res.unwrap_err();
-        }
-    } else {
-        error = EDHOCError::WrongState;
-    }
-
-    match error {
-        EDHOCError::Success => Ok(state),
-        _ => Err(error),
-    }
-}
-
-/// Constructs message_2, which has the following format:
-///   message_2 = (
-///     G_Y_CIPHERTEXT_2 : bstr,
-///   )
-/// Note that the plaintext is:
-///   PLAINTEXT_2 = ( C_R, ID_CRED_R / bstr / -24..23, Signature_or_MAC_2, ? EAD_2 )
-/// returns: (state, message_2, c_r)
-pub fn r_prepare_message_2(
-    mut state: State,
-    id_cred_r: &BytesIdCred,
-    cred_r: &BytesMaxBuffer,
-    cred_r_len: usize,
-    r: &BytesP256ElemLen,  // R's static private DH key
-    y: BytesP256ElemLen,   // R's ephemeral private DH key
-    g_y: BytesP256ElemLen, // R's ephemeral public DH key
-    c_r: U8,
-) -> Result<(State, BufferMessage2, U8), EDHOCError> {
-    let State(
-        mut current_state,
-        mut _y,
-        _c_i,
-        g_x,
-        mut prk_3e2m,
-        _prk_4e3m,
-        _prk_out,
-        _prk_exporter,
-        h_message_1,
-        mut th_3,
-    ) = state;
-
-    let mut error = EDHOCError::UnknownError;
-    let mut message_2 = BufferMessage2::new();
-
-    if current_state == EDHOCState::ProcessedMessage1 {
-        // compute TH_2
-        let th_2 = compute_th_2(&g_y, &h_message_1);
-
-        // compute prk_3e2m
-        let prk_2e = compute_prk_2e(&y, &g_x, &th_2);
-        let salt_3e2m = compute_salt_3e2m(&prk_2e, &th_2);
-        prk_3e2m = compute_prk_3e2m(&salt_3e2m, r, &g_x);
-
-        // compute MAC_2
-        let mac_2 = compute_mac_2(&prk_3e2m, id_cred_r, cred_r, cred_r_len, &th_2);
-
-        let ead_2 = match r_prepare_ead_2() {
-            Some(ead_item) => Some(EADItemHacspec::from_public_item(&ead_item)),
-            None => None,
-        };
-
-        // compute ciphertext_2
-        let plaintext_2 = encode_plaintext_2(c_r, id_cred_r, &mac_2, &ead_2);
-
-        // step is actually from processing of message_3
-        // but we do it here to avoid storing plaintext_2 in State
-        th_3 = compute_th_3(&th_2, &plaintext_2, cred_r, cred_r_len);
-
-        let (ciphertext_2, ciphertext_2_len) =
-            encrypt_decrypt_ciphertext_2(&prk_2e, &th_2, &plaintext_2);
-
-        message_2 = encode_message_2(
-            &g_y,
-            &BufferCiphertext2::from_slice(&ciphertext_2, 0, ciphertext_2_len),
-        );
-
-        error = EDHOCError::Success;
-        current_state = EDHOCState::WaitMessage3;
-
-        state = construct_state(
-            current_state,
-            y,
-            _c_i,
-            g_x,
-            prk_3e2m,
-            _prk_4e3m,
-            _prk_out,
-            _prk_exporter,
-            h_message_1,
-            th_3,
-        );
-    } else {
-        error = EDHOCError::WrongState;
-    }
-
-    match error {
-        EDHOCError::Success => Ok((state, message_2, c_r)),
-        _ => Err(error),
-    }
-}
-
-/// process message_3: decrypt, parse, check kid, check mac
-/// returns (state, prk_out)
-// FIXME fetch ID_CRED_I and CRED_I based on kid
-pub fn r_process_message_3(
-    mut state: State,
-    message_3: &BufferMessage3,
-    id_cred_i_expected: &BytesIdCred,
-    cred_i_expected: &BytesMaxBuffer,
-    cred_i_len: usize,
-    g_i: &BytesP256ElemLen, // I's public DH key
-) -> Result<(State, BytesHashLen), EDHOCError> {
-    let State(
-        mut current_state,
-        y,
-        _c_i,
-        _g_x,
-        prk_3e2m,
-        mut prk_4e3m,
-        mut prk_out,
-        mut prk_exporter,
-        _h_message_1,
-        th_3,
-    ) = state;
-
-    let mut error = EDHOCError::UnknownError;
-
-    if current_state == EDHOCState::WaitMessage3 {
-        let plaintext_3 = decrypt_message_3(&prk_3e2m, &th_3, message_3);
-
-        if plaintext_3.is_ok() {
-            let plaintext_3 = plaintext_3.unwrap();
-            let decoded_p3_res = decode_plaintext_3(&plaintext_3);
-
-            if decoded_p3_res.is_ok() {
-                let (kid, mac_3, ead_3) = decoded_p3_res.unwrap();
-
-                // Step 3: If EAD is present make it available to the application
-                let ead_success = if let Some(ead_3) = ead_3 {
-                    r_process_ead_3(ead_3.to_public_item()).is_ok()
-                } else {
-                    true
-                };
-                if ead_success {
-                    // compare the kid received with the kid expected in id_cred_i
-                    if kid.declassify()
-                        == id_cred_i_expected[id_cred_i_expected.len() - 1].declassify()
-                    {
-                        // compute salt_4e3m
-                        let salt_4e3m = compute_salt_4e3m(&prk_3e2m, &th_3);
-                        // compute prk_4e3m
-                        prk_4e3m = compute_prk_4e3m(&salt_4e3m, &y, g_i);
-
-                        // compute mac_3
-                        let expected_mac_3 = compute_mac_3(
-                            &prk_4e3m,
-                            &th_3,
-                            id_cred_i_expected,
-                            cred_i_expected,
-                            cred_i_len,
-                        );
-
-                        // verify mac_3
-                        if mac_3.declassify_eq(&expected_mac_3) {
-                            error = EDHOCError::Success;
-                            let th_4 =
-                                compute_th_4(&th_3, &plaintext_3, cred_i_expected, cred_i_len);
-
-                            // compute prk_out
-                            // PRK_out = EDHOC-KDF( PRK_4e3m, 7, TH_4, hash_length )
-                            let prk_out_buf = edhoc_kdf(
-                                &prk_4e3m,
-                                U8(7 as u8),
-                                &BytesMaxContextBuffer::from_slice(&th_4, 0, th_4.len()),
-                                th_4.len(),
-                                SHA256_DIGEST_LEN,
-                            );
-                            prk_out = prk_out.update_slice(0, &prk_out_buf, 0, SHA256_DIGEST_LEN);
-
-                            // compute prk_exporter from prk_out
-                            // PRK_exporter  = EDHOC-KDF( PRK_out, 10, h'', hash_length )
-                            let prk_exporter_buf = edhoc_kdf(
-                                &prk_out,
-                                U8(10 as u8),
-                                &BytesMaxContextBuffer::new(),
-                                0,
-                                SHA256_DIGEST_LEN,
-                            );
-                            prk_exporter = prk_exporter.update_slice(
-                                0,
-                                &prk_exporter_buf,
-                                0,
-                                SHA256_DIGEST_LEN,
-                            );
-
-                            error = EDHOCError::Success;
-                            current_state = EDHOCState::Completed;
-
-                            state = construct_state(
-                                current_state,
-                                y,
-                                _c_i,
-                                _g_x,
-                                prk_3e2m,
-                                prk_4e3m,
-                                prk_out,
-                                prk_exporter,
-                                _h_message_1,
-                                th_3,
-                            );
-                        } else {
-                            error = EDHOCError::MacVerificationFailed;
-                        }
-                    } else {
-                        error = EDHOCError::UnknownPeer;
-                    }
-                } else {
-                    error = EDHOCError::EADError;
-                }
-            } else {
-                error = decoded_p3_res.unwrap_err();
-            }
-        } else {
-            // error handling for err = decrypt_message_3(&prk_3e2m, &th_3, message_3);
-            error = plaintext_3.unwrap_err();
-        }
-    } else {
-        error = EDHOCError::WrongState;
-    }
-
-    match error {
-        EDHOCError::Success => Ok((state, prk_out)),
-        _ => Err(error),
-    }
-}
-
-/// Constructs message_2, which has the following format:
-///   message_1 = (
-///     METHOD : int,
-///     SUITES_I : suites,
-///     G_X : bstr,
-///     C_I : bstr / -24..23,
-///     ? EAD_1,
-///   )
-///
-///   suites = [ 2* int ] / int
-///   EAD_1 = 1* ead
-/// returns: (state, message_1)
-pub fn i_prepare_message_1(
-    mut state: State,
-    x: BytesP256ElemLen,
-    g_x: BytesP256ElemLen,
-    c_i: U8,
-) -> Result<(State, BufferMessage1), EDHOCError> {
-    let State(
-        mut current_state,
-        mut _x,
-        mut _c_i,
-        _g_y,
-        _prk_3e2m,
-        _prk_4e3m,
-        _prk_out,
-        _prk_exporter,
-        mut h_message_1,
-        _th_3,
-    ) = state;
-
-    let mut error = EDHOCError::UnknownError;
-
-    let mut message_1 = BufferMessage1::new();
-
-    if current_state == EDHOCState::Start {
-        // we only support a single cipher suite which is already CBOR-encoded
-        let suites_i =
-            BytesSuites::from_slice(&EDHOC_SUPPORTED_SUITES, 0, EDHOC_SUPPORTED_SUITES.len());
-
-        let ead_1 = match i_prepare_ead_1() {
-            Some(ead_item) => Some(EADItemHacspec::from_public_item(&ead_item)),
-            None => None,
-        };
-
-        // Encode message_1 as a sequence of CBOR encoded data items as specified in Section 5.2.1
-        message_1 = encode_message_1(
-            U8(EDHOC_METHOD),
-            &suites_i,
-            EDHOC_SUPPORTED_SUITES.len(),
-            &g_x,
-            c_i,
-            &ead_1,
-        );
-
-        // hash message_1 here to avoid saving the whole message in the state
-        h_message_1 = sha256_digest(
-            &BytesMaxBuffer::from_slice(&message_1.content, 0, message_1.len),
-            message_1.len,
-        );
-        error = EDHOCError::Success;
-        current_state = EDHOCState::WaitMessage2;
-
-        state = construct_state(
-            current_state,
-            x,
-            c_i,
-            _g_y,
-            _prk_3e2m,
-            _prk_4e3m,
-            _prk_out,
-            _prk_exporter,
-            h_message_1,
-            _th_3,
-        );
-    } else {
-        error = EDHOCError::WrongState;
-    }
-
-    match error {
-        EDHOCError::Success => Ok((state, message_1)),
-        _ => Err(error),
-    }
-}
-
-/// process message_2: parse, decrypt, check mac, check kid
-/// returns c_r
-pub fn i_process_message_2(
-    mut state: State,
-    message_2: &BufferMessage2,
-    id_cred_r_expected: &BytesIdCred,
-    cred_r_expected: &BytesMaxBuffer,
-    cred_r_len: usize,
-    g_r: &BytesP256ElemLen, // R's static public DH key
-    i: &BytesP256ElemLen,   // I's static private DH key
-) -> Result<(State, U8, U8), EDHOCError> {
-    let State(
-        mut current_state,
-        x,
-        _c_i,
-        g_y,
-        mut prk_3e2m,
-        mut prk_4e3m,
-        _prk_out,
-        _prk_exporter,
-        h_message_1,
-        mut th_3,
-    ) = state;
-
-    // init error
-    let mut error = EDHOCError::UnknownError;
-    let mut c_r = U8(0xffu8); // invalidate c_r
-    let mut kid = U8(0xffu8); // invalidate kid
-
-    if current_state == EDHOCState::WaitMessage2 {
-        let res = parse_message_2(message_2);
-        if res.is_ok() {
-            let (g_y, ciphertext_2) = res.unwrap();
-
-            let th_2 = compute_th_2(&g_y, &h_message_1);
-
-            // compute prk_2e
-            let prk_2e = compute_prk_2e(&x, &g_y, &th_2);
-
-            let (plaintext_2, plaintext_2_len) =
-                encrypt_decrypt_ciphertext_2(&prk_2e, &th_2, &ciphertext_2);
-
-            // decode plaintext_2
-            let plaintext_2_decoded = decode_plaintext_2(&plaintext_2, plaintext_2_len);
-
-            if plaintext_2_decoded.is_ok() {
-                let (c_r_2, kid, mac_2, ead_2) = plaintext_2_decoded.unwrap();
-                c_r = c_r_2;
-
-                // Step 3: If EAD is present make it available to the application
-                let ead_success = if let Some(ead_2) = ead_2 {
-                    i_process_ead_2(ead_2.to_public_item()).is_ok()
-                } else {
-                    true
-                };
-                if ead_success {
-                    // verify mac_2
-                    let salt_3e2m = compute_salt_3e2m(&prk_2e, &th_2);
-
-                    prk_3e2m = compute_prk_3e2m(&salt_3e2m, &x, g_r);
-
-                    let expected_mac_2 = compute_mac_2(
-                        &prk_3e2m,
-                        id_cred_r_expected,
-                        cred_r_expected,
-                        cred_r_len,
-                        &th_2,
-                    );
-
-                    // Check MAC before checking KID
-                    if mac_2.declassify_eq(&expected_mac_2) {
-                        if kid.declassify()
-                            == id_cred_r_expected[id_cred_r_expected.len() - 1].declassify()
-                        {
-                            // step is actually from processing of message_3
-                            // but we do it here to avoid storing plaintext_2 in State
-                            th_3 = compute_th_3(
-                                &th_2,
-                                &BufferPlaintext2::from_slice(&plaintext_2, 0, plaintext_2_len),
-                                cred_r_expected,
-                                cred_r_len,
-                            );
-                            // message 3 processing
-
-                            let salt_4e3m = compute_salt_4e3m(&prk_3e2m, &th_3);
-
-                            prk_4e3m = compute_prk_4e3m(&salt_4e3m, i, &g_y);
-
-                            error = EDHOCError::Success;
-                            current_state = EDHOCState::ProcessedMessage2;
-
-                            state = construct_state(
-                                current_state,
-                                x,
-                                _c_i,
-                                g_y,
-                                prk_3e2m,
-                                prk_4e3m,
-                                _prk_out,
-                                _prk_exporter,
-                                h_message_1,
-                                th_3,
-                            );
-                        } else {
-                            // Unknown peer
-                            error = EDHOCError::UnknownPeer;
-                        }
-                    } else {
-                        error = EDHOCError::MacVerificationFailed;
-                    }
-                } else {
-                    error = EDHOCError::EADError;
-                }
-            } else {
-                error = EDHOCError::ParsingError;
-            }
-        } else {
-            error = EDHOCError::ParsingError;
-        }
-    } else {
-        error = EDHOCError::WrongState;
-    }
-
-    match error {
-        EDHOCError::Success => Ok((state, c_r, kid)),
-        _ => Err(error),
-    }
-}
-
-/// Build message_3 as follows:
-///   message_3 = (
-///    CIPHERTEXT_3 : bstr,
-///   )
-/// Note that the plaintext is:
-///   PLAINTEXT_3 = ( ID_CRED_I / bstr / -24..23, Signature_or_MAC_3, ? EAD_3 )
-/// returns: (state, message_3, prk_out)
-pub fn i_prepare_message_3(
-    mut state: State,
-    id_cred_i: &BytesIdCred,
-    cred_i: &BytesMaxBuffer,
-    cred_i_len: usize,
-) -> Result<(State, BufferMessage3, BytesHashLen), EDHOCError> {
-    let State(
-        mut current_state,
-        _x,
-        _c_i,
-        _g_y,
-        prk_3e2m,
-        prk_4e3m,
-        mut prk_out,
-        mut prk_exporter,
-        _h_message_1,
-        th_3,
-    ) = state;
-
-    let mut error = EDHOCError::UnknownError;
-    let mut message_3 = BufferMessage3::new();
-
-    if current_state == EDHOCState::ProcessedMessage2 {
-        let mac_3 = compute_mac_3(&prk_4e3m, &th_3, id_cred_i, cred_i, cred_i_len);
-
-        let ead_3 = match i_prepare_ead_3() {
-            Some(ead_item) => Some(EADItemHacspec::from_public_item(&ead_item)),
-            None => None,
-        };
-
-        let plaintext_3 = encode_plaintext_3(id_cred_i, &mac_3, &ead_3);
-        message_3 = encrypt_message_3(&prk_3e2m, &th_3, &plaintext_3);
-
-        let th_4 = compute_th_4(&th_3, &plaintext_3, cred_i, cred_i_len);
-
-        // compute prk_out
-        // PRK_out = EDHOC-KDF( PRK_4e3m, 7, TH_4, hash_length )
-        let prk_out_buf = edhoc_kdf(
-            &prk_4e3m,
-            U8(7 as u8),
-            &BytesMaxContextBuffer::from_slice(&th_4, 0, th_4.len()),
-            th_4.len(),
-            SHA256_DIGEST_LEN,
-        );
-        prk_out = prk_out.update_slice(0, &prk_out_buf, 0, SHA256_DIGEST_LEN);
-
-        // compute prk_exporter from prk_out
-        // PRK_exporter  = EDHOC-KDF( PRK_out, 10, h'', hash_length )
-        let prk_exporter_buf = edhoc_kdf(
-            &prk_out,
-            U8(10 as u8),
-            &BytesMaxContextBuffer::new(),
-            0,
-            SHA256_DIGEST_LEN,
-        );
-        prk_exporter = prk_exporter.update_slice(0, &prk_exporter_buf, 0, SHA256_DIGEST_LEN);
-        error = EDHOCError::Success;
-        current_state = EDHOCState::Completed;
-
-        state = construct_state(
-            current_state,
-            _x,
-            _c_i,
-            _g_y,
-            prk_3e2m,
-            prk_4e3m,
-            prk_out,
-            prk_exporter,
-            _h_message_1,
-            th_3,
-        );
-    } else {
-        error = EDHOCError::WrongState;
-    }
-
-    match error {
-        EDHOCError::Success => Ok((state, message_3, prk_out)),
-        _ => Err(error),
-    }
-}
-
-pub fn construct_state(
-    state: EDHOCState,
-    x_or_y: BytesP256ElemLen,
-    c_i: U8,
-    gx_or_gy: BytesP256ElemLen,
-    prk_3e2m: BytesHashLen,
-    prk_4e3m: BytesHashLen,
-    prk_out: BytesHashLen,
-    prk_exporter: BytesHashLen,
-    h_message_1: BytesHashLen,
-    th_3: BytesHashLen,
-) -> State {
-    State(
-        state,
-        x_or_y,
-        c_i,
-        gx_or_gy,
-        prk_3e2m,
-        prk_4e3m,
-        prk_out,
-        prk_exporter,
-        h_message_1,
-        th_3,
-    )
-}
-
-/// Check for: an unsigned integer encoded as a single byte
-#[inline(always)]
-fn is_cbor_uint_1byte(byte: U8) -> bool {
-    let byte = byte.declassify();
-    return byte >= CBOR_UINT_1BYTE_START && byte <= CBOR_UINT_1BYTE_END;
-}
-
-/// Check for: an unsigned integer encoded as two bytes
-#[inline(always)]
-fn is_cbor_uint_2bytes(byte: U8) -> bool {
-    return byte.declassify() == CBOR_UINT_1BYTE;
-}
-
-/// Check for: a negative integer encoded as a single byte
-#[inline(always)]
-fn is_cbor_neg_int_1byte(byte: U8) -> bool {
-    let byte = byte.declassify();
-    return byte >= CBOR_NEG_INT_1BYTE_START && byte <= CBOR_NEG_INT_1BYTE_END;
-}
-
-/// Check for: a bstr denoted by a single byte which encodes both type and content length
-#[inline(always)]
-fn is_cbor_bstr_1byte_prefix(byte: U8) -> bool {
-    let byte = byte.declassify();
-    return byte >= CBOR_MAJOR_BYTE_STRING && byte <= CBOR_MAJOR_BYTE_STRING_MAX;
-}
-
-/// Check for: a bstr denoted by two bytes, onr for type the other for content length
-#[inline(always)]
-fn is_cbor_bstr_2bytes_prefix(byte: U8) -> bool {
-    return byte.declassify() == CBOR_BYTE_STRING;
-}
-
-/// Check for: an array denoted by a single byte which encodes both type and content length
-#[inline(always)]
-fn is_cbor_array_1byte_prefix(byte: U8) -> bool {
-    let byte = byte.declassify();
-    return byte >= CBOR_MAJOR_ARRAY && byte <= CBOR_MAJOR_ARRAY_MAX;
-}
-
-fn parse_suites_i(
-    rcvd_message_1: &BufferMessage1,
-) -> Result<(BytesSuites, usize, usize), EDHOCError> {
-    let mut error: EDHOCError = EDHOCError::UnknownError;
-    let mut raw_suites_len = 0;
-    let mut suites_i = BytesSuites::new();
-    let mut suites_i_len: usize = 0;
-
-    // match based on first byte of SUITES_I, which can be either an int or an array
-    if is_cbor_uint_1byte(rcvd_message_1.content[1]) {
-        // CBOR unsigned integer (0..=23)
-        suites_i[0] = rcvd_message_1.content[1];
-        suites_i_len = 1;
-        raw_suites_len = 1;
-        error = EDHOCError::Success;
-    } else if is_cbor_uint_2bytes(rcvd_message_1.content[1]) {
-        // CBOR unsigned integer (one-byte uint8_t follows)
-        suites_i[0] = rcvd_message_1.content[2];
-        suites_i_len = 1;
-        raw_suites_len = 2;
-        error = EDHOCError::Success;
-    } else if is_cbor_array_1byte_prefix(rcvd_message_1.content[1]) {
-        // CBOR array (0..=23 data items follow)
-        // the CBOR array length is encoded in the first byte, so we extract it
-        let suites_len: U8 = rcvd_message_1.content[1] - U8(CBOR_MAJOR_ARRAY);
-        let suites_len: usize = suites_len.declassify().into();
-        raw_suites_len = 1; // account for the CBOR_MAJOR_ARRAY byte
-        if suites_len > 1 && suites_len <= EDHOC_SUITES.len() {
-            // cipher suite array must be at least 2 elements long, but not longer than the defined cipher suites
-            let mut error_occurred = false;
-            for j in 0..suites_len {
-                raw_suites_len += 1;
-                if !error_occurred {
-                    // parse based on cipher suite identifier
-                    if is_cbor_uint_1byte(rcvd_message_1.content[raw_suites_len]) {
-                        // CBOR unsigned integer (0..23)
-                        suites_i[j] = rcvd_message_1.content[raw_suites_len];
-                        suites_i_len += 1;
-                    } else if is_cbor_uint_2bytes(rcvd_message_1.content[raw_suites_len]) {
-                        // CBOR unsigned integer (one-byte uint8_t follows)
-                        raw_suites_len += 1; // account for the 0x18 tag byte
-                        suites_i[j] = rcvd_message_1.content[raw_suites_len];
-                        suites_i_len += 1;
-                    } else {
-                        error = EDHOCError::ParsingError;
-                        error_occurred = true;
-                    }
-                }
-            }
-            if !error_occurred {
-                error = EDHOCError::Success;
-            }
-        } else {
-            error = EDHOCError::ParsingError;
-        }
-    } else {
-        error = EDHOCError::ParsingError;
-    }
-
-    match error {
-        EDHOCError::Success => Ok((suites_i, suites_i_len, raw_suites_len)),
-        _ => Err(error),
-    }
-}
-
-fn parse_ead(
-    message: &EdhocMessageBufferHacspec,
-    offset: usize,
-) -> Result<Option<EADItemHacspec>, EDHOCError> {
-    let mut error: EDHOCError = EDHOCError::UnknownError;
-    let mut ead_item = None::<EADItemHacspec>;
-    let mut ead_value = None::<EdhocMessageBufferHacspec>;
-
-    // assume label is a single byte integer (negative or positive)
-    let label = message.content[offset];
-    let res_label = if is_cbor_uint_1byte(label) {
-        // CBOR unsigned integer (0..=23)
-        Ok((label.declassify() as u8, false))
-    } else if is_cbor_neg_int_1byte(label) {
-        // CBOR negative integer (-1..=-24)
-        Ok((label.declassify() - (CBOR_NEG_INT_1BYTE_START - 1), true))
-    } else {
-        Err(EDHOCError::ParsingError)
-    };
-
-    if res_label.is_ok() {
-        let (label, is_critical) = res_label.unwrap();
-        if message.len > (offset + 1) {
-            // EAD value is present
-            let buffer = EdhocMessageBufferHacspec::from_slice(
-                &message.content,
-                offset + 1,
-                message.len - (offset + 1),
-            );
-            ead_value = Some(buffer);
-        }
-        ead_item = Some(EADItemHacspec {
-            label: U8(label),
-            is_critical,
-            value: ead_value,
-        });
-        error = EDHOCError::Success;
-    } else {
-        error = res_label.unwrap_err();
-    }
-
-    match error {
-        EDHOCError::Success => Ok(ead_item),
-        _ => Err(error),
-    }
-}
-
-fn parse_message_1(
-    rcvd_message_1: &BufferMessage1,
-) -> Result<
-    (
-        U8,
-        BytesSuites,
-        usize,
-        BytesP256ElemLen,
-        U8,
-        Option<EADItemHacspec>,
-    ),
-    EDHOCError,
-> {
-    let mut error: EDHOCError = EDHOCError::UnknownError;
-    let mut method: U8 = U8(0xff);
-    let mut g_x: BytesP256ElemLen = BytesP256ElemLen::new();
-    let mut suites_i = BytesSuites::new();
-    let mut suites_i_len: usize = 0;
-    let mut raw_suites_len: usize = 0;
-    let mut c_i = U8(0);
-    let mut ead_1 = None::<EADItemHacspec>;
-
-    // first element of CBOR sequence must be an integer
-    if is_cbor_uint_1byte(rcvd_message_1.content[0]) {
-        method = rcvd_message_1.content[0];
-
-        let res_suites = parse_suites_i(rcvd_message_1);
-
-        if res_suites.is_ok() {
-            (suites_i, suites_i_len, raw_suites_len) = res_suites.unwrap();
-
-            if is_cbor_bstr_2bytes_prefix(rcvd_message_1.content[1 + raw_suites_len]) {
-                g_x = BytesP256ElemLen::from_slice(
-                    &rcvd_message_1.content,
-                    3 + raw_suites_len,
-                    P256_ELEM_LEN,
-                );
-
-                c_i = rcvd_message_1.content[3 + raw_suites_len + P256_ELEM_LEN];
-                // check that c_i is encoded as single-byte int (we still do not support bstr encoding)
-                if is_cbor_neg_int_1byte(c_i) || is_cbor_uint_1byte(c_i) {
-                    // if there is still more to parse, the rest will be the EAD_1
-                    if rcvd_message_1.len > (4 + raw_suites_len + P256_ELEM_LEN) {
-                        // NOTE: since the current implementation only supports one EAD handler,
-                        // we assume only one EAD item
-                        let ead_res = parse_ead(rcvd_message_1, 4 + raw_suites_len + P256_ELEM_LEN);
-                        if ead_res.is_ok() {
-                            ead_1 = ead_res.unwrap();
-                            error = EDHOCError::Success;
-                        } else {
-                            error = ead_res.unwrap_err();
-                        }
-                    } else if rcvd_message_1.len == (4 + raw_suites_len + P256_ELEM_LEN) {
-                        error = EDHOCError::Success;
-                    } else {
-                        error = EDHOCError::ParsingError;
-                    }
-                } else {
-                    error = EDHOCError::ParsingError;
-                }
-            } else {
-                error = EDHOCError::ParsingError;
-            }
-        } else {
-            error = res_suites.unwrap_err();
-        }
-    } else {
-        error = EDHOCError::ParsingError;
-    }
-
-    match error {
-        EDHOCError::Success => Ok((method, suites_i, suites_i_len, g_x, c_i, ead_1)),
-        _ => Err(error),
-    }
-}
-
-fn encode_ead_item(ead_1: &EADItemHacspec) -> EdhocMessageBufferHacspec {
-    let mut output = EdhocMessageBufferHacspec::new();
-
-    // encode label
-    if ead_1.is_critical {
-        output.content[0] = ead_1.label + U8(CBOR_NEG_INT_1BYTE_START - 1);
-    } else {
-        output.content[0] = ead_1.label;
-    }
-    output.len = 1;
-
-    // encode value
-    if let Some(ead_1_value) = &ead_1.value {
-        output.content = output
-            .content
-            .update_slice(1, &ead_1_value.content, 0, ead_1_value.len);
-        output.len += ead_1_value.len;
-    }
-
-    output
-}
-
-fn encode_message_1(
-    method: U8,
-    suites: &BytesSuites,
-    suites_len: usize,
-    g_x: &BytesP256ElemLen,
-    c_i: U8,
-    ead_1: &Option<EADItemHacspec>,
-) -> BufferMessage1 {
-    let mut output = BufferMessage1::new();
-    let mut raw_suites_len: usize = 0;
-
-    output.content[0] = method; // CBOR unsigned int less than 24 is encoded verbatim
-
-    if suites_len == 1 {
-        // only one suite, will be encoded as a single integer
-        if (suites[0] as U8).declassify() <= CBOR_UINT_1BYTE {
-            output.content[1] = suites[0];
-            raw_suites_len = 1;
-        } else {
-            output.content[1] = U8(CBOR_UINT_1BYTE);
-            output.content[2] = suites[0]; // assume it is smaller than 255, which all suites are
-            raw_suites_len = 2;
-        }
-    } else {
-        // several suites, will be encoded as an array
-        output.content[1] = U8(CBOR_MAJOR_ARRAY + suites_len as u8);
-        raw_suites_len += 1;
-        for i in 0..suites_len {
-            if (suites[i] as U8).declassify() <= CBOR_UINT_1BYTE {
-                output.content[1 + raw_suites_len] = suites[i];
-                raw_suites_len += 1;
-            } else {
-                output.content[1 + raw_suites_len] = U8(CBOR_UINT_1BYTE);
-                output.content[2 + raw_suites_len] = suites[i];
-                raw_suites_len += 2;
-            }
-        }
-    };
-
-    output.content[1 + raw_suites_len] = U8(CBOR_BYTE_STRING); // CBOR byte string magic number
-    output.content[2 + raw_suites_len] = U8(P256_ELEM_LEN as u8); // length of the byte string
-    output.content = output.content.update(3 + raw_suites_len, g_x);
-    output.content[3 + raw_suites_len + P256_ELEM_LEN] = c_i;
-    output.len = 3 + raw_suites_len + P256_ELEM_LEN + 1;
-
-    if let Some(ead_1) = ead_1 {
-        let ead_1 = encode_ead_item(ead_1);
-        output.content = output
-            .content
-            .update_slice(output.len, &ead_1.content, 0, ead_1.len);
-        output.len += ead_1.len;
-    }
-
-    output
-}
-
-fn parse_message_2(
-    rcvd_message_2: &BufferMessage2,
-) -> Result<(BytesP256ElemLen, BufferCiphertext2), EDHOCError> {
-    let mut error: EDHOCError = EDHOCError::UnknownError;
-    // FIXME decode negative integers as well
-    let mut g_y: BytesP256ElemLen = BytesP256ElemLen::new();
-    let mut ciphertext_2: BufferCiphertext2 = BufferCiphertext2::new();
-
-    // ensure the whole message is a single CBOR sequence
-    if is_cbor_bstr_2bytes_prefix(rcvd_message_2.content[0])
-        && (rcvd_message_2.content[1] as U8).declassify() == (rcvd_message_2.len as u8 - 2)
-    {
-        g_y = BytesP256ElemLen::from_slice(&rcvd_message_2.content, 2, P256_ELEM_LEN);
-        let ciphertext_2_len = rcvd_message_2.len - P256_ELEM_LEN - 2; // len - gy_len - 2
-        ciphertext_2 = BufferCiphertext2::from_slice(
-            &rcvd_message_2.content,
-            2 + P256_ELEM_LEN,
-            ciphertext_2_len,
-        );
-        error = EDHOCError::Success;
-    } else {
-        error = EDHOCError::ParsingError;
-    }
-
-    match error {
-        EDHOCError::Success => Ok((g_y, ciphertext_2)),
-        _ => Err(error),
-    }
-}
-
-fn encode_message_2(g_y: &BytesP256ElemLen, ciphertext_2: &BufferCiphertext2) -> BufferMessage2 {
-    let mut output = BufferMessage2::new();
-
-    output.content[0] = U8(CBOR_BYTE_STRING);
-    output.content[1] = U8(P256_ELEM_LEN as u8 + ciphertext_2.len as u8);
-    output.content = output.content.update(2, g_y);
-    output.content = output.content.update_slice(
-        2 + P256_ELEM_LEN,
-        &ciphertext_2.content,
-        0,
-        ciphertext_2.len,
-    );
-
-    output.len = 2 + P256_ELEM_LEN + ciphertext_2.len;
-    output
-}
-
-fn compute_th_2(g_y: &BytesP256ElemLen, h_message_1: &BytesHashLen) -> BytesHashLen {
-    let mut message = BytesMaxBuffer::new();
-    message[0] = U8(CBOR_BYTE_STRING);
-    message[1] = U8(P256_ELEM_LEN as u8);
-    message = message.update(2, g_y);
-    message[2 + P256_ELEM_LEN] = U8(CBOR_BYTE_STRING);
-    message[3 + P256_ELEM_LEN] = U8(SHA256_DIGEST_LEN as u8);
-    message = message.update(4 + P256_ELEM_LEN, h_message_1);
-
-    let len = 4 + P256_ELEM_LEN + SHA256_DIGEST_LEN;
-
-    let th_2 = sha256_digest(&message, len);
-
-    th_2
-}
-
-fn compute_th_3(
-    th_2: &BytesHashLen,
-    plaintext_2: &BufferPlaintext2,
-    cred_r: &BytesMaxBuffer,
-    cred_r_len: usize,
-) -> BytesHashLen {
-    let mut message = BytesMaxBuffer::new();
-
-    message[0] = U8(CBOR_BYTE_STRING);
-    message[1] = U8(th_2.len() as u8);
-    message = message.update(2, th_2);
-    message = message.update_slice(2 + th_2.len(), &plaintext_2.content, 0, plaintext_2.len);
-    message = message.update_slice(2 + th_2.len() + plaintext_2.len, cred_r, 0, cred_r_len);
-
-    let output = sha256_digest(&message, th_2.len() + 2 + plaintext_2.len + cred_r_len);
-
-    output
-}
-
-fn compute_th_4(
-    th_3: &BytesHashLen,
-    plaintext_3: &BufferPlaintext3,
-    cred_i: &BytesMaxBuffer,
-    cred_i_len: usize,
-) -> BytesHashLen {
-    let mut message = BytesMaxBuffer::new();
-
-    message[0] = U8(CBOR_BYTE_STRING);
-    message[1] = U8(th_3.len() as u8);
-    message = message.update(2, th_3);
-    message = message.update_slice(2 + th_3.len(), &plaintext_3.content, 0, plaintext_3.len);
-    message = message.update_slice(2 + th_3.len() + plaintext_3.len, cred_i, 0, cred_i_len);
-
-    let output = sha256_digest(&message, th_3.len() + 2 + plaintext_3.len + cred_i_len);
-
-    output
-}
-
-fn edhoc_kdf(
-    prk: &BytesHashLen,
-    label: U8,
-    context: &BytesMaxContextBuffer,
-    context_len: usize,
-    length: usize,
-) -> BytesMaxBuffer {
-    let mut info = BytesMaxInfoBuffer::new();
-    let mut info_len = 0;
-
-    // construct info with inline cbor encoding
-    info[0] = label;
-    if context_len < 24 {
-        info[1] = U8(context_len as u8 | CBOR_MAJOR_BYTE_STRING);
-        info = info.update_slice(2, context, 0, context_len);
-        info_len = 2 + context_len;
-    } else {
-        info[1] = U8(CBOR_BYTE_STRING);
-        info[2] = U8(context_len as u8);
-        info = info.update_slice(3, context, 0, context_len);
-        info_len = 3 + context_len;
-    }
-    if length < 24 {
-        info[info_len] = U8(length as u8);
-        info_len = info_len + 1;
-    } else {
-        info[info_len] = U8(CBOR_UINT_1BYTE);
-        info[info_len + 1] = U8(length as u8);
-        info_len = info_len + 2;
-    }
-
-    let output = hkdf_expand(prk, &info, info_len, length);
-
-    output
-}
-
-fn decode_plaintext_3(
-    plaintext_3: &BufferPlaintext3,
-) -> Result<(U8, BytesMac3, Option<EADItemHacspec>), EDHOCError> {
-    let mut ead_3 = None::<EADItemHacspec>;
-    let mut error = EDHOCError::UnknownError;
-    let mut kid = U8(0xff);
-    let mut mac_3 = BytesMac3::new();
-
-    // check ID_CRED_I and MAC_3
-    if (is_cbor_neg_int_1byte(plaintext_3.content[0]) || is_cbor_uint_1byte(plaintext_3.content[0]))
-        && (is_cbor_bstr_1byte_prefix(plaintext_3.content[1]))
-    {
-        kid = plaintext_3.content[0];
-        // skip the CBOR magic byte as we know how long the MAC is
-        mac_3 = BytesMac3::from_slice(&plaintext_3.content, 2, MAC_LENGTH_3);
-
-        // if there is still more to parse, the rest will be the EAD_3
-        if plaintext_3.len > (2 + MAC_LENGTH_3) {
-            // NOTE: since the current implementation only supports one EAD handler,
-            // we assume only one EAD item
-            let ead_res = parse_ead(plaintext_3, 2 + MAC_LENGTH_3);
-            if ead_res.is_ok() {
-                ead_3 = ead_res.unwrap();
-                error = EDHOCError::Success;
-            } else {
-                error = ead_res.unwrap_err();
-            }
-        } else if plaintext_3.len == (2 + MAC_LENGTH_3) {
-            error = EDHOCError::Success;
-        } else {
-            error = EDHOCError::ParsingError;
-        }
-    } else {
-        error = EDHOCError::ParsingError;
-    }
-
-    match error {
-        EDHOCError::Success => Ok((kid, mac_3, ead_3)),
-        _ => Err(error),
-    }
-}
-
-fn encode_plaintext_3(
-    id_cred_i: &BytesIdCred,
-    mac_3: &BytesMac3,
-    ead_3: &Option<EADItemHacspec>,
-) -> BufferPlaintext3 {
-    let mut plaintext_3 = BufferPlaintext3::new();
-
-    // plaintext: P = ( ? PAD, ID_CRED_I / bstr / int, Signature_or_MAC_3, ? EAD_3 )
-    plaintext_3.content[0] = id_cred_i[id_cred_i.len() - 1]; // hack: take the last byte of ID_CRED_I as KID
-    plaintext_3.content[1] = U8(CBOR_MAJOR_BYTE_STRING | MAC_LENGTH_3 as u8);
-    plaintext_3.content = plaintext_3.content.update(2, mac_3);
-    plaintext_3.len = 2 + MAC_LENGTH_3;
-
-    if let Some(ead_3) = ead_3 {
-        let ead_3 = encode_ead_item(ead_3);
-        plaintext_3.content =
-            plaintext_3
-                .content
-                .update_slice(plaintext_3.len, &ead_3.content, 0, ead_3.len);
-        plaintext_3.len += ead_3.len;
-    }
-
-    plaintext_3
-}
-
-fn encode_enc_structure(th_3: &BytesHashLen) -> BytesEncStructureLen {
-    let mut encrypt0 = Bytes8::new();
-    encrypt0[0] = U8(0x45u8); // 'E'
-    encrypt0[1] = U8(0x6eu8); // 'n'
-    encrypt0[2] = U8(0x63u8); // 'c'
-    encrypt0[3] = U8(0x72u8); // 'r'
-    encrypt0[4] = U8(0x79u8); // 'y'
-    encrypt0[5] = U8(0x70u8); // 'p'
-    encrypt0[6] = U8(0x74u8); // 't'
-    encrypt0[7] = U8(0x30u8); // '0'
-
-    let mut enc_structure = BytesEncStructureLen::new();
-
-    // encode Enc_structure from draft-ietf-cose-rfc8152bis Section 5.3
-    enc_structure[0] = U8(CBOR_MAJOR_ARRAY | 3 as u8); // 3 is the fixed number of elements in the array
-    enc_structure[1] = U8(CBOR_MAJOR_TEXT_STRING | encrypt0.len() as u8);
-    enc_structure = enc_structure.update(2, &encrypt0);
-    enc_structure[encrypt0.len() + 2] = U8(CBOR_MAJOR_BYTE_STRING | 0x00 as u8); // 0 for zero-length byte string
-    enc_structure[encrypt0.len() + 3] = U8(CBOR_BYTE_STRING); // byte string greater than 24
-    enc_structure[encrypt0.len() + 4] = U8(SHA256_DIGEST_LEN as u8);
-    enc_structure = enc_structure.update(encrypt0.len() + 5, th_3);
-
-    enc_structure
-}
-
-fn compute_k_3_iv_3(
-    prk_3e2m: &BytesHashLen,
-    th_3: &BytesHashLen,
-) -> (BytesCcmKeyLen, BytesCcmIvLen) {
-    // K_3 = EDHOC-KDF( PRK_3e2m, 3, TH_3,      key_length )
-    let k_3 = BytesCcmKeyLen::from_slice(
-        &edhoc_kdf(
-            prk_3e2m,
-            U8(3 as u8),
-            &BytesMaxContextBuffer::from_slice(th_3, 0, th_3.len()),
-            th_3.len(),
-            AES_CCM_KEY_LEN,
-        ),
-        0,
-        AES_CCM_KEY_LEN,
-    );
-    // IV_3 = EDHOC-KDF( PRK_3e2m, 4, TH_3,      iv_length )
-    let iv_3 = BytesCcmIvLen::from_slice(
-        &edhoc_kdf(
-            prk_3e2m,
-            U8(4 as u8),
-            &BytesMaxContextBuffer::from_slice(th_3, 0, th_3.len()),
-            th_3.len(),
-            AES_CCM_IV_LEN,
-        ),
-        0,
-        AES_CCM_IV_LEN,
-    );
-
-    (k_3, iv_3)
-}
-
-// calculates ciphertext_3 wrapped in a cbor byte string
-fn encrypt_message_3(
-    prk_3e2m: &BytesHashLen,
-    th_3: &BytesHashLen,
-    plaintext_3: &BufferPlaintext3,
-) -> BufferMessage3 {
-    let mut output = BufferMessage3::new();
-    output.len = 1 + plaintext_3.len + AES_CCM_TAG_LEN;
-    output.content[0] = U8(CBOR_MAJOR_BYTE_STRING | (plaintext_3.len + AES_CCM_TAG_LEN) as u8);
-
-    let enc_structure = encode_enc_structure(th_3);
-
-    let (k_3, iv_3) = compute_k_3_iv_3(prk_3e2m, th_3);
-
-    let ciphertext_3 = aes_ccm_encrypt_tag_8(&k_3, &iv_3, &enc_structure, plaintext_3);
-
-    output.content = output
-        .content
-        .update_slice(1, &ciphertext_3.content, 0, ciphertext_3.len);
-
-    output
-}
-
-fn decrypt_message_3(
-    prk_3e2m: &BytesHashLen,
-    th_3: &BytesHashLen,
-    message_3: &BufferMessage3,
-) -> Result<BufferPlaintext3, EDHOCError> {
-    let mut error = EDHOCError::UnknownError;
-    let mut plaintext_3 = BufferPlaintext3::new();
-
-    // decode message_3
-    let len = (message_3.content[0usize] ^ U8(CBOR_MAJOR_BYTE_STRING)).declassify() as usize;
-
-    let ciphertext_3 = BufferCiphertext3::from_slice(&message_3.content, 1, len);
-
-    let (k_3, iv_3) = compute_k_3_iv_3(prk_3e2m, th_3);
-
-    let enc_structure = encode_enc_structure(th_3);
-
-    let p3 = aes_ccm_decrypt_tag_8(&k_3, &iv_3, &enc_structure, &ciphertext_3);
-
-    if p3.is_ok() {
-        error = EDHOCError::Success;
-        let p3 = p3.unwrap();
-        plaintext_3.content = plaintext_3.content.update_slice(0, &p3.content, 0, p3.len);
-        plaintext_3.len = p3.len;
-    } else {
-        error = p3.unwrap_err();
-    }
-
-    match error {
-        EDHOCError::Success => Ok(plaintext_3),
-        _ => Err(error),
-    }
-}
-
-// output must hold id_cred.len() + cred.len()
-fn encode_kdf_context(
-    id_cred: &BytesIdCred,
-    th: &BytesHashLen,
-    cred: &BytesMaxBuffer,
-    cred_len: usize,
-) -> (BytesMaxContextBuffer, usize) {
-    // encode context in line
-    // assumes ID_CRED_R and CRED_R are already CBOR-encoded
-    let mut output = BytesMaxContextBuffer::new();
-    output = output.update(0, id_cred);
-    output[id_cred.len()] = U8(CBOR_BYTE_STRING);
-    output[id_cred.len() + 1] = U8(SHA256_DIGEST_LEN as u8);
-    output = output.update(id_cred.len() + 2, th);
-    output = output.update_slice(id_cred.len() + 2 + SHA256_DIGEST_LEN, cred, 0, cred_len);
-
-    let output_len = (id_cred.len() + 2 + SHA256_DIGEST_LEN + cred_len) as usize;
-
-    (output, output_len)
-}
-
-fn compute_mac_3(
-    prk_4e3m: &BytesHashLen,
-    th_3: &BytesHashLen,
-    id_cred_i: &BytesIdCred,
-    cred_i: &BytesMaxBuffer,
-    cred_i_len: usize,
-) -> BytesMac3 {
-    // MAC_3 = EDHOC-KDF( PRK_4e3m, 6, context_3, mac_length_3 )
-    let (context, context_len) = encode_kdf_context(id_cred_i, th_3, cred_i, cred_i_len);
-
-    // compute mac_3
-    let output_buf = edhoc_kdf(
-        prk_4e3m,
-        U8(6 as u8), // registered label for "MAC_3"
-        &context,
-        context_len,
-        MAC_LENGTH_3,
-    );
-
-    let output = BytesMac3::from_slice(&output_buf, 0, MAC_LENGTH_3);
-    output
-}
-
-fn compute_mac_2(
-    prk_3e2m: &BytesHashLen,
-    id_cred_r: &BytesIdCred,
-    cred_r: &BytesMaxBuffer,
-    cred_r_len: usize,
-    th_2: &BytesHashLen,
-) -> BytesMac2 {
-    // compute MAC_2
-    let (context, context_len) = encode_kdf_context(id_cred_r, th_2, cred_r, cred_r_len);
-
-    // MAC_2 = EDHOC-KDF( PRK_3e2m, 2, context_2, mac_length_2 )
-    let mac_2 = BytesMac2::from_slice(
-        &edhoc_kdf(prk_3e2m, U8(2 as u8), &context, context_len, MAC_LENGTH_2),
-        0,
-        MAC_LENGTH_2,
-    );
-
-    mac_2
-}
-
-fn decode_plaintext_2(
-    plaintext_2: &BytesMaxBuffer,
-    plaintext_2_len: usize,
-) -> Result<(U8, U8, BytesMac2, Option<EADItemHacspec>), EDHOCError> {
-    let mut error = EDHOCError::UnknownError;
-    let mut ead_2 = None::<EADItemHacspec>;
-    let mut c_r: U8 = U8(0xff);
-    let mut id_cred_r: U8 = U8(0xff);
-    let mut mac_2 = BytesMac2::new();
-
-    // check CBOR sequence types for c_r, id_cred_r, and mac_2
-    if (is_cbor_neg_int_1byte(plaintext_2[0]) || is_cbor_uint_1byte(plaintext_2[0]))
-        && (is_cbor_neg_int_1byte(plaintext_2[1]) || is_cbor_uint_1byte(plaintext_2[1]))
-        && (is_cbor_bstr_1byte_prefix(plaintext_2[2]))
-    // TODO: check mac length as well
-    {
-        c_r = plaintext_2[0];
-        id_cred_r = plaintext_2[1];
-        // NOTE: skipping cbor byte string byte as we know how long the string is
-        mac_2 = BytesMac2::from_slice(plaintext_2, 3, MAC_LENGTH_2);
-
-        // if there is still more to parse, the rest will be the EAD_2
-        if plaintext_2_len > (3 + MAC_LENGTH_2) {
-            // NOTE: since the current implementation only supports one EAD handler,
-            // we assume only one EAD item
-            let ead_res = parse_ead(
-                &EdhocMessageBufferHacspec::from_slice(plaintext_2, 0, plaintext_2_len),
-                3 + MAC_LENGTH_2,
-            );
-            if ead_res.is_ok() {
-                ead_2 = ead_res.unwrap();
-                error = EDHOCError::Success;
-            } else {
-                error = ead_res.unwrap_err();
-            }
-        } else if plaintext_2_len == (3 + MAC_LENGTH_2) {
-            error = EDHOCError::Success;
-        } else {
-            error = EDHOCError::ParsingError;
-        }
-    } else {
-        error = EDHOCError::ParsingError;
-    }
-
-    match error {
-        EDHOCError::Success => Ok((c_r, id_cred_r, mac_2, ead_2)),
-        _ => Err(error),
-    }
-}
-
-fn encode_plaintext_2(
-    c_r: U8,
-    id_cred_r: &BytesIdCred,
-    mac_2: &BytesMac2,
-    ead_2: &Option<EADItemHacspec>,
-) -> BufferPlaintext2 {
-    let mut plaintext_2 = BufferPlaintext2::new();
-    plaintext_2.content[0] = c_r;
-    plaintext_2.content[1] = id_cred_r[id_cred_r.len() - 1];
-    plaintext_2.content[2] = U8(CBOR_MAJOR_BYTE_STRING | MAC_LENGTH_2 as u8);
-    plaintext_2.content = plaintext_2.content.update(3, mac_2);
-    plaintext_2.len = 3 + mac_2.len();
-
-    if let Some(ead_2) = ead_2 {
-        let ead_2 = encode_ead_item(ead_2);
-        plaintext_2.content =
-            plaintext_2
-                .content
-                .update_slice(plaintext_2.len, &ead_2.content, 0, ead_2.len);
-        plaintext_2.len += ead_2.len;
-    }
-
-    plaintext_2
-}
-
-fn encrypt_decrypt_ciphertext_2(
-    prk_2e: &BytesHashLen,
-    th_2: &BytesHashLen,
-    ciphertext_2: &BufferCiphertext2,
-) -> (BytesMaxBuffer, usize) {
-    // convert the transcript hash th_2 to BytesMaxContextBuffer type
-    let th_2_context = BytesMaxContextBuffer::from_slice(th_2, 0, th_2.len());
-
-    // KEYSTREAM_2 = EDHOC-KDF( PRK_2e,   0, TH_2,      plaintext_length )
-    let keystream_2 = edhoc_kdf(
-        prk_2e,
-        U8(0 as u8),
-        &th_2_context,
-        SHA256_DIGEST_LEN,
-        ciphertext_2.len,
-    );
-
-    let mut plaintext_2 = BytesMaxBuffer::new();
-    // decrypt/encrypt ciphertext_2
-    for i in 0..ciphertext_2.len {
-        plaintext_2[i] = ciphertext_2.content[i] ^ keystream_2[i];
-    }
-
-    (plaintext_2, ciphertext_2.len)
-}
-
-fn compute_salt_4e3m(prk_3e2m: &BytesHashLen, th_3: &BytesHashLen) -> BytesHashLen {
-    let th_3_context = BytesMaxContextBuffer::from_slice(th_3, 0, th_3.len());
-    let salt_4e3m_buf = edhoc_kdf(
-        prk_3e2m,
-        U8(5 as u8),
-        &th_3_context,
-        th_3.len(),
-        SHA256_DIGEST_LEN,
-    );
-    let mut salt_4e3m = BytesHashLen::new();
-    salt_4e3m = salt_4e3m.update_slice(0, &salt_4e3m_buf, 0, SHA256_DIGEST_LEN);
-
-    salt_4e3m
-}
-
-fn compute_prk_4e3m(
-    salt_4e3m: &BytesHashLen,
-    i: &BytesP256ElemLen,
-    g_y: &BytesP256ElemLen,
-) -> BytesHashLen {
-    // compute g_rx from static R's public key and private ephemeral key
-    let g_iy = p256_ecdh(i, g_y);
-    let prk_4e3m = hkdf_extract(salt_4e3m, &g_iy);
-
-    prk_4e3m
-}
-
-fn compute_salt_3e2m(prk_2e: &BytesHashLen, th_2: &BytesHashLen) -> BytesHashLen {
-    let th_2_context = BytesMaxContextBuffer::from_slice(th_2, 0, th_2.len());
-
-    let salt_3e2m_buf = edhoc_kdf(
-        prk_2e,
-        U8(1 as u8),
-        &th_2_context,
-        SHA256_DIGEST_LEN,
-        SHA256_DIGEST_LEN,
-    );
-
-    let mut salt_3e2m = BytesHashLen::new();
-    salt_3e2m = salt_3e2m.update_slice(0, &salt_3e2m_buf, 0, SHA256_DIGEST_LEN);
-
-    salt_3e2m
-}
-
-fn compute_prk_3e2m(
-    salt_3e2m: &BytesHashLen,
-    x_or_r: &BytesP256ElemLen,
-    gx_or_gr: &BytesP256ElemLen,
-) -> BytesHashLen {
-    // compute g_rx from static private key and public ephemeral key
-    let g_rx = p256_ecdh(x_or_r, gx_or_gr);
-    let prk_3e2m = hkdf_extract(salt_3e2m, &g_rx);
-
-    prk_3e2m
-}
-
-fn compute_prk_2e(
-    x: &BytesP256ElemLen,
-    g_y: &BytesP256ElemLen,
-    th_2: &BytesHashLen,
-) -> BytesHashLen {
-    // compute the shared secret
-    let g_xy = p256_ecdh(x, g_y);
-    // compute prk_2e as PRK_2e = HMAC-SHA-256( salt, G_XY )
-    let prk_2e = hkdf_extract(th_2, &g_xy);
-
-    prk_2e
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    // test vectors (TV)
-
-    // message_1 (first_time)
-    const METHOD_TV_FIRST_TIME: u8 = 0x03;
-    const SUITES_I_TV_FIRST_TIME: &str = "060000000000000000";
-    const G_X_TV_FIRST_TIME: &str =
-        "741a13d7ba048fbb615e94386aa3b61bea5b3d8f65f32620b749bee8d278efa9";
-    const C_I_TV_FIRST_TIME: u8 = 0x0e;
-    const MESSAGE_1_TV_FIRST_TIME: &str =
-        "03065820741a13d7ba048fbb615e94386aa3b61bea5b3d8f65f32620b749bee8d278efa90e";
-
-    // message_1 (second time)
-    const METHOD_TV: u8 = 0x03;
-    const SUITES_I_TV: &str = "060200000000000000";
-    const G_X_TV: &str = "8af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b6";
-    const C_I_TV: u8 = 0x37;
-    const MESSAGE_1_TV: &str =
-        "0382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b637";
-    // below are a few truncated messages for the purpose of testing cipher suites
-    // message with one cipher suite (23..=255)
-    const MESSAGE_1_TV_SUITE_ONLY_A: &str = "031818";
-    // message with an array having two cipher suites with small values (0..=23)
-    const MESSAGE_1_TV_SUITE_ONLY_B: &str = "03820201";
-    // message with an array having two cipher suites, where one is a large value (23..=255)
-    const MESSAGE_1_TV_SUITE_ONLY_C: &str = "0382021819";
-    // message with an array having too many cipher suites (more than 9)
-    const MESSAGE_1_TV_SUITE_ONLY_ERR: &str = "038A02020202020202020202";
-    const EAD_DUMMY_LABEL_TV: u8 = 0x01;
-    const EAD_DUMMY_VALUE_TV: &str = "cccccc";
-    const EAD_DUMMY_CRITICAL_TV: &str = "20cccccc";
-    const MESSAGE_1_WITH_DUMMY_EAD_NO_VALUE_TV: &str =
-        "0382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b63701";
-    const MESSAGE_1_WITH_DUMMY_EAD_TV: &str =
-        "0382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b63701cccccc";
-    const MESSAGE_1_WITH_DUMMY_CRITICAL_EAD_TV: &str =
-        "0382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b63720cccccc";
-    const PLAINTEXT_2_WITH_DUMMY_CRITICAL_EAD_TV: &str = "3248d0d1a594797d0aaf20cccccc";
-    const PLAINTEXT_3_WITH_DUMMY_CRITICAL_EAD_TV: &str = "2b48ddf106b86fd22fe420cccccc";
-    const G_Y_TV: &str = "419701d7f00a26c2dc587a36dd752549f33763c893422c8ea0f955a13a4ff5d5";
-    const C_R_TV: u8 = 0x27;
-    pub const MESSAGE_2_LEN_TV: usize = 45;
-    pub const CIPHERTEXT_2_LEN_TV: usize = MESSAGE_2_LEN_TV - P256_ELEM_LEN - 2;
-    pub const PLAINTEXT_2_LEN_TV: usize = CIPHERTEXT_2_LEN_TV;
-    const MESSAGE_2_TV: &str =
-    "582b419701d7f00a26c2dc587a36dd752549f33763c893422c8ea0f955a13a4ff5d59862a11de42a95d785386a";
-    const CIPHERTEXT_2_TV: &str = "9862a11de42a95d785386a";
-    const H_MESSAGE_1_TV: &str = "ca02cabda5a8902749b42f711050bb4dbd52153e87527594b39f50cdf019888c";
-    const TH_2_TV: &str = "356efd53771425e008f3fe3a86c83ff4c6b16e57028ff39d5236c182b202084b";
-    const TH_3_TV: &str = "dfe5b065e64c72d226d500c12d49bee6dc4881ded0965e9bdf89d24a54f2e59a";
-    const CIPHERTEXT_3_TV: &str = "473dd16077dd71d65b56e6bd71e7a49d6012";
-    const TH_4_TV: &str = "baf60adbc500fce789af25b108ada2275575056c52c1c2036a2da4a643891cb4";
-    const PRK_2E_TV: &str = "5aa0d69f3e3d1e0c479f0b8a486690c9802630c3466b1dc92371c982563170b5";
-    const KEYSTREAM_2_TV: &str = "bf50e9e7bad0bb68173399";
-    const PRK_3E2M_TV: &str = "0ca3d3398296b3c03900987620c11f6fce70781c1d1219720f9ec08c122d8434";
-    const CONTEXT_INFO_MAC_2_TV: &str = "a10441325820356efd53771425e008f3fe3a86c83ff4c6b16e57028ff39d5236c182b202084ba2026b6578616d706c652e65647508a101a501020241322001215820bbc34960526ea4d32e940cad2a234148ddc21791a12afbcbac93622046dd44f02258204519e257236b2a0ce2023f0931f1f386ca7afda64fcde0108c224c51eabf6072";
-    const MAC_2_TV: &str = "fa5efa2ebf920bf3";
-    const ID_CRED_I_TV: &str = "a104412b";
-    const MAC_3_TV: &str = "a5eeb9effdabfc39";
-    const MESSAGE_3_TV: &str = "52473dd16077dd71d65b56e6bd71e7a49d6012";
-    const PRK_4E3M_TV: &str = "e9cb832a240095d3d0643dbe12e9e2e7b18f0360a3172cea7ac0013ee240e072";
-    const CRED_I_TV: &str = "a2027734322d35302d33312d46462d45462d33372d33322d333908a101a5010202412b2001215820ac75e9ece3e50bfc8ed60399889522405c47bf16df96660a41298cb4307f7eb62258206e5de611388a4b8a8211334ac7d37ecb52a387d257e6db3c2a93df21ff3affc8";
-    const ID_CRED_R_TV: &str = "a1044132";
-    const CRED_R_TV: &str = "a2026b6578616d706c652e65647508a101a501020241322001215820bbc34960526ea4d32e940cad2a234148ddc21791a12afbcbac93622046dd44f02258204519e257236b2a0ce2023f0931f1f386ca7afda64fcde0108c224c51eabf6072";
-    const PLAINTEXT_2_TV: &str = "273248fa5efa2ebf920bf3";
-    const SK_I_TV: &str = "fb13adeb6518cee5f88417660841142e830a81fe334380a953406a1305e8706b";
-    const X_TV: &str = "368ec1f69aeb659ba37d5a8d45b21bdc0299dceaa8ef235f3ca42ce3530f9525";
-    const G_R_TV: &str = "bbc34960526ea4d32e940cad2a234148ddc21791a12afbcbac93622046dd44f0";
-    const PLAINTEXT_3_TV: &str = "2b48a5eeb9effdabfc39";
-    const SALT_3E2M_TV: &str = "af4e103a47cb3cf32570d5c25ad27732bd8d8178e9a69d061c31a27f8e3ca926";
-    const SALT_4E3M_TV: &str = "84f8a2a9534ddd78dcc7e76e0d4df60bfad7cd3ad6e1d531c7f373a7eda52d1c";
-    const G_XY_TV: &str = "2f0cb7e860ba538fbf5c8bded009f6259b4b628fe1eb7dbe9378e5ecf7a824ba";
-    const PRK_OUT_TV: &str = "6b2dae4032306571cfbc2e4f94a255fb9f1f3fb29ca6f379fec989d4fa90dcf0";
-    const PRK_EXPORTER_TV: &str =
-        "4f0a5a823d06d0005e1becda8a6e61f3c8c67a8b15da7d44d3585ec5854e91e2";
-    const OSCORE_MASTER_SECRET_TV: &str = "8c409a332223ad900e44f3434d2d2ce3";
-    const OSCORE_MASTER_SALT_TV: &str = "6163f44be862adfa";
-
-    // invalid test vectors, should result in a parsing error
-    const MESSAGE_1_INVALID_ARRAY_TV: &str =
-        "8403025820741a13d7ba048fbb615e94386aa3b61bea5b3d8f65f32620b749bee8d278efa90e";
-    const MESSAGE_1_INVALID_C_I_TV: &str =
-        "03025820741a13d7ba048fbb615e94386aa3b61bea5b3d8f65f32620b749bee8d278efa9410e";
-    const MESSAGE_1_INVALID_CIPHERSUITE_TV: &str =
-        "0381025820741a13d7ba048fbb615e94386aa3b61bea5b3d8f65f32620b749bee8d278efa90e";
-    const MESSAGE_1_INVALID_TEXT_EPHEMERAL_KEY_TV: &str =
-        "0302782020616972207370656564206F66206120756E6C6164656E207377616C6C6F77200e";
-    const MESSAGE_2_INVALID_NUMBER_OF_CBOR_SEQUENCE_TV: &str =
-        "5820419701d7f00a26c2dc587a36dd752549f33763c893422c8ea0f955a13a4ff5d54B9862a11de42a95d785386a";
-    const PLAINTEXT_2_SURPLUS_MAP_ID_CRED_TV: &str = "27a10442321048fa5efa2ebf920bf3";
-    const PLAINTEXT_2_SURPLUS_BSTR_ID_CRED_TV: &str = "27413248fa5efa2ebf920bf3";
-
-    #[test]
-    fn test_ecdh() {
-        let x_tv = BytesP256ElemLen::from_hex(X_TV);
-        let g_y_tv = BytesP256ElemLen::from_hex(G_Y_TV);
-        let g_xy_tv = BytesP256ElemLen::from_hex(G_XY_TV);
-
-        let g_xy = p256_ecdh(&x_tv, &g_y_tv);
-
-        assert_bytes_eq!(g_xy, g_xy_tv);
-    }
-
-    #[test]
-    fn test_encode_message_1() {
-        let method_tv = U8(METHOD_TV);
-        let suites_i_tv = BytesSuites::from_hex(SUITES_I_TV);
-        let suites_i_tv_len: usize = 2;
-        let g_x_tv = BytesP256ElemLen::from_hex(G_X_TV);
-        let c_i_tv = U8(C_I_TV);
-        let message_1_tv = BufferMessage1::from_hex(MESSAGE_1_TV);
-
-        let message_1 = encode_message_1(
-            method_tv,
-            &suites_i_tv,
-            suites_i_tv_len,
-            &g_x_tv,
-            c_i_tv,
-            &None::<EADItemHacspec>,
-        );
-
-        assert_bytes_eq!(message_1.content, message_1_tv.content);
-    }
-
-    #[test]
-    fn test_parse_suites_i() {
-        let message_1_tv = BufferMessage1::from_hex(MESSAGE_1_TV);
-        let suites_i_tv = BytesSuites::from_hex(SUITES_I_TV);
-
-        let res = parse_suites_i(&message_1_tv);
-        assert!(res.is_ok());
-        let (suites_i, suites_i_len, raw_suites_len) = res.unwrap();
-        assert_bytes_eq!(suites_i, suites_i_tv);
-
-        let res = parse_suites_i(&BufferMessage1::from_hex(MESSAGE_1_TV_SUITE_ONLY_A));
-        assert!(res.is_ok());
-        let (suites_i, suites_i_len, raw_suites_len) = res.unwrap();
-        assert_eq!((suites_i[0] as U8).declassify(), 0x18);
-
-        let (suites_i, suites_i_len, raw_suites_len) =
-            parse_suites_i(&BufferMessage1::from_hex(MESSAGE_1_TV_SUITE_ONLY_B)).unwrap();
-        assert_eq!(suites_i_len, 2);
-        assert_eq!(raw_suites_len, 3);
-        assert_eq!((suites_i[0] as U8).declassify(), 0x02);
-        assert_eq!((suites_i[1] as U8).declassify(), 0x01);
-
-        let (suites_i, suites_i_len, raw_suites_len) =
-            parse_suites_i(&BufferMessage1::from_hex(MESSAGE_1_TV_SUITE_ONLY_C)).unwrap();
-        assert_eq!(suites_i_len, 2);
-        assert_eq!(raw_suites_len, 4);
-        assert_eq!((suites_i[0] as U8).declassify(), 0x02);
-        assert_eq!((suites_i[1] as U8).declassify(), 0x19);
-
-        let res = parse_suites_i(&BufferMessage1::from_hex(MESSAGE_1_TV_SUITE_ONLY_ERR));
-        assert_eq!(res.unwrap_err(), EDHOCError::ParsingError);
-    }
-
-    #[test]
-    fn test_parse_message_1() {
-        let message_1_tv_first_time = BufferMessage1::from_hex(MESSAGE_1_TV_FIRST_TIME);
-        let message_1_tv = BufferMessage1::from_hex(MESSAGE_1_TV);
-        let suites_i_tv_first_time = BytesSuites::from_hex(SUITES_I_TV_FIRST_TIME);
-        let suites_i_tv = BytesSuites::from_hex(SUITES_I_TV);
-        let g_x_tv_first_time = BytesP256ElemLen::from_hex(G_X_TV_FIRST_TIME);
-        let g_x_tv = BytesP256ElemLen::from_hex(G_X_TV);
-        let c_i_tv_first_time = U8(C_I_TV_FIRST_TIME);
-        let c_i_tv = U8(C_I_TV);
-
-        // first time message_1 parsing
-        let res = parse_message_1(&message_1_tv_first_time);
-        assert!(res.is_ok());
-        let (method, suites_i, suites_i_len, g_x, c_i, _ead_1) = res.unwrap();
-        assert_eq!(method.declassify(), METHOD_TV_FIRST_TIME);
-        assert_bytes_eq!(suites_i, suites_i_tv_first_time);
-        assert_bytes_eq!(g_x, g_x_tv_first_time);
-        assert_eq!(c_i.declassify(), c_i_tv_first_time.declassify());
-
-        // second time message_1
-        let res = parse_message_1(&message_1_tv);
-        assert!(res.is_ok());
-        let (method, suites_i, suites_i_len, g_x, c_i, _ead_1) = res.unwrap();
-
-        assert_eq!(method.declassify(), METHOD_TV);
-        assert_bytes_eq!(suites_i, suites_i_tv);
-        assert_bytes_eq!(g_x, g_x_tv);
-        assert_eq!(c_i.declassify(), c_i_tv.declassify());
-    }
-
-    #[test]
-    fn test_parse_message_1_invalid_traces() {
-        let message_1_tv = BufferMessage1::from_hex(MESSAGE_1_INVALID_ARRAY_TV);
-        assert_eq!(
-            parse_message_1(&message_1_tv).unwrap_err(),
-            EDHOCError::ParsingError
-        );
-
-        let message_1_tv = BufferMessage1::from_hex(MESSAGE_1_INVALID_C_I_TV);
-        assert_eq!(
-            parse_message_1(&message_1_tv).unwrap_err(),
-            EDHOCError::ParsingError
-        );
-
-        let message_1_tv = BufferMessage1::from_hex(MESSAGE_1_INVALID_CIPHERSUITE_TV);
-        assert_eq!(
-            parse_message_1(&message_1_tv).unwrap_err(),
-            EDHOCError::ParsingError
-        );
-
-        let message_1_tv = BufferMessage1::from_hex(MESSAGE_1_INVALID_TEXT_EPHEMERAL_KEY_TV);
-        assert_eq!(
-            parse_message_1(&message_1_tv).unwrap_err(),
-            EDHOCError::ParsingError
-        );
-    }
-
-    #[test]
-    fn test_parse_message_2_invalid_traces() {
-        let message_2_tv = BufferMessage1::from_hex(MESSAGE_2_INVALID_NUMBER_OF_CBOR_SEQUENCE_TV);
-        assert_eq!(
-            parse_message_2(&message_2_tv).unwrap_err(),
-            EDHOCError::ParsingError
-        );
-    }
-
-    #[test]
-    fn test_encode_message_2() {
-        let message_2_tv = BufferMessage2::from_hex(MESSAGE_2_TV);
-        let g_y_tv = BytesP256ElemLen::from_hex(G_Y_TV);
-        let ciphertext_2_tv = BufferCiphertext2::from_hex(CIPHERTEXT_2_TV);
-
-        let message_2 = encode_message_2(&g_y_tv, &ciphertext_2_tv);
-
-        assert_bytes_eq!(message_2.content, message_2_tv.content);
-    }
-
-    #[test]
-    fn test_parse_message_2() {
-        let message_2_tv = BufferMessage2::from_hex(MESSAGE_2_TV);
-        let g_y_tv = BytesP256ElemLen::from_hex(G_Y_TV);
-        let ciphertext_2_tv = BufferCiphertext2::from_hex(CIPHERTEXT_2_TV);
-
-        let ret = parse_message_2(&message_2_tv);
-        assert!(ret.is_ok());
-        let (g_y, ciphertext_2) = ret.unwrap();
-
-        assert_bytes_eq!(g_y, g_y_tv);
-        assert_bytes_eq!(ciphertext_2.content, ciphertext_2_tv.content);
-    }
-
-    #[test]
-    fn test_compute_th_2() {
-        let h_message_1_tv = BytesHashLen::from_hex(H_MESSAGE_1_TV);
-        let g_y_tv = BytesP256ElemLen::from_hex(G_Y_TV);
-        let th_2_tv = BytesHashLen::from_hex(TH_2_TV);
-
-        let th_2 = compute_th_2(&g_y_tv, &h_message_1_tv);
-        assert_bytes_eq!(th_2, th_2_tv);
-    }
-
-    #[test]
-    fn test_compute_th_3() {
-        let th_2_tv = BytesHashLen::from_hex(TH_2_TV);
-        let th_3_tv = BytesHashLen::from_hex(TH_3_TV);
-        let plaintext_2_tv = BufferPlaintext2::from_hex(PLAINTEXT_2_TV);
-        let cred_r_tv =
-            BytesMaxBuffer::from_slice(&ByteSeq::from_hex(CRED_R_TV), 0, CRED_R_TV.len() / 2);
-
-        let th_3 = compute_th_3(&th_2_tv, &plaintext_2_tv, &cred_r_tv, CRED_R_TV.len() / 2);
-        assert_bytes_eq!(th_3, th_3_tv);
-    }
-
-    #[test]
-    fn test_compute_th_4() {
-        let th_3_tv = BytesHashLen::from_hex(TH_3_TV);
-        let plaintext_3_tv = BufferPlaintext3::from_hex(PLAINTEXT_3_TV);
-        let th_4_tv = BytesHashLen::from_hex(TH_4_TV);
-        let cred_i_tv =
-            BytesMaxBuffer::from_slice(&ByteSeq::from_hex(CRED_I_TV), 0, CRED_I_TV.len() / 2);
-
-        let th_4 = compute_th_4(&th_3_tv, &plaintext_3_tv, &cred_i_tv, CRED_I_TV.len() / 2);
-        assert_bytes_eq!(th_4, th_4_tv);
-    }
-
-    #[test]
-    fn test_edhoc_kdf() {
-        let th_2_context_tv =
-            BytesMaxContextBuffer::from_slice(&ByteSeq::from_hex(TH_2_TV), 0, TH_2_TV.len() / 2);
-        let prk_2e_tv = BytesHashLen::from_hex(PRK_2E_TV);
-        let keystream_2_tv = BufferPlaintext2::from_hex(KEYSTREAM_2_TV);
-        const LEN_TV: usize = PLAINTEXT_2_LEN_TV;
-
-        let output = edhoc_kdf(
-            &prk_2e_tv,
-            U8(0),
-            &th_2_context_tv,
-            SHA256_DIGEST_LEN,
-            LEN_TV,
-        );
-        for i in 0..keystream_2_tv.len {
-            assert_eq!(
-                keystream_2_tv.content[i].declassify(),
-                output[i].declassify()
-            );
-        }
-
-        let prk_3e2m_tv = BytesHashLen::from_hex(PRK_3E2M_TV);
-        let context_info_mac_2 = BytesMaxContextBuffer::from_slice(
-            &ByteSeq::from_hex(CONTEXT_INFO_MAC_2_TV),
-            0,
-            CONTEXT_INFO_MAC_2_TV.len() / 2,
-        );
-        let mac_2_tv = BytesMac2::from_hex(MAC_2_TV);
-
-        let output_2 = edhoc_kdf(
-            &prk_3e2m_tv,
-            U8(2), // length of "MAC_2"
-            &context_info_mac_2,
-            CONTEXT_INFO_MAC_2_TV.len() / 2, // divide by two to get num of bytes from hex string
-            MAC_LENGTH_2,
-        );
-
-        for i in 0..MAC_2_TV.len() / 2 {
-            assert_eq!(mac_2_tv[i].declassify(), output_2[i].declassify());
-        }
-    }
-
-    #[test]
-    fn test_encrypt_message_3() {
-        let prk_3e2m_tv = BytesHashLen::from_hex(PRK_3E2M_TV);
-        let th_3_tv = BytesHashLen::from_hex(TH_3_TV);
-        let plaintext_3_tv = BufferPlaintext3::from_hex(PLAINTEXT_3_TV);
-        let message_3_tv = BufferMessage3::from_hex(MESSAGE_3_TV);
-
-        let message_3 = encrypt_message_3(&prk_3e2m_tv, &th_3_tv, &plaintext_3_tv);
-        assert_bytes_eq!(message_3.content, message_3_tv.content);
-    }
-
-    #[test]
-    fn test_decrypt_message_3() {
-        let message_3_tv = BufferMessage3::from_hex(MESSAGE_3_TV);
-        let prk_3e2m_tv = BytesHashLen::from_hex(PRK_3E2M_TV);
-        let th_3_tv = BytesHashLen::from_hex(TH_3_TV);
-        let plaintext_3_tv = BufferPlaintext3::from_hex(PLAINTEXT_3_TV);
-
-        let plaintext_3 = decrypt_message_3(&prk_3e2m_tv, &th_3_tv, &message_3_tv);
-
-        assert!(plaintext_3.is_ok());
-        assert_bytes_eq!(plaintext_3.unwrap().content, plaintext_3_tv.content);
-    }
-
-    #[test]
-    fn test_compute_mac_3() {
-        let prk_4e3m_tv = BytesHashLen::from_hex(PRK_4E3M_TV);
-        let th_3_tv = BytesHashLen::from_hex(TH_3_TV);
-        let id_cred_i_tv = BytesIdCred::from_hex(ID_CRED_I_TV);
-        let cred_i_tv =
-            BytesMaxBuffer::from_slice(&ByteSeq::from_hex(CRED_I_TV), 0, CRED_I_TV.len() / 2);
-        let mac_3_tv = BytesMac3::from_hex(MAC_3_TV);
-
-        let mac_3 = compute_mac_3(
-            &prk_4e3m_tv,
-            &th_3_tv,
-            &id_cred_i_tv,
-            &cred_i_tv,
-            CRED_I_TV.len() / 2, // divide by two to get num of bytes from hex string
-        );
-        assert_bytes_eq!(mac_3, mac_3_tv);
-    }
-
-    #[test]
-    fn test_compute_and_verify_mac_2() {
-        let prk_3e2m_tv = BytesHashLen::from_hex(PRK_3E2M_TV);
-        let id_cred_r_tv = BytesIdCred::from_hex(ID_CRED_R_TV);
-        let cred_r_tv =
-            BytesMaxBuffer::from_slice(&ByteSeq::from_hex(CRED_R_TV), 0, CRED_R_TV.len() / 2);
-        let th_2_tv = BytesHashLen::from_hex(TH_2_TV);
-        let mac_2_tv = BytesMac2::from_hex(MAC_2_TV);
-
-        let rcvd_mac_2 = compute_mac_2(
-            &prk_3e2m_tv,
-            &id_cred_r_tv,
-            &cred_r_tv,
-            CRED_R_TV.len() / 2,
-            &th_2_tv,
-        );
-
-        assert_bytes_eq!(rcvd_mac_2, mac_2_tv);
-    }
-
-    #[test]
-    fn test_encode_plaintext_2() {
-        let plaintext_2_tv = BufferPlaintext2::from_hex(PLAINTEXT_2_TV);
-        let c_r_tv = U8(C_R_TV);
-        let id_cred_r_tv = BytesIdCred::from_hex(ID_CRED_R_TV);
-        let mac_2_tv = BytesMac2::from_hex(MAC_2_TV);
-
-        let plaintext_2 =
-            encode_plaintext_2(c_r_tv, &id_cred_r_tv, &mac_2_tv, &None::<EADItemHacspec>);
-
-        assert_bytes_eq!(plaintext_2.content, plaintext_2_tv.content);
-    }
-
-    #[test]
-    fn test_parse_plaintext_2_invalid_traces() {
-        let plaintext_2_tv_len = PLAINTEXT_2_SURPLUS_MAP_ID_CRED_TV.len() / 2;
-        let plaintext_2_tv = BytesMaxBuffer::from_slice(
-            &ByteSeq::from_hex(PLAINTEXT_2_SURPLUS_MAP_ID_CRED_TV),
-            0,
-            plaintext_2_tv_len,
-        );
-        let plaintext_2 = decode_plaintext_2(&plaintext_2_tv, plaintext_2_tv_len);
-        assert_eq!(plaintext_2.unwrap_err(), EDHOCError::ParsingError);
-
-        let plaintext_2_tv_len = PLAINTEXT_2_SURPLUS_BSTR_ID_CRED_TV.len() / 2;
-        let plaintext_2_tv = BytesMaxBuffer::from_slice(
-            &ByteSeq::from_hex(PLAINTEXT_2_SURPLUS_BSTR_ID_CRED_TV),
-            0,
-            plaintext_2_tv_len,
-        );
-        let plaintext_2 = decode_plaintext_2(&plaintext_2_tv, plaintext_2_tv_len);
-        assert_eq!(plaintext_2.unwrap_err(), EDHOCError::ParsingError);
-    }
-
-    #[test]
-    fn test_decode_plaintext_2() {
-        let plaintext_2_tv = BytesMaxBuffer::from_slice(
-            &ByteSeq::from_hex(PLAINTEXT_2_TV),
-            0,
-            PLAINTEXT_2_TV.len() / 2,
-        );
-        let c_r_tv = U8(C_R_TV);
-        let id_cred_r_tv = BytesIdCred::from_hex(ID_CRED_R_TV);
-        let mac_2_tv = BytesMac2::from_hex(MAC_2_TV);
-
-        let plaintext_2 = decode_plaintext_2(&plaintext_2_tv, PLAINTEXT_2_LEN_TV);
-        assert!(plaintext_2.is_ok());
-        let (c_r, id_cred_r, mac_2, ead_2) = plaintext_2.unwrap();
-        assert_eq!(U8::declassify(c_r), U8::declassify(c_r_tv));
-        assert_eq!(U8::declassify(id_cred_r), U8::declassify(id_cred_r_tv[3]));
-        assert_bytes_eq!(mac_2, mac_2_tv);
-        assert!(ead_2.is_none());
-    }
-
-    #[test]
-    fn test_encrypt_decrypt_ciphertext_2() {
-        let prk_2e_tv = BytesHashLen::from_hex(PRK_2E_TV);
-        let th_2_tv = BytesHashLen::from_hex(TH_2_TV);
-        let ciphertext_2_tv = BufferCiphertext2::from_hex(CIPHERTEXT_2_TV);
-        let plaintext_2_tv = BufferPlaintext2::from_hex(PLAINTEXT_2_TV);
-
-        // test decryption
-        let (plaintext_2, plaintext_2_len) =
-            encrypt_decrypt_ciphertext_2(&prk_2e_tv, &th_2_tv, &ciphertext_2_tv);
-
-        assert_eq!(plaintext_2_len, PLAINTEXT_2_LEN_TV);
-        for i in 0..PLAINTEXT_2_LEN_TV {
-            assert_eq!(
-                plaintext_2[i].declassify(),
-                plaintext_2_tv.content[i].declassify()
-            );
-        }
-
-        let plaintext_2_tmp = BufferCiphertext2::from_slice(&plaintext_2, 0, plaintext_2_len);
-
-        // test encryption
-        let (ciphertext_2, ciphertext_2_len) =
-            encrypt_decrypt_ciphertext_2(&prk_2e_tv, &th_2_tv, &plaintext_2_tmp);
-
-        assert_eq!(ciphertext_2_len, CIPHERTEXT_2_LEN_TV);
-        for i in 0..CIPHERTEXT_2_LEN_TV {
-            assert_eq!(
-                ciphertext_2[i].declassify(),
-                ciphertext_2_tv.content[i].declassify()
-            );
-        }
-    }
-
-    #[test]
-    fn test_compute_prk_4e3m() {
-        let salt_4e3m_tv = BytesHashLen::from_hex(SALT_4E3M_TV);
-        let i_tv = BytesP256ElemLen::from_hex(SK_I_TV);
-        let g_y_tv = BytesP256ElemLen::from_hex(G_Y_TV);
-        let prk_4e3m_tv = BytesHashLen::from_hex(PRK_4E3M_TV);
-
-        let prk_4e3m = compute_prk_4e3m(&salt_4e3m_tv, &i_tv, &g_y_tv);
-        assert_bytes_eq!(prk_4e3m, prk_4e3m_tv);
-    }
-
-    #[test]
-    fn test_compute_prk_3e2m() {
-        let salt_3e2m_tv = BytesHashLen::from_hex(SALT_3E2M_TV);
-        let x_tv = BytesP256ElemLen::from_hex(X_TV);
-        let g_r_tv = BytesP256ElemLen::from_hex(G_R_TV);
-        let prk_3e2m_tv = BytesHashLen::from_hex(PRK_3E2M_TV);
-
-        let prk_3e2m = compute_prk_3e2m(&salt_3e2m_tv, &x_tv, &g_r_tv);
-        assert_bytes_eq!(prk_3e2m, prk_3e2m_tv);
-    }
-
-    #[test]
-    fn test_compute_prk_2e() {
-        let x_tv = BytesP256ElemLen::from_hex(X_TV);
-        let g_y_tv = BytesP256ElemLen::from_hex(G_Y_TV);
-        let th_2_tv = BytesHashLen::from_hex(TH_2_TV);
-        let prk_2e_tv = BytesHashLen::from_hex(PRK_2E_TV);
-
-        let prk_2e = compute_prk_2e(&x_tv, &g_y_tv, &th_2_tv);
-        assert_bytes_eq!(prk_2e, prk_2e_tv);
-    }
-
-    #[test]
-    fn test_encode_plaintext_3() {
-        let id_cred_i_tv = BytesIdCred::from_hex(ID_CRED_I_TV);
-        let mac_3_tv = BytesMac3::from_hex(MAC_3_TV);
-        let plaintext_3_tv = BufferPlaintext3::from_hex(PLAINTEXT_3_TV);
-
-        let plaintext_3 = encode_plaintext_3(&id_cred_i_tv, &mac_3_tv, &None::<EADItemHacspec>);
-        assert_bytes_eq!(plaintext_3.content, plaintext_3_tv.content);
-    }
-
-    #[test]
-    fn test_decode_plaintext_3() {
-        let plaintext_3_tv = BufferPlaintext3::from_hex(PLAINTEXT_3_TV);
-        let mac_3_tv = BytesMac3::from_hex(MAC_3_TV);
-        let kid_tv = BytesIdCred::from_hex(ID_CRED_I_TV);
-        let kid_tv = kid_tv[kid_tv.len() - 1];
-
-        let (kid, mac_3, _ead_3) = decode_plaintext_3(&plaintext_3_tv).unwrap();
-
-        assert_bytes_eq!(mac_3, mac_3_tv);
-        assert_eq!(kid.declassify(), kid_tv.declassify());
-    }
-
-    #[test]
-    fn test_encode_ead_item() {
-        let ead_tv = EdhocMessageBufferHacspec::from_hex(EAD_DUMMY_CRITICAL_TV);
-
-        let ead_item = EADItemHacspec {
-            label: U8(EAD_DUMMY_LABEL_TV),
-            is_critical: true,
-            value: Some(EdhocMessageBufferHacspec::from_hex(EAD_DUMMY_VALUE_TV)),
-        };
-
-        let ead_buffer = encode_ead_item(&ead_item);
-        assert_bytes_eq!(ead_buffer.content, ead_tv.content);
-    }
-
-    #[test]
-    fn test_encode_message_with_ead_item() {
-        let method_tv = U8(METHOD_TV);
-        let suites_i_tv = BytesSuites::from_hex(SUITES_I_TV);
-        let suites_i_tv_len: usize = 2;
-        let g_x_tv = BytesP256ElemLen::from_hex(G_X_TV);
-        let c_i_tv = U8(C_I_TV);
-        let message_1_ead_tv = BufferMessage1::from_hex(MESSAGE_1_WITH_DUMMY_CRITICAL_EAD_TV);
-        let ead_item = EADItemHacspec {
-            label: U8(EAD_DUMMY_LABEL_TV),
-            is_critical: true,
-            value: Some(EdhocMessageBufferHacspec::from_hex(EAD_DUMMY_VALUE_TV)),
-        };
-
-        let message_1 = encode_message_1(
-            method_tv,
-            &suites_i_tv,
-            suites_i_tv_len,
-            &g_x_tv,
-            c_i_tv,
-            &Some(ead_item),
-        );
-
-        assert_bytes_eq!(message_1.content, message_1_ead_tv.content);
-    }
-
-    #[test]
-    fn test_parse_ead_item() {
-        let message_tv_offset = MESSAGE_1_TV.len() / 2;
-        let message_ead_tv = BufferMessage1::from_hex(MESSAGE_1_WITH_DUMMY_EAD_TV);
-        let ead_value_tv = EdhocMessageBufferHacspec::from_hex(EAD_DUMMY_VALUE_TV);
-
-        let res = parse_ead(&message_ead_tv, message_tv_offset);
-        assert!(res.is_ok());
-        let ead_item = res.unwrap();
-        assert!(ead_item.is_some());
-        let ead_item = ead_item.unwrap();
-        assert!(!ead_item.is_critical);
-        assert_eq!(ead_item.label.declassify(), EAD_DUMMY_LABEL_TV);
-        assert_bytes_eq!(ead_item.value.unwrap().content, ead_value_tv.content);
-
-        let message_ead_tv = BufferMessage1::from_hex(MESSAGE_1_WITH_DUMMY_CRITICAL_EAD_TV);
-
-        let res = parse_ead(&message_ead_tv, message_tv_offset).unwrap();
-        let ead_item = res.unwrap();
-        assert!(ead_item.is_critical);
-        assert_eq!(ead_item.label.declassify(), EAD_DUMMY_LABEL_TV);
-        assert_bytes_eq!(ead_item.value.unwrap().content, ead_value_tv.content);
-
-        let message_ead_tv = BufferMessage1::from_hex(MESSAGE_1_WITH_DUMMY_EAD_NO_VALUE_TV);
-
-        let res = parse_ead(&message_ead_tv, message_tv_offset).unwrap();
-        let ead_item = res.unwrap();
-        assert!(!ead_item.is_critical);
-        assert_eq!(ead_item.label.declassify(), EAD_DUMMY_LABEL_TV);
-        assert!(ead_item.value.is_none());
-    }
-
-    #[test]
-    fn test_parse_message_with_ead_item() {
-        let message_1_ead_tv = BufferMessage1::from_hex(MESSAGE_1_WITH_DUMMY_CRITICAL_EAD_TV);
-        let ead_value_tv = EdhocMessageBufferHacspec::from_hex(EAD_DUMMY_VALUE_TV);
-
-        let res = parse_message_1(&message_1_ead_tv);
-        assert!(res.is_ok());
-        let (_method, _suites_i, _suites_i_len, _g_x, _c_i, ead_1) = res.unwrap();
-        let ead_1 = ead_1.unwrap();
-        assert!(ead_1.is_critical);
-        assert_eq!(ead_1.label.declassify(), EAD_DUMMY_LABEL_TV);
-        assert_bytes_eq!(ead_1.value.unwrap().content, ead_value_tv.content);
-    }
-
-    #[test]
-    fn test_compute_prk_out() {
-        let prk_4e3m_tv = BytesHashLen::from_hex(PRK_4E3M_TV);
-        let th_4_tv = BytesHashLen::from_hex(TH_4_TV);
-        let prk_out_tv = BytesHashLen::from_hex(PRK_OUT_TV);
-
-        let mut prk_out = BytesHashLen::new();
-
-        let prk_out_buf = edhoc_kdf(
-            &prk_4e3m_tv,
-            U8(7 as u8),
-            &BytesMaxContextBuffer::from_slice(&th_4_tv, 0, th_4_tv.len()),
-            th_4_tv.len(),
-            SHA256_DIGEST_LEN,
-        );
-        prk_out = prk_out.update_slice(0, &prk_out_buf, 0, SHA256_DIGEST_LEN);
-
-        assert_bytes_eq!(prk_out, prk_out_tv);
-    }
-
-    #[test]
-    fn test_compute_prk_exporter() {
-        let prk_out_tv = BytesHashLen::from_hex(PRK_OUT_TV);
-        let prk_exporter_tv = BytesHashLen::from_hex(PRK_EXPORTER_TV);
-
-        let mut prk_exporter = BytesHashLen::new();
-        let prk_exporter_buf = edhoc_kdf(
-            &prk_out_tv,
-            U8(10 as u8),
-            &BytesMaxContextBuffer::new(),
-            0,
-            SHA256_DIGEST_LEN,
-        );
-        prk_exporter = prk_exporter.update_slice(0, &prk_exporter_buf, 0, SHA256_DIGEST_LEN);
-
-        assert_bytes_eq!(prk_exporter, prk_exporter_tv);
-    }
-
-    #[test]
-    fn test_compute_oscore_master_secret_salt() {
-        let prk_exporter_tv = BytesHashLen::from_hex(PRK_EXPORTER_TV);
-        let mut oscore_master_secret_tv_buf = BytesMaxBuffer::new();
-        let oscore_master_secret_tv = BytesCcmKeyLen::from_hex(OSCORE_MASTER_SECRET_TV);
-        oscore_master_secret_tv_buf = oscore_master_secret_tv_buf.update_slice(
-            0,
-            &oscore_master_secret_tv,
-            0,
-            oscore_master_secret_tv.len(),
-        );
-
-        let mut oscore_master_salt_tv_buf = BytesMaxBuffer::new();
-        let oscore_master_salt_tv = Bytes8::from_hex(OSCORE_MASTER_SALT_TV);
-        let oscore_master_salt_tv_buf = oscore_master_salt_tv_buf.update_slice(
-            0,
-            &oscore_master_salt_tv,
-            0,
-            oscore_master_salt_tv.len(),
-        );
-
-        let oscore_master_secret_buf = edhoc_kdf(
-            &prk_exporter_tv,
-            U8(0 as u8),
-            &BytesMaxContextBuffer::new(),
-            0,
-            oscore_master_secret_tv.len(),
-        );
-        assert_bytes_eq!(oscore_master_secret_buf, oscore_master_secret_tv_buf);
-
-        let oscore_master_salt_buf = edhoc_kdf(
-            &prk_exporter_tv,
-            U8(1 as u8),
-            &BytesMaxContextBuffer::new(),
-            0,
-            oscore_master_salt_tv.len(),
-        );
-
-        assert_bytes_eq!(oscore_master_salt_buf, oscore_master_salt_tv_buf);
-    }
-}
diff --git a/lib/Cargo.toml b/lib/Cargo.toml
index 9de74e59..d782b7e6 100644
--- a/lib/Cargo.toml
+++ b/lib/Cargo.toml
@@ -10,10 +10,9 @@ description = "EDHOC implementation in Rust"
 hexlit = "0.5.3"
 hex = { version = "0.4.3", default-features = false }
 
-edhoc-hacspec = { path = "../hacspec", optional = true }
 hacspec-lib = { version = "0.1.0-beta.1", default-features = false, optional = true }
 edhoc-crypto = { path = "../crypto", default-features = false }
-edhoc-consts = { path = "../consts", default-features = false }
+edhoc-consts = { path = "../consts" }
 edhoc-ead = { path = "../ead", default-features = false }
 panic-semihosting = { version = "0.6.0", features = ["exit"], optional = true }
 
@@ -22,14 +21,10 @@ cbindgen = "0.24.5"
 
 [features]
 default = [ "edhoc-ead/ead-none" ]
-hacspec-hacspec = ["hacspec-lib/std", "edhoc-hacspec", "edhoc-crypto/hacspec", "edhoc-consts/hacspec" ]
-hacspec-cc2538 = ["hacspec-lib/alloc", "edhoc-hacspec", "edhoc-crypto/cc2538", "edhoc-consts/hacspec" ] # FIXME: stubs
-hacspec-psa = ["hacspec-lib/alloc", "edhoc-hacspec", "edhoc-crypto/psa", "edhoc-consts/hacspec" ]
-hacspec-psa-baremetal = ["hacspec-psa", "edhoc-crypto/psa-baremetal", "edhoc-consts/hacspec" ]
-hacspec-cryptocell310 = ["hacspec-lib/alloc", "edhoc-hacspec", "edhoc-crypto/cryptocell310", "edhoc-consts/hacspec" ]
-rust-psa = [ "edhoc-consts/rust", "edhoc-crypto/psa-rust" ]
-rust-psa-baremetal = [ "edhoc-crypto/psa-rust-baremetal", "edhoc-consts/rust", "panic-semihosting" ]
-rust-cryptocell310 = [ "edhoc-consts/rust", "edhoc-crypto/cryptocell310-rust", "panic-semihosting" ]
+crypto-hacspec = ["hacspec-lib/std", "edhoc-crypto/hacspec" ]
+crypto-psa = [ "edhoc-crypto/psa" ]
+crypto-psa-baremetal = [ "edhoc-crypto/psa-baremetal", "panic-semihosting" ]
+crypto-cryptocell310 = [ "edhoc-crypto/cryptocell310", "panic-semihosting" ]
 ead-none = [ "edhoc-ead/ead-none" ]
 ead-zeroconf = [ "edhoc-ead/ead-zeroconf" ]
 
diff --git a/lib/src/c_wrapper.rs b/lib/src/c_wrapper.rs
index 45ca9117..1018f607 100644
--- a/lib/src/c_wrapper.rs
+++ b/lib/src/c_wrapper.rs
@@ -1,9 +1,9 @@
-use crate::rust::*;
+use crate::*;
 use core::{slice, str};
 use edhoc_consts::*;
 
 // Panic handler for cortex-m targets
-#[cfg(any(feature = "rust-cryptocell310", feature = "rust-psa-baremetal"))]
+#[cfg(any(feature = "crypto-cryptocell310", feature = "crypto-psa-baremetal"))]
 use panic_semihosting as _;
 
 // This function is mainly used to test the C wrapper
@@ -37,8 +37,8 @@ pub struct EdhocInitiatorC {
 }
 
 impl EdhocInitiatorC {
-    pub fn to_rust(&self) -> RustEdhocInitiator {
-        RustEdhocInitiator::new(
+    pub fn to_rust(&self) -> EdhocInitiator {
+        EdhocInitiator::new(
             self.state,
             unsafe { str::from_utf8_unchecked(slice::from_raw_parts(self.i, self.i_len)) },
             unsafe { str::from_utf8_unchecked(slice::from_raw_parts(self.g_r, self.g_r_len)) },
@@ -76,8 +76,8 @@ pub struct EdhocResponderC {
 }
 
 impl EdhocResponderC {
-    pub fn to_rust(&self) -> RustEdhocResponder {
-        RustEdhocResponder::new(
+    pub fn to_rust(&self) -> EdhocResponder {
+        EdhocResponder::new(
             self.state,
             unsafe { str::from_utf8_unchecked(slice::from_raw_parts(self.r, self.r_len)) },
             unsafe { str::from_utf8_unchecked(slice::from_raw_parts(self.g_i, self.g_i_len)) },
@@ -112,7 +112,7 @@ pub unsafe extern "C" fn responder_new(
     cred_r: *const u8,
     cred_r_len: usize,
 ) -> EdhocResponderC {
-    RustEdhocResponder::new(
+    EdhocResponder::new(
         State::default(),
         str::from_utf8_unchecked(slice::from_raw_parts(r, r_len)),
         str::from_utf8_unchecked(slice::from_raw_parts(g_i, g_i_len)),
@@ -139,7 +139,7 @@ pub unsafe extern "C" fn initiator_new(
     cred_r: *const u8,
     cred_r_len: usize,
 ) -> EdhocInitiatorC {
-    RustEdhocInitiator::new(
+    EdhocInitiator::new(
         State::default(),
         str::from_utf8_unchecked(slice::from_raw_parts(i, i_len)),
         str::from_utf8_unchecked(slice::from_raw_parts(g_r, g_r_len)),
@@ -158,7 +158,8 @@ pub unsafe extern "C" fn initiator_prepare_message_1(
 ) -> i8 {
     let mut initiator = (*initiator_c).to_rust();
 
-    let result = match initiator.prepare_message_1() {
+    let c_i: u8 = generate_connection_identifier_cbor().into();
+    let result = match initiator.prepare_message_1(c_i) {
         Ok(msg_1) => {
             *message_1 = msg_1;
             0
@@ -196,10 +197,11 @@ pub unsafe extern "C" fn responder_prepare_message_2(
 ) -> i8 {
     let mut responder = (*responder_c).to_rust();
 
-    let result = match responder.prepare_message_2() {
-        Ok((msg_2, c_r_res)) => {
+    let c_r_chosen: u8 = generate_connection_identifier_cbor().into();
+    let result = match responder.prepare_message_2(c_r_chosen) {
+        Ok(msg_2) => {
             *message_2 = msg_2;
-            *c_r = c_r_res;
+            *c_r = c_r_chosen;
             0
         }
         Err(err) => err as i8,
diff --git a/lib/src/edhoc.rs b/lib/src/edhoc.rs
index 8c12a296..e8a1c5e8 100644
--- a/lib/src/edhoc.rs
+++ b/lib/src/edhoc.rs
@@ -6,7 +6,7 @@ use edhoc_ead::*;
 
 pub fn edhoc_exporter(
     state: State,
-    label: U8,
+    label: u8,
     context: &BytesMaxContextBuffer,
     context_len: usize,
     length: usize,
@@ -178,8 +178,8 @@ pub fn r_prepare_message_2(
     r: &BytesP256ElemLen, // R's static private DH key
     y: BytesP256ElemLen,
     g_y: BytesP256ElemLen,
-    c_r: U8,
-) -> Result<(State, BufferMessage2, U8), EDHOCError> {
+    c_r: u8,
+) -> Result<(State, BufferMessage2), EDHOCError> {
     let State(
         mut current_state,
         mut _y,
@@ -247,7 +247,7 @@ pub fn r_prepare_message_2(
     }
 
     match error {
-        EDHOCError::Success => Ok((state, message_2, c_r)),
+        EDHOCError::Success => Ok((state, message_2)),
         _ => Err(error),
     }
 }
@@ -381,7 +381,7 @@ pub fn i_prepare_message_1(
     mut state: State,
     x: BytesP256ElemLen,
     g_x: BytesP256ElemLen,
-    c_i: U8,
+    c_i: u8,
 ) -> Result<(State, BufferMessage1), EDHOCError> {
     let State(
         mut current_state,
@@ -456,7 +456,7 @@ pub fn i_process_message_2(
     cred_r_len: usize,
     g_r: &BytesP256ElemLen, // R's static public DH key
     i: &BytesP256ElemLen,   // I's static private DH key
-) -> Result<(State, U8, U8), EDHOCError> {
+) -> Result<(State, u8, u8), EDHOCError> {
     let State(
         mut current_state,
         x,
@@ -649,7 +649,7 @@ pub fn i_prepare_message_3(
 pub fn construct_state(
     state: EDHOCState,
     x_or_y: BytesP256ElemLen,
-    c_i: U8,
+    c_i: u8,
     gx_or_gy: BytesP256ElemLen,
     prk_3e2m: BytesHashLen,
     prk_4e3m: BytesHashLen,
@@ -674,37 +674,37 @@ pub fn construct_state(
 
 /// Check for: an unsigned integer encoded as a single byte
 #[inline(always)]
-fn is_cbor_uint_1byte(byte: U8) -> bool {
+fn is_cbor_uint_1byte(byte: u8) -> bool {
     return byte >= CBOR_UINT_1BYTE_START && byte <= CBOR_UINT_1BYTE_END;
 }
 
 /// Check for: an unsigned integer encoded as two bytes
 #[inline(always)]
-fn is_cbor_uint_2bytes(byte: U8) -> bool {
+fn is_cbor_uint_2bytes(byte: u8) -> bool {
     return byte == CBOR_UINT_1BYTE;
 }
 
 /// Check for: a negative integer encoded as a single byte
 #[inline(always)]
-fn is_cbor_neg_int_1byte(byte: U8) -> bool {
+fn is_cbor_neg_int_1byte(byte: u8) -> bool {
     return byte >= CBOR_NEG_INT_1BYTE_START && byte <= CBOR_NEG_INT_1BYTE_END;
 }
 
 /// Check for: a bstr denoted by a single byte which encodes both type and content length
 #[inline(always)]
-fn is_cbor_bstr_1byte_prefix(byte: U8) -> bool {
+fn is_cbor_bstr_1byte_prefix(byte: u8) -> bool {
     return byte >= CBOR_MAJOR_BYTE_STRING && byte <= CBOR_MAJOR_BYTE_STRING_MAX;
 }
 
 /// Check for: a bstr denoted by two bytes, onr for type the other for content length
 #[inline(always)]
-fn is_cbor_bstr_2bytes_prefix(byte: U8) -> bool {
+fn is_cbor_bstr_2bytes_prefix(byte: u8) -> bool {
     return byte == CBOR_BYTE_STRING;
 }
 
 /// Check for: an array denoted by a single byte which encodes both type and content length
 #[inline(always)]
-fn is_cbor_array_1byte_prefix(byte: U8) -> bool {
+fn is_cbor_array_1byte_prefix(byte: u8) -> bool {
     return byte >= CBOR_MAJOR_ARRAY && byte <= CBOR_MAJOR_ARRAY_MAX;
 }
 
@@ -819,17 +819,17 @@ fn parse_message_1(
     rcvd_message_1: &BufferMessage1,
 ) -> Result<
     (
-        U8,
+        u8,
         BytesSuites,
         usize,
         BytesP256ElemLen,
-        U8,
+        u8,
         Option<EADItem>,
     ),
     EDHOCError,
 > {
     let mut error: EDHOCError = EDHOCError::UnknownError;
-    let mut method: U8 = 0xff;
+    let mut method: u8 = 0xff;
     let mut g_x: BytesP256ElemLen = [0x00; P256_ELEM_LEN];
     let mut suites_i: BytesSuites = [0u8; SUITES_LEN];
     let mut suites_i_len: usize = 0;
@@ -910,11 +910,11 @@ fn encode_ead_item(ead_1: &EADItem) -> EdhocMessageBuffer {
 }
 
 fn encode_message_1(
-    method: U8,
+    method: u8,
     suites: &BytesSuites,
     suites_len: usize,
     g_x: &BytesP256ElemLen,
-    c_i: U8,
+    c_i: u8,
     ead_1: &Option<EADItem>,
 ) -> BufferMessage1 {
     let mut output = BufferMessage1::new();
@@ -936,8 +936,7 @@ fn encode_message_1(
         // several suites, will be encoded as an array
         output.content[1] = CBOR_MAJOR_ARRAY + (suites_len as u8);
         raw_suites_len += 1;
-        let mut i: usize = 0;
-        while i < suites_len {
+        for i in 0..suites_len {
             if suites[i] <= CBOR_UINT_1BYTE {
                 output.content[1 + raw_suites_len] = suites[i];
                 raw_suites_len += 1;
@@ -946,7 +945,6 @@ fn encode_message_1(
                 output.content[2 + raw_suites_len] = suites[i];
                 raw_suites_len += 2;
             }
-            i += 1;
         }
     };
 
@@ -1070,7 +1068,7 @@ fn compute_th_4(
 
 fn edhoc_kdf(
     prk: &BytesHashLen,
-    label: U8,
+    label: u8,
     context: &BytesMaxContextBuffer,
     context_len: usize,
     length: usize,
@@ -1106,7 +1104,7 @@ fn edhoc_kdf(
 
 fn decode_plaintext_3(
     plaintext_3: &BufferPlaintext3,
-) -> Result<(U8, BytesMac3, Option<EADItem>), EDHOCError> {
+) -> Result<(u8, BytesMac3, Option<EADItem>), EDHOCError> {
     let mut ead_3 = None::<EADItem>;
     let mut error = EDHOCError::UnknownError;
     let mut kid: u8 = 0xff;
@@ -1338,11 +1336,11 @@ fn compute_mac_2(
 fn decode_plaintext_2(
     plaintext_2: &BytesMaxBuffer,
     plaintext_2_len: usize,
-) -> Result<(U8, U8, BytesMac2, Option<EADItem>), EDHOCError> {
+) -> Result<(u8, u8, BytesMac2, Option<EADItem>), EDHOCError> {
     let mut error = EDHOCError::UnknownError;
     let mut ead_2 = None::<EADItem>;
-    let mut c_r: U8 = 0xff;
-    let mut id_cred_r: U8 = 0xff;
+    let mut c_r: u8 = 0xff;
+    let mut id_cred_r: u8 = 0xff;
     let mut mac_2: BytesMac2 = [0x00; MAC_LENGTH_2];
 
     // check CBOR sequence types for c_r, id_cred_r, and mac_2
@@ -1386,7 +1384,7 @@ fn decode_plaintext_2(
 }
 
 fn encode_plaintext_2(
-    c_r: U8,
+    c_r: u8,
     id_cred_r: &BytesIdCred,
     mac_2: &BytesMac2,
     ead_2: &Option<EADItem>,
diff --git a/lib/src/lib.rs b/lib/src/lib.rs
index 95f1b5f8..2d98c958 100644
--- a/lib/src/lib.rs
+++ b/lib/src/lib.rs
@@ -1,724 +1,343 @@
 #![cfg_attr(not(test), no_std)]
 
-#[cfg(any(
-    feature = "hacspec-hacspec",
-    feature = "hacspec-cc2538",
-    feature = "hacspec-psa",
-    feature = "hacspec-cryptocell310"
-))]
 pub use {
     edhoc_consts::State as EdhocState, edhoc_consts::*, edhoc_crypto::*,
-    hacspec::generate_connection_identifier, hacspec::HacspecEdhocInitiator as EdhocInitiator,
-    hacspec::HacspecEdhocResponder as EdhocResponder,
-};
-
-#[cfg(any(
-    feature = "rust-psa",
-    feature = "rust-psa-baremetal",
-    feature = "rust-cryptocell310"
-))]
-pub use {
-    edhoc_consts::State as EdhocState, edhoc_consts::*, edhoc_crypto::*,
-    rust::generate_connection_identifier, rust::RustEdhocInitiator as EdhocInitiator,
-    rust::RustEdhocResponder as EdhocResponder,
+    EdhocInitiatorState as EdhocInitiator, EdhocResponderState as EdhocResponder,
 };
 
 #[cfg(any(feature = "ead-none", feature = "ead-zeroconf"))]
 pub use edhoc_ead::*;
 
-#[cfg(any(
-    feature = "rust-psa",
-    feature = "rust-psa-baremetal",
-    feature = "rust-cryptocell310"
-))]
 mod edhoc;
-
-#[cfg(any(
-    feature = "rust-psa",
-    feature = "rust-psa-baremetal",
-    feature = "rust-cryptocell310"
-))]
 use edhoc::*;
 
-#[cfg(any(
-    feature = "rust-psa",
-    feature = "rust-psa-baremetal",
-    feature = "rust-cryptocell310"
-))]
 mod c_wrapper;
-
-#[cfg(any(
-    feature = "rust-psa",
-    feature = "rust-psa-baremetal",
-    feature = "rust-cryptocell310"
-))]
 use c_wrapper::*;
 
-#[cfg(any(
-    feature = "hacspec-hacspec",
-    feature = "hacspec-cc2538",
-    feature = "hacspec-psa",
-    feature = "hacspec-cryptocell310"
-))]
-mod hacspec {
-    use edhoc_consts::*;
-    use edhoc_hacspec::*;
-    use hacspec_lib::*;
-
-    #[repr(C)]
-    #[derive(Default, Copy, Clone, Debug)]
-    pub struct HacspecEdhocInitiator<'a> {
-        state: State,       // opaque state
-        i: &'a str,         // private authentication key of I
-        g_r: &'a str,       // public authentication key of R
-        id_cred_i: &'a str, // identifier of I's credential
-        cred_i: &'a str,    // I's full credential
-        id_cred_r: &'a str, // identifier of R's credential
-        cred_r: &'a str,    // R's full credential
-    }
-
-    #[repr(C)]
-    #[derive(Default, Copy, Clone, Debug)]
-    pub struct HacspecEdhocResponder<'a> {
-        state: State,       // opaque state
-        r: &'a str,         // private authentication key of R
-        g_i: &'a str,       // public authentication key of I
-        id_cred_i: &'a str, // identifier of I's credential
-        cred_i: &'a str,    // I's full credential
-        id_cred_r: &'a str, // identifier of R's credential
-        cred_r: &'a str,    // R's full credential
-    }
-
-    impl<'a> HacspecEdhocResponder<'a> {
-        pub fn new(
-            state: State,
-            r: &'a str,
-            g_i: &'a str,
-            id_cred_i: &'a str,
-            cred_i: &'a str,
-            id_cred_r: &'a str,
-            cred_r: &'a str,
-        ) -> HacspecEdhocResponder<'a> {
-            assert!(r.len() == P256_ELEM_LEN * 2);
-            assert!(g_i.len() == P256_ELEM_LEN * 2);
-            assert!(id_cred_i.len() == ID_CRED_LEN * 2);
-            assert!(id_cred_r.len() == ID_CRED_LEN * 2);
-
-            HacspecEdhocResponder {
-                state: state,
-                r: r,
-                g_i: g_i,
-                id_cred_i: id_cred_i,
-                cred_i: cred_i,
-                id_cred_r: id_cred_r,
-                cred_r: cred_r,
-            }
-        }
-
-        pub fn process_message_1(
-            self: &mut HacspecEdhocResponder<'a>,
-            message_1: &EdhocMessageBuffer,
-        ) -> Result<(), EDHOCError> {
-            match r_process_message_1(self.state, &BufferMessage1::from_public_buffer(message_1)) {
-                Ok(state) => {
-                    self.state = state;
-                    Ok(())
-                }
-                Err(error) => Err(error),
-            }
-        }
-
-        pub fn prepare_message_2(
-            self: &mut HacspecEdhocResponder<'a>,
-        ) -> Result<(EdhocMessageBuffer, u8), EDHOCError> {
-            // init hacspec structs for id_cred_r and cred_r
-            let id_cred_r = BytesIdCred::from_hex(self.id_cred_r);
-            let mut cred_r = BytesMaxBuffer::new();
-            cred_r = cred_r.update(0, &ByteSeq::from_hex(self.cred_r));
-            let cred_r_len = self.cred_r.len() / 2;
-
-            // init hacspec structs for R's public static DH key
-            let r = BytesP256ElemLen::from_hex(self.r);
-
-            // Generate ephemeral key pair
-            let (y, g_y) = edhoc_crypto::p256_generate_key_pair();
-            let c_r = generate_connection_identifier_cbor();
-
-            match r_prepare_message_2(self.state, &id_cred_r, &cred_r, cred_r_len, &r, y, g_y, c_r)
-            {
-                Ok((state, message_2, c_r)) => {
-                    self.state = state;
-                    Ok((message_2.to_public_buffer(), c_r.declassify()))
-                }
-                Err(error) => Err(error),
-            }
-        }
+use edhoc_consts::*;
+use hex::FromHex;
+
+#[derive(Default, Copy, Clone, Debug)]
+pub struct EdhocInitiatorState<'a> {
+    state: State,       // opaque state
+    i: &'a str,         // private authentication key of I
+    g_r: &'a str,       // public authentication key of R
+    id_cred_i: &'a str, // identifier of I's credential
+    cred_i: &'a str,    // I's full credential
+    id_cred_r: &'a str, // identifier of R's credential
+    cred_r: &'a str,    // R's full credential
+}
 
-        pub fn process_message_3(
-            self: &mut HacspecEdhocResponder<'a>,
-            message_3: &EdhocMessageBuffer,
-        ) -> Result<[u8; SHA256_DIGEST_LEN], EDHOCError> {
-            // init hacspec structs for id_cred_r and cred_r
-            let id_cred_i = BytesIdCred::from_hex(self.id_cred_i);
-            let mut cred_i = BytesMaxBuffer::new();
-            cred_i = cred_i.update(0, &ByteSeq::from_hex(self.cred_i));
-            let cred_i_len = self.cred_i.len() / 2;
-
-            // init hacspec structs for R's public static DH key
-            let g_i = BytesP256ElemLen::from_hex(self.g_i);
-
-            match r_process_message_3(
-                self.state,
-                &BufferMessage3::from_public_buffer(&message_3),
-                &id_cred_i,
-                &cred_i,
-                cred_i_len,
-                &g_i,
-            ) {
-                Ok((state, prk_out)) => {
-                    self.state = state;
-                    Ok(prk_out.to_public_array())
-                }
-                Err(error) => Err(error),
-            }
-        }
+#[derive(Default, Copy, Clone, Debug)]
+pub struct EdhocResponderState<'a> {
+    state: State,       // opaque state
+    r: &'a str,         // private authentication key of R
+    g_i: &'a str,       // public authentication key of I
+    id_cred_i: &'a str, // identifier of I's credential
+    cred_i: &'a str,    // I's full credential
+    id_cred_r: &'a str, // identifier of R's credential
+    cred_r: &'a str,    // R's full credential
+}
 
-        pub fn edhoc_exporter(
-            self: &mut HacspecEdhocResponder<'a>,
-            label: u8,
-            context: &[u8],
-            length: usize,
-        ) -> Result<[u8; MAX_BUFFER_LEN], EDHOCError> {
-            // init hacspec struct for context
-            let mut context_hacspec = BytesMaxContextBuffer::new();
-            context_hacspec = context_hacspec.update(0, &ByteSeq::from_public_slice(context));
-
-            match edhoc_exporter(
-                self.state,
-                U8(label),
-                &context_hacspec,
-                context.len(),
-                length,
-            ) {
-                Ok((state, output)) => {
-                    self.state = state;
-                    Ok(output.to_public_array())
-                }
-                Err(error) => Err(error),
-            }
+impl<'a> EdhocResponderState<'a> {
+    pub fn to_c(&self) -> EdhocResponderC {
+        EdhocResponderC {
+            state: self.state,
+            r: self.r.as_ptr(),
+            r_len: self.r.len(),
+            g_i: self.g_i.as_ptr(),
+            g_i_len: self.g_i.len(),
+            id_cred_i: self.id_cred_i.as_ptr(),
+            id_cred_i_len: self.id_cred_i.len(),
+            cred_i: self.cred_i.as_ptr(),
+            cred_i_len: self.cred_i.len(),
+            id_cred_r: self.id_cred_r.as_ptr(),
+            id_cred_r_len: self.id_cred_r.len(),
+            cred_r: self.cred_r.as_ptr(),
+            cred_r_len: self.cred_r.len(),
         }
+    }
 
-        pub fn edhoc_key_update(
-            self: &mut HacspecEdhocResponder<'a>,
-            context: &[u8],
-        ) -> Result<[u8; SHA256_DIGEST_LEN], EDHOCError> {
-            // init hacspec struct for context
-            let mut context_hacspec = BytesMaxContextBuffer::new();
-            context_hacspec = context_hacspec.update(0, &ByteSeq::from_public_slice(context));
-
-            match edhoc_key_update(self.state, &context_hacspec, context.len()) {
-                Ok((state, prk_out_new)) => {
-                    self.state = state;
-                    Ok(prk_out_new.to_public_array())
-                }
-                Err(error) => Err(error),
-            }
+    pub fn new(
+        state: State,
+        r: &'a str,
+        g_i: &'a str,
+        id_cred_i: &'a str,
+        cred_i: &'a str,
+        id_cred_r: &'a str,
+        cred_r: &'a str,
+    ) -> EdhocResponderState<'a> {
+        assert!(r.len() == P256_ELEM_LEN * 2);
+        assert!(g_i.len() == P256_ELEM_LEN * 2);
+        assert!(id_cred_i.len() == ID_CRED_LEN * 2);
+        assert!(id_cred_r.len() == ID_CRED_LEN * 2);
+
+        EdhocResponderState {
+            state: state,
+            r: r,
+            g_i: g_i,
+            id_cred_i: id_cred_i,
+            cred_i: cred_i,
+            id_cred_r: id_cred_r,
+            cred_r: cred_r,
         }
     }
 
-    impl<'a> HacspecEdhocInitiator<'a> {
-        pub fn new(
-            state: State,
-            i: &'a str,
-            g_r: &'a str,
-            id_cred_i: &'a str,
-            cred_i: &'a str,
-            id_cred_r: &'a str,
-            cred_r: &'a str,
-        ) -> HacspecEdhocInitiator<'a> {
-            assert!(i.len() == P256_ELEM_LEN * 2);
-            assert!(g_r.len() == P256_ELEM_LEN * 2);
-            assert!(id_cred_i.len() == ID_CRED_LEN * 2);
-            assert!(id_cred_r.len() == ID_CRED_LEN * 2);
-
-            HacspecEdhocInitiator {
-                state: state,
-                i: i,
-                g_r: g_r,
-                id_cred_i: id_cred_i,
-                cred_i: cred_i,
-                id_cred_r: id_cred_r,
-                cred_r: cred_r,
-            }
-        }
+    pub fn process_message_1(
+        self: &mut EdhocResponderState<'a>,
+        message_1: &BufferMessage1,
+    ) -> Result<(), EDHOCError> {
+        let state = r_process_message_1(self.state, message_1)?;
+        self.state = state;
 
-        pub fn prepare_message_1(
-            self: &mut HacspecEdhocInitiator<'a>,
-        ) -> Result<EdhocMessageBuffer, EDHOCError> {
-            // Generate ephemeral key pair
-            let (x, g_x) = edhoc_crypto::p256_generate_key_pair();
-            let c_i = generate_connection_identifier_cbor();
-
-            match edhoc_hacspec::i_prepare_message_1(self.state, x, g_x, c_i) {
-                Ok((state, message_1)) => {
-                    self.state = state;
-                    Ok(message_1.to_public_buffer())
-                }
-                Err(error) => Err(error),
-            }
-        }
+        Ok(())
+    }
 
-        pub fn process_message_2(
-            self: &mut HacspecEdhocInitiator<'a>,
-            message_2: &EdhocMessageBuffer,
-        ) -> Result<u8, EDHOCError> {
-            // init hacspec struct for I, I's private static DH key
-            let i = BytesP256ElemLen::from_hex(self.i);
-
-            // init hacspec structs for id_cred_r and cred_r
-            let id_cred_r = BytesIdCred::from_hex(self.id_cred_r);
-            let mut cred_r = BytesMaxBuffer::new();
-            cred_r = cred_r.update(0, &ByteSeq::from_hex(self.cred_r));
-            let cred_r_len = self.cred_r.len() / 2;
-
-            // init hacspec structs for R's public static DH key
-            let g_r = BytesP256ElemLen::from_hex(self.g_r);
-
-            // init hacspec struct for message_2
-            let message_2_hacspec = BufferMessage2::from_public_buffer(&message_2);
-
-            match edhoc_hacspec::i_process_message_2(
-                self.state,
-                &message_2_hacspec,
-                &id_cred_r,
-                &cred_r,
-                cred_r_len,
-                &g_r,
-                &i,
-            ) {
-                Ok((state, c_r, _id_cred_r)) => {
-                    self.state = state;
-                    Ok(c_r.declassify())
-                }
-                Err(error) => Err(error),
+    pub fn prepare_message_2(
+        self: &mut EdhocResponderState<'a>,
+        c_r: u8,
+    ) -> Result<BufferMessage2, EDHOCError> {
+        let mut cred_r: BytesMaxBuffer = [0x00; MAX_BUFFER_LEN];
+        hex::decode_to_slice(self.cred_r, &mut cred_r[..self.cred_r.len() / 2])
+            .expect("Decoding failed");
+        let (y, g_y) = edhoc_crypto::p256_generate_key_pair();
+
+        match r_prepare_message_2(
+            self.state,
+            &<BytesIdCred>::from_hex(self.id_cred_r).expect("Decoding failed"),
+            &cred_r,
+            self.cred_r.len() / 2,
+            &<BytesP256ElemLen>::from_hex(self.r).expect("Decoding failed"),
+            y,
+            g_y,
+            c_r,
+        ) {
+            Ok((state, message_2)) => {
+                self.state = state;
+                Ok(message_2)
             }
+            Err(error) => Err(error),
         }
+    }
 
-        pub fn prepare_message_3(
-            self: &mut HacspecEdhocInitiator<'a>,
-        ) -> Result<(EdhocMessageBuffer, [u8; SHA256_DIGEST_LEN]), EDHOCError> {
-            // init hacspec structs for id_cred_i and cred_i
-            let id_cred_i = BytesIdCred::from_hex(self.id_cred_i);
-            let mut cred_i = BytesMaxBuffer::new();
-            cred_i = cred_i.update(0, &ByteSeq::from_hex(self.cred_i));
-            let cred_i_len = self.cred_i.len() / 2;
-
-            match i_prepare_message_3(self.state, &id_cred_i, &cred_i, cred_i_len) {
-                Ok((state, message_3, prk_out)) => {
-                    self.state = state;
-                    Ok((message_3.to_public_buffer(), prk_out.to_public_array()))
-                }
-                Err(error) => Err(error),
+    pub fn process_message_3(
+        self: &mut EdhocResponderState<'a>,
+        message_3: &BufferMessage3,
+    ) -> Result<[u8; SHA256_DIGEST_LEN], EDHOCError> {
+        let mut cred_i: BytesMaxBuffer = [0x00; MAX_BUFFER_LEN];
+        hex::decode_to_slice(self.cred_i, &mut cred_i[..self.cred_i.len() / 2])
+            .expect("Decoding failed");
+
+        match r_process_message_3(
+            self.state,
+            message_3,
+            &<BytesIdCred>::from_hex(self.id_cred_i).expect("Decoding failed"),
+            &cred_i,
+            self.cred_i.len() / 2,
+            &<BytesP256ElemLen>::from_hex(self.g_i).expect("Decoding failed"),
+        ) {
+            Ok((state, prk_out)) => {
+                self.state = state;
+                Ok(prk_out)
             }
+            Err(error) => Err(error),
         }
+    }
 
-        pub fn edhoc_exporter(
-            self: &mut HacspecEdhocInitiator<'a>,
-            label: u8,
-            context: &[u8],
-            length: usize,
-        ) -> Result<[u8; MAX_BUFFER_LEN], EDHOCError> {
-            // init hacspec struct for context
-            let mut context_hacspec = BytesMaxContextBuffer::new();
-            context_hacspec = context_hacspec.update(0, &ByteSeq::from_public_slice(context));
-
-            match edhoc_exporter(
-                self.state,
-                U8(label),
-                &context_hacspec,
-                context.len(),
-                length,
-            ) {
-                Ok((state, output)) => {
-                    self.state = state;
-                    Ok(output.to_public_array())
-                }
-                Err(error) => Err(error),
+    pub fn edhoc_exporter(
+        self: &mut EdhocResponderState<'a>,
+        label: u8,
+        context: &[u8],
+        length: usize,
+    ) -> Result<[u8; MAX_BUFFER_LEN], EDHOCError> {
+        let mut context_buf: BytesMaxContextBuffer = [0x00u8; MAX_KDF_CONTEXT_LEN];
+        context_buf[..context.len()].copy_from_slice(context);
+
+        match edhoc_exporter(self.state, label, &context_buf, context.len(), length) {
+            Ok((state, output)) => {
+                self.state = state;
+                Ok(output)
             }
+            Err(error) => Err(error),
         }
+    }
 
-        pub fn edhoc_key_update(
-            self: &mut HacspecEdhocInitiator<'a>,
-            context: &[u8],
-        ) -> Result<[u8; SHA256_DIGEST_LEN], EDHOCError> {
-            // init hacspec struct for context
-            let mut context_hacspec = BytesMaxContextBuffer::new();
-            context_hacspec = context_hacspec.update(0, &ByteSeq::from_public_slice(context));
-
-            match edhoc_key_update(self.state, &context_hacspec, context.len()) {
-                Ok((state, prk_out_new)) => {
-                    self.state = state;
-                    Ok(prk_out_new.to_public_array())
-                }
-                Err(error) => Err(error),
+    pub fn edhoc_key_update(
+        self: &mut EdhocResponderState<'a>,
+        context: &[u8],
+    ) -> Result<[u8; SHA256_DIGEST_LEN], EDHOCError> {
+        let mut context_buf = [0x00u8; MAX_KDF_CONTEXT_LEN];
+        context_buf[..context.len()].copy_from_slice(context);
+
+        match edhoc_key_update(self.state, &context_buf, context.len()) {
+            Ok((state, prk_out_new)) => {
+                self.state = state;
+                Ok(prk_out_new)
             }
+            Err(error) => Err(error),
         }
     }
+}
 
-    pub fn generate_connection_identifier_cbor() -> U8 {
-        let c_i = generate_connection_identifier();
-        if c_i >= 0 && c_i <= 23 {
-            return U8(c_i as u8); // verbatim encoding of single byte integer
-        } else if c_i < 0 && c_i >= -24 {
-            // negative single byte integer encoding
-            return U8(CBOR_NEG_INT_1BYTE_START - 1 + (c_i.abs() as u8));
-        } else {
-            return U8(0);
+impl<'a> EdhocInitiatorState<'a> {
+    pub fn to_c(&self) -> EdhocInitiatorC {
+        EdhocInitiatorC {
+            state: self.state,
+            i: self.i.as_ptr(),
+            i_len: self.i.len(),
+            g_r: self.g_r.as_ptr(),
+            g_r_len: self.g_r.len(),
+            id_cred_i: self.id_cred_i.as_ptr(),
+            id_cred_i_len: self.id_cred_i.len(),
+            cred_i: self.cred_i.as_ptr(),
+            cred_i_len: self.cred_i.len(),
+            id_cred_r: self.id_cred_r.as_ptr(),
+            id_cred_r_len: self.id_cred_r.len(),
+            cred_r: self.cred_r.as_ptr(),
+            cred_r_len: self.cred_r.len(),
         }
     }
 
-    /// generates an identifier that can be serialized as a single CBOR integer, i.e. -24 <= x <= 23
-    pub fn generate_connection_identifier() -> i8 {
-        let mut conn_id = edhoc_crypto::get_random_byte().declassify() as i8;
-        while conn_id < -24 || conn_id > 23 {
-            conn_id = edhoc_crypto::get_random_byte().declassify() as i8;
+    pub fn new(
+        state: State,
+        i: &'a str,
+        g_r: &'a str,
+        id_cred_i: &'a str,
+        cred_i: &'a str,
+        id_cred_r: &'a str,
+        cred_r: &'a str,
+    ) -> EdhocInitiatorState<'a> {
+        assert!(i.len() == P256_ELEM_LEN * 2);
+        assert!(g_r.len() == P256_ELEM_LEN * 2);
+        assert!(id_cred_i.len() == ID_CRED_LEN * 2);
+        assert!(id_cred_r.len() == ID_CRED_LEN * 2);
+
+        EdhocInitiatorState {
+            state: state,
+            i: i,
+            g_r: g_r,
+            id_cred_i: id_cred_i,
+            cred_i: cred_i,
+            id_cred_r: id_cred_r,
+            cred_r: cred_r,
         }
-        conn_id
     }
-}
 
-#[cfg(any(
-    feature = "rust-psa",
-    feature = "rust-psa-baremetal",
-    feature = "rust-cryptocell310"
-))]
-mod rust {
-    use super::*;
-    use edhoc_consts::*;
-    use hex::FromHex;
-
-    #[derive(Default, Copy, Clone, Debug)]
-    pub struct RustEdhocInitiator<'a> {
-        state: State,       // opaque state
-        i: &'a str,         // private authentication key of I
-        g_r: &'a str,       // public authentication key of R
-        id_cred_i: &'a str, // identifier of I's credential
-        cred_i: &'a str,    // I's full credential
-        id_cred_r: &'a str, // identifier of R's credential
-        cred_r: &'a str,    // R's full credential
-    }
-
-    #[derive(Default, Copy, Clone, Debug)]
-    pub struct RustEdhocResponder<'a> {
-        state: State,       // opaque state
-        r: &'a str,         // private authentication key of R
-        g_i: &'a str,       // public authentication key of I
-        id_cred_i: &'a str, // identifier of I's credential
-        cred_i: &'a str,    // I's full credential
-        id_cred_r: &'a str, // identifier of R's credential
-        cred_r: &'a str,    // R's full credential
-    }
-
-    impl<'a> RustEdhocResponder<'a> {
-        pub fn to_c(&self) -> EdhocResponderC {
-            EdhocResponderC {
-                state: self.state,
-                r: self.r.as_ptr(),
-                r_len: self.r.len(),
-                g_i: self.g_i.as_ptr(),
-                g_i_len: self.g_i.len(),
-                id_cred_i: self.id_cred_i.as_ptr(),
-                id_cred_i_len: self.id_cred_i.len(),
-                cred_i: self.cred_i.as_ptr(),
-                cred_i_len: self.cred_i.len(),
-                id_cred_r: self.id_cred_r.as_ptr(),
-                id_cred_r_len: self.id_cred_r.len(),
-                cred_r: self.cred_r.as_ptr(),
-                cred_r_len: self.cred_r.len(),
-            }
-        }
+    pub fn prepare_message_1(
+        self: &mut EdhocInitiatorState<'a>,
+        c_i: u8,
+    ) -> Result<BufferMessage1, EDHOCError> {
+        let (x, g_x) = edhoc_crypto::p256_generate_key_pair();
 
-        pub fn new(
-            state: State,
-            r: &'a str,
-            g_i: &'a str,
-            id_cred_i: &'a str,
-            cred_i: &'a str,
-            id_cred_r: &'a str,
-            cred_r: &'a str,
-        ) -> RustEdhocResponder<'a> {
-            assert!(r.len() == P256_ELEM_LEN * 2);
-            assert!(g_i.len() == P256_ELEM_LEN * 2);
-            assert!(id_cred_i.len() == ID_CRED_LEN * 2);
-            assert!(id_cred_r.len() == ID_CRED_LEN * 2);
-
-            RustEdhocResponder {
-                state: state,
-                r: r,
-                g_i: g_i,
-                id_cred_i: id_cred_i,
-                cred_i: cred_i,
-                id_cred_r: id_cred_r,
-                cred_r: cred_r,
-            }
-        }
-
-        pub fn process_message_1(
-            self: &mut RustEdhocResponder<'a>,
-            message_1: &BufferMessage1,
-        ) -> Result<(), EDHOCError> {
-            let state = r_process_message_1(self.state, message_1)?;
-            self.state = state;
-
-            Ok(())
-        }
-
-        pub fn prepare_message_2(
-            self: &mut RustEdhocResponder<'a>,
-        ) -> Result<(BufferMessage2, u8), EDHOCError> {
-            let mut cred_r: BytesMaxBuffer = [0x00; MAX_BUFFER_LEN];
-            hex::decode_to_slice(self.cred_r, &mut cred_r[..self.cred_r.len() / 2])
-                .expect("Decoding failed");
-            let (y, g_y) = edhoc_crypto::p256_generate_key_pair();
-            let c_r = generate_connection_identifier_cbor();
-
-            match r_prepare_message_2(
-                self.state,
-                &<BytesIdCred>::from_hex(self.id_cred_r).expect("Decoding failed"),
-                &cred_r,
-                self.cred_r.len() / 2,
-                &<BytesP256ElemLen>::from_hex(self.r).expect("Decoding failed"),
-                y,
-                g_y,
-                c_r,
-            ) {
-                Ok((state, message_2, c_r)) => {
-                    self.state = state;
-                    Ok((message_2, c_r))
-                }
-                Err(error) => Err(error),
-            }
-        }
-
-        pub fn process_message_3(
-            self: &mut RustEdhocResponder<'a>,
-            message_3: &BufferMessage3,
-        ) -> Result<[u8; SHA256_DIGEST_LEN], EDHOCError> {
-            let mut cred_i: BytesMaxBuffer = [0x00; MAX_BUFFER_LEN];
-            hex::decode_to_slice(self.cred_i, &mut cred_i[..self.cred_i.len() / 2])
-                .expect("Decoding failed");
-
-            match r_process_message_3(
-                self.state,
-                message_3,
-                &<BytesIdCred>::from_hex(self.id_cred_i).expect("Decoding failed"),
-                &cred_i,
-                self.cred_i.len() / 2,
-                &<BytesP256ElemLen>::from_hex(self.g_i).expect("Decoding failed"),
-            ) {
-                Ok((state, prk_out)) => {
-                    self.state = state;
-                    Ok(prk_out)
-                }
-                Err(error) => Err(error),
-            }
-        }
-
-        pub fn edhoc_exporter(
-            self: &mut RustEdhocResponder<'a>,
-            label: u8,
-            context: &[u8],
-            length: usize,
-        ) -> Result<[u8; MAX_BUFFER_LEN], EDHOCError> {
-            let mut context_buf: BytesMaxContextBuffer = [0x00u8; MAX_KDF_CONTEXT_LEN];
-            context_buf[..context.len()].copy_from_slice(context);
-
-            match edhoc_exporter(self.state, label, &context_buf, context.len(), length) {
-                Ok((state, output)) => {
-                    self.state = state;
-                    Ok(output)
-                }
-                Err(error) => Err(error),
-            }
-        }
-
-        pub fn edhoc_key_update(
-            self: &mut RustEdhocResponder<'a>,
-            context: &[u8],
-        ) -> Result<[u8; SHA256_DIGEST_LEN], EDHOCError> {
-            let mut context_buf = [0x00u8; MAX_KDF_CONTEXT_LEN];
-            context_buf[..context.len()].copy_from_slice(context);
-
-            match edhoc_key_update(self.state, &context_buf, context.len()) {
-                Ok((state, prk_out_new)) => {
-                    self.state = state;
-                    Ok(prk_out_new)
-                }
-                Err(error) => Err(error),
+        match i_prepare_message_1(self.state, x, g_x, c_i) {
+            Ok((state, message_1)) => {
+                self.state = state;
+                Ok(message_1)
             }
+            Err(error) => Err(error),
         }
     }
 
-    impl<'a> RustEdhocInitiator<'a> {
-        pub fn to_c(&self) -> EdhocInitiatorC {
-            EdhocInitiatorC {
-                state: self.state,
-                i: self.i.as_ptr(),
-                i_len: self.i.len(),
-                g_r: self.g_r.as_ptr(),
-                g_r_len: self.g_r.len(),
-                id_cred_i: self.id_cred_i.as_ptr(),
-                id_cred_i_len: self.id_cred_i.len(),
-                cred_i: self.cred_i.as_ptr(),
-                cred_i_len: self.cred_i.len(),
-                id_cred_r: self.id_cred_r.as_ptr(),
-                id_cred_r_len: self.id_cred_r.len(),
-                cred_r: self.cred_r.as_ptr(),
-                cred_r_len: self.cred_r.len(),
-            }
-        }
-
-        pub fn new(
-            state: State,
-            i: &'a str,
-            g_r: &'a str,
-            id_cred_i: &'a str,
-            cred_i: &'a str,
-            id_cred_r: &'a str,
-            cred_r: &'a str,
-        ) -> RustEdhocInitiator<'a> {
-            assert!(i.len() == P256_ELEM_LEN * 2);
-            assert!(g_r.len() == P256_ELEM_LEN * 2);
-            assert!(id_cred_i.len() == ID_CRED_LEN * 2);
-            assert!(id_cred_r.len() == ID_CRED_LEN * 2);
-
-            RustEdhocInitiator {
-                state: state,
-                i: i,
-                g_r: g_r,
-                id_cred_i: id_cred_i,
-                cred_i: cred_i,
-                id_cred_r: id_cred_r,
-                cred_r: cred_r,
-            }
-        }
-
-        pub fn prepare_message_1(
-            self: &mut RustEdhocInitiator<'a>,
-        ) -> Result<BufferMessage1, EDHOCError> {
-            let (x, g_x) = edhoc_crypto::p256_generate_key_pair();
-            let c_i = generate_connection_identifier_cbor();
-
-            match i_prepare_message_1(self.state, x, g_x, c_i) {
-                Ok((state, message_1)) => {
-                    self.state = state;
-                    Ok(message_1)
-                }
-                Err(error) => Err(error),
-            }
-        }
-
-        pub fn process_message_2(
-            self: &mut RustEdhocInitiator<'a>,
-            message_2: &BufferMessage2,
-        ) -> Result<u8, EDHOCError> {
-            let mut cred_r: BytesMaxBuffer = [0x00u8; MAX_BUFFER_LEN];
-            hex::decode_to_slice(self.cred_r, &mut cred_r[..self.cred_r.len() / 2])
-                .expect("Decoding failed");
-
-            match i_process_message_2(
-                self.state,
-                message_2,
-                &<BytesIdCred>::from_hex(self.id_cred_r).expect("Decoding failed"),
-                &cred_r,
-                self.cred_r.len() / 2,
-                &<BytesP256ElemLen>::from_hex(self.g_r).expect("Decoding failed"),
-                &<BytesP256ElemLen>::from_hex(self.i).expect("Decoding failed"),
-            ) {
-                Ok((state, c_r, _kid)) => {
-                    self.state = state;
-                    Ok(c_r)
-                }
-                Err(error) => Err(error),
+    pub fn process_message_2(
+        self: &mut EdhocInitiatorState<'a>,
+        message_2: &BufferMessage2,
+    ) -> Result<u8, EDHOCError> {
+        let mut cred_r: BytesMaxBuffer = [0x00u8; MAX_BUFFER_LEN];
+        hex::decode_to_slice(self.cred_r, &mut cred_r[..self.cred_r.len() / 2])
+            .expect("Decoding failed");
+
+        match i_process_message_2(
+            self.state,
+            message_2,
+            &<BytesIdCred>::from_hex(self.id_cred_r).expect("Decoding failed"),
+            &cred_r,
+            self.cred_r.len() / 2,
+            &<BytesP256ElemLen>::from_hex(self.g_r).expect("Decoding failed"),
+            &<BytesP256ElemLen>::from_hex(self.i).expect("Decoding failed"),
+        ) {
+            Ok((state, c_r, _kid)) => {
+                self.state = state;
+                Ok(c_r)
             }
+            Err(error) => Err(error),
         }
+    }
 
-        pub fn prepare_message_3(
-            self: &mut RustEdhocInitiator<'a>,
-        ) -> Result<(BufferMessage3, [u8; SHA256_DIGEST_LEN]), EDHOCError> {
-            let mut cred_i: BytesMaxBuffer = [0x00u8; MAX_BUFFER_LEN];
-            hex::decode_to_slice(self.cred_i, &mut cred_i[..self.cred_i.len() / 2])
-                .expect("Decoding failed");
-
-            match i_prepare_message_3(
-                self.state,
-                &<BytesIdCred>::from_hex(self.id_cred_i).expect("Decoding failed"),
-                &cred_i,
-                self.cred_i.len() / 2,
-            ) {
-                Ok((state, message_3, prk_out)) => {
-                    self.state = state;
-                    Ok((message_3, prk_out))
-                }
-                Err(error) => Err(error),
+    pub fn prepare_message_3(
+        self: &mut EdhocInitiatorState<'a>,
+    ) -> Result<(BufferMessage3, [u8; SHA256_DIGEST_LEN]), EDHOCError> {
+        let mut cred_i: BytesMaxBuffer = [0x00u8; MAX_BUFFER_LEN];
+        hex::decode_to_slice(self.cred_i, &mut cred_i[..self.cred_i.len() / 2])
+            .expect("Decoding failed");
+
+        match i_prepare_message_3(
+            self.state,
+            &<BytesIdCred>::from_hex(self.id_cred_i).expect("Decoding failed"),
+            &cred_i,
+            self.cred_i.len() / 2,
+        ) {
+            Ok((state, message_3, prk_out)) => {
+                self.state = state;
+                Ok((message_3, prk_out))
             }
+            Err(error) => Err(error),
         }
+    }
 
-        pub fn edhoc_exporter(
-            self: &mut RustEdhocInitiator<'a>,
-            label: u8,
-            context: &[u8],
-            length: usize,
-        ) -> Result<[u8; MAX_BUFFER_LEN], EDHOCError> {
-            let mut context_buf: BytesMaxContextBuffer = [0x00u8; MAX_KDF_CONTEXT_LEN];
-            context_buf[..context.len()].copy_from_slice(context);
-
-            match edhoc_exporter(self.state, label, &context_buf, context.len(), length) {
-                Ok((state, output)) => {
-                    self.state = state;
-                    Ok(output)
-                }
-                Err(error) => Err(error),
+    pub fn edhoc_exporter(
+        self: &mut EdhocInitiatorState<'a>,
+        label: u8,
+        context: &[u8],
+        length: usize,
+    ) -> Result<[u8; MAX_BUFFER_LEN], EDHOCError> {
+        let mut context_buf: BytesMaxContextBuffer = [0x00u8; MAX_KDF_CONTEXT_LEN];
+        context_buf[..context.len()].copy_from_slice(context);
+
+        match edhoc_exporter(self.state, label, &context_buf, context.len(), length) {
+            Ok((state, output)) => {
+                self.state = state;
+                Ok(output)
             }
+            Err(error) => Err(error),
         }
+    }
 
-        pub fn edhoc_key_update(
-            self: &mut RustEdhocInitiator<'a>,
-            context: &[u8],
-        ) -> Result<[u8; SHA256_DIGEST_LEN], EDHOCError> {
-            let mut context_buf = [0x00u8; MAX_KDF_CONTEXT_LEN];
-            context_buf[..context.len()].copy_from_slice(context);
-
-            match edhoc_key_update(self.state, &context_buf, context.len()) {
-                Ok((state, prk_out_new)) => {
-                    self.state = state;
-                    Ok(prk_out_new)
-                }
-                Err(error) => Err(error),
+    pub fn edhoc_key_update(
+        self: &mut EdhocInitiatorState<'a>,
+        context: &[u8],
+    ) -> Result<[u8; SHA256_DIGEST_LEN], EDHOCError> {
+        let mut context_buf = [0x00u8; MAX_KDF_CONTEXT_LEN];
+        context_buf[..context.len()].copy_from_slice(context);
+
+        match edhoc_key_update(self.state, &context_buf, context.len()) {
+            Ok((state, prk_out_new)) => {
+                self.state = state;
+                Ok(prk_out_new)
             }
+            Err(error) => Err(error),
         }
     }
+}
 
-    pub fn generate_connection_identifier_cbor() -> u8 {
-        let c_i = generate_connection_identifier();
-        if c_i >= 0 && c_i <= 23 {
-            return c_i as u8; // verbatim encoding of single byte integer
-        } else if c_i < 0 && c_i >= -24 {
-            // negative single byte integer encoding
-            return CBOR_NEG_INT_1BYTE_START - 1 + (c_i.abs() as u8);
-        } else {
-            return 0;
-        }
+pub fn generate_connection_identifier_cbor() -> u8 {
+    let c_i = generate_connection_identifier();
+    if c_i >= 0 && c_i <= 23 {
+        return c_i as u8; // verbatim encoding of single byte integer
+    } else if c_i < 0 && c_i >= -24 {
+        // negative single byte integer encoding
+        return CBOR_NEG_INT_1BYTE_START - 1 + (c_i.abs() as u8);
+    } else {
+        return 0;
     }
+}
 
-    /// generates an identifier that can be serialized as a single CBOR integer, i.e. -24 <= x <= 23
-    pub fn generate_connection_identifier() -> i8 {
-        let mut conn_id = edhoc_crypto::get_random_byte() as i8;
-        while conn_id < -24 || conn_id > 23 {
-            conn_id = edhoc_crypto::get_random_byte() as i8;
-        }
-        conn_id
+/// generates an identifier that can be serialized as a single CBOR integer, i.e. -24 <= x <= 23
+pub fn generate_connection_identifier() -> i8 {
+    let mut conn_id = edhoc_crypto::get_random_byte() as i8;
+    while conn_id < -24 || conn_id > 23 {
+        conn_id = edhoc_crypto::get_random_byte() as i8;
     }
+    conn_id
 }
 
 #[cfg(test)]
@@ -761,7 +380,8 @@ mod test {
         let mut initiator =
             EdhocInitiator::new(state, I, G_R, ID_CRED_I, CRED_I, ID_CRED_R, CRED_R);
 
-        let message_1 = initiator.prepare_message_1();
+        let c_i = generate_connection_identifier_cbor();
+        let message_1 = initiator.prepare_message_1(c_i);
         assert!(message_1.is_ok());
     }
 
@@ -812,16 +432,18 @@ mod test {
             CRED_R,
         );
 
-        let result = initiator.prepare_message_1(); // to update the state
+        let c_i: u8 = generate_connection_identifier_cbor();
+        let result = initiator.prepare_message_1(c_i); // to update the state
         assert!(result.is_ok());
 
         let error = responder.process_message_1(&result.unwrap());
         assert!(error.is_ok());
 
-        let ret = responder.prepare_message_2();
+        let c_r = generate_connection_identifier_cbor();
+        let ret = responder.prepare_message_2(c_r);
         assert!(ret.is_ok());
 
-        let (message_2, c_r) = ret.unwrap();
+        let message_2 = ret.unwrap();
 
         assert!(c_r != 0xff);
         let _c_r = initiator.process_message_2(&message_2);
@@ -905,7 +527,8 @@ mod test {
             EADResponderProtocolState::Start
         );
 
-        let message_1 = initiator.prepare_message_1().unwrap();
+        let c_i = generate_connection_identifier_cbor();
+        let message_1 = initiator.prepare_message_1(c_i).unwrap();
         assert_eq!(
             ead_initiator_state.protocol_state,
             EADInitiatorProtocolState::WaitEAD2
@@ -917,7 +540,8 @@ mod test {
             EADResponderProtocolState::ProcessedEAD1
         );
 
-        let (message_2, _c_r) = responder.prepare_message_2().unwrap();
+        let c_r = generate_connection_identifier_cbor();
+        let message_2 = responder.prepare_message_2(c_r).unwrap();
         assert_eq!(
             ead_responder_state.protocol_state,
             EADResponderProtocolState::Completed


Test patch:
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index 47f8f9ec..de5e0a08 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -5,6 +5,7 @@ on:
     branches: [main]
     tags: 'v*'
   pull_request:
+  workflow_dispatch:
 
 env:
   CARGO_TERM_COLOR: always
@@ -27,19 +28,15 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        edhoc_lib: [hacspec, rust]
-        crypto: [hacspec, psa]
+        crypto_backend: [crypto-hacspec, crypto-psa]
         ead: [ead-none, ead-zeroconf]
-        exclude:
-          - edhoc_lib: rust
-            crypto: hacspec
 
     steps:
     - name: Checkout repo
       uses: actions/checkout@v3
 
     - name: Run unit tests # note that we only add `--package edhoc-hacspec` when testing the hacspec version of the lib
-      run: cargo test --package edhoc-rs --package edhoc-crypto --package edhoc-ead ${{ matrix.edhoc_lib == 'hacspec' && '--package edhoc-hacspec' || '' }} --no-default-features --features="${{ matrix.edhoc_lib }}-${{ matrix.crypto }}, ${{ matrix.ead }}" --no-fail-fast
+      run: cargo test --package edhoc-rs --package edhoc-crypto --package edhoc-ead --no-default-features --features="${{ matrix.crypto_backend }}, ${{ matrix.ead }}" --no-fail-fast
 
 
   build-edhoc-package:
@@ -49,12 +46,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        edhoc_lib: [hacspec, rust]
-        crypto: [hacspec, psa, psa-baremetal, cryptocell310]
+        crypto_backend: [crypto-hacspec, crypto-psa, crypto-psa-baremetal, crypto-cryptocell310]
         ead: [ead-none, ead-zeroconf]
-        exclude:
-          - edhoc_lib: rust
-            crypto: hacspec
 
     steps:
     - name: Checkout repo
@@ -66,19 +59,20 @@ jobs:
       run: sudo apt-get -y update && sudo apt-get -y install gcc-arm-none-eabi
 
     - name: Build
-      run: cargo build --package edhoc-rs --package edhoc-crypto --package edhoc-ead --no-default-features --features="${{ matrix.edhoc_lib }}-${{ matrix.crypto }}, ${{ matrix.ead }}" --release
+      run: cargo build --package edhoc-rs --package edhoc-crypto --package edhoc-ead --no-default-features --features="${{ matrix.crypto_backend }}, ${{ matrix.ead }}" --release
 
 
-  hacspec-to-fstar:
+  generate-fstar:
     needs: unit-tests
     runs-on: ubuntu-latest
+    if: >-
+      (github.event_name == 'workflow_dispatch') ||
+      (github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags'))
+
     steps:
     - name: Checkout edhoc-rs
       uses: actions/checkout@v3
 
-    - name: Patch edhoc-rs features
-      run: git apply ./hacspec/adjust_features_for_hax.patch
-
     - name: Checkout hacspec-v2
       uses: actions/checkout@v3
       with:
@@ -89,9 +83,9 @@ jobs:
       run: docker build -f .docker/Dockerfile . -t hacspec-v2
       working-directory: hacspec-v2-repo
 
-    - name: Generate fstar
+    - name: Generate fstar code using the main library and the hacspec crypto backend
       run: |
-        docker run --rm -v ${{ github.workspace }}:/edhoc-rs hacspec-v2 bash -c "cd edhoc-rs/hacspec && cargo-hax -C -p edhoc-hacspec -p edhoc-crypto -p edhoc-consts -p edhoc-crypto-hacspec \; into fstar"
+        docker run --rm -v ${{ github.workspace }}:/edhoc-rs hacspec-v2 bash -c "cd edhoc-rs && cargo-hax -C -p edhoc-rs -p edhoc-crypto -p edhoc-ead --no-default-features --features='crypto-hacspec, ead-none' --release \; into -i '-c_wrapper::**' fstar"
         zip -j -r edhoc-rs-fstar.zip $(find . -name *fst)
 
     - name: Upload artifact
@@ -108,8 +102,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        edhoc_lib: [rust]
-        crypto: [psa-baremetal, cryptocell310]
+        crypto_backend: [crypto-psa-baremetal, crypto-cryptocell310]
 
     steps:
     - name: Checkout repo
@@ -121,7 +114,7 @@ jobs:
       run: sudo apt-get -y update && sudo apt-get -y install gcc-arm-none-eabi
 
     - name: Build static library, generate headers, and zip to file
-      run: ./build_for_c.sh "${{ matrix.edhoc_lib }}-${{ matrix.crypto }}"
+      run: ./build_for_c.sh "${{ matrix.crypto_backend }}"
 
     - name: Upload artifact
       uses: actions/upload-artifact@v3
@@ -137,7 +130,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        config: [hacspec-psa, rust-psa]
+        crypto_backend: [crypto-psa]
         ead: [ead-none, ead-zeroconf]
 
     steps:
@@ -152,7 +145,7 @@ jobs:
       run: sudo apt-get -y install qemu-system-arm
 
     - name: Run tests in QEMU
-      run: cd examples/edhoc-rs-no_std && cargo run --target="thumbv7m-none-eabi" --no-default-features --features="${{ matrix.config }}, ${{ matrix.ead }}" --release
+      run: cd examples/edhoc-rs-no_std && cargo run --target="thumbv7m-none-eabi" --no-default-features --features="${{ matrix.crypto_backend }}, ${{ matrix.ead }}" --release
 
 
   build-example-for-cortex-m4:
@@ -162,8 +155,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        edhoc_lib: [hacspec, rust]
-        crypto: [psa, cryptocell310]
+        crypto_backend: [crypto-psa, crypto-cryptocell310]
         ead: [ead-none, ead-zeroconf]
 
     steps:
@@ -176,7 +168,7 @@ jobs:
       run: sudo apt-get -y update && sudo apt-get -y install gcc-arm-none-eabi
 
     - name: Build example
-      run: cd examples/edhoc-rs-no_std && cargo build --target="thumbv7em-none-eabihf" --no-default-features --features="${{ matrix.edhoc_lib }}-${{ matrix.crypto }}, ${{ matrix.ead }}, rtt" --release
+      run: cd examples/edhoc-rs-no_std && cargo build --target="thumbv7em-none-eabihf" --no-default-features --features="${{ matrix.crypto_backend }}, ${{ matrix.ead }}, rtt" --release
 
 
   build-coap-example:
@@ -196,10 +188,11 @@ jobs:
 
   release:
     runs-on: ubuntu-latest
-    needs: [build-edhoc-package, run-example-on-qemu, build-example-for-cortex-m4, build-coap-example, hacspec-to-fstar, build-static-lib-and-headers]
+    needs: [build-edhoc-package, run-example-on-qemu, build-example-for-cortex-m4, build-coap-example, generate-fstar, build-static-lib-and-headers]
     if: >-
       github.event_name == 'push' &&
       startsWith(github.event.ref, 'refs/tags')
+
     steps:
       - name: Checkout repo
         uses: actions/checkout@v3