signstar_crypto/signer/
openpgp.rs

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