signstar_crypto/signer/
openpgp.rs

1//! OpenPGP signer interface.
2
3use std::{backtrace::Backtrace, io::Cursor};
4
5use chrono::{DateTime, Utc};
6use digest::DynDigest;
7use ed25519_dalek::VerifyingKey;
8use log::{error, warn};
9// Publicly re-export `pgp` facilities, used in the API of `signstar_crypto::signer::openpgp`.
10pub use pgp::composed::{Deserializable, SignedSecretKey};
11use pgp::{
12    composed::{ArmorOptions, DetachedSignature, SignedPublicKey},
13    crypto::{hash::HashAlgorithm, public_key::PublicKeyAlgorithm},
14    packet::{
15        Notation,
16        PacketTrait,
17        PubKeyInner,
18        PublicKey,
19        Signature,
20        SignatureConfig,
21        SignatureType,
22        Subpacket,
23        SubpacketData,
24        UserId,
25    },
26    ser::Serialize,
27    types::{
28        CompressionAlgorithm,
29        EcdsaPublicParams,
30        KeyDetails as _,
31        KeyId,
32        KeyVersion,
33        Mpi,
34        Password,
35        PlainSecretParams,
36        PublicKeyTrait as _,
37        PublicParams,
38        RsaPublicParams,
39        SecretKeyTrait,
40        SecretParams,
41        SignatureBytes,
42    },
43};
44use rsa::BigUint;
45use rsa::traits::PublicKeyParts as _;
46use sha2::digest::Digest as _;
47
48use crate::{
49    key::{KeyMechanism, KeyType, PrivateKeyImport, key_type_matches_length},
50    openpgp::{OpenPgpKeyUsageFlags, OpenPgpUserId, OpenPgpVersion},
51    signer::{
52        error::Error,
53        traits::{RawPublicKey, RawSigningKey},
54    },
55};
56
57/// PGP-adapter for a [raw HSM key][RawSigningKey].
58///
59/// All PGP-related operations executed on objects of this type will be forwarded to
60/// the HSM instance.
61struct SigningKey<'a> {
62    public_key: PublicKey,
63    raw_signer: &'a dyn RawSigningKey,
64}
65
66impl std::fmt::Debug for SigningKey<'_> {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        f.debug_struct("SigningKey")
69            .field("public_key", &self.public_key)
70            .finish()
71    }
72}
73
74/// Wraps an [`Error`] in a [`std::io::Error`] and returns it as a [`pgp::errors::Error`].
75///
76/// Since it is currently not possible to wrap the arbitrary [`Error`] of an external function
77/// cleanly in a [`pgp::errors::Error`], this function first wraps it in a [`std::io::Error`].
78/// This behavior has been suggested upstream in <https://github.com/rpgp/rpgp/issues/517#issuecomment-2778245199>
79#[inline]
80fn to_rpgp_error(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> pgp::errors::Error {
81    pgp::errors::Error::IO {
82        source: std::io::Error::other(e),
83        backtrace: Some(Backtrace::capture()),
84    }
85}
86
87impl<'a> SigningKey<'a> {
88    /// Creates a new [`SigningKey`] from a [`RawSigningKey`] implementation and a [`PublicKey`].
89    fn new(raw_signer: &'a dyn RawSigningKey, public_key: PublicKey) -> Self {
90        Self {
91            raw_signer,
92            public_key,
93        }
94    }
95
96    /// Creates a new [`SigningKey`] from a [`RawSigningKey`] implementation.
97    ///
98    /// The [`RawSigningKey`] implementation is expected to already have a certificate setup for
99    /// itself.
100    ///
101    /// # Errors
102    ///
103    /// Returns an error if
104    ///
105    /// - retrieval of the certificate from `raw_signer` fails
106    /// - parsing of the certificate retrieved fails
107    /// - the certificate is missing  ([`Error::OpenPpgCertificateMissing`])
108    fn new_provisioned(raw_signer: &'a dyn RawSigningKey) -> Result<Self, Error> {
109        let public_key = if let Some(cert) = raw_signer.certificate()?.as_ref() {
110            SignedPublicKey::from_bytes(Cursor::new(cert))?.primary_key
111        } else {
112            return Err(Error::OpenPpgCertificateMissing);
113        };
114        Ok(Self::new(raw_signer, public_key))
115    }
116}
117
118impl pgp::types::KeyDetails for SigningKey<'_> {
119    fn version(&self) -> KeyVersion {
120        self.public_key.version()
121    }
122
123    fn fingerprint(&self) -> pgp::types::Fingerprint {
124        self.public_key.fingerprint()
125    }
126
127    fn key_id(&self) -> KeyId {
128        self.public_key.key_id()
129    }
130
131    fn algorithm(&self) -> PublicKeyAlgorithm {
132        self.public_key.algorithm()
133    }
134}
135
136impl SecretKeyTrait for SigningKey<'_> {
137    /// Creates a data signature.
138    ///
139    /// # Note
140    ///
141    /// If `self` targets an HSM, it is expected to be unlocked and configured with access to the
142    /// signing key.
143    ///
144    /// Using a [`Password`] is not necessary as the operation deals with unencrypted cryptographic
145    /// key material.
146    ///
147    /// # Errors
148    ///
149    /// Returns an error if
150    /// - the key uses unsupported parameters (e.g. brainpool curves),
151    /// - digest serialization fails (e.g. ASN1 encoding of digest for RSA signatures),
152    /// - [`RawSigningKey::sign`] call fails,
153    /// - parsing of signature returned from the HSM fails.
154    fn create_signature(
155        &self,
156        _key_pw: &Password,
157        hash: HashAlgorithm,
158        data: &[u8],
159    ) -> pgp::errors::Result<SignatureBytes> {
160        if hash != self.hash_alg() {
161            error!(
162                "Requested signing hash is different from the default supported, got {hash} expected {expected}",
163                expected = self.hash_alg()
164            );
165            return Err(to_rpgp_error(Error::UnsupportedHashAlgorithm {
166                actual: hash,
167                expected: self.hash_alg(),
168            }));
169        }
170        let sig = self.raw_signer.sign(data).map_err(|e| {
171            error!("RawSigner::sign failed: {e:?}");
172            to_rpgp_error(e)
173        })?;
174
175        Ok(SignatureBytes::Mpis(
176            sig.into_iter().map(|b| Mpi::from_slice(&b)).collect(),
177        ))
178    }
179
180    /// Returns the preferred hash algorithm for data digests.
181    ///
182    /// # Note
183    /// We always return SHA-512 as it is faster than SHA-256 on modern hardware and of
184    /// sufficient size to accommodate all elliptic-curve algorithms.
185    fn hash_alg(&self) -> HashAlgorithm {
186        HashAlgorithm::Sha512
187    }
188}
189
190/// Generates an OpenPGP certificate for a [`RawSigningKey`] implementation.
191///
192/// # Errors
193///
194/// Returns an error if
195///
196/// - conversion of the HSM public key to OpenPGP public key fails
197/// - signing the certificate with the HSM key fails
198/// - writing the resulting certificate to buffer fails
199pub fn add_certificate(
200    raw_signer: &dyn RawSigningKey,
201    flags: OpenPgpKeyUsageFlags,
202    user_id: OpenPgpUserId,
203    created_at: DateTime<Utc>,
204    version: OpenPgpVersion,
205) -> Result<Vec<u8>, Error> {
206    if version != OpenPgpVersion::V4 {
207        return Err(crate::openpgp::Error::InvalidOpenPgpVersion(version.to_string()).into());
208    }
209    let public_key = raw_signer.public()?.to_openpgp_public_key(created_at)?;
210    let signer = SigningKey::new(raw_signer, public_key.clone());
211
212    let composed_pk = pgp::composed::PublicKey::new(
213        public_key.clone(),
214        pgp::composed::KeyDetails::new(
215            Some(UserId::from_str(Default::default(), user_id.as_ref())?),
216            vec![],
217            vec![],
218            flags.into(),
219            Default::default(),
220            Default::default(),
221            Default::default(),
222            vec![CompressionAlgorithm::Uncompressed].into(),
223            vec![].into(),
224        ),
225        vec![],
226    );
227
228    let signed_pk =
229        composed_pk.sign(rand::thread_rng(), &signer, &public_key, &Password::empty())?;
230
231    let mut buffer = vec![];
232    signed_pk.to_writer(&mut buffer)?;
233    Ok(buffer)
234}
235
236/// Converts an OpenPGP Transferable Secret Key into [`PrivateKeyImport`] object.
237///
238/// # Errors
239///
240/// Returns an [`Error`] if creating a [`PrivateKeyImport`] from `key_data` is not
241/// possible.
242///
243/// Returns an [`crate::key::Error::InvalidKeyLengthRsa`] if `key_data` is an RSA public key and is
244/// shorter than [`crate::key::base::MIN_RSA_BIT_LENGTH`].
245pub fn tsk_to_private_key_import(
246    key: &SignedSecretKey,
247) -> Result<(PrivateKeyImport, KeyMechanism), Error> {
248    if !key.secret_subkeys.is_empty() {
249        return Err(Error::OpenPgpTskContainsMultipleComponentKeys {
250            fingerprint: key.fingerprint(),
251        });
252    }
253    let SecretParams::Plain(secret) = key.primary_key.secret_params() else {
254        return Err(Error::OpenPgpTskIsPassphraseProtected {
255            fingerprint: key.fingerprint(),
256        });
257    };
258    Ok(match (secret, key.public_key().public_params()) {
259        (PlainSecretParams::RSA(secret), PublicParams::RSA(public)) => {
260            // ensure, that we have sufficient bit length
261            key_type_matches_length(
262                KeyType::Rsa,
263                Some(public.key.n().to_bytes_be().len() as u32 * 8),
264            )?;
265
266            let (_d, p, q, _u) = secret.to_bytes();
267
268            (
269                PrivateKeyImport::from_rsa(p, q, public.key.e().to_bytes_be().to_vec()),
270                KeyMechanism::RsaSignaturePkcs1,
271            )
272        }
273        (PlainSecretParams::ECDSA(secret_key), _) => {
274            let ec = if let PublicParams::ECDSA(pp) = key.primary_key.public_key().public_params() {
275                match pp {
276                    EcdsaPublicParams::P256 { .. } => KeyType::EcP256,
277                    EcdsaPublicParams::P384 { .. } => KeyType::EcP384,
278                    EcdsaPublicParams::P521 { .. } => KeyType::EcP521,
279                    pp => {
280                        warn!("Unsupported ECDSA parameters: {pp:?}");
281                        return Err(Error::UnsupportedKeyFormat {
282                            context: "converting ECDSA key to private key import",
283                            public_params: Box::new(key.public_key().public_params().clone()),
284                        })?;
285                    }
286                }
287            } else {
288                return Err(Error::UnsupportedKeyFormat {
289                    context: "converting non-ECDSA key to private key import",
290                    public_params: Box::new(key.public_key().public_params().clone()),
291                });
292            };
293
294            let bytes = match secret_key {
295                pgp::crypto::ecdsa::SecretKey::P256(secret_key) => secret_key.to_bytes().to_vec(),
296                pgp::crypto::ecdsa::SecretKey::P384(secret_key) => secret_key.to_bytes().to_vec(),
297                pgp::crypto::ecdsa::SecretKey::P521(secret_key) => secret_key.to_bytes().to_vec(),
298
299                pgp::crypto::ecdsa::SecretKey::Secp256k1(secret_key) => {
300                    secret_key.to_bytes().to_vec()
301                }
302                secret_key => {
303                    warn!("Unsupported secret key parameters: {secret_key:?}");
304                    return Err(Error::UnsupportedKeyFormat {
305                        context: "converting unsupported ECDSA key to private key import",
306                        public_params: Box::new(key.public_key().public_params().clone()),
307                    })?;
308                }
309            };
310
311            (
312                PrivateKeyImport::from_raw_bytes(ec, bytes).map_err(to_rpgp_error)?,
313                KeyMechanism::EcdsaSignature,
314            )
315        }
316        (PlainSecretParams::Ed25519Legacy(bytes), _) => (
317            PrivateKeyImport::from_raw_bytes(KeyType::Curve25519, bytes.as_bytes())
318                .map_err(to_rpgp_error)?,
319            KeyMechanism::EdDsaSignature,
320        ),
321        (_, public_params) => {
322            return Err(Error::UnsupportedKeyFormat {
323                context: "converting unknown key format to private key import",
324                public_params: Box::new(public_params.clone()),
325            });
326        }
327    })
328}
329
330/// Generates an OpenPGP signature using a [`RawSigningKey`] implementation.
331///
332/// Signs the message `message` using the [`RawSigningKey`] and returns a binary [OpenPGP data
333/// signature].
334///
335/// # Errors
336///
337/// Returns an [`Error`] if creating an [OpenPGP signature] for the hasher state fails:
338///
339/// - the certificate for a given key has not been generated or is invalid
340/// - subpacket lengths exceed maximum values
341/// - hashing signed data fails
342/// - signature creation using a [`RawSigningKey`] implementation fails
343/// - constructing OpenPGP signature from parts fails
344/// - writing the signature to vector fails
345///
346/// [OpenPGP signature]: https://openpgp.dev/book/signing_data.html
347/// [OpenPGP data signature]: https://openpgp.dev/book/signing_data.html
348pub fn sign(raw_signer: &dyn RawSigningKey, message: &[u8]) -> Result<Vec<u8>, Error> {
349    let signer = SigningKey::new_provisioned(raw_signer)?;
350
351    let mut sig_config =
352        SignatureConfig::v4(SignatureType::Binary, signer.algorithm(), signer.hash_alg());
353    sig_config.hashed_subpackets = vec![
354        Subpacket::regular(SubpacketData::SignatureCreationTime(
355            std::time::SystemTime::now().into(),
356        ))?,
357        Subpacket::regular(SubpacketData::Issuer(signer.key_id()))?,
358        Subpacket::regular(SubpacketData::IssuerFingerprint(signer.fingerprint()))?,
359    ];
360
361    let mut hasher = sig_config.hash_alg.new_hasher().map_err(to_rpgp_error)?;
362    sig_config.hash_data_to_sign(&mut hasher, message)?;
363
364    let len = sig_config.hash_signature_data(&mut hasher)?;
365
366    hasher.update(&sig_config.trailer(len)?);
367
368    let hash = &hasher.finalize()[..];
369
370    let signed_hash_value = [hash[0], hash[1]];
371    let raw_sig = signer.create_signature(&Password::empty(), sig_config.hash_alg, hash)?;
372
373    let signature = Signature::from_config(sig_config, signed_hash_value, raw_sig)?;
374
375    let mut out = vec![];
376    signature.to_writer_with_header(&mut out)?;
377
378    Ok(out)
379}
380
381/// Provides an adapter bridging two versions of the `digest` crate.
382///
383/// # Note
384///
385/// rPGP uses a different version of the `digest` crate than the latest (as used by e.g.
386/// `signstar-request-signature`). This adapter exposes the old `digest` 0.10 interface for
387/// the [sha2::Sha512] object which uses digest 0.11.
388///
389/// When rPGP updates to digest 0.11 this entire struct can be removed.
390#[derive(Clone, Default)]
391struct Hasher(sha2::Sha512);
392
393impl DynDigest for Hasher {
394    /// Updates the digest with input data.
395    ///
396    /// This method can be called repeatedly for use with streaming messages.
397    fn update(&mut self, data: &[u8]) {
398        self.0.update(data);
399    }
400
401    /// Writes digest into provided buffer `buf` and consumes `self`.
402    ///
403    /// # Errors
404    ///
405    /// Returns an error if the length of `buf` is too small for `self`.
406    fn finalize_into(self, buf: &mut [u8]) -> Result<(), digest::InvalidBufferSize> {
407        sha2::digest::DynDigest::finalize_into(self.0, buf)
408            .map_err(|_| digest::InvalidBufferSize)?;
409        Ok(())
410    }
411
412    /// Writes digest into provided buffer `buf` and resets `self` to an empty hasher.
413    ///
414    /// # Errors
415    ///
416    /// Returns an error if the length of `buf` is too small for `self`.
417    fn finalize_into_reset(&mut self, out: &mut [u8]) -> Result<(), digest::InvalidBufferSize> {
418        sha2::digest::DynDigest::finalize_into_reset(&mut self.0, out)
419            .map_err(|_| digest::InvalidBufferSize)?;
420        Ok(())
421    }
422
423    /// Reset hasher instance to its initial state.
424    fn reset(&mut self) {
425        sha2::digest::DynDigest::reset(&mut self.0)
426    }
427
428    /// Get output size of the hasher
429    fn output_size(&self) -> usize {
430        sha2::digest::DynDigest::output_size(&self.0)
431    }
432
433    /// Clone hasher state into a boxed trait object
434    fn box_clone(&self) -> Box<dyn DynDigest> {
435        Box::new(self.clone())
436    }
437}
438
439/// Generates an armored OpenPGP signature based on provided hasher state.
440///
441/// Signs the hasher `state` using the [`RawSigningKey`] and returns a binary [OpenPGP data
442/// signature].
443///
444/// # Errors
445///
446/// Returns an [`Error`] if creating an [OpenPGP signature] for the hasher state fails:
447///
448/// - the certificate for a given key has not been generated or is invalid
449/// - subpacket lengths exceed maximum values
450/// - hashing signed data fails
451/// - signature creation using the HSM fails
452/// - constructing OpenPGP signature from parts fails
453/// - writing the signature to vector fails
454///
455/// [OpenPGP signature]: https://openpgp.dev/book/signing_data.html
456/// [OpenPGP data signature]: https://openpgp.dev/book/signing_data.html
457pub fn sign_hasher_state(
458    raw_signer: &dyn RawSigningKey,
459    state: sha2::Sha512,
460) -> Result<String, Error> {
461    let signer = SigningKey::new_provisioned(raw_signer)?;
462    let hasher = state.clone();
463
464    let file_hash = Box::new(hasher).finalize().to_vec();
465
466    let sig_config = {
467        let mut sig_config =
468            SignatureConfig::v4(SignatureType::Binary, signer.algorithm(), signer.hash_alg());
469        sig_config.hashed_subpackets = vec![
470            Subpacket::regular(SubpacketData::SignatureCreationTime(
471                std::time::SystemTime::now().into(),
472            ))?,
473            Subpacket::regular(SubpacketData::Issuer(signer.key_id()))?,
474            Subpacket::regular(SubpacketData::IssuerFingerprint(signer.fingerprint()))?,
475            Subpacket::regular(SubpacketData::Notation(Notation {
476                readable: false,
477                name: "data-digest@archlinux.org".into(),
478                value: file_hash.into(),
479            }))?,
480        ];
481        sig_config
482    };
483
484    let mut hasher = Box::new(Hasher(state.clone())) as Box<dyn DynDigest + Send>;
485
486    let len = sig_config.hash_signature_data(&mut hasher)?;
487
488    hasher.update(&sig_config.trailer(len)?);
489
490    let hash = &hasher.finalize()[..];
491
492    let signed_hash_value = [hash[0], hash[1]];
493
494    let raw_sig = signer.create_signature(&Password::empty(), sig_config.hash_alg, hash)?;
495
496    let signature = pgp::packet::Signature::from_config(sig_config, signed_hash_value, raw_sig)?;
497
498    let signature = DetachedSignature { signature };
499    Ok(signature.to_armored_string(ArmorOptions::default())?)
500}
501
502/// Creates a [`PublicKey`] object from ECDSA parameters.
503///
504/// Takes a `created_at` date and ECDSA `key` parameters.
505///
506/// # Errors
507///
508/// Returns an error if
509///
510/// - the ECDSA algorithm is unsupported by rPGP,
511/// - or the calculated packet length is invalid.
512fn ecdsa_to_public_key(
513    created_at: DateTime<Utc>,
514    key: EcdsaPublicParams,
515) -> Result<PublicKey, Error> {
516    Ok(PublicKey::from_inner(PubKeyInner::new(
517        KeyVersion::V4,
518        PublicKeyAlgorithm::ECDSA,
519        created_at,
520        None,
521        PublicParams::ECDSA(key),
522    )?)?)
523}
524
525impl RawPublicKey {
526    /// Converts [raw public key][RawPublicKey] to OpenPGP public key packet.
527    ///
528    /// OpenPGP public keys have a date of creation, which is e.g. used
529    /// for fingerprint calculation.
530    /// This date of creation needs to be passed in specifically using
531    /// the `created_at` parameter.
532    ///
533    /// # Errors
534    ///
535    /// Returns an error if
536    ///
537    /// - creation of modulus or exponent fails (in case of RSA keys)
538    /// - public key is of wrong size (in case of ed25519 keys)
539    /// - decoding ECDSA public key fails (in case of NIST curves)
540    /// - rpgp fails when encoding raw packet lengths
541    fn to_openpgp_public_key(&self, created_at: DateTime<Utc>) -> Result<PublicKey, Error> {
542        Ok(match self {
543            RawPublicKey::Rsa { modulus, exponent } => PublicKey::from_inner(PubKeyInner::new(
544                KeyVersion::V4,
545                PublicKeyAlgorithm::RSA,
546                created_at,
547                None,
548                PublicParams::RSA(RsaPublicParams {
549                    key: rsa::RsaPublicKey::new(
550                        BigUint::from_bytes_be(modulus),
551                        BigUint::from_bytes_be(exponent),
552                    )
553                    .map_err(to_rpgp_error)?,
554                }),
555            )?)?,
556
557            RawPublicKey::Ed25519(pubkey) => PublicKey::from_inner(PubKeyInner::new(
558                KeyVersion::V4,
559                PublicKeyAlgorithm::EdDSALegacy,
560                created_at,
561                None,
562                PublicParams::EdDSALegacy(pgp::types::EddsaLegacyPublicParams::Ed25519 {
563                    key: VerifyingKey::from_bytes(&pubkey[..].try_into().map_err(to_rpgp_error)?)
564                        .map_err(to_rpgp_error)?,
565                }),
566            )?)?,
567
568            RawPublicKey::P256(pubkey) => ecdsa_to_public_key(
569                created_at,
570                EcdsaPublicParams::P256 {
571                    key: p256::PublicKey::from_sec1_bytes(pubkey)?,
572                },
573            )?,
574
575            RawPublicKey::P384(pubkey) => ecdsa_to_public_key(
576                created_at,
577                EcdsaPublicParams::P384 {
578                    key: p384::PublicKey::from_sec1_bytes(pubkey)?,
579                },
580            )?,
581
582            RawPublicKey::P521(pubkey) => ecdsa_to_public_key(
583                created_at,
584                EcdsaPublicParams::P521 {
585                    key: p521::PublicKey::from_sec1_bytes(pubkey)?,
586                },
587            )?,
588        })
589    }
590}
591
592/// Extracts an OpenPGP certificate from an OpenPGP private key.
593///
594/// The bytes in `key_data` are expected to contain valid OpenPGP private key data.
595/// From this a [`SignedSecretKey`] is created and a [`SignedPublicKey`] exported, which is returned
596/// as bytes vector.
597///
598/// # Errors
599///
600/// Returns an error if
601///
602/// - a secret key cannot be decoded from `key_data`,
603/// - or writing a serialized certificate into a vector fails.
604pub fn extract_certificate(key: SignedSecretKey) -> Result<Vec<u8>, Error> {
605    let public: SignedPublicKey = key.into();
606    let mut buffer = vec![];
607    public.to_writer(&mut buffer)?;
608    Ok(buffer)
609}
610
611#[cfg(test)]
612mod tests {
613    use std::time::SystemTime;
614
615    use base64ct::{Base64, Encoding as _};
616    use ed25519_dalek::{Signer, SigningKey};
617    use pgp::{
618        composed::SecretKeyParamsBuilder,
619        crypto::ecc_curve::ECCCurve,
620        types::{EcdsaPublicParams, PublicParams},
621    };
622    use rand::RngCore;
623    use rsa::rand_core::OsRng;
624    use testresult::TestResult;
625
626    use super::*;
627
628    #[test]
629    fn convert_ed25519_to_pgp() -> TestResult {
630        let hsm_key = RawPublicKey::Ed25519(vec![
631            252, 224, 232, 104, 60, 215, 247, 16, 227, 167, 29, 139, 125, 29, 3, 8, 136, 29, 198,
632            163, 167, 117, 143, 109, 186, 65, 5, 45, 80, 142, 109, 10,
633        ]);
634
635        let pgp_key = hsm_key.to_openpgp_public_key(DateTime::UNIX_EPOCH)?;
636        let PublicParams::EdDSALegacy(pgp::types::EddsaLegacyPublicParams::Ed25519 { key }) =
637            pgp_key.public_params()
638        else {
639            panic!("Wrong type of public params");
640        };
641        assert_eq!(
642            key.to_bytes(),
643            [
644                252, 224, 232, 104, 60, 215, 247, 16, 227, 167, 29, 139, 125, 29, 3, 8, 136, 29,
645                198, 163, 167, 117, 143, 109, 186, 65, 5, 45, 80, 142, 109, 10
646            ]
647        );
648
649        Ok(())
650    }
651
652    #[test]
653    fn convert_p256_to_pgp() -> TestResult {
654        let hsm_key = RawPublicKey::P256(vec![
655            4, 222, 106, 236, 96, 145, 243, 13, 81, 181, 119, 76, 5, 29, 72, 112, 134, 130, 169,
656            182, 231, 247, 107, 204, 228, 178, 45, 77, 196, 91, 117, 122, 57, 69, 240, 240, 134,
657            114, 138, 232, 63, 45, 141, 102, 164, 169, 118, 214, 99, 215, 138, 122, 89, 2, 180, 2,
658            237, 15, 248, 104, 83, 142, 22, 185, 133,
659        ]);
660        let pgp_key = hsm_key.to_openpgp_public_key(DateTime::UNIX_EPOCH)?;
661        let PublicParams::ECDSA(EcdsaPublicParams::P256 { key, .. }) = pgp_key.public_params()
662        else {
663            panic!("Wrong type of public params");
664        };
665        assert_eq!(
666            key.to_sec1_bytes().to_vec(),
667            [
668                4, 222, 106, 236, 96, 145, 243, 13, 81, 181, 119, 76, 5, 29, 72, 112, 134, 130,
669                169, 182, 231, 247, 107, 204, 228, 178, 45, 77, 196, 91, 117, 122, 57, 69, 240,
670                240, 134, 114, 138, 232, 63, 45, 141, 102, 164, 169, 118, 214, 99, 215, 138, 122,
671                89, 2, 180, 2, 237, 15, 248, 104, 83, 142, 22, 185, 133
672            ]
673        );
674
675        Ok(())
676    }
677
678    #[test]
679    fn convert_p384_to_pgp() -> TestResult {
680        let hsm_key = RawPublicKey::P384(vec![
681            4, 127, 136, 147, 111, 187, 191, 131, 84, 166, 118, 67, 76, 107, 52, 142, 175, 72, 250,
682            64, 197, 76, 154, 162, 48, 211, 135, 63, 153, 60, 213, 168, 40, 41, 111, 8, 8, 66, 117,
683            221, 162, 244, 233, 210, 205, 206, 70, 64, 116, 30, 98, 186, 88, 17, 8, 75, 151, 252,
684            123, 98, 182, 40, 183, 6, 28, 110, 29, 53, 15, 90, 227, 116, 185, 82, 134, 134, 6, 17,
685            117, 218, 83, 181, 230, 154, 106, 235, 244, 112, 227, 231, 139, 217, 90, 220, 239, 191,
686            148,
687        ]);
688        let pgp_key = hsm_key.to_openpgp_public_key(DateTime::UNIX_EPOCH)?;
689        let PublicParams::ECDSA(EcdsaPublicParams::P384 { key, .. }) = pgp_key.public_params()
690        else {
691            panic!("Wrong type of public params");
692        };
693        assert_eq!(
694            key.to_sec1_bytes().to_vec(),
695            [
696                4, 127, 136, 147, 111, 187, 191, 131, 84, 166, 118, 67, 76, 107, 52, 142, 175, 72,
697                250, 64, 197, 76, 154, 162, 48, 211, 135, 63, 153, 60, 213, 168, 40, 41, 111, 8, 8,
698                66, 117, 221, 162, 244, 233, 210, 205, 206, 70, 64, 116, 30, 98, 186, 88, 17, 8,
699                75, 151, 252, 123, 98, 182, 40, 183, 6, 28, 110, 29, 53, 15, 90, 227, 116, 185, 82,
700                134, 134, 6, 17, 117, 218, 83, 181, 230, 154, 106, 235, 244, 112, 227, 231, 139,
701                217, 90, 220, 239, 191, 148
702            ]
703        );
704
705        Ok(())
706    }
707
708    #[test]
709    fn convert_p521_to_pgp() -> TestResult {
710        let hsm_key = RawPublicKey::P521(vec![
711            4, 1, 33, 39, 193, 238, 201, 51, 127, 12, 24, 192, 161, 112, 247, 31, 184, 211, 118,
712            95, 147, 192, 236, 9, 222, 214, 138, 194, 173, 170, 248, 123, 1, 138, 201, 96, 102, 55,
713            160, 212, 150, 101, 58, 235, 53, 50, 30, 47, 136, 171, 244, 138, 236, 26, 190, 40, 157,
714            208, 63, 92, 170, 195, 182, 80, 114, 205, 253, 1, 211, 88, 102, 243, 67, 14, 159, 46,
715            35, 89, 188, 38, 134, 184, 208, 223, 213, 206, 126, 106, 33, 76, 198, 240, 32, 108, 48,
716            124, 170, 158, 30, 4, 11, 37, 233, 254, 171, 163, 153, 10, 65, 118, 233, 79, 179, 90,
717            185, 21, 71, 99, 21, 47, 223, 100, 224, 196, 110, 102, 113, 26, 103, 127, 234, 47, 81,
718        ]);
719        let pgp_key = hsm_key.to_openpgp_public_key(DateTime::UNIX_EPOCH)?;
720        let PublicParams::ECDSA(EcdsaPublicParams::P521 { key, .. }) = pgp_key.public_params()
721        else {
722            panic!("Wrong type of public params");
723        };
724        assert_eq!(
725            key.to_sec1_bytes().to_vec(),
726            [
727                4, 1, 33, 39, 193, 238, 201, 51, 127, 12, 24, 192, 161, 112, 247, 31, 184, 211,
728                118, 95, 147, 192, 236, 9, 222, 214, 138, 194, 173, 170, 248, 123, 1, 138, 201, 96,
729                102, 55, 160, 212, 150, 101, 58, 235, 53, 50, 30, 47, 136, 171, 244, 138, 236, 26,
730                190, 40, 157, 208, 63, 92, 170, 195, 182, 80, 114, 205, 253, 1, 211, 88, 102, 243,
731                67, 14, 159, 46, 35, 89, 188, 38, 134, 184, 208, 223, 213, 206, 126, 106, 33, 76,
732                198, 240, 32, 108, 48, 124, 170, 158, 30, 4, 11, 37, 233, 254, 171, 163, 153, 10,
733                65, 118, 233, 79, 179, 90, 185, 21, 71, 99, 21, 47, 223, 100, 224, 196, 110, 102,
734                113, 26, 103, 127, 234, 47, 81
735            ]
736        );
737
738        Ok(())
739    }
740
741    #[test]
742    fn convert_rsa_to_pgp() -> TestResult {
743        let hsm_key = RawPublicKey::Rsa {
744            modulus: vec![
745                227, 127, 58, 151, 86, 130, 213, 238, 13, 247, 122, 241, 51, 227, 105, 143, 231,
746                114, 208, 33, 152, 209, 109, 207, 53, 179, 147, 4, 100, 99, 238, 212, 196, 126, 89,
747                4, 151, 106, 177, 219, 21, 187, 147, 41, 158, 242, 194, 208, 67, 252, 177, 135, 34,
748                120, 154, 170, 63, 130, 4, 125, 56, 55, 239, 99, 43, 115, 198, 196, 191, 159, 243,
749                13, 103, 7, 64, 76, 96, 184, 64, 48, 99, 62, 254, 248, 179, 254, 117, 156, 47, 224,
750                100, 122, 189, 87, 59, 216, 171, 118, 230, 23, 71, 180, 88, 216, 151, 69, 61, 233,
751                231, 118, 104, 126, 107, 245, 8, 16, 207, 4, 64, 235, 172, 154, 183, 50, 175, 142,
752                223, 228, 199, 243, 251, 171, 220, 227, 140, 130, 243, 113, 216, 32, 224, 195, 4,
753                53, 88, 100, 150, 221, 114, 19, 55, 215, 164, 102, 154, 35, 254, 31, 28, 195, 17,
754                100, 207, 153, 99, 155, 40, 2, 45, 27, 87, 116, 213, 171, 205, 82, 70, 91, 113,
755                185, 47, 242, 115, 246, 199, 82, 124, 77, 173, 201, 191, 62, 223, 93, 136, 84, 82,
756                121, 239, 55, 47, 71, 40, 42, 2, 73, 18, 215, 91, 152, 32, 252, 110, 161, 166, 211,
757                232, 130, 124, 74, 148, 156, 126, 169, 109, 26, 197, 55, 142, 32, 11, 43, 33, 81,
758                87, 159, 8, 247, 82, 148, 149, 119, 160, 141, 69, 81, 223, 81, 49, 21, 205, 30, 0,
759                59, 161, 187,
760            ],
761            exponent: vec![1, 0, 1],
762        };
763        let pgp_key = hsm_key.to_openpgp_public_key(DateTime::UNIX_EPOCH)?;
764        let PublicParams::RSA(public) = pgp_key.public_params() else {
765            panic!("Wrong type of public params");
766        };
767        assert_eq!(public.key.e().to_bytes_be(), [1, 0, 1]);
768        assert_eq!(
769            public.key.n().to_bytes_be(),
770            [
771                227, 127, 58, 151, 86, 130, 213, 238, 13, 247, 122, 241, 51, 227, 105, 143, 231,
772                114, 208, 33, 152, 209, 109, 207, 53, 179, 147, 4, 100, 99, 238, 212, 196, 126, 89,
773                4, 151, 106, 177, 219, 21, 187, 147, 41, 158, 242, 194, 208, 67, 252, 177, 135, 34,
774                120, 154, 170, 63, 130, 4, 125, 56, 55, 239, 99, 43, 115, 198, 196, 191, 159, 243,
775                13, 103, 7, 64, 76, 96, 184, 64, 48, 99, 62, 254, 248, 179, 254, 117, 156, 47, 224,
776                100, 122, 189, 87, 59, 216, 171, 118, 230, 23, 71, 180, 88, 216, 151, 69, 61, 233,
777                231, 118, 104, 126, 107, 245, 8, 16, 207, 4, 64, 235, 172, 154, 183, 50, 175, 142,
778                223, 228, 199, 243, 251, 171, 220, 227, 140, 130, 243, 113, 216, 32, 224, 195, 4,
779                53, 88, 100, 150, 221, 114, 19, 55, 215, 164, 102, 154, 35, 254, 31, 28, 195, 17,
780                100, 207, 153, 99, 155, 40, 2, 45, 27, 87, 116, 213, 171, 205, 82, 70, 91, 113,
781                185, 47, 242, 115, 246, 199, 82, 124, 77, 173, 201, 191, 62, 223, 93, 136, 84, 82,
782                121, 239, 55, 47, 71, 40, 42, 2, 73, 18, 215, 91, 152, 32, 252, 110, 161, 166, 211,
783                232, 130, 124, 74, 148, 156, 126, 169, 109, 26, 197, 55, 142, 32, 11, 43, 33, 81,
784                87, 159, 8, 247, 82, 148, 149, 119, 160, 141, 69, 81, 223, 81, 49, 21, 205, 30, 0,
785                59, 161, 187
786            ]
787        );
788
789        Ok(())
790    }
791
792    #[test]
793    fn private_key_import_ed25199_is_correctly_zero_padded() -> TestResult {
794        let key = SignedSecretKey::from_armor_single(std::fs::File::open(
795            "tests/fixtures/ed25519-key-with-31-byte-private-key-scalar.asc",
796        )?)?
797        .0;
798
799        let import: nethsm_sdk_rs::models::KeyPrivateData =
800            tsk_to_private_key_import(&key)?.0.into();
801
802        let data = Base64::decode_vec(&import.data.unwrap())?;
803
804        // data needs to be zero-padded for NetHSM import even if the
805        // input is *not* zero-padded
806        assert_eq!(data.len(), 32);
807        assert_eq!(data[0], 0x00);
808
809        Ok(())
810    }
811
812    #[test]
813    fn private_key_import_rsa_key_with_nonstandard_moduli_is_read_correctly() -> TestResult {
814        let key = SignedSecretKey::from_armor_single(std::fs::File::open(
815            "tests/fixtures/rsa-key-with-modulus-e-257.asc",
816        )?)?
817        .0;
818
819        let import: nethsm_sdk_rs::models::KeyPrivateData =
820            tsk_to_private_key_import(&key)?.0.into();
821
822        let data = Base64::decode_vec(&import.public_exponent.unwrap())?;
823
824        // this key used a non-standard modulus (e) of 257
825        assert_eq!(data, vec![0x01, 0x01]); // 257 in hex
826
827        Ok(())
828    }
829
830    /// Software ed25519 key.
831    struct Ed25519SoftKey {
832        /// Backing software key.
833        signing_key: SigningKey,
834
835        /// OpenPGP certificate associated with the software key, if present.
836        certificate: Option<Vec<u8>>,
837    }
838
839    impl Ed25519SoftKey {
840        /// Generates a new software ed25519 key for signing.
841        ///
842        /// The `certificate` is unset ([`None`]).
843        fn new() -> Self {
844            Self {
845                // ed25519-dalek does not re-export rand_core so reusing rsa one
846                // which is maintained by the same Rust Crypto team
847                signing_key: SigningKey::generate(&mut OsRng),
848                certificate: None,
849            }
850        }
851    }
852
853    impl RawSigningKey for Ed25519SoftKey {
854        /// Returns a static string "Software key".
855        fn key_id(&self) -> String {
856            "Software key".into()
857        }
858
859        /// Sign a `digest` and return signature parts `R` and `s` (in this order).
860        ///
861        /// # Errors
862        ///
863        /// This implementation never fails.
864        fn sign(&self, digest: &[u8]) -> Result<Vec<Vec<u8>>, Error> {
865            let signature = self.signing_key.sign(digest);
866            Ok(vec![signature.r_bytes().into(), signature.s_bytes().into()])
867        }
868
869        /// Return certificate associated with this software key.
870        ///
871        /// # Errors
872        ///
873        /// This implementation never fails.
874        fn certificate(&self) -> Result<Option<Vec<u8>>, Error> {
875            Ok(self.certificate.clone())
876        }
877
878        /// Return [raw public key][RawPublicKey] associated with this signing key.
879        ///
880        /// # Errors
881        ///
882        /// This implementation never fails.
883        fn public(&self) -> Result<RawPublicKey, Error> {
884            Ok(RawPublicKey::Ed25519(
885                self.signing_key.verifying_key().to_bytes().into(),
886            ))
887        }
888    }
889
890    #[test]
891    fn sign_dummy() -> TestResult {
892        let mut raw_signer = Ed25519SoftKey::new();
893
894        let cert = add_certificate(
895            &raw_signer,
896            Default::default(),
897            OpenPgpUserId::new("test".into())?,
898            SystemTime::now().into(),
899            Default::default(),
900        )?;
901
902        raw_signer.certificate = Some(cert);
903
904        let mut data_to_sign = [0; 32];
905        OsRng::fill_bytes(&mut OsRng, &mut data_to_sign);
906
907        let signature = sign(&raw_signer, &data_to_sign)?;
908        assert!(!signature.is_empty());
909
910        Ok(())
911    }
912
913    #[rstest::rstest]
914    #[case::p256(ECCCurve::P256, KeyType::EcP256)]
915    #[case::p384(ECCCurve::P384, KeyType::EcP384)]
916    #[case::p521(ECCCurve::P521, KeyType::EcP521)]
917    fn import_ecdsa(#[case] pgp_curve: ECCCurve, #[case] expected_type: KeyType) -> TestResult {
918        let params = SecretKeyParamsBuilder::default()
919            .key_type(pgp::composed::KeyType::ECDSA(pgp_curve))
920            .can_sign(true)
921            .build()?;
922
923        let rng = rsa::rand_core::OsRng;
924
925        let key = params.generate(rng)?.sign(rng, &Default::default())?;
926        let actual_type = tsk_to_private_key_import(&key)?.0.key_type();
927        assert_eq!(actual_type, expected_type);
928
929        Ok(())
930    }
931
932    #[test]
933    fn test_unsupported_ecdsa_curve() -> TestResult {
934        let key = SecretKeyParamsBuilder::default()
935            .key_type(pgp::composed::KeyType::ECDSA(ECCCurve::Secp256k1))
936            .can_sign(true)
937            .build()?
938            .generate(rsa::rand_core::OsRng)?
939            .sign(rsa::rand_core::OsRng, &Default::default())?;
940
941        assert!(tsk_to_private_key_import(&key).is_err());
942
943        Ok(())
944    }
945}