Allow creation of HKDF using PRK bytes.

Change-Id: I34a326fb8c0e83c81380bc6608b7f0b52c63e7c0
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/67987
Reviewed-by: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
diff --git a/rust/bssl-crypto/src/hkdf.rs b/rust/bssl-crypto/src/hkdf.rs
index 973ed88..532468f 100644
--- a/rust/bssl-crypto/src/hkdf.rs
+++ b/rust/bssl-crypto/src/hkdf.rs
@@ -63,6 +63,20 @@
 //!
 //! assert_eq!(out, HkdfSha256::derive(b"secret", hkdf::Salt::None, b"info"));
 //! ```
+//!
+//! To expand output from the explicit bytes of a PRK, use `Prk::new`:
+//!
+//! ```
+//! use bssl_crypto::{digest::Sha256, digest::Algorithm, hkdf};
+//!
+//! let prk: [u8; Sha256::OUTPUT_LEN] = bssl_crypto::rand_array();
+//! // unwrap: only fails if the input is not equal to the digest length, which
+//! // cannot happen here.
+//! let prk = hkdf::Prk::new::<Sha256>(&prk).unwrap();
+//! let mut out = vec![0u8; 42];
+//! prk.expand_into(b"info", &mut out)?;
+//! # Ok::<(), hkdf::TooLong>(())
+//! ```
 
 use crate::{digest, sealed, with_output_array, FfiMutSlice, FfiSlice, ForeignTypeRef};
 use core::marker::PhantomData;
@@ -173,6 +187,33 @@
 
 #[allow(clippy::let_unit_value)]
 impl Prk {
+    /// Creates a Prk from bytes.
+    pub fn new<MD: digest::Algorithm>(prk_bytes: &[u8]) -> Option<Self> {
+        if prk_bytes.len() != MD::OUTPUT_LEN {
+            return None;
+        }
+
+        let mut prk = [0u8; bssl_sys::EVP_MAX_MD_SIZE as usize];
+        prk.get_mut(..MD::OUTPUT_LEN)
+            // unwrap: `EVP_MAX_MD_SIZE` must be greater than the length of any
+            // digest function thus this is always successful.
+            .unwrap()
+            .copy_from_slice(prk_bytes);
+
+        Some(Prk {
+            prk,
+            len: MD::OUTPUT_LEN,
+            evp_md: MD::get_md(sealed::Sealed).as_ptr(),
+        })
+    }
+
+    /// Returns the bytes of the pseudorandom key.
+    pub fn as_bytes(&self) -> &[u8] {
+        // `self.len` must be less than the length of `self.prk` thus
+        // this is always in bounds.
+        &self.prk[..self.len]
+    }
+
     /// Derive key material for the given info parameter. Attempting
     /// to derive more than 255 bytes is a compile-time error, see `expand_into`
     /// for longer outputs.
@@ -243,7 +284,8 @@
 )]
 mod tests {
     use crate::{
-        hkdf::{HkdfSha256, HkdfSha512, Salt},
+        digest::Sha256,
+        hkdf::{HkdfSha256, HkdfSha512, Prk, Salt},
         test_helpers::{decode_hex, decode_hex_into_vec},
     };
 
@@ -280,6 +322,7 @@
             ikm: Vec<u8>,
             salt: Vec<u8>,
             info: Vec<u8>,
+            prk: Vec<u8>,
             okm: Vec<u8>,
         }
         let tests = [
@@ -288,6 +331,10 @@
                 ikm: decode_hex_into_vec("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"),
                 salt: decode_hex_into_vec("000102030405060708090a0b0c"),
                 info: decode_hex_into_vec("f0f1f2f3f4f5f6f7f8f9"),
+                prk: decode_hex_into_vec(
+                    "077709362c2e32df0ddc3f0dc47bba63\
+                    90b6c73bb50f9c3122ec844ad7c2b3e5",
+                ),
                 okm: decode_hex_into_vec("3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865")
             },
             Test {
@@ -313,6 +360,10 @@
                     e0e1e2e3e4e5e6e7e8e9eaebecedeeef\
                     f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
                 ),
+                prk: decode_hex_into_vec(
+                    "06a6b88c5853361a06104c9ceb35b45c\
+                    ef760014904671014a193f40c15fc244",
+                ),
                 okm: decode_hex_into_vec(
                     "b11e398dc80327a1c8e7f78c596a4934\
                     4f012eda2d4efad8a050cc4c19afa97c\
@@ -327,6 +378,10 @@
                 ikm: decode_hex_into_vec("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"),
                 salt: Vec::new(),
                 info: Vec::new(),
+                prk: decode_hex_into_vec(
+                    "19ef24a32c717b167f33a91d6f648bdf\
+                    96596776afdb6377ac434c1c293ccb04",
+                ),
                 okm: decode_hex_into_vec(
                     "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8"),
             },
@@ -336,6 +391,7 @@
             ikm,
             salt,
             info,
+            prk,
             okm,
         } in tests.iter()
         {
@@ -349,6 +405,12 @@
                 HkdfSha256::derive_into(ikm.as_slice(), salt, info.as_slice(), &mut okm2).is_ok()
             );
             assert_eq!(okm2.as_slice(), okm.as_slice());
+
+            let prk2 = Prk::new::<Sha256>(prk.as_slice()).unwrap();
+            assert_eq!(prk2.as_bytes(), prk.as_slice());
+            let mut okm3 = vec![0u8; okm.len()];
+            let _ = prk2.expand_into(info.as_slice(), &mut okm3);
+            assert_eq!(okm3.as_slice(), okm.as_slice());
         }
     }
 
@@ -361,4 +423,16 @@
         let mut too_long = vec![0u8; HkdfSha256::MAX_OUTPUT_LEN + 1];
         assert!(hkdf.expand_into(b"", &mut too_long).is_err());
     }
+
+    #[test]
+    fn wrong_prk_len() {
+        assert!(Prk::new::<Sha256>(
+            decode_hex_into_vec("077709362c2e32df0ddc3f0dc47bba63").as_slice()
+        )
+        .is_none());
+        assert!(Prk::new::<Sha256>(
+            decode_hex_into_vec("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e590b6c73bb50f9c3122ec844ad7c2b3e5").as_slice())
+            .is_none()
+        );
+    }
 }