1 //! This module contains the Endpoint Encryption Keys (EEK) utilities for encrypting
2 //! factory payloads for CSR versions 1 & 2. EEK usage was dropped for CSR v3.
3 //!
4 //! NOTE: Since CSR version 3 drops the encryption requirement, Google is publishing
5 //! the private Google EEKs so that testing on platforms that support CSR versions
6 //! 1 & 2 have diagnostic parity with CSR v3 systems.
7
8 use anyhow::{bail, Context, Result};
9 use coset::{CborSerializable, CoseKdfContextBuilder, Nonce, PartyInfo, SuppPubInfoBuilder};
10 use openssl::{
11 bn::{BigNum, BigNumContext},
12 derive::Deriver,
13 ec::{EcGroup, EcKey, EcPoint, PointConversionForm},
14 md::Md,
15 nid::Nid,
16 pkey::{HasPublic, Id, PKey, PKeyRef, Private, Public},
17 pkey_ctx::PkeyCtx,
18 };
19
20 pub const X25519_EEK_ID: &[u8] = b"\xd0\xae\xc1\x15\xca\x2a\xcf\x73\xae\x6b\xcc\xcb\xd1\x96\x1d\x65\xe8\xb1\xdd\xd7\x4a\x1a\x37\xb9\x43\x3a\x97\xd5\x99\xdf\x98\x08";
21 // X25519_EEK_PUBLIC_X: b"\xbe\x85\xe7\x46\xc4\xa3\x42\x5a\x40\xd9\x36\x3a\xa6\x15\xd0\x2c\x58\x7e\x3d\xdc\x33\x02\x32\xd2\xfc\x5e\x1e\x87\x25\x5f\x72\x60";
22 pub const X25519_EEK_PRIVATE: &[u8] = b"\x47\x66\x65\xdb\xaa\xe1\x1d\xdb\xd3\x03\xf7\xad\x68\xbb\xff\x8d\x90\xaf\x8a\x4f\xb8\xbf\x7c\xf3\x39\xc7\xe9\x73\xec\x70\x75\x8c";
23
24 pub const P256_EEK_ID: &[u8] = b"\x35\x73\xb7\x3f\xa0\x8a\x80\x89\xb1\x26\x67\xe9\xcb\x7c\x75\xa1\xaf\x02\x61\xfc\x6e\x65\x03\x91\x3b\xd3\x4b\x7d\x14\x94\x3e\x46";
25 pub const P256_EEK_PUBLIC_X: &[u8] = b"\xe0\x41\xcf\x2f\x0f\x34\x0f\x1c\x33\x2c\x41\xb0\xcf\xd7\x0c\x30\x55\x35\xd2\x1e\x6a\x47\x13\x4b\x2e\xd1\x48\x96\x7e\x24\x9c\x68";
26 pub const P256_EEK_PUBLIC_Y: &[u8] = b"\x1f\xce\x45\xc5\xfb\x61\xba\x81\x21\xf9\xe5\x05\x9b\x9b\x39\x0e\x76\x86\x86\x47\xb8\x1e\x2f\x45\xf1\xce\xaf\xda\x3f\x80\x68\xdb";
27 pub const P256_EEK_PRIVATE: &[u8] = b"\x00\xd1\x0a\x51\x59\xb2\xa0\x39\x43\xc8\x58\xf1\xdc\x3f\xc4\x0a\x03\x39\x31\x9d\x34\x01\x8f\x30\xa9\xc4\x46\x7e\x64\x61\x35\x6d\xdf";
28
29 /// Get the well-known P256 GEEK private key.
p256_geek() -> PKey<Private>30 pub fn p256_geek() -> PKey<Private> {
31 let p256 = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
32 let private = BigNum::from_slice(P256_EEK_PRIVATE).unwrap();
33 let mut public_key = EcPoint::new(&p256).unwrap();
34 public_key
35 .set_affine_coordinates_gfp(
36 &p256,
37 &BigNum::from_slice(P256_EEK_PUBLIC_X).unwrap(),
38 &BigNum::from_slice(P256_EEK_PUBLIC_Y).unwrap(),
39 &mut BigNumContext::new().unwrap(),
40 )
41 .unwrap();
42
43 let ec_key = EcKey::from_private_components(&p256, &private, &public_key).unwrap();
44 PKey::from_ec_key(ec_key).unwrap()
45 }
46
47 /// Get the well-known X25519 GEEK private key.
x25519_geek() -> PKey<Private>48 pub fn x25519_geek() -> PKey<Private> {
49 PKey::private_key_from_raw_bytes(X25519_EEK_PRIVATE, Id::X25519).unwrap()
50 }
51
derive_ephemeral_symmetric_key( private: &PKeyRef<Private>, public: &PKeyRef<Public>, ) -> Result<[u8; 32]>52 pub fn derive_ephemeral_symmetric_key(
53 private: &PKeyRef<Private>,
54 public: &PKeyRef<Public>,
55 ) -> Result<[u8; 32]> {
56 let mut deriver = Deriver::new(private)?;
57 deriver.set_peer(public)?;
58 let shared_secret = deriver.derive_to_vec()?;
59
60 let supp_pub_info = SuppPubInfoBuilder::new().key_data_length(256).build();
61
62 let context = CoseKdfContextBuilder::new()
63 .algorithm(coset::iana::Algorithm::A256GCM)
64 .party_u_info(kdf_party_info(b"client", public)?)
65 .party_v_info(kdf_party_info(b"server", private)?)
66 .supp_pub_info(supp_pub_info)
67 .build();
68 let hkdf_info = match context.to_vec() {
69 Ok(v) => v,
70 Err(e) => bail!("Error serializing COSE_KDF_Context: {e}"),
71 };
72
73 let mut kdf = PkeyCtx::new_id(Id::HKDF)?;
74 kdf.derive_init()?;
75 kdf.set_hkdf_key(&shared_secret)?;
76 kdf.set_hkdf_md(Md::sha256())?;
77 kdf.add_hkdf_info(&hkdf_info)?;
78
79 let mut derived_key = [0; 32];
80 kdf.derive(Some(&mut derived_key))?;
81 Ok(derived_key)
82 }
83
kdf_party_info<T: HasPublic>(identity: &[u8], key: &PKeyRef<T>) -> Result<PartyInfo>84 fn kdf_party_info<T: HasPublic>(identity: &[u8], key: &PKeyRef<T>) -> Result<PartyInfo> {
85 Ok(PartyInfo {
86 identity: Some(identity.to_vec()),
87 nonce: Some(Nonce::Bytes(vec![])),
88 other: Some(raw_key_bytes(key)?),
89 })
90 }
91
raw_key_bytes<T: HasPublic>(key: &PKeyRef<T>) -> Result<Vec<u8>>92 fn raw_key_bytes<T: HasPublic>(key: &PKeyRef<T>) -> Result<Vec<u8>> {
93 match key.id() {
94 Id::X25519 => key.raw_public_key().context("Unable to fetch raw public key"),
95 Id::EC => {
96 let ec = key.ec_key()?;
97 let mut ctx = BigNumContext::new()?;
98 let mut uncompressed = ec.public_key().to_bytes(
99 ec.group(),
100 PointConversionForm::UNCOMPRESSED,
101 &mut ctx,
102 )?;
103 // uncompressed format is '04 | X | Y'. For KDF purposes, we just want 'X | Y'
104 uncompressed.remove(0);
105 Ok(uncompressed)
106 }
107 other => bail!("Unsupported public key type {other:?}"),
108 }
109 }
110