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