Skip to main content

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