1use std::{backtrace::Backtrace, borrow::Cow, fmt::Debug};
4
5use base64ct::{Base64, Encoding as _};
6use chrono::{DateTime, Utc};
7use digest::DynDigest;
8use ed25519_dalek::VerifyingKey;
9use log::{error, warn};
10use pgp::{
11 composed::{
12 ArmorOptions,
13 Deserializable as _,
14 SignedPublicKey,
15 SignedSecretKey,
16 StandaloneSignature,
17 },
18 crypto::{hash::HashAlgorithm, public_key::PublicKeyAlgorithm},
19 packet::{
20 Notation,
21 PacketTrait,
22 PubKeyInner,
23 PublicKey,
24 Signature,
25 SignatureConfig,
26 SignatureType,
27 Subpacket,
28 SubpacketData,
29 UserId,
30 },
31 ser::Serialize,
32 types::{
33 CompressionAlgorithm,
34 EcdsaPublicParams,
35 KeyDetails as _,
36 KeyId,
37 KeyVersion,
38 Mpi,
39 Password,
40 PlainSecretParams,
41 PublicKeyTrait as _,
42 PublicParams,
43 RsaPublicParams,
44 SecretKeyTrait,
45 SecretParams,
46 SignatureBytes,
47 },
48};
49use picky_asn1_x509::{
50 AlgorithmIdentifier,
51 DigestInfo,
52 ShaVariant,
53 signature::EcdsaSignatureValue,
54};
55use rsa::BigUint;
56use rsa::traits::PublicKeyParts as _;
57use sha2::digest::Digest as _;
58
59use crate::{
60 KeyMechanism,
61 KeyType,
62 NetHsm,
63 OpenPgpKeyUsageFlags,
64 OpenPgpUserId,
65 OpenPgpVersion,
66 PrivateKeyImport,
67 key_type_matches_length,
68};
69
70#[derive(Debug, thiserror::Error)]
72pub enum Error {
73 #[error("Decoding Base64 string failed: {0}")]
75 Base64Decode(#[from] base64ct::Error),
76
77 #[error("Certificate for the key \"{0}\" has not been initialized")]
79 CertificateMissing(crate::KeyId),
80
81 #[error("Elliptic curve error: {0}")]
83 EllipticCurve(#[from] p256::elliptic_curve::Error),
84
85 #[error("Key data invalid: {0}")]
87 KeyData(String),
88
89 #[error("rPGP error: {0}")]
91 Pgp(#[from] pgp::errors::Error),
92
93 #[error("Transferable Secret Key is passphrase protected")]
95 PrivateKeyPassphraseProtected,
96
97 #[error("Unsupported multiple component keys")]
99 UnsupportedMultipleComponentKeys,
100
101 #[error("Unsupported key format: {public_params:?}")]
103 UnsupportedKeyFormat {
104 public_params: Box<PublicParams>,
106 },
107
108 #[error("A signstar_crypto key error:\n{0}")]
110 SignstarCryptoKey(#[from] signstar_crypto::key::Error),
111}
112
113struct HsmKey<'a, 'b> {
118 public_key: PublicKey,
119 nethsm: &'a NetHsm,
120 key_id: &'b crate::KeyId,
121}
122
123impl Debug for HsmKey<'_, '_> {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 f.debug_struct("HsmKey")
126 .field("public_key", &self.public_key)
127 .field("key_id", &self.key_id)
128 .finish()
129 }
130}
131
132#[inline]
138fn to_rpgp_error(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> pgp::errors::Error {
139 pgp::errors::Error::IO {
140 source: std::io::Error::other(e),
141 backtrace: Some(Backtrace::capture()),
142 }
143}
144
145fn parse_signature(sig_type: crate::SignatureType, sig: &[u8]) -> pgp::errors::Result<Vec<Mpi>> {
147 use crate::SignatureType;
148 Ok(match sig_type {
149 SignatureType::EcdsaP256 | SignatureType::EcdsaP384 | SignatureType::EcdsaP521 => {
150 let sig: EcdsaSignatureValue = picky_asn1_der::from_bytes(sig).map_err(|e| {
151 error!("DER decoding error when parsing ECDSA signature: {e:?}");
152 to_rpgp_error(e)
153 })?;
154 vec![
155 Mpi::from_slice(sig.r.as_unsigned_bytes_be()),
156 Mpi::from_slice(sig.s.as_unsigned_bytes_be()),
157 ]
158 }
159 SignatureType::EdDsa => {
160 if sig.len() != 64 {
161 return Err(pgp::errors::Error::InvalidKeyLength);
162 }
163
164 vec![Mpi::from_slice(&sig[..32]), Mpi::from_slice(&sig[32..])]
165 }
166 SignatureType::Pkcs1 => {
167 vec![Mpi::from_slice(sig)]
169 }
170 _ => {
171 warn!("Unsupported signature type: {sig_type}");
172 return Err(pgp::errors::Error::InvalidInput {
173 backtrace: Some(Backtrace::capture()),
174 });
175 }
176 })
177}
178
179impl<'a, 'b> HsmKey<'a, 'b> {
180 fn new(nethsm: &'a NetHsm, public_key: PublicKey, key_id: &'b crate::KeyId) -> Self {
182 Self {
183 nethsm,
184 public_key,
185 key_id,
186 }
187 }
188
189 fn sign_mode(&self) -> pgp::errors::Result<crate::SignatureType> {
191 Ok(match self.public_key.public_params() {
192 PublicParams::ECDSA(ecdsa) => match ecdsa {
193 EcdsaPublicParams::P256 { .. } => crate::SignatureType::EcdsaP256,
194 EcdsaPublicParams::P384 { .. } => crate::SignatureType::EcdsaP384,
195 EcdsaPublicParams::P521 { .. } => crate::SignatureType::EcdsaP521,
196 _ => {
197 warn!("Unsupported ECDSA parameter type: {ecdsa:?}");
198 return Err(pgp::errors::Error::InvalidInput {
199 backtrace: Some(Backtrace::capture()),
200 });
201 }
202 },
203 PublicParams::EdDSALegacy { .. } => crate::SignatureType::EdDsa,
204 PublicParams::RSA { .. } => crate::SignatureType::Pkcs1,
205 params => {
206 warn!("Unsupported signing parameters: {params:?}");
207 return Err(pgp::errors::Error::InvalidInput {
208 backtrace: Some(Backtrace::capture()),
209 });
210 }
211 })
212 }
213}
214
215impl pgp::types::KeyDetails for HsmKey<'_, '_> {
216 fn version(&self) -> KeyVersion {
217 self.public_key.version()
218 }
219
220 fn fingerprint(&self) -> pgp::types::Fingerprint {
221 self.public_key.fingerprint()
222 }
223
224 fn key_id(&self) -> KeyId {
225 self.public_key.key_id()
226 }
227
228 fn algorithm(&self) -> PublicKeyAlgorithm {
229 self.public_key.algorithm()
230 }
231}
232
233fn prepare_digest_data(
243 signature_type: crate::SignatureType,
244 hash: HashAlgorithm,
245 digest: &[u8],
246) -> pgp::errors::Result<Cow<'_, [u8]>> {
247 Ok(match signature_type {
248 crate::SignatureType::Pkcs1 => picky_asn1_der::to_vec(&DigestInfo {
253 oid: hash_to_oid(hash)?,
254 digest: digest.to_vec().into(),
255 })
256 .map_err(|e| {
257 error!("Encoding signature to PKCS#1 format failed: {e:?}");
258 to_rpgp_error(e)
259 })?
260 .into(),
261
262 crate::SignatureType::EcdsaP256 => digest[..usize::min(32, digest.len())].into(),
265 crate::SignatureType::EcdsaP384 => digest[..usize::min(48, digest.len())].into(),
266
267 _ => digest.into(),
270 })
271}
272
273impl SecretKeyTrait for HsmKey<'_, '_> {
274 fn create_signature(
291 &self,
292 _key_pw: &Password,
293 hash: HashAlgorithm,
294 data: &[u8],
295 ) -> pgp::errors::Result<SignatureBytes> {
296 let signature_type = self.sign_mode()?;
297 let request_data = prepare_digest_data(signature_type, hash, data)?;
298
299 let sig = self
300 .nethsm
301 .sign_digest(self.key_id, signature_type, &request_data)
302 .map_err(|e| {
303 error!("NetHsm::sign_digest failed: {e:?}");
304 to_rpgp_error(e)
305 })?;
306
307 Ok(SignatureBytes::Mpis(parse_signature(signature_type, &sig)?))
308 }
309
310 fn hash_alg(&self) -> HashAlgorithm {
316 HashAlgorithm::Sha512
317 }
318}
319
320pub fn add_certificate(
322 nethsm: &NetHsm,
323 flags: OpenPgpKeyUsageFlags,
324 key_id: &crate::KeyId,
325 user_id: OpenPgpUserId,
326 created_at: DateTime<Utc>,
327 version: OpenPgpVersion,
328) -> Result<Vec<u8>, Error> {
329 if version != OpenPgpVersion::V4 {
330 unimplemented!(
331 "Support for creating OpenPGP {version} certificates is not yet implemented!"
332 );
333 }
334
335 let public_key = nethsm.get_key(key_id).map_err(to_rpgp_error)?;
336 let signer = HsmKey::new(nethsm, hsm_pk_to_pgp_pk(public_key, created_at)?, key_id);
337
338 let composed_pk = pgp::composed::PublicKey::new(
339 signer.public_key.clone(),
340 pgp::composed::KeyDetails::new(
341 Some(UserId::from_str(Default::default(), user_id.as_ref())?),
342 vec![],
343 vec![],
344 flags.into(),
345 Default::default(),
346 Default::default(),
347 Default::default(),
348 vec![CompressionAlgorithm::Uncompressed].into(),
349 vec![].into(),
350 ),
351 vec![],
352 );
353
354 let signed_pk = composed_pk.sign(
355 rand::thread_rng(),
356 &signer,
357 &signer.public_key,
358 &Password::empty(),
359 )?;
360
361 let mut buffer = vec![];
362 signed_pk.to_writer(&mut buffer)?;
363 Ok(buffer)
364}
365
366fn hash_to_oid(hash: HashAlgorithm) -> pgp::errors::Result<AlgorithmIdentifier> {
368 Ok(AlgorithmIdentifier::new_sha(match hash {
369 HashAlgorithm::Sha1 => ShaVariant::SHA1,
370 HashAlgorithm::Sha256 => ShaVariant::SHA2_256,
371 HashAlgorithm::Sha384 => ShaVariant::SHA2_384,
372 HashAlgorithm::Sha512 => ShaVariant::SHA2_512,
373 HashAlgorithm::Sha224 => ShaVariant::SHA2_224,
374 HashAlgorithm::Sha3_256 => ShaVariant::SHA3_256,
375 HashAlgorithm::Sha3_512 => ShaVariant::SHA3_512,
376 _ => {
377 warn!("Unsupported hash algorithm: {hash}");
378 return Err(pgp::errors::Error::InvalidInput {
379 backtrace: Some(Backtrace::capture()),
380 });
381 }
382 }))
383}
384
385pub fn tsk_to_private_key_import(
395 key_data: &[u8],
396) -> Result<(PrivateKeyImport, KeyMechanism), Error> {
397 let key = SignedSecretKey::from_bytes(key_data)?;
398 if !key.secret_subkeys.is_empty() {
399 return Err(Error::UnsupportedMultipleComponentKeys);
400 }
401 let SecretParams::Plain(secret) = key.primary_key.secret_params() else {
402 return Err(Error::PrivateKeyPassphraseProtected);
403 };
404 Ok(match (secret, key.public_key().public_params()) {
405 (PlainSecretParams::RSA(secret), PublicParams::RSA(public)) => {
406 key_type_matches_length(
408 KeyType::Rsa,
409 Some(public.key.n().to_bytes_be().len() as u32 * 8),
410 )
411 .map_err(to_rpgp_error)?;
412
413 let (_d, p, q, _u) = secret.to_bytes();
414
415 (
416 PrivateKeyImport::from_rsa(p, q, public.key.e().to_bytes_be().to_vec()),
417 KeyMechanism::RsaSignaturePkcs1,
418 )
419 }
420 (PlainSecretParams::ECDSA(secret_key), _) => {
421 let ec = if let PublicParams::ECDSA(pp) = key.primary_key.public_key().public_params() {
422 match pp {
423 EcdsaPublicParams::P256 { .. } => crate::KeyType::EcP256,
424 EcdsaPublicParams::P384 { .. } => crate::KeyType::EcP384,
425 EcdsaPublicParams::P521 { .. } => crate::KeyType::EcP521,
426 pp => {
427 warn!("Unsupported ECDSA parameters: {pp:?}");
428 return Err(Error::UnsupportedKeyFormat {
429 public_params: Box::new(key.public_key().public_params().clone()),
430 })?;
431 }
432 }
433 } else {
434 return Err(Error::UnsupportedKeyFormat {
435 public_params: Box::new(key.public_key().public_params().clone()),
436 });
437 };
438
439 let bytes = match secret_key {
440 pgp::crypto::ecdsa::SecretKey::P256(secret_key) => secret_key.to_bytes().to_vec(),
441 pgp::crypto::ecdsa::SecretKey::P384(secret_key) => secret_key.to_bytes().to_vec(),
442 pgp::crypto::ecdsa::SecretKey::P521(secret_key) => secret_key.to_bytes().to_vec(),
443
444 pgp::crypto::ecdsa::SecretKey::Secp256k1(secret_key) => {
445 secret_key.to_bytes().to_vec()
446 }
447 secret_key => {
448 warn!("Unsupported secret key parameters: {secret_key:?}");
449 return Err(Error::UnsupportedKeyFormat {
450 public_params: Box::new(key.public_key().public_params().clone()),
451 })?;
452 }
453 };
454
455 (
456 PrivateKeyImport::from_raw_bytes(ec, bytes).map_err(to_rpgp_error)?,
457 KeyMechanism::EcdsaSignature,
458 )
459 }
460 (PlainSecretParams::Ed25519Legacy(bytes), _) => (
461 PrivateKeyImport::from_raw_bytes(crate::KeyType::Curve25519, bytes.as_bytes())
462 .map_err(to_rpgp_error)?,
463 KeyMechanism::EdDsaSignature,
464 ),
465 (_, public_params) => {
466 return Err(Error::UnsupportedKeyFormat {
467 public_params: Box::new(public_params.clone()),
468 });
469 }
470 })
471}
472
473pub fn sign(nethsm: &NetHsm, key_id: &crate::KeyId, message: &[u8]) -> Result<Vec<u8>, Error> {
514 let Some(public_key) = nethsm.get_key_certificate(key_id).map_err(to_rpgp_error)? else {
515 return Err(Error::CertificateMissing(key_id.clone()));
516 };
517
518 let signer = HsmKey::new(
519 nethsm,
520 SignedPublicKey::from_bytes(&*public_key)?.primary_key,
521 key_id,
522 );
523
524 let mut sig_config =
525 SignatureConfig::v4(SignatureType::Binary, signer.algorithm(), signer.hash_alg());
526 sig_config.hashed_subpackets = vec![
527 Subpacket::regular(SubpacketData::SignatureCreationTime(
528 std::time::SystemTime::now().into(),
529 ))?,
530 Subpacket::regular(SubpacketData::Issuer(signer.key_id()))?,
531 Subpacket::regular(SubpacketData::IssuerFingerprint(signer.fingerprint()))?,
532 ];
533
534 let mut hasher = sig_config.hash_alg.new_hasher().map_err(to_rpgp_error)?;
535 sig_config.hash_data_to_sign(&mut hasher, message)?;
536
537 let len = sig_config.hash_signature_data(&mut hasher)?;
538
539 hasher.update(&sig_config.trailer(len)?);
540
541 let hash = &hasher.finalize()[..];
542
543 let signed_hash_value = [hash[0], hash[1]];
544 let raw_sig = signer.create_signature(&Password::empty(), sig_config.hash_alg, hash)?;
545
546 let signature = Signature::from_config(sig_config, signed_hash_value, raw_sig)?;
547
548 let mut out = vec![];
549 signature.to_writer_with_header(&mut out)?;
550
551 Ok(out)
552}
553
554#[derive(Clone, Default)]
564struct Hasher(sha2::Sha512);
565
566impl DynDigest for Hasher {
567 fn update(&mut self, data: &[u8]) {
571 self.0.update(data);
572 }
573
574 fn finalize_into(self, buf: &mut [u8]) -> Result<(), digest::InvalidBufferSize> {
580 sha2::digest::DynDigest::finalize_into(self.0, buf)
581 .map_err(|_| digest::InvalidBufferSize)?;
582 Ok(())
583 }
584
585 fn finalize_into_reset(&mut self, out: &mut [u8]) -> Result<(), digest::InvalidBufferSize> {
591 sha2::digest::DynDigest::finalize_into_reset(&mut self.0, out)
592 .map_err(|_| digest::InvalidBufferSize)?;
593 Ok(())
594 }
595
596 fn reset(&mut self) {
598 sha2::digest::DynDigest::reset(&mut self.0)
599 }
600
601 fn output_size(&self) -> usize {
603 sha2::digest::DynDigest::output_size(&self.0)
604 }
605
606 fn box_clone(&self) -> Box<dyn DynDigest> {
608 Box::new(self.clone())
609 }
610}
611
612pub fn sign_hasher_state(
647 nethsm: &NetHsm,
648 key_id: &crate::KeyId,
649 state: sha2::Sha512,
650) -> Result<String, Error> {
651 let Some(public_key) = nethsm.get_key_certificate(key_id).map_err(to_rpgp_error)? else {
652 return Err(Error::CertificateMissing(key_id.clone()));
653 };
654
655 let signer = HsmKey::new(
656 nethsm,
657 SignedPublicKey::from_bytes(public_key.as_slice())?.primary_key,
658 key_id,
659 );
660
661 let hasher = state.clone();
662
663 let file_hash = Box::new(hasher).finalize().to_vec();
664
665 let sig_config = {
666 let mut sig_config =
667 SignatureConfig::v4(SignatureType::Binary, signer.algorithm(), signer.hash_alg());
668 sig_config.hashed_subpackets = vec![
669 Subpacket::regular(SubpacketData::SignatureCreationTime(
670 std::time::SystemTime::now().into(),
671 ))?,
672 Subpacket::regular(SubpacketData::Issuer(signer.key_id()))?,
673 Subpacket::regular(SubpacketData::IssuerFingerprint(signer.fingerprint()))?,
674 Subpacket::regular(SubpacketData::Notation(Notation {
675 readable: false,
676 name: "data-digest@archlinux.org".into(),
677 value: file_hash.into(),
678 }))?,
679 ];
680 sig_config
681 };
682
683 let mut hasher = Box::new(Hasher(state.clone())) as Box<dyn DynDigest + Send>;
684
685 let len = sig_config.hash_signature_data(&mut hasher)?;
686
687 hasher.update(&sig_config.trailer(len)?);
688
689 let hash = &hasher.finalize()[..];
690
691 let signed_hash_value = [hash[0], hash[1]];
692
693 let raw_sig = signer.create_signature(&Password::empty(), sig_config.hash_alg, hash)?;
694
695 let signature = pgp::packet::Signature::from_config(sig_config, signed_hash_value, raw_sig)?;
696
697 let signature = StandaloneSignature { signature };
698 Ok(signature.to_armored_string(ArmorOptions::default())?)
699}
700
701fn ecdsa_to_public_key(
712 created_at: DateTime<Utc>,
713 key: EcdsaPublicParams,
714) -> Result<PublicKey, Error> {
715 Ok(PublicKey::from_inner(PubKeyInner::new(
716 KeyVersion::V4,
717 PublicKeyAlgorithm::ECDSA,
718 created_at,
719 None,
720 PublicParams::ECDSA(key),
721 )?)?)
722}
723
724fn data_to_bytes(data: Option<&str>) -> Result<Vec<u8>, Error> {
733 Ok(Base64::decode_vec(data.ok_or(Error::KeyData(
734 "missing EC public key data".into(),
735 ))?)?)
736}
737
738fn hsm_pk_to_pgp_pk(
744 pk: nethsm_sdk_rs::models::PublicKey,
745 created_at: DateTime<Utc>,
746) -> Result<PublicKey, Error> {
747 let public = &pk
748 .public
749 .ok_or(Error::KeyData("missing public key data".into()))?;
750
751 let key_type: KeyType = pk.r#type.try_into()?;
752 Ok(match key_type {
753 KeyType::Rsa => PublicKey::from_inner(PubKeyInner::new(
754 KeyVersion::V4,
755 PublicKeyAlgorithm::RSA,
756 created_at,
757 None,
758 PublicParams::RSA(RsaPublicParams {
759 key: rsa::RsaPublicKey::new(
760 BigUint::from_bytes_be(&Base64::decode_vec(
761 public
762 .modulus
763 .as_ref()
764 .ok_or(Error::KeyData("missing RSA modulus".into()))?,
765 )?),
766 BigUint::from_bytes_be(&Base64::decode_vec(
767 public
768 .public_exponent
769 .as_ref()
770 .ok_or(Error::KeyData("missing RSA exponent".into()))?,
771 )?),
772 )
773 .map_err(to_rpgp_error)?,
774 }),
775 )?)?,
776
777 KeyType::Curve25519 => {
778 let pubkey: &[u8] = &data_to_bytes(public.data.as_deref())?;
779
780 PublicKey::from_inner(PubKeyInner::new(
781 KeyVersion::V4,
782 PublicKeyAlgorithm::EdDSALegacy,
783 created_at,
784 None,
785 PublicParams::EdDSALegacy(pgp::types::EddsaLegacyPublicParams::Ed25519 {
786 key: VerifyingKey::from_bytes(pubkey.try_into().map_err(to_rpgp_error)?)
787 .map_err(to_rpgp_error)?,
788 }),
789 )?)?
790 }
791
792 KeyType::EcP256 => ecdsa_to_public_key(
793 created_at,
794 EcdsaPublicParams::P256 {
795 key: p256::PublicKey::from_sec1_bytes(&data_to_bytes(public.data.as_deref())?)?,
796 },
797 )?,
798 KeyType::EcP384 => ecdsa_to_public_key(
799 created_at,
800 EcdsaPublicParams::P384 {
801 key: p384::PublicKey::from_sec1_bytes(&data_to_bytes(public.data.as_deref())?)?,
802 },
803 )?,
804 KeyType::EcP521 => ecdsa_to_public_key(
805 created_at,
806 EcdsaPublicParams::P521 {
807 key: p521::PublicKey::from_sec1_bytes(&data_to_bytes(public.data.as_deref())?)?,
808 },
809 )?,
810
811 _ => {
812 warn!("Unsupported key type: {key_type}");
813 return Err(pgp::errors::Error::InvalidInput {
814 backtrace: Some(Backtrace::capture()),
815 })?;
816 }
817 })
818}
819
820pub fn extract_certificate(key_data: &[u8]) -> Result<Vec<u8>, Error> {
829 let key = SignedSecretKey::from_bytes(key_data)?;
830 let public: SignedPublicKey = key.into();
831 let mut buffer = vec![];
832 public.to_writer(&mut buffer)?;
833 Ok(buffer)
834}
835
836#[cfg(test)]
837mod tests {
838 use nethsm_sdk_rs::models::{KeyMechanism, KeyPublicData, KeyRestrictions, KeyType};
839 use pgp::types::{EcdsaPublicParams, PublicParams};
840 use rstest::rstest;
841 use testresult::TestResult;
842
843 use super::*;
844
845 #[test]
846 fn convert_ed25519_to_pgp() -> TestResult {
847 let hsm_key = nethsm_sdk_rs::models::PublicKey {
848 mechanisms: vec![KeyMechanism::EdDsaSignature],
849 r#type: KeyType::Curve25519,
850 restrictions: Box::new(KeyRestrictions {
851 tags: Some(vec!["signing1".into()]),
852 }),
853 public: Some(Box::new(KeyPublicData {
854 modulus: None,
855 public_exponent: None,
856 data: Some("/ODoaDzX9xDjpx2LfR0DCIgdxqOndY9tukEFLVCObQo=".into()),
857 })),
858 operations: 1,
859 };
860
861 let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
862 let PublicParams::EdDSALegacy(pgp::types::EddsaLegacyPublicParams::Ed25519 { key }) =
863 pgp_key.public_params()
864 else {
865 panic!("Wrong type of public params");
866 };
867 assert_eq!(
868 key.to_bytes(),
869 [
870 252, 224, 232, 104, 60, 215, 247, 16, 227, 167, 29, 139, 125, 29, 3, 8, 136, 29,
871 198, 163, 167, 117, 143, 109, 186, 65, 5, 45, 80, 142, 109, 10
872 ]
873 );
874
875 Ok(())
876 }
877
878 #[test]
879 fn convert_p256_to_pgp() -> TestResult {
880 let hsm_key = nethsm_sdk_rs::models::PublicKey {
881 mechanisms: vec![KeyMechanism::EcdsaSignature],
882 r#type: KeyType::EcP256,
883 restrictions: Box::new(KeyRestrictions {
884 tags: Some(vec!["signing2".into()]),
885 }),
886 public: Some(Box::new(KeyPublicData {
887 modulus: None,
888 public_exponent: None,
889 data: Some(
890 "BN5q7GCR8w1RtXdMBR1IcIaCqbbn92vM5LItTcRbdXo5RfDwhnKK6D8tjWakqXbWY9eKelkCtALtD/hoU44WuYU="
891 .into(),
892 ),
893 })),
894 operations: 1,
895 };
896 let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
897 let PublicParams::ECDSA(EcdsaPublicParams::P256 { key, .. }) = pgp_key.public_params()
898 else {
899 panic!("Wrong type of public params");
900 };
901 assert_eq!(
902 key.to_sec1_bytes().to_vec(),
903 [
904 4, 222, 106, 236, 96, 145, 243, 13, 81, 181, 119, 76, 5, 29, 72, 112, 134, 130,
905 169, 182, 231, 247, 107, 204, 228, 178, 45, 77, 196, 91, 117, 122, 57, 69, 240,
906 240, 134, 114, 138, 232, 63, 45, 141, 102, 164, 169, 118, 214, 99, 215, 138, 122,
907 89, 2, 180, 2, 237, 15, 248, 104, 83, 142, 22, 185, 133
908 ]
909 );
910
911 Ok(())
912 }
913
914 #[test]
915 fn convert_p384_to_pgp() -> TestResult {
916 let hsm_key = nethsm_sdk_rs::models::PublicKey {
917 mechanisms: vec![KeyMechanism::EcdsaSignature],
918 r#type: KeyType::EcP384,
919 restrictions: Box::new(KeyRestrictions {
920 tags: Some(vec!["signing2".into()]),
921 }),
922 public: Some(Box::new(KeyPublicData {
923 modulus: None,
924 public_exponent: None,
925 data: Some(
926 "BH+Ik2+7v4NUpnZDTGs0jq9I+kDFTJqiMNOHP5k81agoKW8ICEJ13aL06dLNzkZAdB5iulgRCEuX/Htitii3BhxuHTUPWuN0uVKGhgYRddpTteaaauv0cOPni9la3O+/lA=="
927 .into(),
928 ),
929 })),
930 operations: 3,
931 };
932 let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
933 let PublicParams::ECDSA(EcdsaPublicParams::P384 { key, .. }) = pgp_key.public_params()
934 else {
935 panic!("Wrong type of public params");
936 };
937 assert_eq!(
938 key.to_sec1_bytes().to_vec(),
939 [
940 4, 127, 136, 147, 111, 187, 191, 131, 84, 166, 118, 67, 76, 107, 52, 142, 175, 72,
941 250, 64, 197, 76, 154, 162, 48, 211, 135, 63, 153, 60, 213, 168, 40, 41, 111, 8, 8,
942 66, 117, 221, 162, 244, 233, 210, 205, 206, 70, 64, 116, 30, 98, 186, 88, 17, 8,
943 75, 151, 252, 123, 98, 182, 40, 183, 6, 28, 110, 29, 53, 15, 90, 227, 116, 185, 82,
944 134, 134, 6, 17, 117, 218, 83, 181, 230, 154, 106, 235, 244, 112, 227, 231, 139,
945 217, 90, 220, 239, 191, 148
946 ]
947 );
948
949 Ok(())
950 }
951
952 #[test]
953 fn convert_p521_to_pgp() -> TestResult {
954 let hsm_key = nethsm_sdk_rs::models::PublicKey {
955 mechanisms: vec![KeyMechanism::EcdsaSignature],
956 r#type: KeyType::EcP521,
957 restrictions: Box::new(KeyRestrictions {
958 tags: Some(vec!["signing2".into()]),
959 }),
960 public: Some(Box::new(KeyPublicData {
961 modulus: None,
962 public_exponent: None,
963 data: Some(
964 "BAEhJ8HuyTN/DBjAoXD3H7jTdl+TwOwJ3taKwq2q+HsBislgZjeg1JZlOus1Mh4viKv0iuwaviid0D9cqsO2UHLN/QHTWGbzQw6fLiNZvCaGuNDf1c5+aiFMxvAgbDB8qp4eBAsl6f6ro5kKQXbpT7NauRVHYxUv32TgxG5mcRpnf+ovUQ=="
965 .into(),
966 ),
967 })),
968 operations: 2,
969 };
970 let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
971 let PublicParams::ECDSA(EcdsaPublicParams::P521 { key, .. }) = pgp_key.public_params()
972 else {
973 panic!("Wrong type of public params");
974 };
975 assert_eq!(
976 key.to_sec1_bytes().to_vec(),
977 [
978 4, 1, 33, 39, 193, 238, 201, 51, 127, 12, 24, 192, 161, 112, 247, 31, 184, 211,
979 118, 95, 147, 192, 236, 9, 222, 214, 138, 194, 173, 170, 248, 123, 1, 138, 201, 96,
980 102, 55, 160, 212, 150, 101, 58, 235, 53, 50, 30, 47, 136, 171, 244, 138, 236, 26,
981 190, 40, 157, 208, 63, 92, 170, 195, 182, 80, 114, 205, 253, 1, 211, 88, 102, 243,
982 67, 14, 159, 46, 35, 89, 188, 38, 134, 184, 208, 223, 213, 206, 126, 106, 33, 76,
983 198, 240, 32, 108, 48, 124, 170, 158, 30, 4, 11, 37, 233, 254, 171, 163, 153, 10,
984 65, 118, 233, 79, 179, 90, 185, 21, 71, 99, 21, 47, 223, 100, 224, 196, 110, 102,
985 113, 26, 103, 127, 234, 47, 81
986 ]
987 );
988
989 Ok(())
990 }
991
992 #[test]
993 fn convert_rsa_to_pgp() -> TestResult {
994 let hsm_key = nethsm_sdk_rs::models::PublicKey {
995 mechanisms: vec![KeyMechanism::RsaSignaturePkcs1],
996 r#type: KeyType::Rsa,
997 restrictions: Box::new(KeyRestrictions {
998 tags: Some(vec!["signing8".into()]) }),
999 public: Some(Box::new(KeyPublicData {
1000 modulus: Some("4386l1aC1e4N93rxM+Npj+dy0CGY0W3PNbOTBGRj7tTEflkEl2qx2xW7kyme8sLQQ/yxhyJ4mqo/ggR9ODfvYytzxsS/n/MNZwdATGC4QDBjPv74s/51nC/gZHq9VzvYq3bmF0e0WNiXRT3p53Zofmv1CBDPBEDrrJq3Mq+O3+TH8/ur3OOMgvNx2CDgwwQ1WGSW3XITN9ekZpoj/h8cwxFkz5ljmygCLRtXdNWrzVJGW3G5L/Jz9sdSfE2tyb8+312IVFJ57zcvRygqAkkS11uYIPxuoabT6IJ8SpScfqltGsU3jiALKyFRV58I91KUlXegjUVR31ExFc0eADuhuw==".into()),
1001 public_exponent: Some("AQAB".into()),
1002 data: None })),
1003 operations: 2 };
1004 let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
1005 let PublicParams::RSA(public) = pgp_key.public_params() else {
1006 panic!("Wrong type of public params");
1007 };
1008 assert_eq!(public.key.e().to_bytes_be(), [1, 0, 1]);
1009 assert_eq!(
1010 public.key.n().to_bytes_be(),
1011 [
1012 227, 127, 58, 151, 86, 130, 213, 238, 13, 247, 122, 241, 51, 227, 105, 143, 231,
1013 114, 208, 33, 152, 209, 109, 207, 53, 179, 147, 4, 100, 99, 238, 212, 196, 126, 89,
1014 4, 151, 106, 177, 219, 21, 187, 147, 41, 158, 242, 194, 208, 67, 252, 177, 135, 34,
1015 120, 154, 170, 63, 130, 4, 125, 56, 55, 239, 99, 43, 115, 198, 196, 191, 159, 243,
1016 13, 103, 7, 64, 76, 96, 184, 64, 48, 99, 62, 254, 248, 179, 254, 117, 156, 47, 224,
1017 100, 122, 189, 87, 59, 216, 171, 118, 230, 23, 71, 180, 88, 216, 151, 69, 61, 233,
1018 231, 118, 104, 126, 107, 245, 8, 16, 207, 4, 64, 235, 172, 154, 183, 50, 175, 142,
1019 223, 228, 199, 243, 251, 171, 220, 227, 140, 130, 243, 113, 216, 32, 224, 195, 4,
1020 53, 88, 100, 150, 221, 114, 19, 55, 215, 164, 102, 154, 35, 254, 31, 28, 195, 17,
1021 100, 207, 153, 99, 155, 40, 2, 45, 27, 87, 116, 213, 171, 205, 82, 70, 91, 113,
1022 185, 47, 242, 115, 246, 199, 82, 124, 77, 173, 201, 191, 62, 223, 93, 136, 84, 82,
1023 121, 239, 55, 47, 71, 40, 42, 2, 73, 18, 215, 91, 152, 32, 252, 110, 161, 166, 211,
1024 232, 130, 124, 74, 148, 156, 126, 169, 109, 26, 197, 55, 142, 32, 11, 43, 33, 81,
1025 87, 159, 8, 247, 82, 148, 149, 119, 160, 141, 69, 81, 223, 81, 49, 21, 205, 30, 0,
1026 59, 161, 187
1027 ]
1028 );
1029
1030 Ok(())
1031 }
1032
1033 #[test]
1034 fn parse_rsa_signature_produces_valid_data() -> TestResult {
1035 let sig = parse_signature(crate::SignatureType::Pkcs1, &[0, 1, 2])?;
1036 assert_eq!(sig.len(), 1);
1037 assert_eq!(&sig[0].as_ref(), &[1, 2]);
1038
1039 Ok(())
1040 }
1041
1042 #[test]
1043 fn parse_ed25519_signature_produces_valid_data() -> TestResult {
1044 let sig = parse_signature(
1045 crate::SignatureType::EdDsa,
1046 &[
1047 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,
1048 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,
1049 1, 1, 1, 1, 1, 1, 1, 1,
1050 ],
1051 )?;
1052 assert_eq!(sig.len(), 2);
1053 assert_eq!(sig[0].as_ref(), vec![2; 32]);
1054 assert_eq!(sig[1].as_ref(), vec![1; 32]);
1055
1056 Ok(())
1057 }
1058
1059 #[test]
1060 fn parse_p256_signature_produces_valid_data() -> TestResult {
1061 let sig = parse_signature(
1062 crate::SignatureType::EcdsaP256,
1063 &[
1064 48, 70, 2, 33, 0, 193, 176, 219, 0, 133, 254, 212, 239, 236, 122, 85, 239, 73, 161,
1065 179, 53, 100, 172, 103, 45, 123, 21, 169, 28, 59, 150, 72, 92, 242, 9, 53, 143, 2,
1066 33, 0, 165, 1, 144, 97, 102, 109, 66, 50, 185, 234, 211, 150, 253, 228, 210, 126,
1067 26, 0, 189, 184, 230, 163, 36, 203, 232, 161, 12, 75, 121, 171, 45, 107,
1068 ],
1069 )?;
1070 assert_eq!(sig.len(), 2);
1071 assert_eq!(
1072 sig[0].as_ref(),
1073 [
1074 193, 176, 219, 0, 133, 254, 212, 239, 236, 122, 85, 239, 73, 161, 179, 53, 100,
1075 172, 103, 45, 123, 21, 169, 28, 59, 150, 72, 92, 242, 9, 53, 143
1076 ]
1077 );
1078 assert_eq!(
1079 sig[1].as_ref(),
1080 [
1081 165, 1, 144, 97, 102, 109, 66, 50, 185, 234, 211, 150, 253, 228, 210, 126, 26, 0,
1082 189, 184, 230, 163, 36, 203, 232, 161, 12, 75, 121, 171, 45, 107
1083 ]
1084 );
1085
1086 Ok(())
1087 }
1088
1089 #[test]
1090 fn parse_p384_signature_produces_valid_data() -> TestResult {
1091 let sig = parse_signature(
1092 crate::SignatureType::EcdsaP384,
1093 &[
1094 48, 101, 2, 49, 0, 134, 13, 108, 74, 135, 234, 174, 105, 208, 46, 109, 18, 77, 21,
1095 177, 59, 73, 150, 228, 26, 244, 134, 187, 217, 172, 34, 2, 1, 229, 123, 105, 202,
1096 132, 233, 72, 41, 243, 138, 127, 107, 135, 95, 139, 19, 121, 179, 170, 27, 2, 48,
1097 44, 80, 117, 90, 18, 137, 36, 190, 8, 60, 201, 235, 242, 168, 164, 245, 119, 136,
1098 207, 178, 237, 64, 117, 69, 218, 189, 209, 110, 2, 9, 191, 194, 70, 50, 227, 47, 6,
1099 34, 8, 135, 43, 188, 236, 192, 184, 227, 59, 40,
1100 ],
1101 )?;
1102 assert_eq!(sig.len(), 2);
1103 assert_eq!(
1104 sig[0].as_ref(),
1105 [
1106 134, 13, 108, 74, 135, 234, 174, 105, 208, 46, 109, 18, 77, 21, 177, 59, 73, 150,
1107 228, 26, 244, 134, 187, 217, 172, 34, 2, 1, 229, 123, 105, 202, 132, 233, 72, 41,
1108 243, 138, 127, 107, 135, 95, 139, 19, 121, 179, 170, 27
1109 ]
1110 );
1111 assert_eq!(
1112 sig[1].as_ref(),
1113 [
1114 44, 80, 117, 90, 18, 137, 36, 190, 8, 60, 201, 235, 242, 168, 164, 245, 119, 136,
1115 207, 178, 237, 64, 117, 69, 218, 189, 209, 110, 2, 9, 191, 194, 70, 50, 227, 47, 6,
1116 34, 8, 135, 43, 188, 236, 192, 184, 227, 59, 40
1117 ]
1118 );
1119
1120 Ok(())
1121 }
1122
1123 #[test]
1124 fn parse_p521_signature_produces_valid_data() -> TestResult {
1125 let sig = parse_signature(
1126 crate::SignatureType::EcdsaP521,
1127 &[
1128 48, 129, 136, 2, 66, 0, 203, 246, 21, 57, 217, 6, 101, 73, 103, 113, 98, 39, 223,
1129 246, 199, 136, 238, 213, 134, 163, 153, 151, 116, 237, 207, 181, 107, 183, 204,
1130 110, 97, 160, 95, 160, 193, 3, 219, 46, 105, 191, 0, 139, 124, 234, 90, 125, 114,
1131 115, 205, 109, 15, 193, 166, 100, 224, 108, 87, 143, 240, 65, 41, 93, 164, 166, 2,
1132 2, 66, 1, 203, 115, 121, 219, 49, 18, 3, 101, 130, 153, 95, 80, 27, 148, 249, 221,
1133 198, 251, 149, 118, 119, 32, 44, 160, 24, 125, 72, 161, 168, 71, 48, 138, 223, 200,
1134 37, 124, 234, 17, 237, 246, 13, 123, 102, 151, 83, 95, 186, 161, 112, 41, 158, 138,
1135 144, 55, 23, 110, 100, 185, 237, 13, 174, 83, 4, 153, 34,
1136 ],
1137 )?;
1138 assert_eq!(sig.len(), 2);
1139 assert_eq!(
1140 sig[0].as_ref(),
1141 [
1142 203, 246, 21, 57, 217, 6, 101, 73, 103, 113, 98, 39, 223, 246, 199, 136, 238, 213,
1143 134, 163, 153, 151, 116, 237, 207, 181, 107, 183, 204, 110, 97, 160, 95, 160, 193,
1144 3, 219, 46, 105, 191, 0, 139, 124, 234, 90, 125, 114, 115, 205, 109, 15, 193, 166,
1145 100, 224, 108, 87, 143, 240, 65, 41, 93, 164, 166, 2
1146 ]
1147 );
1148 assert_eq!(
1149 sig[1].as_ref(),
1150 [
1151 1, 203, 115, 121, 219, 49, 18, 3, 101, 130, 153, 95, 80, 27, 148, 249, 221, 198,
1152 251, 149, 118, 119, 32, 44, 160, 24, 125, 72, 161, 168, 71, 48, 138, 223, 200, 37,
1153 124, 234, 17, 237, 246, 13, 123, 102, 151, 83, 95, 186, 161, 112, 41, 158, 138,
1154 144, 55, 23, 110, 100, 185, 237, 13, 174, 83, 4, 153, 34
1155 ]
1156 );
1157
1158 Ok(())
1159 }
1160
1161 #[test]
1162 fn private_key_import_ed25199_is_correctly_zero_padded() -> TestResult {
1163 let mut key_data = vec![];
1164 SignedSecretKey::from_armor_single(std::fs::File::open(
1165 "tests/fixtures/ed25519-key-with-31-byte-private-key-scalar.asc",
1166 )?)?
1167 .0
1168 .to_writer(&mut key_data)?;
1169
1170 let import: nethsm_sdk_rs::models::KeyPrivateData =
1171 tsk_to_private_key_import(&key_data)?.0.into();
1172
1173 let data = Base64::decode_vec(&import.data.unwrap())?;
1174
1175 assert_eq!(data.len(), 32);
1178 assert_eq!(data[0], 0x00);
1179
1180 Ok(())
1181 }
1182
1183 #[test]
1184 fn private_key_import_rsa_key_with_nonstandard_moduli_is_read_correctly() -> TestResult {
1185 let mut key_data = vec![];
1186 SignedSecretKey::from_armor_single(std::fs::File::open(
1187 "tests/fixtures/rsa-key-with-modulus-e-257.asc",
1188 )?)?
1189 .0
1190 .to_writer(&mut key_data)?;
1191
1192 let import: nethsm_sdk_rs::models::KeyPrivateData =
1193 tsk_to_private_key_import(&key_data)?.0.into();
1194
1195 let data = Base64::decode_vec(&import.public_exponent.unwrap())?;
1196
1197 assert_eq!(data, vec![0x01, 0x01]); Ok(())
1201 }
1202
1203 #[test]
1204 fn rsa_digest_info_is_wrapped() -> TestResult {
1205 let data = prepare_digest_data(crate::SignatureType::Pkcs1, HashAlgorithm::Sha1, &[0; 20])?;
1206
1207 assert_eq!(
1208 data,
1209 vec![
1210 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,
1211 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1212 ]
1213 );
1214
1215 Ok(())
1216 }
1217
1218 #[rstest]
1219 #[case(crate::SignatureType::EcdsaP256, 32)]
1220 #[case(crate::SignatureType::EcdsaP384, 48)]
1221 #[case(crate::SignatureType::EcdsaP521, 64)]
1222 fn ecdsa_wrapped_up_to_max_len(
1223 #[case] sig_type: crate::SignatureType,
1224 #[case] max_len: usize,
1225 #[values(HashAlgorithm::Sha1, HashAlgorithm::Sha256, HashAlgorithm::Sha512)]
1226 hash_algo: HashAlgorithm,
1227 ) -> TestResult {
1228 let digest = hash_algo.new_hasher()?.finalize();
1230 let data = prepare_digest_data(sig_type, hash_algo, &digest)?;
1231
1232 assert_eq!(
1236 data.len(),
1237 usize::min(max_len, digest.len()),
1238 "the data to be signed's length ({}) cannot exceed maximum length imposed by the curve ({})",
1239 data.len(),
1240 max_len
1241 );
1242
1243 Ok(())
1244 }
1245
1246 #[rstest]
1247 fn eddsa_is_not_wrapped(
1248 #[values(HashAlgorithm::Sha1, HashAlgorithm::Sha256, HashAlgorithm::Sha512)]
1249 hash_algo: HashAlgorithm,
1250 ) -> TestResult {
1251 let digest = &hash_algo.new_hasher()?.finalize()[..];
1253
1254 let data = prepare_digest_data(crate::SignatureType::EdDsa, hash_algo, digest)?;
1255
1256 assert_eq!(data, digest);
1257
1258 Ok(())
1259 }
1260}