1use super::TpmPublicTemplate;
8use crate::{TpmCryptoError, TpmExternalKey, TpmHash, KDF_LABEL_DUPLICATE};
9use num_bigint::{BigUint, RandBigInt};
10use num_traits::ops::bytes::ToBytes;
11use openssl::{
12 bn::{BigNum, BigNumContext},
13 derive::Deriver,
14 ec::{EcGroup, EcGroupRef, EcKey, EcPoint, EcPointRef, PointConversionForm},
15 nid::Nid,
16 pkey::{PKey, Private},
17};
18use rand::{CryptoRng, RngCore};
19use strum::{Display, EnumString};
20use tpm2_protocol::{
21 constant::TPM_MAX_COMMAND_SIZE,
22 data::{
23 Tpm2bDigest, Tpm2bEccParameter, Tpm2bEncryptedSecret, TpmAlgId, TpmEccCurve, TpmsEccParms,
24 TpmsEccPoint, TpmsSchemeHash, TpmtEccScheme, TpmtKdfScheme, TpmtPublic, TpmuAsymScheme,
25 TpmuPublicId, TpmuPublicParms,
26 },
27 TpmMarshal, TpmWriter,
28};
29
30const UNCOMPRESSED_POINT_TAG: u8 = 0x04;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumString, Display)]
34#[strum(serialize_all = "kebab-case")]
35pub enum TpmEllipticCurve {
36 NistP192,
37 NistP224,
38 NistP256,
39 NistP384,
40 NistP521,
41 BnP256,
42 BnP638,
43 Sm2P256,
44 #[strum(serialize = "bp-p256-r1")]
45 BpP256R1,
46 #[strum(serialize = "bp-p384-r1")]
47 BpP384R1,
48 #[strum(serialize = "bp-p512-r1")]
49 BpP512R1,
50 Curve25519,
51 Curve448,
52 None,
53}
54
55impl From<TpmEccCurve> for TpmEllipticCurve {
56 fn from(curve: TpmEccCurve) -> Self {
57 match curve {
58 TpmEccCurve::NistP192 => Self::NistP192,
59 TpmEccCurve::NistP224 => Self::NistP224,
60 TpmEccCurve::NistP256 => Self::NistP256,
61 TpmEccCurve::NistP384 => Self::NistP384,
62 TpmEccCurve::NistP521 => Self::NistP521,
63 TpmEccCurve::BnP256 => Self::BnP256,
64 TpmEccCurve::BnP638 => Self::BnP638,
65 TpmEccCurve::Sm2P256 => Self::Sm2P256,
66 TpmEccCurve::BpP256R1 => Self::BpP256R1,
67 TpmEccCurve::BpP384R1 => Self::BpP384R1,
68 TpmEccCurve::BpP512R1 => Self::BpP512R1,
69 TpmEccCurve::Curve25519 => Self::Curve25519,
70 TpmEccCurve::Curve448 => Self::Curve448,
71 TpmEccCurve::None => Self::None,
72 }
73 }
74}
75
76impl From<TpmEllipticCurve> for TpmEccCurve {
77 fn from(curve: TpmEllipticCurve) -> Self {
78 match curve {
79 TpmEllipticCurve::NistP192 => Self::NistP192,
80 TpmEllipticCurve::NistP224 => Self::NistP224,
81 TpmEllipticCurve::NistP256 => Self::NistP256,
82 TpmEllipticCurve::NistP384 => Self::NistP384,
83 TpmEllipticCurve::NistP521 => Self::NistP521,
84 TpmEllipticCurve::BnP256 => Self::BnP256,
85 TpmEllipticCurve::BnP638 => Self::BnP638,
86 TpmEllipticCurve::Sm2P256 => Self::Sm2P256,
87 TpmEllipticCurve::BpP256R1 => Self::BpP256R1,
88 TpmEllipticCurve::BpP384R1 => Self::BpP384R1,
89 TpmEllipticCurve::BpP512R1 => Self::BpP512R1,
90 TpmEllipticCurve::Curve25519 => Self::Curve25519,
91 TpmEllipticCurve::Curve448 => Self::Curve448,
92 TpmEllipticCurve::None => Self::None,
93 }
94 }
95}
96
97impl From<TpmEllipticCurve> for Nid {
98 fn from(curve: TpmEllipticCurve) -> Self {
100 match curve {
101 TpmEllipticCurve::NistP192 => Nid::X9_62_PRIME192V1,
102 TpmEllipticCurve::NistP224 => Nid::SECP224R1,
103 TpmEllipticCurve::NistP256 => Nid::X9_62_PRIME256V1,
104 TpmEllipticCurve::NistP384 => Nid::SECP384R1,
105 TpmEllipticCurve::NistP521 => Nid::SECP521R1,
106 TpmEllipticCurve::BpP256R1 => Nid::BRAINPOOL_P256R1,
107 TpmEllipticCurve::BpP384R1 => Nid::BRAINPOOL_P384R1,
108 TpmEllipticCurve::BpP512R1 => Nid::BRAINPOOL_P512R1,
109 TpmEllipticCurve::Sm2P256 => Nid::SM2,
110 _ => Nid::UNDEF,
111 }
112 }
113}
114
115impl From<Nid> for TpmEllipticCurve {
116 fn from(nid: Nid) -> Self {
117 match nid {
118 Nid::X9_62_PRIME192V1 => TpmEllipticCurve::NistP192,
119 Nid::SECP224R1 => TpmEllipticCurve::NistP224,
120 Nid::X9_62_PRIME256V1 => TpmEllipticCurve::NistP256,
121 Nid::SECP384R1 => TpmEllipticCurve::NistP384,
122 Nid::SECP521R1 => TpmEllipticCurve::NistP521,
123 Nid::BRAINPOOL_P256R1 => TpmEllipticCurve::BpP256R1,
124 Nid::BRAINPOOL_P384R1 => TpmEllipticCurve::BpP384R1,
125 Nid::BRAINPOOL_P512R1 => TpmEllipticCurve::BpP512R1,
126 Nid::SM2 => TpmEllipticCurve::Sm2P256,
127 _ => TpmEllipticCurve::None,
128 }
129 }
130}
131
132#[derive(Debug, Clone)]
134pub struct TpmEccExternalKey {
135 curve: TpmEllipticCurve,
136 unique: TpmsEccPoint,
137}
138
139impl TpmEccExternalKey {
140 #[must_use]
141 pub fn new(curve: TpmEllipticCurve, unique: TpmsEccPoint) -> Self {
142 Self { curve, unique }
143 }
144
145 #[must_use]
147 pub fn curve(&self) -> TpmEllipticCurve {
148 self.curve
149 }
150 #[must_use]
152 pub fn unique(&self) -> &TpmsEccPoint {
153 &self.unique
154 }
155}
156
157impl TryFrom<&TpmtPublic> for TpmEccExternalKey {
158 type Error = TpmCryptoError;
159
160 fn try_from(public: &TpmtPublic) -> Result<Self, Self::Error> {
161 let params = match &public.parameters {
162 TpmuPublicParms::Ecc(params) => Ok(params),
163 _ => Err(TpmCryptoError::InvalidEccParameters),
164 }?;
165
166 let (x, y) = match &public.unique {
167 TpmuPublicId::Ecc(point) => Ok((point.x, point.y)),
168 _ => Err(TpmCryptoError::InvalidEccParameters),
169 }?;
170
171 Ok(Self {
172 curve: params.curve_id.into(),
173 unique: TpmsEccPoint { x, y },
174 })
175 }
176}
177
178impl TryFrom<&PKey<Private>> for TpmEccExternalKey {
179 type Error = TpmCryptoError;
180
181 fn try_from(pkey: &PKey<Private>) -> Result<Self, Self::Error> {
182 let ec_key = pkey
183 .ec_key()
184 .map_err(|_| TpmCryptoError::InvalidEccParameters)?;
185 let group = ec_key.group();
186 let nid = group
187 .curve_name()
188 .ok_or(TpmCryptoError::InvalidEccParameters)?;
189 let curve = TpmEllipticCurve::from(nid);
190
191 if curve == TpmEllipticCurve::None {
192 return Err(TpmCryptoError::InvalidEccCurve);
193 }
194
195 let mut ctx = BigNumContext::new().map_err(|_| TpmCryptoError::OutOfMemory)?;
196 let unique = tpm_make_point(ec_key.public_key(), group, &mut ctx)?;
197
198 Ok(Self { curve, unique })
199 }
200}
201
202impl TpmExternalKey for TpmEccExternalKey {
203 fn from_der(bytes: &[u8]) -> Result<(Self, Vec<u8>), TpmCryptoError> {
204 let pkey =
205 PKey::private_key_from_der(bytes).map_err(|_| TpmCryptoError::OperationFailed)?;
206 let public_key = TpmEccExternalKey::try_from(&pkey)?;
207 let ec_key = pkey
208 .ec_key()
209 .map_err(|_| TpmCryptoError::InvalidEccParameters)?;
210 let sensitive = ec_key.private_key().to_vec();
211 Ok((public_key, sensitive))
212 }
213
214 fn to_public(&self, template: &TpmPublicTemplate) -> TpmtPublic {
215 TpmtPublic {
216 object_type: TpmAlgId::Ecc,
217 name_alg: template.name_alg(),
218 object_attributes: template.object_attributes(),
219 auth_policy: Tpm2bDigest::default(),
220 parameters: TpmuPublicParms::Ecc(TpmsEccParms {
221 symmetric: template.symmetric(),
222 scheme: TpmtEccScheme {
223 scheme: TpmAlgId::Ecdh,
224 details: TpmuAsymScheme::Hash(TpmsSchemeHash {
225 hash_alg: template.name_alg(),
226 }),
227 },
228 curve_id: self.curve.into(),
229 kdf: TpmtKdfScheme::default(),
230 }),
231 unique: TpmuPublicId::Ecc(self.unique),
232 }
233 }
234
235 fn to_seed(
236 &self,
237 name_alg: TpmHash,
238 rng: &mut (impl RngCore + CryptoRng),
239 ) -> Result<(Vec<u8>, Tpm2bEncryptedSecret), TpmCryptoError> {
240 let (derived_seed, ephemeral_point) = self.ecdh(name_alg, rng)?;
241
242 let mut point_bytes_buf = [0u8; TPM_MAX_COMMAND_SIZE];
243 let len = {
244 let mut writer = TpmWriter::new(&mut point_bytes_buf);
245 ephemeral_point
246 .marshal(&mut writer)
247 .map_err(TpmCryptoError::Marshal)?;
248 writer.len()
249 };
250 let point_bytes = &point_bytes_buf[..len];
251
252 let secret =
253 Tpm2bEncryptedSecret::try_from(point_bytes).map_err(TpmCryptoError::Unmarshal)?;
254
255 Ok((derived_seed, secret))
256 }
257}
258
259impl TpmEccExternalKey {
260 fn ecdh(
274 &self,
275 name_alg: TpmHash,
276 rng: &mut (impl RngCore + CryptoRng),
277 ) -> Result<(Vec<u8>, TpmsEccPoint), TpmCryptoError> {
278 let nid = self.curve.into();
279 if nid == Nid::UNDEF {
280 return Err(TpmCryptoError::InvalidEccCurve);
281 }
282 let group = EcGroup::from_curve_name(nid).map_err(|_| TpmCryptoError::OutOfMemory)?;
283 let mut ctx = BigNumContext::new().map_err(|_| TpmCryptoError::OutOfMemory)?;
284
285 let parent_x =
286 BigNum::from_slice(self.unique.x.as_ref()).map_err(|_| TpmCryptoError::OutOfMemory)?;
287 let parent_y =
288 BigNum::from_slice(self.unique.y.as_ref()).map_err(|_| TpmCryptoError::OutOfMemory)?;
289 let parent_key = EcKey::from_public_key_affine_coordinates(&group, &parent_x, &parent_y)
290 .map_err(|_| TpmCryptoError::OperationFailed)?;
291 let parent_public_key =
292 PKey::from_ec_key(parent_key).map_err(|_| TpmCryptoError::OperationFailed)?;
293
294 let mut order = BigNum::new().map_err(|_| TpmCryptoError::OutOfMemory)?;
295 group
296 .order(&mut order, &mut ctx)
297 .map_err(|_| TpmCryptoError::OperationFailed)?;
298 let order_uint = BigUint::from_bytes_be(&order.to_vec());
299 let one = BigUint::from(1u8);
300
301 let priv_uint = rng.gen_biguint_range(&one, &order_uint);
302 let priv_bn = BigNum::from_slice(&priv_uint.to_be_bytes())
303 .map_err(|_| TpmCryptoError::OutOfMemory)?;
304
305 let mut ephemeral_pub_point =
306 EcPoint::new(&group).map_err(|_| TpmCryptoError::OutOfMemory)?;
307 ephemeral_pub_point
308 .mul_generator(&group, &priv_bn, &ctx)
309 .map_err(|_| TpmCryptoError::OperationFailed)?;
310 let ephemeral_key = EcKey::from_private_components(&group, &priv_bn, &ephemeral_pub_point)
311 .map_err(|_| TpmCryptoError::OutOfMemory)?;
312
313 let ephemeral_public_key =
314 PKey::from_ec_key(ephemeral_key).map_err(|_| TpmCryptoError::OutOfMemory)?;
315 let mut deriver =
316 Deriver::new(&ephemeral_public_key).map_err(|_| TpmCryptoError::OutOfMemory)?;
317 deriver
318 .set_peer(&parent_public_key)
319 .map_err(|_| TpmCryptoError::OperationFailed)?;
320 let z = deriver
321 .derive_to_vec()
322 .map_err(|_| TpmCryptoError::OperationFailed)?;
323
324 let ephemeral = tpm_make_point(&ephemeral_pub_point, &group, &mut ctx)?;
325
326 let seed_bits =
327 u16::try_from(name_alg.size() * 8).map_err(|_| TpmCryptoError::OperationFailed)?;
328 let context_u = ephemeral.x.as_ref();
329 let context_v = self.unique.x.as_ref();
330
331 let seed = name_alg.kdfe(&z, KDF_LABEL_DUPLICATE, context_u, context_v, seed_bits)?;
332
333 Ok((seed, ephemeral))
334 }
335}
336
337fn tpm_make_point(
338 point: &EcPointRef,
339 group: &EcGroupRef,
340 ctx: &mut BigNumContext,
341) -> Result<TpmsEccPoint, TpmCryptoError> {
342 let pub_bytes = point
343 .to_bytes(group, PointConversionForm::UNCOMPRESSED, ctx)
344 .map_err(|_| TpmCryptoError::OperationFailed)?;
345
346 if pub_bytes.is_empty() || pub_bytes[0] != UNCOMPRESSED_POINT_TAG {
347 return Err(TpmCryptoError::InvalidEccParameters);
348 }
349
350 let coord_len = (pub_bytes.len() - 1) / 2;
351 let x = Tpm2bEccParameter::try_from(&pub_bytes[1..=coord_len])
352 .map_err(TpmCryptoError::Unmarshal)?;
353 let y = Tpm2bEccParameter::try_from(&pub_bytes[1 + coord_len..])
354 .map_err(TpmCryptoError::Unmarshal)?;
355
356 Ok(TpmsEccPoint { x, y })
357}