1use std::borrow::Cow;
4
5use base64ct::{Base64, Encoding as _};
6use log::{error, warn};
7use nethsm_sdk_rs::models::KeyType;
8use picky_asn1_x509::{
9 AlgorithmIdentifier,
10 DigestInfo,
11 ShaVariant,
12 signature::EcdsaSignatureValue,
13};
14use signstar_crypto::signer::{
15 error::Error,
16 traits::{RawPublicKey, RawSigningKey},
17};
18
19use crate::{KeyId, NetHsm, SignatureType};
20
21#[derive(Debug)]
28pub struct NetHsmKey<'a, 'b> {
29 signature_type: SignatureType,
30 nethsm: &'a NetHsm,
31 key_id: &'b KeyId,
32}
33
34pub(crate) fn nethsm_signature_type(key_type: KeyType) -> Result<SignatureType, crate::Error> {
44 Ok(match key_type {
45 KeyType::Rsa => SignatureType::Pkcs1,
46 KeyType::Curve25519 => SignatureType::EdDsa,
47 KeyType::EcP224 => {
48 return Err(crate::Error::Default(
49 "P-224 keys are unsupported by the NetHSM".into(),
50 ));
51 }
52 KeyType::EcP256 => SignatureType::EcdsaP256,
53 KeyType::EcP384 => SignatureType::EcdsaP384,
54 KeyType::EcP521 => SignatureType::EcdsaP521,
55 KeyType::Generic => {
56 return Err(crate::Error::Default(
57 "Generic keys cannot be used to sign OpenPGP data".into(),
58 ));
59 }
60 })
61}
62
63impl<'a, 'b> NetHsmKey<'a, 'b> {
64 pub fn new(nethsm: &'a NetHsm, key_id: &'b KeyId) -> Result<Self, crate::Error> {
70 let pk = nethsm.get_key(key_id)?;
71 let signature_type = nethsm_signature_type(pk.r#type)?;
72
73 Ok(Self {
74 nethsm,
75 signature_type,
76 key_id,
77 })
78 }
79}
80
81fn ec_public_key_data_to_bytes(data: Option<&str>) -> Result<Vec<u8>, Error> {
90 Base64::decode_vec(data.ok_or(Error::InvalidPublicKeyData {
91 context: "EC public key data is missing".into(),
92 })?)
93 .map_err(|e| Error::Hsm {
94 context: "deserializing EC data",
95 source: Box::new(e),
96 })
97}
98
99impl RawSigningKey for NetHsmKey<'_, '_> {
100 fn key_id(&self) -> String {
101 self.key_id.to_string()
102 }
103
104 fn sign(&self, digest: &[u8]) -> Result<Vec<Vec<u8>>, Error> {
105 let hash = AlgorithmIdentifier::new_sha(ShaVariant::SHA2_512);
106 let request_data = prepare_digest_data_for_openpgp(self.signature_type, hash, digest)?;
107
108 let sig = self
109 .nethsm
110 .sign_digest(self.key_id, self.signature_type, &request_data)
111 .map_err(|e| {
112 error!("NetHsm::sign_digest failed: {e:?}");
113 Error::Hsm {
114 context: "executing NetHsm::sign_digest",
115 source: e.into(),
116 }
117 })?;
118
119 raw_signature_to_mpis(self.signature_type, &sig)
120 }
121
122 fn certificate(&self) -> Result<Option<Vec<u8>>, Error> {
123 self.nethsm
124 .get_key_certificate(self.key_id)
125 .map_err(|e| Error::Hsm {
126 context: "executing NetHsm::get_key_certificate",
127 source: e.into(),
128 })
129 }
130
131 fn public(&self) -> Result<RawPublicKey, Error> {
132 let pk = self.nethsm.get_key(self.key_id).map_err(|e| Error::Hsm {
133 context: "executing NetHsm::get_key",
134 source: e.into(),
135 })?;
136
137 let public = &pk.public.ok_or(Error::InvalidPublicKeyData {
138 context: "public key data is missing".into(),
139 })?;
140
141 let key_type: KeyType = pk.r#type;
142 Ok(match key_type {
143 KeyType::Rsa => RawPublicKey::Rsa {
144 modulus: Base64::decode_vec(public.modulus.as_ref().ok_or(
145 Error::InvalidPublicKeyData {
146 context: "RSA modulus is missing".into(),
147 },
148 )?)
149 .map_err(|e| Error::Hsm {
150 context: "deserializing modulus",
151 source: Box::new(e),
152 })?,
153 exponent: Base64::decode_vec(public.public_exponent.as_ref().ok_or(
154 Error::InvalidPublicKeyData {
155 context: "RSA exponent is missing".into(),
156 },
157 )?)
158 .map_err(|e| Error::Hsm {
159 context: "deserializing exponent",
160 source: Box::new(e),
161 })?,
162 },
163 KeyType::Curve25519 => {
164 RawPublicKey::Ed25519(ec_public_key_data_to_bytes(public.data.as_deref())?)
165 }
166 KeyType::EcP256 => {
167 RawPublicKey::P256(ec_public_key_data_to_bytes(public.data.as_deref())?)
168 }
169 KeyType::EcP384 => {
170 RawPublicKey::P384(ec_public_key_data_to_bytes(public.data.as_deref())?)
171 }
172 KeyType::EcP521 => {
173 RawPublicKey::P521(ec_public_key_data_to_bytes(public.data.as_deref())?)
174 }
175 KeyType::EcP224 | KeyType::Generic => {
176 warn!("Unsupported key type: {key_type}");
177 return Err(Error::InvalidPublicKeyData {
178 context: format!("Unsupported key type: {key_type}"),
179 });
180 }
181 })
182 }
183}
184
185#[derive(Debug)]
192pub struct OwnedNetHsmKey {
193 signature_type: SignatureType,
194 nethsm: NetHsm,
195 key_id: KeyId,
196}
197
198impl OwnedNetHsmKey {
199 pub fn new(nethsm: NetHsm, key_id: KeyId) -> Result<Self, crate::Error> {
210 let pk = nethsm.get_key(&key_id)?;
211 let signature_type = nethsm_signature_type(pk.r#type)?;
212
213 Ok(Self {
214 nethsm,
215 signature_type,
216 key_id,
217 })
218 }
219
220 pub(crate) fn as_nethsm_key<'a>(&'a self) -> NetHsmKey<'a, 'a> {
222 NetHsmKey {
223 signature_type: self.signature_type,
224 nethsm: &self.nethsm,
225 key_id: &self.key_id,
226 }
227 }
228}
229
230impl RawSigningKey for OwnedNetHsmKey {
231 fn key_id(&self) -> String {
232 self.as_nethsm_key().key_id()
233 }
234
235 fn sign(&self, digest: &[u8]) -> Result<Vec<Vec<u8>>, Error> {
236 self.as_nethsm_key().sign(digest)
237 }
238
239 fn certificate(&self) -> Result<Option<Vec<u8>>, Error> {
240 self.as_nethsm_key().certificate()
241 }
242
243 fn public(&self) -> Result<RawPublicKey, Error> {
244 self.as_nethsm_key().public()
245 }
246}
247
248fn prepare_digest_data_for_openpgp(
263 signature_type: SignatureType,
264 oid: AlgorithmIdentifier,
265 digest: &[u8],
266) -> Result<Cow<'_, [u8]>, Error> {
267 Ok(match signature_type {
268 SignatureType::Pkcs1 => picky_asn1_der::to_vec(&DigestInfo {
273 oid,
274 digest: digest.to_vec().into(),
275 })
276 .map_err(|e| {
277 error!("Encoding signature to PKCS#1 format failed: {e:?}");
278 Error::Hsm {
279 context: "preparing digest data",
280 source: Box::new(e),
281 }
282 })?
283 .into(),
284
285 SignatureType::EcdsaP256 => digest[..usize::min(32, digest.len())].into(),
288 SignatureType::EcdsaP384 => digest[..usize::min(48, digest.len())].into(),
289
290 SignatureType::EdDsa | SignatureType::EcdsaP521 => digest.into(),
293
294 SignatureType::PssSha1
295 | SignatureType::PssSha224
296 | SignatureType::PssSha256
297 | SignatureType::PssSha384
298 | SignatureType::PssSha512 => {
299 return Err(Error::UnsupportedSignatureAlgorithm(signature_type));
300 }
301 })
302}
303
304fn raw_signature_to_mpis(sig_type: SignatureType, sig: &[u8]) -> Result<Vec<Vec<u8>>, Error> {
319 use SignatureType;
320 Ok(match sig_type {
321 SignatureType::EcdsaP256 | SignatureType::EcdsaP384 | SignatureType::EcdsaP521 => {
322 let sig: EcdsaSignatureValue = picky_asn1_der::from_bytes(sig).map_err(|e| {
323 error!("DER decoding error when parsing ECDSA signature: {e:?}");
324 Error::Hsm {
325 context: "DER decoding ECDSA signature",
326 source: Box::new(e),
327 }
328 })?;
329 vec![
330 sig.r.as_unsigned_bytes_be().into(),
331 sig.s.as_unsigned_bytes_be().into(),
332 ]
333 }
334 SignatureType::EdDsa => {
335 if sig.len() != 64 {
336 error!(
337 "Signature length should be exactly 64 bytes but is: {}",
338 sig.len()
339 );
340 return Err(Error::InvalidSignature {
341 context: "decoding EdDSA signature",
342 signature_type: sig_type,
343 });
344 }
345
346 vec![sig[..32].into(), sig[32..].into()]
347 }
348 SignatureType::Pkcs1 => {
349 vec![sig.into()]
351 }
352 SignatureType::PssSha1
353 | SignatureType::PssSha224
354 | SignatureType::PssSha256
355 | SignatureType::PssSha384
356 | SignatureType::PssSha512 => {
357 error!("Unsupported signature type: {sig_type}");
358 return Err(Error::InvalidSignature {
359 context: "parsing signature",
360 signature_type: sig_type,
361 });
362 }
363 })
364}
365
366#[cfg(test)]
367mod tests {
368 use rstest::rstest;
369 use testresult::TestResult;
370
371 use super::*;
372
373 #[test]
374 fn parse_rsa_signature_produces_valid_data() -> TestResult {
375 let sig = raw_signature_to_mpis(SignatureType::Pkcs1, &[0, 1, 2])?;
376 assert_eq!(sig.len(), 1);
377 assert_eq!(&sig[0].as_ref(), &[0, 1, 2]);
378
379 Ok(())
380 }
381
382 #[test]
383 fn parse_ed25519_signature_produces_valid_data() -> TestResult {
384 let sig = raw_signature_to_mpis(
385 SignatureType::EdDsa,
386 &[
387 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
388 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
389 1, 1, 1, 1, 1, 1, 1, 1,
390 ],
391 )?;
392 assert_eq!(sig.len(), 2);
393 assert_eq!(sig[0].as_ref(), vec![2; 32]);
394 assert_eq!(sig[1].as_ref(), vec![1; 32]);
395
396 Ok(())
397 }
398
399 #[test]
400 fn parse_p256_signature_produces_valid_data() -> TestResult {
401 let sig = raw_signature_to_mpis(
402 SignatureType::EcdsaP256,
403 &[
404 48, 70, 2, 33, 0, 193, 176, 219, 0, 133, 254, 212, 239, 236, 122, 85, 239, 73, 161,
405 179, 53, 100, 172, 103, 45, 123, 21, 169, 28, 59, 150, 72, 92, 242, 9, 53, 143, 2,
406 33, 0, 165, 1, 144, 97, 102, 109, 66, 50, 185, 234, 211, 150, 253, 228, 210, 126,
407 26, 0, 189, 184, 230, 163, 36, 203, 232, 161, 12, 75, 121, 171, 45, 107,
408 ],
409 )?;
410 assert_eq!(sig.len(), 2);
411 assert_eq!(
412 sig[0].as_ref(),
413 [
414 193, 176, 219, 0, 133, 254, 212, 239, 236, 122, 85, 239, 73, 161, 179, 53, 100,
415 172, 103, 45, 123, 21, 169, 28, 59, 150, 72, 92, 242, 9, 53, 143
416 ]
417 );
418 assert_eq!(
419 sig[1].as_ref(),
420 [
421 165, 1, 144, 97, 102, 109, 66, 50, 185, 234, 211, 150, 253, 228, 210, 126, 26, 0,
422 189, 184, 230, 163, 36, 203, 232, 161, 12, 75, 121, 171, 45, 107
423 ]
424 );
425
426 Ok(())
427 }
428
429 #[test]
430 fn parse_p384_signature_produces_valid_data() -> TestResult {
431 let sig = raw_signature_to_mpis(
432 SignatureType::EcdsaP384,
433 &[
434 48, 101, 2, 49, 0, 134, 13, 108, 74, 135, 234, 174, 105, 208, 46, 109, 18, 77, 21,
435 177, 59, 73, 150, 228, 26, 244, 134, 187, 217, 172, 34, 2, 1, 229, 123, 105, 202,
436 132, 233, 72, 41, 243, 138, 127, 107, 135, 95, 139, 19, 121, 179, 170, 27, 2, 48,
437 44, 80, 117, 90, 18, 137, 36, 190, 8, 60, 201, 235, 242, 168, 164, 245, 119, 136,
438 207, 178, 237, 64, 117, 69, 218, 189, 209, 110, 2, 9, 191, 194, 70, 50, 227, 47, 6,
439 34, 8, 135, 43, 188, 236, 192, 184, 227, 59, 40,
440 ],
441 )?;
442 assert_eq!(sig.len(), 2);
443 assert_eq!(
444 sig[0].as_ref(),
445 [
446 134, 13, 108, 74, 135, 234, 174, 105, 208, 46, 109, 18, 77, 21, 177, 59, 73, 150,
447 228, 26, 244, 134, 187, 217, 172, 34, 2, 1, 229, 123, 105, 202, 132, 233, 72, 41,
448 243, 138, 127, 107, 135, 95, 139, 19, 121, 179, 170, 27
449 ]
450 );
451 assert_eq!(
452 sig[1].as_ref(),
453 [
454 44, 80, 117, 90, 18, 137, 36, 190, 8, 60, 201, 235, 242, 168, 164, 245, 119, 136,
455 207, 178, 237, 64, 117, 69, 218, 189, 209, 110, 2, 9, 191, 194, 70, 50, 227, 47, 6,
456 34, 8, 135, 43, 188, 236, 192, 184, 227, 59, 40
457 ]
458 );
459
460 Ok(())
461 }
462
463 #[test]
464 fn parse_p521_signature_produces_valid_data() -> TestResult {
465 let sig = raw_signature_to_mpis(
466 SignatureType::EcdsaP521,
467 &[
468 48, 129, 136, 2, 66, 0, 203, 246, 21, 57, 217, 6, 101, 73, 103, 113, 98, 39, 223,
469 246, 199, 136, 238, 213, 134, 163, 153, 151, 116, 237, 207, 181, 107, 183, 204,
470 110, 97, 160, 95, 160, 193, 3, 219, 46, 105, 191, 0, 139, 124, 234, 90, 125, 114,
471 115, 205, 109, 15, 193, 166, 100, 224, 108, 87, 143, 240, 65, 41, 93, 164, 166, 2,
472 2, 66, 1, 203, 115, 121, 219, 49, 18, 3, 101, 130, 153, 95, 80, 27, 148, 249, 221,
473 198, 251, 149, 118, 119, 32, 44, 160, 24, 125, 72, 161, 168, 71, 48, 138, 223, 200,
474 37, 124, 234, 17, 237, 246, 13, 123, 102, 151, 83, 95, 186, 161, 112, 41, 158, 138,
475 144, 55, 23, 110, 100, 185, 237, 13, 174, 83, 4, 153, 34,
476 ],
477 )?;
478 assert_eq!(sig.len(), 2);
479 assert_eq!(
480 sig[0].as_ref(),
481 [
482 203, 246, 21, 57, 217, 6, 101, 73, 103, 113, 98, 39, 223, 246, 199, 136, 238, 213,
483 134, 163, 153, 151, 116, 237, 207, 181, 107, 183, 204, 110, 97, 160, 95, 160, 193,
484 3, 219, 46, 105, 191, 0, 139, 124, 234, 90, 125, 114, 115, 205, 109, 15, 193, 166,
485 100, 224, 108, 87, 143, 240, 65, 41, 93, 164, 166, 2
486 ]
487 );
488 assert_eq!(
489 sig[1].as_ref(),
490 [
491 1, 203, 115, 121, 219, 49, 18, 3, 101, 130, 153, 95, 80, 27, 148, 249, 221, 198,
492 251, 149, 118, 119, 32, 44, 160, 24, 125, 72, 161, 168, 71, 48, 138, 223, 200, 37,
493 124, 234, 17, 237, 246, 13, 123, 102, 151, 83, 95, 186, 161, 112, 41, 158, 138,
494 144, 55, 23, 110, 100, 185, 237, 13, 174, 83, 4, 153, 34
495 ]
496 );
497
498 Ok(())
499 }
500
501 #[test]
502 fn rsa_digest_info_is_wrapped_sha1() -> TestResult {
503 let hash = AlgorithmIdentifier::new_sha(ShaVariant::SHA1);
504 let data = prepare_digest_data_for_openpgp(SignatureType::Pkcs1, hash, &[0; 20])?;
505
506 assert_eq!(
507 data,
508 &[
509 48, 33, 48, 9, 6, 5, 43, 14, 3, 2, 26, 5, 0, 4, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
510 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
511 ][..]
512 );
513
514 Ok(())
515 }
516
517 #[test]
518 fn rsa_digest_info_is_wrapped_sha512() -> TestResult {
519 let hash = AlgorithmIdentifier::new_sha(ShaVariant::SHA2_512);
520 let data = prepare_digest_data_for_openpgp(SignatureType::Pkcs1, hash, &[0; 64])?;
521
522 assert_eq!(
523 data,
524 &[
525 48, 81, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 3, 5, 0, 4, 64, 0, 0, 0, 0, 0,
526 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
527 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
528 0, 0, 0
529 ][..]
530 );
531
532 Ok(())
533 }
534
535 #[rstest]
536 #[case(SignatureType::EcdsaP256, 32)]
537 #[case(SignatureType::EcdsaP384, 48)]
538 #[case(SignatureType::EcdsaP521, 64)]
539 fn ecdsa_wrapped_up_to_max_len(
540 #[case] sig_type: SignatureType,
541 #[case] max_len: usize,
542 ) -> TestResult {
543 let digest = [0; 512 / 8];
545 let hash = AlgorithmIdentifier::new_sha(ShaVariant::SHA2_512);
546 let data = prepare_digest_data_for_openpgp(sig_type, hash, &digest)?;
547
548 assert_eq!(
552 data.len(),
553 usize::min(max_len, digest.len()),
554 "the data to be signed's length ({}) cannot exceed maximum length imposed by the curve ({})",
555 data.len(),
556 max_len
557 );
558
559 Ok(())
560 }
561
562 #[rstest]
563 fn eddsa_is_not_wrapped() -> TestResult {
564 let digest = &[0; 512 / 8][..];
566
567 let hash = AlgorithmIdentifier::new_sha(ShaVariant::SHA2_512);
568 let data = prepare_digest_data_for_openpgp(SignatureType::EdDsa, hash, digest)?;
569
570 assert_eq!(data, digest);
571
572 Ok(())
573 }
574}