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