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