hsmtool/util/key/
ecdsa.rs1use anyhow::{Context, Result};
6use der::{Encode, Reader, SliceReader, asn1::OctetStringRef};
7use ecdsa::elliptic_curve::pkcs8::{
8 self, AssociatedOid, DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey,
9};
10use p256::NistP256;
11use p256::ecdsa::{SigningKey, VerifyingKey};
12
13use std::convert::{AsRef, TryFrom};
14use std::path::Path;
15
16use super::KeyEncoding;
17use crate::error::HsmError;
18use crate::util::attribute::{AttrData, AttributeMap, AttributeType, KeyType, ObjectClass};
19
20fn _load_private_key(path: &Path) -> Result<SigningKey> {
21 let data = std::fs::read(path)?;
22 let sdata = std::str::from_utf8(&data);
23 let format = match sdata {
24 Ok(s) if s.contains("-----BEGIN PRIVATE KEY-----") => KeyEncoding::Pkcs8Pem,
25 _ => KeyEncoding::Der,
26 };
27 match format {
28 KeyEncoding::Pkcs8Pem => {
29 SigningKey::from_pkcs8_pem(sdata.unwrap()).context("Loading private key")
30 }
31 _ => SigningKey::from_pkcs8_der(&data).context("Loading private key"),
32 }
33}
34
35pub fn load_private_key<P: AsRef<Path>>(path: P) -> Result<SigningKey> {
38 _load_private_key(path.as_ref())
39}
40
41impl TryFrom<&SigningKey> for AttributeMap {
42 type Error = HsmError;
43 fn try_from(k: &SigningKey) -> std::result::Result<Self, Self::Error> {
46 let mut attr = AttributeMap::default();
47 attr.insert(
48 AttributeType::Class,
49 AttrData::ObjectClass(ObjectClass::PrivateKey),
50 );
51 attr.insert(AttributeType::KeyType, AttrData::KeyType(KeyType::Ec));
52 attr.insert(
53 AttributeType::EcParams,
54 AttrData::from(NistP256::OID.to_der().unwrap().as_slice()),
55 );
56 attr.insert(
57 AttributeType::Value,
58 AttrData::from(k.to_bytes().as_slice()),
59 );
60 Ok(attr)
61 }
62}
63
64impl TryFrom<&AttributeMap> for SigningKey {
65 type Error = HsmError;
66 fn try_from(a: &AttributeMap) -> std::result::Result<Self, Self::Error> {
68 let class: ObjectClass = a
69 .get(&AttributeType::Class)
70 .ok_or_else(|| HsmError::KeyError("missing class".into()))?
71 .try_into()
72 .map_err(HsmError::AttributeError)?;
73 let key_type: KeyType = a
74 .get(&AttributeType::KeyType)
75 .ok_or_else(|| HsmError::KeyError("missing key_type".into()))?
76 .try_into()
77 .map_err(HsmError::AttributeError)?;
78 if class != ObjectClass::PrivateKey || key_type != KeyType::Ec {
79 return Err(HsmError::KeyError("bad key type".into()));
80 }
81 let param: Vec<u8> = a
82 .get(&AttributeType::EcParams)
83 .ok_or_else(|| HsmError::KeyError("missing EC param".into()))?
84 .try_into()
85 .map_err(HsmError::AttributeError)?;
86 let oid = NistP256::OID.to_der().unwrap();
87 if param.as_slice() != oid.as_slice() {
88 return Err(HsmError::KeyError("not a P256 key".into()));
89 }
90 let value: Vec<u8> = a
91 .get(&AttributeType::Value)
92 .ok_or_else(|| HsmError::KeyError("missing value".into()))?
93 .try_into()
94 .map_err(HsmError::AttributeError)?;
95
96 SigningKey::from_slice(&value).map_err(|e| HsmError::KeyError(format!("{e:?}")))
97 }
98}
99
100fn _load_public_key(path: &Path) -> Result<VerifyingKey> {
101 let data = std::fs::read(path)?;
102 let sdata = std::str::from_utf8(&data);
103 let format = match sdata {
104 Ok(s) if s.contains("-----BEGIN PUBLIC KEY-----") => KeyEncoding::Pkcs8Pem,
105 _ => KeyEncoding::Der,
106 };
107 match format {
108 KeyEncoding::Pkcs8Pem => {
109 VerifyingKey::from_public_key_pem(sdata.unwrap()).context("Loading public key")
110 }
111 _ => VerifyingKey::from_public_key_der(&data).context("Loading public key"),
112 }
113}
114
115pub fn load_public_key<P: AsRef<Path>>(path: P) -> Result<VerifyingKey> {
118 _load_public_key(path.as_ref())
119}
120
121impl TryFrom<&VerifyingKey> for AttributeMap {
122 type Error = HsmError;
123 fn try_from(k: &VerifyingKey) -> std::result::Result<Self, Self::Error> {
126 let mut attr = AttributeMap::default();
127 attr.insert(
128 AttributeType::Class,
129 AttrData::ObjectClass(ObjectClass::PublicKey),
130 );
131 attr.insert(AttributeType::KeyType, AttrData::KeyType(KeyType::Ec));
132 let k_sec1 = k.to_sec1_bytes();
133 let k_os = OctetStringRef::new(&k_sec1)
134 .or(Err(HsmError::DerError("building OctetString".into())))?;
135 let k_der = k_os
136 .to_der()
137 .or(Err(HsmError::DerError("encoding".into())))?;
138 attr.insert(AttributeType::EcPoint, AttrData::from(k_der.as_slice()));
139 attr.insert(
140 AttributeType::EcParams,
141 AttrData::from(NistP256::OID.to_der().unwrap().as_slice()),
142 );
143 Ok(attr)
144 }
145}
146
147impl TryFrom<&AttributeMap> for VerifyingKey {
148 type Error = HsmError;
149 fn try_from(a: &AttributeMap) -> std::result::Result<Self, Self::Error> {
151 let class: ObjectClass = a
152 .get(&AttributeType::Class)
153 .ok_or_else(|| HsmError::KeyError("missing class".into()))?
154 .try_into()
155 .map_err(HsmError::AttributeError)?;
156 let key_type: KeyType = a
157 .get(&AttributeType::KeyType)
158 .ok_or_else(|| HsmError::KeyError("missing key_type".into()))?
159 .try_into()
160 .map_err(HsmError::AttributeError)?;
161 if class != ObjectClass::PublicKey || key_type != KeyType::Ec {
162 return Err(HsmError::KeyError("bad key type".into()));
163 }
164 let param: Vec<u8> = a
165 .get(&AttributeType::EcParams)
166 .ok_or_else(|| HsmError::KeyError("missing EC param".into()))?
167 .try_into()
168 .map_err(HsmError::AttributeError)?;
169 let oid = NistP256::OID.to_der().unwrap();
170 if param.as_slice() != oid.as_slice() {
171 return Err(HsmError::KeyError("not a P256 key".into()));
172 }
173
174 let point_asn1: Vec<u8> = a
175 .get(&AttributeType::EcPoint)
176 .ok_or_else(|| HsmError::KeyError("missing EC point".into()))?
177 .try_into()
178 .map_err(HsmError::AttributeError)?;
179 let mut reader = SliceReader::new(point_asn1.as_slice())
180 .or(Err(HsmError::DerError("instanciating reader".into())))?;
181 let point_os: OctetStringRef = reader
182 .decode()
183 .or(Err(HsmError::DerError("decoding OctetString".into())))?;
184
185 VerifyingKey::from_sec1_bytes(point_os.as_bytes())
186 .map_err(|e| HsmError::KeyError(format!("sec1: {e:?}")))
187 }
188}
189
190pub fn _save_private_key<P: AsRef<Path>>(
191 path: P,
192 key: &SigningKey,
193 enc: KeyEncoding,
194) -> Result<()> {
195 match enc {
196 KeyEncoding::Pem | KeyEncoding::Pkcs8 | KeyEncoding::Pkcs8Pem => key
197 .write_pkcs8_pem_file(path, pkcs8::LineEnding::default())
198 .context("Saving private key"),
199 KeyEncoding::Der | KeyEncoding::Pkcs8Der => {
200 key.write_pkcs8_der_file(path).context("Saving private key")
201 }
202 KeyEncoding::Pkcs1Der | KeyEncoding::Pkcs1 | KeyEncoding::Pkcs1Pem => {
203 Err(HsmError::Unsupported(format!("{enc:?}")).into())
204 }
205 }
206}
207
208pub fn save_private_key<P: AsRef<Path>>(path: P, key: &SigningKey, enc: KeyEncoding) -> Result<()> {
210 _save_private_key(path, key, enc)
211}
212
213pub fn _save_public_key<P: AsRef<Path>>(
214 path: P,
215 key: &VerifyingKey,
216 enc: KeyEncoding,
217) -> Result<()> {
218 match enc {
219 KeyEncoding::Pem | KeyEncoding::Pkcs8 | KeyEncoding::Pkcs8Pem => key
220 .write_public_key_pem_file(path, pkcs8::LineEnding::default())
221 .context("Saving public key"),
222 KeyEncoding::Der | KeyEncoding::Pkcs8Der => key
223 .write_public_key_der_file(path)
224 .context("Saving public key"),
225 KeyEncoding::Pkcs1Der | KeyEncoding::Pkcs1 | KeyEncoding::Pkcs1Pem => {
226 Err(HsmError::Unsupported(format!("{enc:?}")).into())
227 }
228 }
229}
230
231pub fn save_public_key<P: AsRef<Path>>(
233 path: P,
234 key: &VerifyingKey,
235 enc: KeyEncoding,
236) -> Result<()> {
237 _save_public_key(path, key, enc)
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243 use crate::util::testdata;
244
245 const TEST2_PRIVATE_KEY: &str = r#"{
246 "CKA_CLASS": "CKO_PRIVATE_KEY",
247 "CKA_KEY_TYPE": "CKK_EC",
248 "CKA_EC_PARAMS": "06:08:2A:86:48:CE:3D:03:01:07",
249 "CKA_VALUE": "5C:6D:5F:11:6E:AE:33:00:EC:F9:12:89:47:4E:38:D5:2F:47:D4:1A:40:B2:C8:3B:2E:D1:12:74:50:3D:5E:49"
250}"#;
251
252 const TEST2_PUBLIC_KEY: &str = r#"{
253 "CKA_CLASS": "CKO_PUBLIC_KEY",
254 "CKA_KEY_TYPE": "CKK_EC",
255 "CKA_EC_POINT": "04:41:04:1A:D2:74:FC:D8:79:7C:52:02:BF:A5:E1:0A:0E:DA:22:4C:23:68:31:8E:89:83:F6:F2:4E:40:73:3C:1E:29:35:1C:99:7D:E3:4B:93:41:F1:47:F3:D2:79:52:89:CE:80:42:D0:D1:A4:27:A3:99:5B:E0:0A:4F:5F:91:00:B9:15",
256 "CKA_EC_PARAMS": "06:08:2A:86:48:CE:3D:03:01:07"
257}"#;
258
259 #[test]
260 fn test_load_public_keys() -> Result<()> {
261 assert!(load_public_key(testdata("key/test2_pkcs8.pub.der")).is_ok());
262 assert!(load_public_key(testdata("key/test2_pkcs8.pub.pem")).is_ok());
265 Ok(())
266 }
267
268 #[test]
269 fn test_load_private_keys() -> Result<()> {
270 assert!(load_private_key(testdata("key/test2_pkcs8.der")).is_ok());
271 assert!(load_private_key(testdata("key/test2_pkcs8.pem")).is_ok());
274 Ok(())
275 }
276
277 #[test]
278 fn test_convert_key_to_hsm() -> Result<()> {
279 let k = load_private_key(testdata("key/test2_pkcs8.pem"))?;
280 let hsm = AttributeMap::try_from(&k)?;
281 assert_eq!(serde_json::to_string_pretty(&hsm)?, TEST2_PRIVATE_KEY);
282
283 let k = load_public_key(testdata("key/test2_pkcs8.pub.pem"))?;
284 let hsm = AttributeMap::try_from(&k)?;
285 assert_eq!(serde_json::to_string_pretty(&hsm)?, TEST2_PUBLIC_KEY);
286 Ok(())
287 }
288
289 #[test]
290 fn test_convert_hsm_to_key() -> Result<()> {
291 let hsm = serde_json::from_str::<AttributeMap>(TEST2_PRIVATE_KEY)?;
292 let kpriv = SigningKey::try_from(&hsm)?;
293 let k = load_private_key(testdata("key/test2_pkcs8.pem"))?;
294 assert_eq!(kpriv, k);
295
296 let hsm = serde_json::from_str::<AttributeMap>(TEST2_PUBLIC_KEY)?;
297 let kpub = VerifyingKey::try_from(&hsm)?;
298 let k = load_public_key(testdata("key/test2_pkcs8.pub.pem"))?;
299 assert_eq!(kpub, k);
300 Ok(())
301 }
302}