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