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::PssMd5
295 | SignatureType::PssSha1
296 | SignatureType::PssSha224
297 | SignatureType::PssSha256
298 | SignatureType::PssSha384
299 | SignatureType::PssSha512 => {
300 return Err(Error::UnsupportedSignatureAlgorithm(signature_type));
301 }
302 })
303}
304
305fn raw_signature_to_mpis(sig_type: SignatureType, sig: &[u8]) -> Result<Vec<Vec<u8>>, Error> {
320 use SignatureType;
321 Ok(match sig_type {
322 SignatureType::EcdsaP256 | SignatureType::EcdsaP384 | SignatureType::EcdsaP521 => {
323 let sig: EcdsaSignatureValue = picky_asn1_der::from_bytes(sig).map_err(|e| {
324 error!("DER decoding error when parsing ECDSA signature: {e:?}");
325 Error::Hsm {
326 context: "DER decoding ECDSA signature",
327 source: Box::new(e),
328 }
329 })?;
330 vec![
331 sig.r.as_unsigned_bytes_be().into(),
332 sig.s.as_unsigned_bytes_be().into(),
333 ]
334 }
335 SignatureType::EdDsa => {
336 if sig.len() != 64 {
337 error!(
338 "Signature length should be exactly 64 bytes but is: {}",
339 sig.len()
340 );
341 return Err(Error::InvalidSignature {
342 context: "decoding EdDSA signature",
343 signature_type: sig_type,
344 });
345 }
346
347 vec![sig[..32].into(), sig[32..].into()]
348 }
349 SignatureType::Pkcs1 => {
350 vec![sig.into()]
352 }
353 SignatureType::PssMd5
354 | SignatureType::PssSha1
355 | SignatureType::PssSha224
356 | SignatureType::PssSha256
357 | SignatureType::PssSha384
358 | SignatureType::PssSha512 => {
359 error!("Unsupported signature type: {sig_type}");
360 return Err(Error::InvalidSignature {
361 context: "parsing signature",
362 signature_type: sig_type,
363 });
364 }
365 })
366}
367
368#[cfg(test)]
369mod tests {
370 use rstest::rstest;
371 use testresult::TestResult;
372
373 use super::*;
374
375 #[test]
376 fn parse_rsa_signature_produces_valid_data() -> TestResult {
377 let sig = raw_signature_to_mpis(SignatureType::Pkcs1, &[0, 1, 2])?;
378 assert_eq!(sig.len(), 1);
379 assert_eq!(&sig[0].as_ref(), &[0, 1, 2]);
380
381 Ok(())
382 }
383
384 #[test]
385 fn parse_ed25519_signature_produces_valid_data() -> TestResult {
386 let sig = raw_signature_to_mpis(
387 SignatureType::EdDsa,
388 &[
389 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,
390 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,
391 1, 1, 1, 1, 1, 1, 1, 1,
392 ],
393 )?;
394 assert_eq!(sig.len(), 2);
395 assert_eq!(sig[0].as_ref(), vec![2; 32]);
396 assert_eq!(sig[1].as_ref(), vec![1; 32]);
397
398 Ok(())
399 }
400
401 #[test]
402 fn parse_p256_signature_produces_valid_data() -> TestResult {
403 let sig = raw_signature_to_mpis(
404 SignatureType::EcdsaP256,
405 &[
406 48, 70, 2, 33, 0, 193, 176, 219, 0, 133, 254, 212, 239, 236, 122, 85, 239, 73, 161,
407 179, 53, 100, 172, 103, 45, 123, 21, 169, 28, 59, 150, 72, 92, 242, 9, 53, 143, 2,
408 33, 0, 165, 1, 144, 97, 102, 109, 66, 50, 185, 234, 211, 150, 253, 228, 210, 126,
409 26, 0, 189, 184, 230, 163, 36, 203, 232, 161, 12, 75, 121, 171, 45, 107,
410 ],
411 )?;
412 assert_eq!(sig.len(), 2);
413 assert_eq!(
414 sig[0].as_ref(),
415 [
416 193, 176, 219, 0, 133, 254, 212, 239, 236, 122, 85, 239, 73, 161, 179, 53, 100,
417 172, 103, 45, 123, 21, 169, 28, 59, 150, 72, 92, 242, 9, 53, 143
418 ]
419 );
420 assert_eq!(
421 sig[1].as_ref(),
422 [
423 165, 1, 144, 97, 102, 109, 66, 50, 185, 234, 211, 150, 253, 228, 210, 126, 26, 0,
424 189, 184, 230, 163, 36, 203, 232, 161, 12, 75, 121, 171, 45, 107
425 ]
426 );
427
428 Ok(())
429 }
430
431 #[test]
432 fn parse_p384_signature_produces_valid_data() -> TestResult {
433 let sig = raw_signature_to_mpis(
434 SignatureType::EcdsaP384,
435 &[
436 48, 101, 2, 49, 0, 134, 13, 108, 74, 135, 234, 174, 105, 208, 46, 109, 18, 77, 21,
437 177, 59, 73, 150, 228, 26, 244, 134, 187, 217, 172, 34, 2, 1, 229, 123, 105, 202,
438 132, 233, 72, 41, 243, 138, 127, 107, 135, 95, 139, 19, 121, 179, 170, 27, 2, 48,
439 44, 80, 117, 90, 18, 137, 36, 190, 8, 60, 201, 235, 242, 168, 164, 245, 119, 136,
440 207, 178, 237, 64, 117, 69, 218, 189, 209, 110, 2, 9, 191, 194, 70, 50, 227, 47, 6,
441 34, 8, 135, 43, 188, 236, 192, 184, 227, 59, 40,
442 ],
443 )?;
444 assert_eq!(sig.len(), 2);
445 assert_eq!(
446 sig[0].as_ref(),
447 [
448 134, 13, 108, 74, 135, 234, 174, 105, 208, 46, 109, 18, 77, 21, 177, 59, 73, 150,
449 228, 26, 244, 134, 187, 217, 172, 34, 2, 1, 229, 123, 105, 202, 132, 233, 72, 41,
450 243, 138, 127, 107, 135, 95, 139, 19, 121, 179, 170, 27
451 ]
452 );
453 assert_eq!(
454 sig[1].as_ref(),
455 [
456 44, 80, 117, 90, 18, 137, 36, 190, 8, 60, 201, 235, 242, 168, 164, 245, 119, 136,
457 207, 178, 237, 64, 117, 69, 218, 189, 209, 110, 2, 9, 191, 194, 70, 50, 227, 47, 6,
458 34, 8, 135, 43, 188, 236, 192, 184, 227, 59, 40
459 ]
460 );
461
462 Ok(())
463 }
464
465 #[test]
466 fn parse_p521_signature_produces_valid_data() -> TestResult {
467 let sig = raw_signature_to_mpis(
468 SignatureType::EcdsaP521,
469 &[
470 48, 129, 136, 2, 66, 0, 203, 246, 21, 57, 217, 6, 101, 73, 103, 113, 98, 39, 223,
471 246, 199, 136, 238, 213, 134, 163, 153, 151, 116, 237, 207, 181, 107, 183, 204,
472 110, 97, 160, 95, 160, 193, 3, 219, 46, 105, 191, 0, 139, 124, 234, 90, 125, 114,
473 115, 205, 109, 15, 193, 166, 100, 224, 108, 87, 143, 240, 65, 41, 93, 164, 166, 2,
474 2, 66, 1, 203, 115, 121, 219, 49, 18, 3, 101, 130, 153, 95, 80, 27, 148, 249, 221,
475 198, 251, 149, 118, 119, 32, 44, 160, 24, 125, 72, 161, 168, 71, 48, 138, 223, 200,
476 37, 124, 234, 17, 237, 246, 13, 123, 102, 151, 83, 95, 186, 161, 112, 41, 158, 138,
477 144, 55, 23, 110, 100, 185, 237, 13, 174, 83, 4, 153, 34,
478 ],
479 )?;
480 assert_eq!(sig.len(), 2);
481 assert_eq!(
482 sig[0].as_ref(),
483 [
484 203, 246, 21, 57, 217, 6, 101, 73, 103, 113, 98, 39, 223, 246, 199, 136, 238, 213,
485 134, 163, 153, 151, 116, 237, 207, 181, 107, 183, 204, 110, 97, 160, 95, 160, 193,
486 3, 219, 46, 105, 191, 0, 139, 124, 234, 90, 125, 114, 115, 205, 109, 15, 193, 166,
487 100, 224, 108, 87, 143, 240, 65, 41, 93, 164, 166, 2
488 ]
489 );
490 assert_eq!(
491 sig[1].as_ref(),
492 [
493 1, 203, 115, 121, 219, 49, 18, 3, 101, 130, 153, 95, 80, 27, 148, 249, 221, 198,
494 251, 149, 118, 119, 32, 44, 160, 24, 125, 72, 161, 168, 71, 48, 138, 223, 200, 37,
495 124, 234, 17, 237, 246, 13, 123, 102, 151, 83, 95, 186, 161, 112, 41, 158, 138,
496 144, 55, 23, 110, 100, 185, 237, 13, 174, 83, 4, 153, 34
497 ]
498 );
499
500 Ok(())
501 }
502
503 #[test]
504 fn rsa_digest_info_is_wrapped_sha1() -> TestResult {
505 let hash = AlgorithmIdentifier::new_sha(ShaVariant::SHA1);
506 let data = prepare_digest_data_for_openpgp(SignatureType::Pkcs1, hash, &[0; 20])?;
507
508 assert_eq!(
509 data,
510 &[
511 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,
512 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
513 ][..]
514 );
515
516 Ok(())
517 }
518
519 #[test]
520 fn rsa_digest_info_is_wrapped_sha512() -> TestResult {
521 let hash = AlgorithmIdentifier::new_sha(ShaVariant::SHA2_512);
522 let data = prepare_digest_data_for_openpgp(SignatureType::Pkcs1, hash, &[0; 64])?;
523
524 assert_eq!(
525 data,
526 &[
527 48, 81, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 3, 5, 0, 4, 64, 0, 0, 0, 0, 0,
528 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,
529 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,
530 0, 0, 0
531 ][..]
532 );
533
534 Ok(())
535 }
536
537 #[rstest]
538 #[case(SignatureType::EcdsaP256, 32)]
539 #[case(SignatureType::EcdsaP384, 48)]
540 #[case(SignatureType::EcdsaP521, 64)]
541 fn ecdsa_wrapped_up_to_max_len(
542 #[case] sig_type: SignatureType,
543 #[case] max_len: usize,
544 ) -> TestResult {
545 let digest = [0; 512 / 8];
547 let hash = AlgorithmIdentifier::new_sha(ShaVariant::SHA2_512);
548 let data = prepare_digest_data_for_openpgp(sig_type, hash, &digest)?;
549
550 assert_eq!(
554 data.len(),
555 usize::min(max_len, digest.len()),
556 "the data to be signed's length ({}) cannot exceed maximum length imposed by the curve ({})",
557 data.len(),
558 max_len
559 );
560
561 Ok(())
562 }
563
564 #[rstest]
565 fn eddsa_is_not_wrapped() -> TestResult {
566 let digest = &[0; 512 / 8][..];
568
569 let hash = AlgorithmIdentifier::new_sha(ShaVariant::SHA2_512);
570 let data = prepare_digest_data_for_openpgp(SignatureType::EdDsa, hash, digest)?;
571
572 assert_eq!(data, digest);
573
574 Ok(())
575 }
576}