1#![allow(clippy::no_effect_underscore_binding)]
5
6use crate::{
7 crypto::UNCOMPRESSED_POINT_TAG,
8 key::{external_key::ExternalKey, KeyError},
9};
10
11use std::borrow::Cow;
12
13use p256::elliptic_curve::sec1::ToEncodedPoint;
14use rasn::{
15 types::{BitString, ObjectIdentifier, OctetString},
16 AsnType, Decode, Decoder, Encode, Encoder,
17};
18use rsa::traits::PrivateKeyParts;
19use tpm2_protocol::data::{
20 Tpm2bDigest, Tpm2bEccParameter, TpmAlgId, TpmEccCurve, TpmaObject, TpmsEccParms, TpmsEccPoint,
21 TpmtEccScheme, TpmtKdfScheme, TpmtPublic, TpmtSymDefObject, TpmuAsymScheme, TpmuPublicId,
22 TpmuPublicParms,
23};
24
25pub const SECP_256_R_1: ObjectIdentifier =
26 ObjectIdentifier::new_unchecked(Cow::Borrowed(&[1, 2, 840, 10045, 3, 1, 7]));
27pub const SECP_384_R_1: ObjectIdentifier =
28 ObjectIdentifier::new_unchecked(Cow::Borrowed(&[1, 3, 132, 0, 34]));
29pub const SECP_521_R_1: ObjectIdentifier =
30 ObjectIdentifier::new_unchecked(Cow::Borrowed(&[1, 3, 132, 0, 35]));
31
32#[allow(clippy::no_effect_underscore_binding)]
33#[derive(AsnType, Decode, Encode, Debug)]
34pub struct Sec1EcPrivateKey {
35 pub version: u8,
36 pub private_key: OctetString,
37 #[rasn(tag(explicit(context, 0)))]
38 pub parameters: Option<ObjectIdentifier>,
39 #[rasn(tag(explicit(context, 1)))]
40 pub public_key: Option<BitString>,
41}
42
43pub fn parse_ecc_from_der(
54 der_bytes: &[u8],
55 inherited_oid: Option<&ObjectIdentifier>,
56) -> Result<ExternalKey, KeyError> {
57 let sec1_key = rasn::der::decode::<Sec1EcPrivateKey>(der_bytes)?;
58
59 let oid =
60 sec1_key
61 .parameters
62 .as_ref()
63 .or(inherited_oid)
64 .ok_or(KeyError::ValueConversionFailed(
65 "missing ECC parameters".to_string(),
66 ))?;
67
68 let key_bytes = sec1_key.private_key.as_ref();
69
70 if oid == &SECP_256_R_1 {
71 Ok(ExternalKey::EccP256(Box::new(
72 p256::SecretKey::from_slice(key_bytes)
73 .map_err(|e| KeyError::ValueConversionFailed(e.to_string()))?,
74 )))
75 } else if oid == &SECP_384_R_1 {
76 Ok(ExternalKey::EccP384(Box::new(
77 p384::SecretKey::from_slice(key_bytes)
78 .map_err(|e| KeyError::ValueConversionFailed(e.to_string()))?,
79 )))
80 } else if oid == &SECP_521_R_1 {
81 Ok(ExternalKey::EccP521(Box::new(
82 p521::SecretKey::from_slice(key_bytes)
83 .map_err(|e| KeyError::ValueConversionFailed(e.to_string()))?,
84 )))
85 } else {
86 Err(KeyError::UnsupportedOid(oid.to_string()))
87 }
88}
89
90pub fn ecc_to_public(
97 pub_bytes: &[u8],
98 curve_id: TpmEccCurve,
99 hash_alg: TpmAlgId,
100 symmetric: TpmtSymDefObject,
101) -> Result<TpmtPublic, KeyError> {
102 if pub_bytes.is_empty() || pub_bytes[0] != UNCOMPRESSED_POINT_TAG {
103 return Err(KeyError::InvalidEccPoint(hex::encode(pub_bytes)));
104 }
105
106 let coord_len = (pub_bytes.len() - 1) / 2;
107 let x = &pub_bytes[1..=coord_len];
108 let y = &pub_bytes[1 + coord_len..];
109
110 Ok(TpmtPublic {
111 object_type: TpmAlgId::Ecc,
112 name_alg: hash_alg,
113 object_attributes: TpmaObject::USER_WITH_AUTH | TpmaObject::DECRYPT,
114 auth_policy: Tpm2bDigest::default(),
115 parameters: TpmuPublicParms::Ecc(TpmsEccParms {
116 symmetric,
117 scheme: TpmtEccScheme {
118 scheme: TpmAlgId::Ecdh,
119 details: TpmuAsymScheme::Any(tpm2_protocol::data::TpmsSchemeHash { hash_alg }),
120 },
121 curve_id,
122 kdf: TpmtKdfScheme::default(),
123 }),
124 unique: TpmuPublicId::Ecc(TpmsEccPoint {
125 x: Tpm2bEccParameter::try_from(x)
126 .map_err(|e| KeyError::ValueConversionFailed(e.to_string()))?,
127 y: Tpm2bEccParameter::try_from(y)
128 .map_err(|e| KeyError::ValueConversionFailed(e.to_string()))?,
129 }),
130 })
131}
132
133impl ExternalKey {
134 pub fn to_public(&self, hash_alg: TpmAlgId) -> Result<TpmtPublic, KeyError> {
140 let symmetric = TpmtSymDefObject::default();
141
142 match self {
143 ExternalKey::Rsa2048(key) => super::rsa::rsa_to_public(key, 2048, hash_alg, symmetric),
144 ExternalKey::Rsa3072(key) => super::rsa::rsa_to_public(key, 3072, hash_alg, symmetric),
145 ExternalKey::Rsa4096(key) => super::rsa::rsa_to_public(key, 4096, hash_alg, symmetric),
146 ExternalKey::EccP256(secret_key) => {
147 let encoded_point = secret_key.public_key().to_encoded_point(false);
148 ecc_to_public(
149 encoded_point.as_bytes(),
150 TpmEccCurve::NistP256,
151 hash_alg,
152 symmetric,
153 )
154 }
155 ExternalKey::EccP384(secret_key) => {
156 let encoded_point = secret_key.public_key().to_encoded_point(false);
157 ecc_to_public(
158 encoded_point.as_bytes(),
159 TpmEccCurve::NistP384,
160 hash_alg,
161 symmetric,
162 )
163 }
164 ExternalKey::EccP521(secret_key) => {
165 let encoded_point = secret_key.public_key().to_encoded_point(false);
166 ecc_to_public(
167 encoded_point.as_bytes(),
168 TpmEccCurve::NistP521,
169 hash_alg,
170 symmetric,
171 )
172 }
173 }
174 }
175
176 #[must_use]
178 pub fn sensitive_blob(&self) -> Vec<u8> {
179 match self {
180 ExternalKey::Rsa2048(key) | ExternalKey::Rsa3072(key) | ExternalKey::Rsa4096(key) => {
181 key.primes()[0].to_bytes_be()
182 }
183 ExternalKey::EccP256(secret_key) => secret_key.to_bytes().to_vec(),
184 ExternalKey::EccP384(secret_key) => secret_key.to_bytes().to_vec(),
185 ExternalKey::EccP521(secret_key) => secret_key.to_bytes().to_vec(),
186 }
187 }
188}