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