Skip to main content

signstar_crypto/signer/
openpgp.rs

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