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

Issue:
Avoid accessing private ephemeral key from application code
The private key was accessed by the application after the split-message and new EAD architecture PRs.


Discussion / hints:

Fix patch:
diff --git a/ead/lakers-ead-authz/src/device.rs b/ead/lakers-ead-authz/src/device.rs
index b59da79c..13877ef2 100644
--- a/ead/lakers-ead-authz/src/device.rs
+++ b/ead/lakers-ead-authz/src/device.rs
@@ -4,7 +4,7 @@ use lakers_shared::{Crypto as CryptoTrait, *};
 #[derive(Debug)]
 pub struct ZeroTouchDevice {
     pub(crate) id_u: EdhocMessageBuffer, // identifier of the device (U), equivalent to ID_CRED_I in EDHOC
-    pub(crate) g_w: BytesP256ElemLen,    // public key of the enrollment server (W)
+    pub g_w: BytesP256ElemLen,           // public key of the enrollment server (W)
     pub(crate) loc_w: EdhocMessageBuffer, // address of the enrollment server (W)
 }
 
@@ -27,11 +27,11 @@ impl ZeroTouchDevice {
     pub fn prepare_ead_1<Crypto: CryptoTrait>(
         &self,
         crypto: &mut Crypto,
-        x: &BytesP256ElemLen,
+        secret: BytesP256ElemLen,
         ss: u8,
     ) -> (EADItem, ZeroTouchDeviceWaitEAD2) {
         // PRK = EDHOC-Extract(salt, IKM)
-        let prk = compute_prk(crypto, x, &self.g_w);
+        let prk = compute_prk_from_secret(crypto, &secret);
 
         // plaintext = (ID_U: bstr)
         let encoded_id_u = encode_id_u(&self.id_u);
@@ -155,7 +155,7 @@ mod test_device {
         );
 
         let (ead_1, _ead_authz) =
-            ead_device.prepare_ead_1(&mut default_crypto(), &X_TV.try_into().unwrap(), SS_TV);
+            ead_device.prepare_ead_1(&mut default_crypto(), G_XW_TV.try_into().unwrap(), SS_TV);
         assert_eq!(ead_1.label, EAD_ZEROCONF_LABEL);
         assert_eq!(ead_1.is_critical, true);
         assert_eq!(ead_1.value.unwrap().content, ead_1_value_tv.content);
diff --git a/ead/lakers-ead-authz/src/lib.rs b/ead/lakers-ead-authz/src/lib.rs
index c58dbca0..b9c6bd85 100644
--- a/ead/lakers-ead-authz/src/lib.rs
+++ b/ead/lakers-ead-authz/src/lib.rs
@@ -37,7 +37,7 @@ mod test_authz {
         // using .unwrap below since detailed errors are tested in each entity's tests
 
         let (ead_1, mut device) =
-            device.prepare_ead_1(&mut default_crypto(), &X_TV.try_into().unwrap(), SS_TV);
+            device.prepare_ead_1(&mut default_crypto(), G_XW_TV.try_into().unwrap(), SS_TV);
         device.set_h_message_1(H_MESSAGE_1_TV.try_into().unwrap());
 
         // ead_1 will be transported within message_1
@@ -75,7 +75,7 @@ mod test_authz {
         );
 
         let (ead_1, mut device) =
-            device.prepare_ead_1(&mut default_crypto(), &X_TV.try_into().unwrap(), SS_TV);
+            device.prepare_ead_1(&mut default_crypto(), G_XW_TV.try_into().unwrap(), SS_TV);
         device.set_h_message_1(H_MESSAGE_1_TV.try_into().unwrap());
 
         let (_loc_w, voucher_request, _authenticator) = authenticator
diff --git a/ead/lakers-ead-authz/src/shared.rs b/ead/lakers-ead-authz/src/shared.rs
index 504563a0..1c0382cc 100644
--- a/ead/lakers-ead-authz/src/shared.rs
+++ b/ead/lakers-ead-authz/src/shared.rs
@@ -12,6 +12,16 @@ pub(crate) fn compute_prk<Crypto: CryptoTrait>(
     crypto.hkdf_extract(&salt, &g_ab)
 }
 
+pub(crate) fn compute_prk_from_secret<Crypto: CryptoTrait>(
+    crypto: &mut Crypto,
+    g_ab: &BytesP256ElemLen,
+) -> BytesHashLen {
+    // NOTE: salt should be h'' (the zero-length byte string), but crypto backends are hardcoded to salts of size SHA256_DIGEST_LEN (32).
+    //       nevertheless, using a salt of HashLen zeros works as well (see RFC 5869, Section 2.2).
+    let salt: BytesHashLen = [0u8; SHA256_DIGEST_LEN];
+    crypto.hkdf_extract(&salt, &g_ab)
+}
+
 pub(crate) fn verify_voucher<Crypto: CryptoTrait>(
     crypto: &mut Crypto,
     received_voucher: &BytesEncodedVoucher,
diff --git a/lib/src/lib.rs b/lib/src/lib.rs
index 4be6c62a..1e55778c 100644
--- a/lib/src/lib.rs
+++ b/lib/src/lib.rs
@@ -306,6 +306,14 @@ impl<'a, Crypto: CryptoTrait> EdhocInitiator<'a, Crypto> {
             Err(error) => Err(error),
         }
     }
+
+    pub fn compute_ephemeral_secret(&mut self, g_a: &BytesP256ElemLen) -> BytesP256ElemLen {
+        self.crypto.p256_ecdh(&self.state.x, g_a)
+    }
+
+    pub fn selected_cipher_suite(&self) -> u8 {
+        self.state.suites_i[self.state.suites_i_len - 1]
+    }
 }
 
 impl<'a, Crypto: CryptoTrait> EdhocInitiatorWaitM2<'a, Crypto> {
@@ -655,7 +663,7 @@ mod test {
     #[test]
     fn test_ead_authz() {
         // ==== initialize edhoc ====
-        let initiator = EdhocInitiator::new(default_crypto(), I, CRED_I, Some(CRED_R));
+        let mut initiator = EdhocInitiator::new(default_crypto(), I, CRED_I, Some(CRED_R));
         let responder = EdhocResponder::new(default_crypto(), R, CRED_R, Some(CRED_I));
 
         // ==== initialize ead-authz ====
@@ -677,8 +685,8 @@ mod test {
 
         let (ead_1, mut device) = device.prepare_ead_1(
             &mut default_crypto(),
-            &initiator.state.x, // FIXME: avoid accessing private ephemeral key from application code
-            initiator.state.suites_i[initiator.state.suites_i_len - 1],
+            initiator.compute_ephemeral_secret(&device.g_w),
+            initiator.selected_cipher_suite(),
         );
         let (initiator, message_1) = initiator.prepare_message_1(None, &Some(ead_1)).unwrap();
         device.set_h_message_1(initiator.state.h_message_1.clone());


Test patch:
diff --git a/ead/lakers-ead-authz/src/test_vectors.rs b/ead/lakers-ead-authz/src/test_vectors.rs
index 1a10cb43..d0c8b9e4 100644
--- a/ead/lakers-ead-authz/src/test_vectors.rs
+++ b/ead/lakers-ead-authz/src/test_vectors.rs
@@ -24,6 +24,8 @@ pub const ACL_INVALID_TV: &[u8] = &hex!("fe"); // random invalid kid
 // EAD_1
 pub const SS_TV: u8 = 2;
 pub const ENC_ID_TV: &[u8] = &hex!("da9784962883c96ed01ff122c3");
+pub const G_XW_TV: &[u8] =
+    &hex!("03a658e9628c79c3f1e59239ca5e604953d11e01c2a442823c944da6682d0b6c");
 pub const PRK_TV: &[u8] = &hex!("d40f1601b577dbe7827bb3a20e0d16f7231c3a25225c1ed733f9094050d59666");
 pub const K_1_TV: &[u8] = &hex!("6f2a9112801a5011aa33576b5c7862ad");
 pub const IV_1_TV: &[u8] = &hex!("d31bc0d128349f290e79f0bde3");