1use crate::{Error, Hash, PublicKey, 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 EccCurve {
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 EccCurve {
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<EccCurve> for TpmEccCurve {
74 fn from(curve: EccCurve) -> Self {
75 match curve {
76 EccCurve::NistP192 => Self::NistP192,
77 EccCurve::NistP224 => Self::NistP224,
78 EccCurve::NistP256 => Self::NistP256,
79 EccCurve::NistP384 => Self::NistP384,
80 EccCurve::NistP521 => Self::NistP521,
81 EccCurve::BnP256 => Self::BnP256,
82 EccCurve::BnP638 => Self::BnP638,
83 EccCurve::Sm2P256 => Self::Sm2P256,
84 EccCurve::BpP256R1 => Self::BpP256R1,
85 EccCurve::BpP384R1 => Self::BpP384R1,
86 EccCurve::BpP512R1 => Self::BpP512R1,
87 EccCurve::Curve25519 => Self::Curve25519,
88 EccCurve::Curve448 => Self::Curve448,
89 EccCurve::None => Self::None,
90 }
91 }
92}
93
94impl From<EccCurve> for Nid {
95 fn from(curve: EccCurve) -> Self {
97 match curve {
98 EccCurve::NistP192 => Nid::X9_62_PRIME192V1,
99 EccCurve::NistP224 => Nid::SECP224R1,
100 EccCurve::NistP256 => Nid::X9_62_PRIME256V1,
101 EccCurve::NistP384 => Nid::SECP384R1,
102 EccCurve::NistP521 => Nid::SECP521R1,
103 EccCurve::BpP256R1 => Nid::BRAINPOOL_P256R1,
104 EccCurve::BpP384R1 => Nid::BRAINPOOL_P384R1,
105 EccCurve::BpP512R1 => Nid::BRAINPOOL_P512R1,
106 EccCurve::Sm2P256 => Nid::SM2,
107 _ => Nid::UNDEF,
108 }
109 }
110}
111
112impl TryFrom<Nid> for EccCurve {
113 type Error = Error;
114
115 fn try_from(nid: Nid) -> Result<Self, Self::Error> {
116 match nid {
117 Nid::X9_62_PRIME192V1 => Ok(EccCurve::NistP192),
118 Nid::SECP224R1 => Ok(EccCurve::NistP224),
119 Nid::X9_62_PRIME256V1 => Ok(EccCurve::NistP256),
120 Nid::SECP384R1 => Ok(EccCurve::NistP384),
121 Nid::SECP521R1 => Ok(EccCurve::NistP521),
122 Nid::BRAINPOOL_P256R1 => Ok(EccCurve::BpP256R1),
123 Nid::BRAINPOOL_P384R1 => Ok(EccCurve::BpP384R1),
124 Nid::BRAINPOOL_P512R1 => Ok(EccCurve::BpP512R1),
125 Nid::SM2 => Ok(EccCurve::Sm2P256),
126 _ => Err(Error::InvalidEccCurve),
127 }
128 }
129}
130
131#[derive(Debug, Clone)]
133pub struct EccPublicKey {
134 pub curve: EccCurve,
135 pub x: Tpm2bEccParameter,
136 pub y: Tpm2bEccParameter,
137}
138
139impl TryFrom<&TpmtPublic> for EccPublicKey {
140 type Error = Error;
141
142 fn try_from(public: &TpmtPublic) -> Result<Self, Self::Error> {
143 let params = match &public.parameters {
144 TpmuPublicParms::Ecc(params) => Ok(params),
145 _ => Err(Error::InvalidEccParameters),
146 }?;
147
148 let (x, y) = match &public.unique {
149 TpmuPublicId::Ecc(point) => Ok((point.x, point.y)),
150 _ => Err(Error::InvalidEccParameters),
151 }?;
152
153 Ok(Self {
154 curve: params.curve_id.into(),
155 x,
156 y,
157 })
158 }
159}
160
161impl TryFrom<&PKey<Private>> for EccPublicKey {
162 type Error = Error;
163
164 fn try_from(pkey: &PKey<Private>) -> Result<Self, Self::Error> {
165 let ec_key = pkey.ec_key().map_err(|_| Error::InvalidEccParameters)?;
166 let group = ec_key.group();
167 let nid = group.curve_name().ok_or(Error::InvalidEccParameters)?;
168 let curve = EccCurve::try_from(nid)?;
169
170 let mut ctx = BigNumContext::new().map_err(|_| Error::OutOfMemory)?;
171 let (x, y) = crate::tpm_make_point(ec_key.public_key(), group, &mut ctx)?;
172
173 Ok(Self { curve, x, y })
174 }
175}
176
177impl PublicKey for EccPublicKey {
178 fn from_der(bytes: &[u8]) -> Result<(Self, Vec<u8>), Error> {
179 let pkey = PKey::private_key_from_der(bytes).map_err(|_| Error::OperationFailed)?;
180 let public_key = EccPublicKey::try_from(&pkey)?;
181 let ec_key = pkey.ec_key().map_err(|_| Error::InvalidEccParameters)?;
182 let sensitive = ec_key.private_key().to_vec();
183 Ok((public_key, sensitive))
184 }
185
186 fn to_public(&self, hash_alg: TpmAlgId, symmetric: TpmtSymDefObject) -> TpmtPublic {
187 tpm2_protocol::data::TpmtPublic {
188 object_type: TpmAlgId::Ecc,
189 name_alg: hash_alg,
190 object_attributes: TpmaObject::USER_WITH_AUTH | TpmaObject::DECRYPT,
191 auth_policy: Tpm2bDigest::default(),
192 parameters: TpmuPublicParms::Ecc(TpmsEccParms {
193 symmetric,
194 scheme: TpmtEccScheme {
195 scheme: TpmAlgId::Ecdh,
196 details: TpmuAsymScheme::Hash(TpmsSchemeHash { hash_alg }),
197 },
198 curve_id: self.curve.into(),
199 kdf: TpmtKdfScheme::default(),
200 }),
201 unique: TpmuPublicId::Ecc(TpmsEccPoint {
202 x: self.x,
203 y: self.y,
204 }),
205 }
206 }
207
208 fn to_seed(
209 &self,
210 name_alg: Hash,
211 rng: &mut (impl RngCore + CryptoRng),
212 ) -> Result<(Vec<u8>, Tpm2bEncryptedSecret), Error> {
213 let (derived_seed, ephemeral_point) = self.ecdh(name_alg, rng)?;
214
215 let mut point_bytes_buf = [0u8; TPM_MAX_COMMAND_SIZE as usize];
216 let len = {
217 let mut writer = TpmWriter::new(&mut point_bytes_buf);
218 ephemeral_point
219 .marshal(&mut writer)
220 .map_err(|_| Error::OperationFailed)?;
221 writer.len()
222 };
223 let point_bytes = &point_bytes_buf[..len];
224
225 let secret = Tpm2bEncryptedSecret::try_from(point_bytes).map_err(|_| Error::OutOfMemory)?;
226
227 Ok((derived_seed, secret))
228 }
229}
230
231impl EccPublicKey {
232 fn ecdh(
245 &self,
246 name_alg: Hash,
247 rng: &mut (impl RngCore + CryptoRng),
248 ) -> Result<(Vec<u8>, TpmsEccPoint), Error> {
249 let nid = self.curve.into();
250 if nid == Nid::UNDEF {
251 return Err(Error::InvalidEccCurve);
252 }
253 let group = EcGroup::from_curve_name(nid).map_err(|_| Error::OutOfMemory)?;
254 let mut ctx = BigNumContext::new().map_err(|_| Error::OutOfMemory)?;
255
256 let parent_x = BigNum::from_slice(self.x.as_ref()).map_err(|_| Error::OutOfMemory)?;
257 let parent_y = BigNum::from_slice(self.y.as_ref()).map_err(|_| Error::OutOfMemory)?;
258 let parent_key = EcKey::from_public_key_affine_coordinates(&group, &parent_x, &parent_y)
259 .map_err(|_| Error::OperationFailed)?;
260 let parent_public_key =
261 PKey::from_ec_key(parent_key).map_err(|_| Error::OperationFailed)?;
262
263 let mut order = BigNum::new().map_err(|_| Error::OutOfMemory)?;
264 group
265 .order(&mut order, &mut ctx)
266 .map_err(|_| Error::OperationFailed)?;
267 let order_uint = BigUint::from_bytes_be(&order.to_vec());
268 let one = BigUint::from(1u8);
269
270 let priv_uint = rng.gen_biguint_range(&one, &order_uint);
271 let priv_bn =
272 BigNum::from_slice(&priv_uint.to_be_bytes()).map_err(|_| Error::OutOfMemory)?;
273
274 let mut ephemeral_pub_point = EcPoint::new(&group).map_err(|_| Error::OutOfMemory)?;
275 ephemeral_pub_point
276 .mul_generator(&group, &priv_bn, &ctx)
277 .map_err(|_| Error::OperationFailed)?;
278 let ephemeral_key = EcKey::from_private_components(&group, &priv_bn, &ephemeral_pub_point)
279 .map_err(|_| Error::OutOfMemory)?;
280
281 let ephemeral_public_key =
282 PKey::from_ec_key(ephemeral_key).map_err(|_| Error::OutOfMemory)?;
283 let mut deriver = Deriver::new(&ephemeral_public_key).map_err(|_| Error::OutOfMemory)?;
284 deriver
285 .set_peer(&parent_public_key)
286 .map_err(|_| Error::OperationFailed)?;
287 let z = deriver
288 .derive_to_vec()
289 .map_err(|_| Error::OperationFailed)?;
290
291 let (ephemeral_x, ephemeral_y) =
292 crate::tpm_make_point(&ephemeral_pub_point, &group, &mut ctx)?;
293
294 let seed_bits = u16::try_from(name_alg.size() * 8).map_err(|_| Error::OperationFailed)?;
295 let context_u = ephemeral_x.as_ref();
296 let context_v = self.x.as_ref();
297
298 let seed = name_alg.kdfe(&z, KDF_LABEL_DUPLICATE, context_u, context_v, seed_bits)?;
299
300 let ephemeral_point_tpm = TpmsEccPoint {
301 x: ephemeral_x,
302 y: ephemeral_y,
303 };
304
305 Ok((seed, ephemeral_point_tpm))
306 }
307}