nethsm/
openpgp.rs

1//! OpenPGP-related functions.
2
3use std::{backtrace::Backtrace, borrow::Cow, fmt::Debug};
4
5use base64ct::{Base64, Encoding as _};
6use chrono::{DateTime, Utc};
7use digest::DynDigest;
8use ed25519_dalek::VerifyingKey;
9use log::{error, warn};
10use pgp::{
11    composed::{
12        ArmorOptions,
13        Deserializable as _,
14        SignedPublicKey,
15        SignedSecretKey,
16        StandaloneSignature,
17    },
18    crypto::{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        KeyDetails as _,
36        KeyId,
37        KeyVersion,
38        Mpi,
39        Password,
40        PlainSecretParams,
41        PublicKeyTrait as _,
42        PublicParams,
43        RsaPublicParams,
44        SecretKeyTrait,
45        SecretParams,
46        SignatureBytes,
47    },
48};
49use picky_asn1_x509::{
50    AlgorithmIdentifier,
51    DigestInfo,
52    ShaVariant,
53    signature::EcdsaSignatureValue,
54};
55use rsa::BigUint;
56use rsa::traits::PublicKeyParts as _;
57use sha2::digest::Digest as _;
58
59use crate::{
60    KeyMechanism,
61    KeyType,
62    NetHsm,
63    OpenPgpKeyUsageFlags,
64    OpenPgpUserId,
65    OpenPgpVersion,
66    PrivateKeyImport,
67    key_type_matches_length,
68};
69
70/// An error that may occur when working with OpenPGP data.
71#[derive(Debug, thiserror::Error)]
72pub enum Error {
73    /// A Base64 encoded string can not be decode
74    #[error("Decoding Base64 string failed: {0}")]
75    Base64Decode(#[from] base64ct::Error),
76
77    /// Certificate for the key has not been initialized
78    #[error("Certificate for the key \"{0}\" has not been initialized")]
79    CertificateMissing(crate::KeyId),
80
81    /// Elliptic curve error
82    #[error("Elliptic curve error: {0}")]
83    EllipticCurve(#[from] p256::elliptic_curve::Error),
84
85    /// Provided key data is invalid
86    #[error("Key data invalid: {0}")]
87    KeyData(String),
88
89    /// OpenPGP error
90    #[error("rPGP error: {0}")]
91    Pgp(#[from] pgp::errors::Error),
92
93    /// The Transferable Secret Key is passphrase protected
94    #[error("Transferable Secret Key is passphrase protected")]
95    PrivateKeyPassphraseProtected,
96
97    /// Multiple component keys are unsupported
98    #[error("Unsupported multiple component keys")]
99    UnsupportedMultipleComponentKeys,
100
101    /// The key format used is unsupported
102    #[error("Unsupported key format: {public_params:?}")]
103    UnsupportedKeyFormat {
104        /// The unsupported public key parameters.
105        public_params: Box<PublicParams>,
106    },
107
108    /// A signstar_crypto key  error.
109    #[error("A signstar_crypto key error:\n{0}")]
110    SignstarCryptoKey(#[from] signstar_crypto::key::Error),
111}
112
113/// PGP-adapter for a NetHSM key.
114///
115/// All PGP-related operations executed on objects of this type will be forwarded to
116/// the NetHSM instance.
117struct HsmKey<'a, 'b> {
118    public_key: PublicKey,
119    nethsm: &'a NetHsm,
120    key_id: &'b crate::KeyId,
121}
122
123impl Debug for HsmKey<'_, '_> {
124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125        f.debug_struct("HsmKey")
126            .field("public_key", &self.public_key)
127            .field("key_id", &self.key_id)
128            .finish()
129    }
130}
131
132/// Wraps an [`Error`] in a [`std::io::Error`] and returns it as a [`pgp::errors::Error`].
133///
134/// Since it is currently not possible to wrap the arbitrary [`Error`] of an external function
135/// cleanly in a [`pgp::errors::Error`], this function first wraps it in a [`std::io::Error`].
136/// This behavior has been suggested upstream in <https://github.com/rpgp/rpgp/issues/517#issuecomment-2778245199>
137#[inline]
138fn to_rpgp_error(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> pgp::errors::Error {
139    pgp::errors::Error::IO {
140        source: std::io::Error::other(e),
141        backtrace: Some(Backtrace::capture()),
142    }
143}
144
145/// Parse signature bytes into algorithm-specific vector of MPIs.
146fn parse_signature(sig_type: crate::SignatureType, sig: &[u8]) -> pgp::errors::Result<Vec<Mpi>> {
147    use crate::SignatureType;
148    Ok(match sig_type {
149        SignatureType::EcdsaP256 | SignatureType::EcdsaP384 | SignatureType::EcdsaP521 => {
150            let sig: EcdsaSignatureValue = picky_asn1_der::from_bytes(sig).map_err(|e| {
151                error!("DER decoding error when parsing ECDSA signature: {e:?}");
152                to_rpgp_error(e)
153            })?;
154            vec![
155                Mpi::from_slice(sig.r.as_unsigned_bytes_be()),
156                Mpi::from_slice(sig.s.as_unsigned_bytes_be()),
157            ]
158        }
159        SignatureType::EdDsa => {
160            if sig.len() != 64 {
161                return Err(pgp::errors::Error::InvalidKeyLength);
162            }
163
164            vec![Mpi::from_slice(&sig[..32]), Mpi::from_slice(&sig[32..])]
165        }
166        SignatureType::Pkcs1 => {
167            // RSA
168            vec![Mpi::from_slice(sig)]
169        }
170        _ => {
171            warn!("Unsupported signature type: {sig_type}");
172            return Err(pgp::errors::Error::InvalidInput {
173                backtrace: Some(Backtrace::capture()),
174            });
175        }
176    })
177}
178
179impl<'a, 'b> HsmKey<'a, 'b> {
180    /// Creates a new remote signing key which will use `key_id` key for signing.
181    fn new(nethsm: &'a NetHsm, public_key: PublicKey, key_id: &'b crate::KeyId) -> Self {
182        Self {
183            nethsm,
184            public_key,
185            key_id,
186        }
187    }
188
189    /// Returns correct mode to use for signatures which depend on the public key.
190    fn sign_mode(&self) -> pgp::errors::Result<crate::SignatureType> {
191        Ok(match self.public_key.public_params() {
192            PublicParams::ECDSA(ecdsa) => match ecdsa {
193                EcdsaPublicParams::P256 { .. } => crate::SignatureType::EcdsaP256,
194                EcdsaPublicParams::P384 { .. } => crate::SignatureType::EcdsaP384,
195                EcdsaPublicParams::P521 { .. } => crate::SignatureType::EcdsaP521,
196                _ => {
197                    warn!("Unsupported ECDSA parameter type: {ecdsa:?}");
198                    return Err(pgp::errors::Error::InvalidInput {
199                        backtrace: Some(Backtrace::capture()),
200                    });
201                }
202            },
203            PublicParams::EdDSALegacy { .. } => crate::SignatureType::EdDsa,
204            PublicParams::RSA { .. } => crate::SignatureType::Pkcs1,
205            params => {
206                warn!("Unsupported signing parameters: {params:?}");
207                return Err(pgp::errors::Error::InvalidInput {
208                    backtrace: Some(Backtrace::capture()),
209                });
210            }
211        })
212    }
213}
214
215impl pgp::types::KeyDetails for HsmKey<'_, '_> {
216    fn version(&self) -> KeyVersion {
217        self.public_key.version()
218    }
219
220    fn fingerprint(&self) -> pgp::types::Fingerprint {
221        self.public_key.fingerprint()
222    }
223
224    fn key_id(&self) -> KeyId {
225        self.public_key.key_id()
226    }
227
228    fn algorithm(&self) -> PublicKeyAlgorithm {
229        self.public_key.algorithm()
230    }
231}
232
233/// Transforms the raw digest data for cryptographic signing.
234///
235/// Raw cryptographic signing primitives have special provisions that
236/// need to be taken care of when using certain combinations of
237/// signing schemes and hashing algorithms.
238///
239/// This function transforms the digest into bytes that are ready to
240/// be passed to raw cryptographic functions. The exact specifics of
241/// the transformations are documented inside the function.
242fn prepare_digest_data(
243    signature_type: crate::SignatureType,
244    hash: HashAlgorithm,
245    digest: &[u8],
246) -> pgp::errors::Result<Cow<'_, [u8]>> {
247    Ok(match signature_type {
248        // RSA-PKCS#1 signing scheme needs to wrap the digest value
249        // in an DER-encoded ASN.1 DigestInfo structure which captures
250        // the hash used.
251        // See: https://www.rfc-editor.org/rfc/rfc8017#appendix-A.2.4
252        crate::SignatureType::Pkcs1 => picky_asn1_der::to_vec(&DigestInfo {
253            oid: hash_to_oid(hash)?,
254            digest: digest.to_vec().into(),
255        })
256        .map_err(|e| {
257            error!("Encoding signature to PKCS#1 format failed: {e:?}");
258            to_rpgp_error(e)
259        })?
260        .into(),
261
262        // ECDSA may need to truncate the digest if it's too long
263        // See: https://www.rfc-editor.org/rfc/rfc9580#section-5.2.3.2
264        crate::SignatureType::EcdsaP256 => digest[..usize::min(32, digest.len())].into(),
265        crate::SignatureType::EcdsaP384 => digest[..usize::min(48, digest.len())].into(),
266
267        // All other schemes that we use will not need any kind of
268        // digest transformations.
269        _ => digest.into(),
270    })
271}
272
273impl SecretKeyTrait for HsmKey<'_, '_> {
274    /// Creates a data signature.
275    ///
276    /// # Note
277    ///
278    /// The [`NetHsm`] in use is expected to be unlocked and configured to use a user in the
279    /// [`Operator`][`crate::UserRole::Operator`] role with access to the signing key.
280    /// Using a [`Password`] is not necessary as the operation deals with unencrypted cryptographic
281    /// key material.
282    ///
283    /// # Errors
284    ///
285    /// Returns an error if
286    /// - the key uses unsupported parameters (e.g. brainpool curves),
287    /// - digest serialization fails (e.g. ASN1 encoding of digest for RSA signatures),
288    /// - NetHSM `sign_digest` call fails,
289    /// - parsing of signature returned from the NetHSM fails.
290    fn create_signature(
291        &self,
292        _key_pw: &Password,
293        hash: HashAlgorithm,
294        data: &[u8],
295    ) -> pgp::errors::Result<SignatureBytes> {
296        let signature_type = self.sign_mode()?;
297        let request_data = prepare_digest_data(signature_type, hash, data)?;
298
299        let sig = self
300            .nethsm
301            .sign_digest(self.key_id, signature_type, &request_data)
302            .map_err(|e| {
303                error!("NetHsm::sign_digest failed: {e:?}");
304                to_rpgp_error(e)
305            })?;
306
307        Ok(SignatureBytes::Mpis(parse_signature(signature_type, &sig)?))
308    }
309
310    /// Returns the preferred hash algorithm for data digests.
311    ///
312    /// # Note
313    /// We always return SHA-512 since this it is faster than SHA-256 on modern hardware and of
314    /// sufficient size to accommodate all elliptic-curve algorithms.
315    fn hash_alg(&self) -> HashAlgorithm {
316        HashAlgorithm::Sha512
317    }
318}
319
320/// Generates an OpenPGP certificate for the given NetHSM key.
321pub fn add_certificate(
322    nethsm: &NetHsm,
323    flags: OpenPgpKeyUsageFlags,
324    key_id: &crate::KeyId,
325    user_id: OpenPgpUserId,
326    created_at: DateTime<Utc>,
327    version: OpenPgpVersion,
328) -> Result<Vec<u8>, Error> {
329    if version != OpenPgpVersion::V4 {
330        unimplemented!(
331            "Support for creating OpenPGP {version} certificates is not yet implemented!"
332        );
333    }
334
335    let public_key = nethsm.get_key(key_id).map_err(to_rpgp_error)?;
336    let signer = HsmKey::new(nethsm, hsm_pk_to_pgp_pk(public_key, created_at)?, key_id);
337
338    let composed_pk = pgp::composed::PublicKey::new(
339        signer.public_key.clone(),
340        pgp::composed::KeyDetails::new(
341            Some(UserId::from_str(Default::default(), user_id.as_ref())?),
342            vec![],
343            vec![],
344            flags.into(),
345            Default::default(),
346            Default::default(),
347            Default::default(),
348            vec![CompressionAlgorithm::Uncompressed].into(),
349            vec![].into(),
350        ),
351        vec![],
352    );
353
354    let signed_pk = composed_pk.sign(
355        rand::thread_rng(),
356        &signer,
357        &signer.public_key,
358        &Password::empty(),
359    )?;
360
361    let mut buffer = vec![];
362    signed_pk.to_writer(&mut buffer)?;
363    Ok(buffer)
364}
365
366/// Converts OpenPGP hash algorithm into an OID form for PKCS#1 signing.
367fn hash_to_oid(hash: HashAlgorithm) -> pgp::errors::Result<AlgorithmIdentifier> {
368    Ok(AlgorithmIdentifier::new_sha(match hash {
369        HashAlgorithm::Sha1 => ShaVariant::SHA1,
370        HashAlgorithm::Sha256 => ShaVariant::SHA2_256,
371        HashAlgorithm::Sha384 => ShaVariant::SHA2_384,
372        HashAlgorithm::Sha512 => ShaVariant::SHA2_512,
373        HashAlgorithm::Sha224 => ShaVariant::SHA2_224,
374        HashAlgorithm::Sha3_256 => ShaVariant::SHA3_256,
375        HashAlgorithm::Sha3_512 => ShaVariant::SHA3_512,
376        _ => {
377            warn!("Unsupported hash algorithm: {hash}");
378            return Err(pgp::errors::Error::InvalidInput {
379                backtrace: Some(Backtrace::capture()),
380            });
381        }
382    }))
383}
384
385/// Converts an OpenPGP Transferable Secret Key into [`PrivateKeyImport`] object.
386///
387/// # Errors
388///
389/// Returns an [`Error::Pgp`] if creating a [`PrivateKeyImport`] from `key_data` is not
390/// possible.
391///
392/// Returns an [`crate::Error::Key`] if `key_data` is an RSA public key and is shorter than
393/// [`signstar_crypto::key::MIN_RSA_BIT_LENGTH`].
394pub fn tsk_to_private_key_import(
395    key_data: &[u8],
396) -> Result<(PrivateKeyImport, KeyMechanism), Error> {
397    let key = SignedSecretKey::from_bytes(key_data)?;
398    if !key.secret_subkeys.is_empty() {
399        return Err(Error::UnsupportedMultipleComponentKeys);
400    }
401    let SecretParams::Plain(secret) = key.primary_key.secret_params() else {
402        return Err(Error::PrivateKeyPassphraseProtected);
403    };
404    Ok(match (secret, key.public_key().public_params()) {
405        (PlainSecretParams::RSA(secret), PublicParams::RSA(public)) => {
406            // ensure, that we have sufficient bit length
407            key_type_matches_length(
408                KeyType::Rsa,
409                Some(public.key.n().to_bytes_be().len() as u32 * 8),
410            )
411            .map_err(to_rpgp_error)?;
412
413            let (_d, p, q, _u) = secret.to_bytes();
414
415            (
416                PrivateKeyImport::from_rsa(p, q, public.key.e().to_bytes_be().to_vec()),
417                KeyMechanism::RsaSignaturePkcs1,
418            )
419        }
420        (PlainSecretParams::ECDSA(secret_key), _) => {
421            let ec = if let PublicParams::ECDSA(pp) = key.primary_key.public_key().public_params() {
422                match pp {
423                    EcdsaPublicParams::P256 { .. } => crate::KeyType::EcP256,
424                    EcdsaPublicParams::P384 { .. } => crate::KeyType::EcP384,
425                    EcdsaPublicParams::P521 { .. } => crate::KeyType::EcP521,
426                    pp => {
427                        warn!("Unsupported ECDSA parameters: {pp:?}");
428                        return Err(Error::UnsupportedKeyFormat {
429                            public_params: Box::new(key.public_key().public_params().clone()),
430                        })?;
431                    }
432                }
433            } else {
434                return Err(Error::UnsupportedKeyFormat {
435                    public_params: Box::new(key.public_key().public_params().clone()),
436                });
437            };
438
439            let bytes = match secret_key {
440                pgp::crypto::ecdsa::SecretKey::P256(secret_key) => secret_key.to_bytes().to_vec(),
441                pgp::crypto::ecdsa::SecretKey::P384(secret_key) => secret_key.to_bytes().to_vec(),
442                pgp::crypto::ecdsa::SecretKey::P521(secret_key) => secret_key.to_bytes().to_vec(),
443
444                pgp::crypto::ecdsa::SecretKey::Secp256k1(secret_key) => {
445                    secret_key.to_bytes().to_vec()
446                }
447                secret_key => {
448                    warn!("Unsupported secret key parameters: {secret_key:?}");
449                    return Err(Error::UnsupportedKeyFormat {
450                        public_params: Box::new(key.public_key().public_params().clone()),
451                    })?;
452                }
453            };
454
455            (
456                PrivateKeyImport::from_raw_bytes(ec, bytes).map_err(to_rpgp_error)?,
457                KeyMechanism::EcdsaSignature,
458            )
459        }
460        (PlainSecretParams::Ed25519Legacy(bytes), _) => (
461            PrivateKeyImport::from_raw_bytes(crate::KeyType::Curve25519, bytes.as_bytes())
462                .map_err(to_rpgp_error)?,
463            KeyMechanism::EdDsaSignature,
464        ),
465        (_, public_params) => {
466            return Err(Error::UnsupportedKeyFormat {
467                public_params: Box::new(public_params.clone()),
468            });
469        }
470    })
471}
472
473/// Generates an OpenPGP signature using a given NetHSM key for the message.
474///
475/// Signs the message `message` using the key identified by `key_id`
476/// and returns a binary [OpenPGP data signature].
477///
478/// This call requires using a user in the [`Operator`][`crate::UserRole::Operator`] [role], which
479/// carries a tag (see [`add_user_tag`][`NetHsm::add_user_tag`]) matching one of the tags of
480/// the targeted key (see [`add_key_tag`][`NetHsm::add_key_tag`]).
481///
482/// ## Namespaces
483///
484/// * [`Operator`][`crate::UserRole::Operator`] users in a [namespace] only have access to keys in
485///   their own [namespace].
486/// * System-wide [`Operator`][`crate::UserRole::Operator`] users only have access to system-wide
487///   keys.
488///
489/// # Errors
490///
491/// Returns an [`crate::Error::Api`] if creating an [OpenPGP signature] for the hasher state fails:
492/// * the NetHSM is not in [`Operational`][`crate::SystemState::Operational`] [state]
493/// * no key identified by `key_id` exists on the NetHSM
494/// * the [`Operator`][`crate::UserRole::Operator`] user does not have access to the key (e.g.
495///   different [namespace])
496/// * the [`Operator`][`crate::UserRole::Operator`] user does not carry a tag matching one of the
497///   key tags
498/// * the used [`Credentials`][`crate::Credentials`] are not correct
499/// * the used [`Credentials`][`crate::Credentials`] are not those of a user in the
500///   [`Operator`][`crate::UserRole::Operator`] [role]
501/// * the certificate for a given key has not been generated or is invalid
502/// * subpacket lengths exceed maximum values
503/// * hashing signed data fails
504/// * signature creation using the NetHSM fails
505/// * constructing OpenPGP signature from parts fails
506/// * writing the signature to vector fails
507///
508/// [OpenPGP signature]: https://openpgp.dev/book/signing_data.html
509/// [OpenPGP data signature]: https://openpgp.dev/book/signing_data.html
510/// [namespace]: https://docs.nitrokey.com/nethsm/administration#namespaces
511/// [role]: https://docs.nitrokey.com/nethsm/administration#roles
512/// [state]: https://docs.nitrokey.com/nethsm/administration#state
513pub fn sign(nethsm: &NetHsm, key_id: &crate::KeyId, message: &[u8]) -> Result<Vec<u8>, Error> {
514    let Some(public_key) = nethsm.get_key_certificate(key_id).map_err(to_rpgp_error)? else {
515        return Err(Error::CertificateMissing(key_id.clone()));
516    };
517
518    let signer = HsmKey::new(
519        nethsm,
520        SignedPublicKey::from_bytes(&*public_key)?.primary_key,
521        key_id,
522    );
523
524    let mut sig_config =
525        SignatureConfig::v4(SignatureType::Binary, signer.algorithm(), signer.hash_alg());
526    sig_config.hashed_subpackets = vec![
527        Subpacket::regular(SubpacketData::SignatureCreationTime(
528            std::time::SystemTime::now().into(),
529        ))?,
530        Subpacket::regular(SubpacketData::Issuer(signer.key_id()))?,
531        Subpacket::regular(SubpacketData::IssuerFingerprint(signer.fingerprint()))?,
532    ];
533
534    let mut hasher = sig_config.hash_alg.new_hasher().map_err(to_rpgp_error)?;
535    sig_config.hash_data_to_sign(&mut hasher, message)?;
536
537    let len = sig_config.hash_signature_data(&mut hasher)?;
538
539    hasher.update(&sig_config.trailer(len)?);
540
541    let hash = &hasher.finalize()[..];
542
543    let signed_hash_value = [hash[0], hash[1]];
544    let raw_sig = signer.create_signature(&Password::empty(), sig_config.hash_alg, hash)?;
545
546    let signature = Signature::from_config(sig_config, signed_hash_value, raw_sig)?;
547
548    let mut out = vec![];
549    signature.to_writer_with_header(&mut out)?;
550
551    Ok(out)
552}
553
554/// Provides an adapter bridging two versions of the `digest` crate.
555///
556/// # Note
557///
558/// rPGP uses a different version of the `digest` crate than the latest (as used by e.g.
559/// `signstar-request-signature`). This adapter exposes the old `digest` 0.10 interface for
560/// the [sha2::Sha512] object which uses digest 0.11.
561///
562/// When rPGP updates to digest 0.11 this entire struct can be removed.
563#[derive(Clone, Default)]
564struct Hasher(sha2::Sha512);
565
566impl DynDigest for Hasher {
567    /// Updates the digest with input data.
568    ///
569    /// This method can be called repeatedly for use with streaming messages.
570    fn update(&mut self, data: &[u8]) {
571        self.0.update(data);
572    }
573
574    /// Writes digest into provided buffer `buf` and consumes `self`.
575    ///
576    /// # Errors
577    ///
578    /// Returns an error if the length of `buf` is too small for `self`.
579    fn finalize_into(self, buf: &mut [u8]) -> Result<(), digest::InvalidBufferSize> {
580        sha2::digest::DynDigest::finalize_into(self.0, buf)
581            .map_err(|_| digest::InvalidBufferSize)?;
582        Ok(())
583    }
584
585    /// Writes digest into provided buffer `buf` and resets `self` to an empty hasher.
586    ///
587    /// # Errors
588    ///
589    /// Returns an error if the length of `buf` is too small for `self`.
590    fn finalize_into_reset(&mut self, out: &mut [u8]) -> Result<(), digest::InvalidBufferSize> {
591        sha2::digest::DynDigest::finalize_into_reset(&mut self.0, out)
592            .map_err(|_| digest::InvalidBufferSize)?;
593        Ok(())
594    }
595
596    /// Reset hasher instance to its initial state.
597    fn reset(&mut self) {
598        sha2::digest::DynDigest::reset(&mut self.0)
599    }
600
601    /// Get output size of the hasher
602    fn output_size(&self) -> usize {
603        sha2::digest::DynDigest::output_size(&self.0)
604    }
605
606    /// Clone hasher state into a boxed trait object
607    fn box_clone(&self) -> Box<dyn DynDigest> {
608        Box::new(self.clone())
609    }
610}
611
612/// Generates an armored OpenPGP signature based on provided hasher state.
613///
614/// Signs the hasher `state` using the key identified by `key_id`
615/// and returns a binary [OpenPGP data signature].
616///
617/// This call requires using a user in the [`Operator`][`crate::UserRole::Operator`] [role], which
618/// carries a tag (see [`add_user_tag`][`NetHsm::add_user_tag`]) matching one of the tags of
619/// the targeted key (see [`add_key_tag`][`NetHsm::add_key_tag`]).
620///
621/// ## Namespaces
622///
623/// * [`Operator`][`crate::UserRole::Operator`] users in a [namespace] only have access to keys in
624///   their own [namespace].
625/// * System-wide [`Operator`][`crate::UserRole::Operator`] users only have access to system-wide
626///   keys.
627///
628/// # Errors
629///
630/// Returns an [`crate::Error::Api`] if creating an [OpenPGP signature] for the hasher state fails:
631/// * the NetHSM is not in [`Operational`][`crate::SystemState::Operational`] [state]
632/// * no key identified by `key_id` exists on the NetHSM
633/// * the [`Operator`][`crate::UserRole::Operator`] user does not have access to the key (e.g.
634///   different [namespace])
635/// * the [`Operator`][`crate::UserRole::Operator`] user does not carry a tag matching one of the
636///   key tags
637/// * the used [`Credentials`][`crate::Credentials`] are not correct
638/// * the used [`Credentials`][`crate::Credentials`] are not those of a user in the
639///   [`Operator`][`crate::UserRole::Operator`] [role]
640///
641/// [OpenPGP signature]: https://openpgp.dev/book/signing_data.html
642/// [OpenPGP data signature]: https://openpgp.dev/book/signing_data.html
643/// [namespace]: https://docs.nitrokey.com/nethsm/administration#namespaces
644/// [role]: https://docs.nitrokey.com/nethsm/administration#roles
645/// [state]: https://docs.nitrokey.com/nethsm/administration#state
646pub fn sign_hasher_state(
647    nethsm: &NetHsm,
648    key_id: &crate::KeyId,
649    state: sha2::Sha512,
650) -> Result<String, Error> {
651    let Some(public_key) = nethsm.get_key_certificate(key_id).map_err(to_rpgp_error)? else {
652        return Err(Error::CertificateMissing(key_id.clone()));
653    };
654
655    let signer = HsmKey::new(
656        nethsm,
657        SignedPublicKey::from_bytes(public_key.as_slice())?.primary_key,
658        key_id,
659    );
660
661    let hasher = state.clone();
662
663    let file_hash = Box::new(hasher).finalize().to_vec();
664
665    let sig_config = {
666        let mut sig_config =
667            SignatureConfig::v4(SignatureType::Binary, signer.algorithm(), signer.hash_alg());
668        sig_config.hashed_subpackets = vec![
669            Subpacket::regular(SubpacketData::SignatureCreationTime(
670                std::time::SystemTime::now().into(),
671            ))?,
672            Subpacket::regular(SubpacketData::Issuer(signer.key_id()))?,
673            Subpacket::regular(SubpacketData::IssuerFingerprint(signer.fingerprint()))?,
674            Subpacket::regular(SubpacketData::Notation(Notation {
675                readable: false,
676                name: "data-digest@archlinux.org".into(),
677                value: file_hash.into(),
678            }))?,
679        ];
680        sig_config
681    };
682
683    let mut hasher = Box::new(Hasher(state.clone())) as Box<dyn DynDigest + Send>;
684
685    let len = sig_config.hash_signature_data(&mut hasher)?;
686
687    hasher.update(&sig_config.trailer(len)?);
688
689    let hash = &hasher.finalize()[..];
690
691    let signed_hash_value = [hash[0], hash[1]];
692
693    let raw_sig = signer.create_signature(&Password::empty(), sig_config.hash_alg, hash)?;
694
695    let signature = pgp::packet::Signature::from_config(sig_config, signed_hash_value, raw_sig)?;
696
697    let signature = StandaloneSignature { signature };
698    Ok(signature.to_armored_string(ArmorOptions::default())?)
699}
700
701/// Creates a [`PublicKey`] object from ECDSA parameters.
702///
703/// Takes a `created_at` date and ECDSA `key` parameters.
704///
705/// # Errors
706///
707/// Returns an error if
708///
709/// - the ECDSA algorithm is unsupported by rPGP,
710/// - or the calculated packet length is invalid.
711fn ecdsa_to_public_key(
712    created_at: DateTime<Utc>,
713    key: EcdsaPublicParams,
714) -> Result<PublicKey, Error> {
715    Ok(PublicKey::from_inner(PubKeyInner::new(
716        KeyVersion::V4,
717        PublicKeyAlgorithm::ECDSA,
718        created_at,
719        None,
720        PublicParams::ECDSA(key),
721    )?)?)
722}
723
724/// Converts base64-encoded data into a vector of bytes.
725///
726/// # Errors
727///
728/// Returns an error if
729///
730/// - `data` is [`None`],
731/// - or `data` provides invalid base64 encoding.
732fn data_to_bytes(data: Option<&str>) -> Result<Vec<u8>, Error> {
733    Ok(Base64::decode_vec(data.ok_or(Error::KeyData(
734        "missing EC public key data".into(),
735    ))?)?)
736}
737
738/// Converts NetHSM public key to OpenPGP public key.
739///
740/// Since OpenPGP public keys have a date of creation (which is used
741/// for fingerprint calculation) this is an additional, explicit
742/// parameter.
743fn hsm_pk_to_pgp_pk(
744    pk: nethsm_sdk_rs::models::PublicKey,
745    created_at: DateTime<Utc>,
746) -> Result<PublicKey, Error> {
747    let public = &pk
748        .public
749        .ok_or(Error::KeyData("missing public key data".into()))?;
750
751    let key_type: KeyType = pk.r#type.try_into()?;
752    Ok(match key_type {
753        KeyType::Rsa => PublicKey::from_inner(PubKeyInner::new(
754            KeyVersion::V4,
755            PublicKeyAlgorithm::RSA,
756            created_at,
757            None,
758            PublicParams::RSA(RsaPublicParams {
759                key: rsa::RsaPublicKey::new(
760                    BigUint::from_bytes_be(&Base64::decode_vec(
761                        public
762                            .modulus
763                            .as_ref()
764                            .ok_or(Error::KeyData("missing RSA modulus".into()))?,
765                    )?),
766                    BigUint::from_bytes_be(&Base64::decode_vec(
767                        public
768                            .public_exponent
769                            .as_ref()
770                            .ok_or(Error::KeyData("missing RSA exponent".into()))?,
771                    )?),
772                )
773                .map_err(to_rpgp_error)?,
774            }),
775        )?)?,
776
777        KeyType::Curve25519 => {
778            let pubkey: &[u8] = &data_to_bytes(public.data.as_deref())?;
779
780            PublicKey::from_inner(PubKeyInner::new(
781                KeyVersion::V4,
782                PublicKeyAlgorithm::EdDSALegacy,
783                created_at,
784                None,
785                PublicParams::EdDSALegacy(pgp::types::EddsaLegacyPublicParams::Ed25519 {
786                    key: VerifyingKey::from_bytes(pubkey.try_into().map_err(to_rpgp_error)?)
787                        .map_err(to_rpgp_error)?,
788                }),
789            )?)?
790        }
791
792        KeyType::EcP256 => ecdsa_to_public_key(
793            created_at,
794            EcdsaPublicParams::P256 {
795                key: p256::PublicKey::from_sec1_bytes(&data_to_bytes(public.data.as_deref())?)?,
796            },
797        )?,
798        KeyType::EcP384 => ecdsa_to_public_key(
799            created_at,
800            EcdsaPublicParams::P384 {
801                key: p384::PublicKey::from_sec1_bytes(&data_to_bytes(public.data.as_deref())?)?,
802            },
803        )?,
804        KeyType::EcP521 => ecdsa_to_public_key(
805            created_at,
806            EcdsaPublicParams::P521 {
807                key: p521::PublicKey::from_sec1_bytes(&data_to_bytes(public.data.as_deref())?)?,
808            },
809        )?,
810
811        _ => {
812            warn!("Unsupported key type: {key_type}");
813            return Err(pgp::errors::Error::InvalidInput {
814                backtrace: Some(Backtrace::capture()),
815            })?;
816        }
817    })
818}
819
820/// Extracts certificate (public key) from an OpenPGP TSK.
821///
822/// # Errors
823///
824/// Returns an error if
825///
826/// - a secret key cannot be decoded from `key_data`,
827/// - or writing a serialized certificate into a vector fails.
828pub fn extract_certificate(key_data: &[u8]) -> Result<Vec<u8>, Error> {
829    let key = SignedSecretKey::from_bytes(key_data)?;
830    let public: SignedPublicKey = key.into();
831    let mut buffer = vec![];
832    public.to_writer(&mut buffer)?;
833    Ok(buffer)
834}
835
836#[cfg(test)]
837mod tests {
838    use nethsm_sdk_rs::models::{KeyMechanism, KeyPublicData, KeyRestrictions, KeyType};
839    use pgp::types::{EcdsaPublicParams, PublicParams};
840    use rstest::rstest;
841    use testresult::TestResult;
842
843    use super::*;
844
845    #[test]
846    fn convert_ed25519_to_pgp() -> TestResult {
847        let hsm_key = nethsm_sdk_rs::models::PublicKey {
848            mechanisms: vec![KeyMechanism::EdDsaSignature],
849            r#type: KeyType::Curve25519,
850            restrictions: Box::new(KeyRestrictions {
851                tags: Some(vec!["signing1".into()]),
852            }),
853            public: Some(Box::new(KeyPublicData {
854                modulus: None,
855                public_exponent: None,
856                data: Some("/ODoaDzX9xDjpx2LfR0DCIgdxqOndY9tukEFLVCObQo=".into()),
857            })),
858            operations: 1,
859        };
860
861        let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
862        let PublicParams::EdDSALegacy(pgp::types::EddsaLegacyPublicParams::Ed25519 { key }) =
863            pgp_key.public_params()
864        else {
865            panic!("Wrong type of public params");
866        };
867        assert_eq!(
868            key.to_bytes(),
869            [
870                252, 224, 232, 104, 60, 215, 247, 16, 227, 167, 29, 139, 125, 29, 3, 8, 136, 29,
871                198, 163, 167, 117, 143, 109, 186, 65, 5, 45, 80, 142, 109, 10
872            ]
873        );
874
875        Ok(())
876    }
877
878    #[test]
879    fn convert_p256_to_pgp() -> TestResult {
880        let hsm_key = nethsm_sdk_rs::models::PublicKey {
881            mechanisms: vec![KeyMechanism::EcdsaSignature],
882            r#type: KeyType::EcP256,
883            restrictions: Box::new(KeyRestrictions {
884                tags: Some(vec!["signing2".into()]),
885            }),
886            public: Some(Box::new(KeyPublicData {
887                modulus: None,
888                public_exponent: None,
889                data: Some(
890                    "BN5q7GCR8w1RtXdMBR1IcIaCqbbn92vM5LItTcRbdXo5RfDwhnKK6D8tjWakqXbWY9eKelkCtALtD/hoU44WuYU="
891                        .into(),
892                ),
893            })),
894            operations: 1,
895        };
896        let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
897        let PublicParams::ECDSA(EcdsaPublicParams::P256 { key, .. }) = pgp_key.public_params()
898        else {
899            panic!("Wrong type of public params");
900        };
901        assert_eq!(
902            key.to_sec1_bytes().to_vec(),
903            [
904                4, 222, 106, 236, 96, 145, 243, 13, 81, 181, 119, 76, 5, 29, 72, 112, 134, 130,
905                169, 182, 231, 247, 107, 204, 228, 178, 45, 77, 196, 91, 117, 122, 57, 69, 240,
906                240, 134, 114, 138, 232, 63, 45, 141, 102, 164, 169, 118, 214, 99, 215, 138, 122,
907                89, 2, 180, 2, 237, 15, 248, 104, 83, 142, 22, 185, 133
908            ]
909        );
910
911        Ok(())
912    }
913
914    #[test]
915    fn convert_p384_to_pgp() -> TestResult {
916        let hsm_key = nethsm_sdk_rs::models::PublicKey {
917            mechanisms: vec![KeyMechanism::EcdsaSignature],
918            r#type: KeyType::EcP384,
919            restrictions: Box::new(KeyRestrictions {
920                tags: Some(vec!["signing2".into()]),
921            }),
922            public: Some(Box::new(KeyPublicData {
923                modulus: None,
924                public_exponent: None,
925                data: Some(
926                    "BH+Ik2+7v4NUpnZDTGs0jq9I+kDFTJqiMNOHP5k81agoKW8ICEJ13aL06dLNzkZAdB5iulgRCEuX/Htitii3BhxuHTUPWuN0uVKGhgYRddpTteaaauv0cOPni9la3O+/lA=="
927                        .into(),
928                ),
929            })),
930            operations: 3,
931        };
932        let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
933        let PublicParams::ECDSA(EcdsaPublicParams::P384 { key, .. }) = pgp_key.public_params()
934        else {
935            panic!("Wrong type of public params");
936        };
937        assert_eq!(
938            key.to_sec1_bytes().to_vec(),
939            [
940                4, 127, 136, 147, 111, 187, 191, 131, 84, 166, 118, 67, 76, 107, 52, 142, 175, 72,
941                250, 64, 197, 76, 154, 162, 48, 211, 135, 63, 153, 60, 213, 168, 40, 41, 111, 8, 8,
942                66, 117, 221, 162, 244, 233, 210, 205, 206, 70, 64, 116, 30, 98, 186, 88, 17, 8,
943                75, 151, 252, 123, 98, 182, 40, 183, 6, 28, 110, 29, 53, 15, 90, 227, 116, 185, 82,
944                134, 134, 6, 17, 117, 218, 83, 181, 230, 154, 106, 235, 244, 112, 227, 231, 139,
945                217, 90, 220, 239, 191, 148
946            ]
947        );
948
949        Ok(())
950    }
951
952    #[test]
953    fn convert_p521_to_pgp() -> TestResult {
954        let hsm_key = nethsm_sdk_rs::models::PublicKey {
955            mechanisms: vec![KeyMechanism::EcdsaSignature],
956            r#type: KeyType::EcP521,
957            restrictions: Box::new(KeyRestrictions {
958                tags: Some(vec!["signing2".into()]),
959            }),
960            public: Some(Box::new(KeyPublicData {
961                modulus: None,
962                public_exponent: None,
963                data: Some(
964                    "BAEhJ8HuyTN/DBjAoXD3H7jTdl+TwOwJ3taKwq2q+HsBislgZjeg1JZlOus1Mh4viKv0iuwaviid0D9cqsO2UHLN/QHTWGbzQw6fLiNZvCaGuNDf1c5+aiFMxvAgbDB8qp4eBAsl6f6ro5kKQXbpT7NauRVHYxUv32TgxG5mcRpnf+ovUQ=="
965                        .into(),
966                ),
967            })),
968            operations: 2,
969        };
970        let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
971        let PublicParams::ECDSA(EcdsaPublicParams::P521 { key, .. }) = pgp_key.public_params()
972        else {
973            panic!("Wrong type of public params");
974        };
975        assert_eq!(
976            key.to_sec1_bytes().to_vec(),
977            [
978                4, 1, 33, 39, 193, 238, 201, 51, 127, 12, 24, 192, 161, 112, 247, 31, 184, 211,
979                118, 95, 147, 192, 236, 9, 222, 214, 138, 194, 173, 170, 248, 123, 1, 138, 201, 96,
980                102, 55, 160, 212, 150, 101, 58, 235, 53, 50, 30, 47, 136, 171, 244, 138, 236, 26,
981                190, 40, 157, 208, 63, 92, 170, 195, 182, 80, 114, 205, 253, 1, 211, 88, 102, 243,
982                67, 14, 159, 46, 35, 89, 188, 38, 134, 184, 208, 223, 213, 206, 126, 106, 33, 76,
983                198, 240, 32, 108, 48, 124, 170, 158, 30, 4, 11, 37, 233, 254, 171, 163, 153, 10,
984                65, 118, 233, 79, 179, 90, 185, 21, 71, 99, 21, 47, 223, 100, 224, 196, 110, 102,
985                113, 26, 103, 127, 234, 47, 81
986            ]
987        );
988
989        Ok(())
990    }
991
992    #[test]
993    fn convert_rsa_to_pgp() -> TestResult {
994        let hsm_key = nethsm_sdk_rs::models::PublicKey {
995            mechanisms: vec![KeyMechanism::RsaSignaturePkcs1],
996            r#type: KeyType::Rsa,
997            restrictions: Box::new(KeyRestrictions {
998                tags: Some(vec!["signing8".into()]) }),
999                public: Some(Box::new(KeyPublicData {
1000                    modulus: Some("4386l1aC1e4N93rxM+Npj+dy0CGY0W3PNbOTBGRj7tTEflkEl2qx2xW7kyme8sLQQ/yxhyJ4mqo/ggR9ODfvYytzxsS/n/MNZwdATGC4QDBjPv74s/51nC/gZHq9VzvYq3bmF0e0WNiXRT3p53Zofmv1CBDPBEDrrJq3Mq+O3+TH8/ur3OOMgvNx2CDgwwQ1WGSW3XITN9ekZpoj/h8cwxFkz5ljmygCLRtXdNWrzVJGW3G5L/Jz9sdSfE2tyb8+312IVFJ57zcvRygqAkkS11uYIPxuoabT6IJ8SpScfqltGsU3jiALKyFRV58I91KUlXegjUVR31ExFc0eADuhuw==".into()),
1001                    public_exponent: Some("AQAB".into()),
1002                    data: None })),
1003            operations: 2 };
1004        let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
1005        let PublicParams::RSA(public) = pgp_key.public_params() else {
1006            panic!("Wrong type of public params");
1007        };
1008        assert_eq!(public.key.e().to_bytes_be(), [1, 0, 1]);
1009        assert_eq!(
1010            public.key.n().to_bytes_be(),
1011            [
1012                227, 127, 58, 151, 86, 130, 213, 238, 13, 247, 122, 241, 51, 227, 105, 143, 231,
1013                114, 208, 33, 152, 209, 109, 207, 53, 179, 147, 4, 100, 99, 238, 212, 196, 126, 89,
1014                4, 151, 106, 177, 219, 21, 187, 147, 41, 158, 242, 194, 208, 67, 252, 177, 135, 34,
1015                120, 154, 170, 63, 130, 4, 125, 56, 55, 239, 99, 43, 115, 198, 196, 191, 159, 243,
1016                13, 103, 7, 64, 76, 96, 184, 64, 48, 99, 62, 254, 248, 179, 254, 117, 156, 47, 224,
1017                100, 122, 189, 87, 59, 216, 171, 118, 230, 23, 71, 180, 88, 216, 151, 69, 61, 233,
1018                231, 118, 104, 126, 107, 245, 8, 16, 207, 4, 64, 235, 172, 154, 183, 50, 175, 142,
1019                223, 228, 199, 243, 251, 171, 220, 227, 140, 130, 243, 113, 216, 32, 224, 195, 4,
1020                53, 88, 100, 150, 221, 114, 19, 55, 215, 164, 102, 154, 35, 254, 31, 28, 195, 17,
1021                100, 207, 153, 99, 155, 40, 2, 45, 27, 87, 116, 213, 171, 205, 82, 70, 91, 113,
1022                185, 47, 242, 115, 246, 199, 82, 124, 77, 173, 201, 191, 62, 223, 93, 136, 84, 82,
1023                121, 239, 55, 47, 71, 40, 42, 2, 73, 18, 215, 91, 152, 32, 252, 110, 161, 166, 211,
1024                232, 130, 124, 74, 148, 156, 126, 169, 109, 26, 197, 55, 142, 32, 11, 43, 33, 81,
1025                87, 159, 8, 247, 82, 148, 149, 119, 160, 141, 69, 81, 223, 81, 49, 21, 205, 30, 0,
1026                59, 161, 187
1027            ]
1028        );
1029
1030        Ok(())
1031    }
1032
1033    #[test]
1034    fn parse_rsa_signature_produces_valid_data() -> TestResult {
1035        let sig = parse_signature(crate::SignatureType::Pkcs1, &[0, 1, 2])?;
1036        assert_eq!(sig.len(), 1);
1037        assert_eq!(&sig[0].as_ref(), &[1, 2]);
1038
1039        Ok(())
1040    }
1041
1042    #[test]
1043    fn parse_ed25519_signature_produces_valid_data() -> TestResult {
1044        let sig = parse_signature(
1045            crate::SignatureType::EdDsa,
1046            &[
1047                2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1048                2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1049                1, 1, 1, 1, 1, 1, 1, 1,
1050            ],
1051        )?;
1052        assert_eq!(sig.len(), 2);
1053        assert_eq!(sig[0].as_ref(), vec![2; 32]);
1054        assert_eq!(sig[1].as_ref(), vec![1; 32]);
1055
1056        Ok(())
1057    }
1058
1059    #[test]
1060    fn parse_p256_signature_produces_valid_data() -> TestResult {
1061        let sig = parse_signature(
1062            crate::SignatureType::EcdsaP256,
1063            &[
1064                48, 70, 2, 33, 0, 193, 176, 219, 0, 133, 254, 212, 239, 236, 122, 85, 239, 73, 161,
1065                179, 53, 100, 172, 103, 45, 123, 21, 169, 28, 59, 150, 72, 92, 242, 9, 53, 143, 2,
1066                33, 0, 165, 1, 144, 97, 102, 109, 66, 50, 185, 234, 211, 150, 253, 228, 210, 126,
1067                26, 0, 189, 184, 230, 163, 36, 203, 232, 161, 12, 75, 121, 171, 45, 107,
1068            ],
1069        )?;
1070        assert_eq!(sig.len(), 2);
1071        assert_eq!(
1072            sig[0].as_ref(),
1073            [
1074                193, 176, 219, 0, 133, 254, 212, 239, 236, 122, 85, 239, 73, 161, 179, 53, 100,
1075                172, 103, 45, 123, 21, 169, 28, 59, 150, 72, 92, 242, 9, 53, 143
1076            ]
1077        );
1078        assert_eq!(
1079            sig[1].as_ref(),
1080            [
1081                165, 1, 144, 97, 102, 109, 66, 50, 185, 234, 211, 150, 253, 228, 210, 126, 26, 0,
1082                189, 184, 230, 163, 36, 203, 232, 161, 12, 75, 121, 171, 45, 107
1083            ]
1084        );
1085
1086        Ok(())
1087    }
1088
1089    #[test]
1090    fn parse_p384_signature_produces_valid_data() -> TestResult {
1091        let sig = parse_signature(
1092            crate::SignatureType::EcdsaP384,
1093            &[
1094                48, 101, 2, 49, 0, 134, 13, 108, 74, 135, 234, 174, 105, 208, 46, 109, 18, 77, 21,
1095                177, 59, 73, 150, 228, 26, 244, 134, 187, 217, 172, 34, 2, 1, 229, 123, 105, 202,
1096                132, 233, 72, 41, 243, 138, 127, 107, 135, 95, 139, 19, 121, 179, 170, 27, 2, 48,
1097                44, 80, 117, 90, 18, 137, 36, 190, 8, 60, 201, 235, 242, 168, 164, 245, 119, 136,
1098                207, 178, 237, 64, 117, 69, 218, 189, 209, 110, 2, 9, 191, 194, 70, 50, 227, 47, 6,
1099                34, 8, 135, 43, 188, 236, 192, 184, 227, 59, 40,
1100            ],
1101        )?;
1102        assert_eq!(sig.len(), 2);
1103        assert_eq!(
1104            sig[0].as_ref(),
1105            [
1106                134, 13, 108, 74, 135, 234, 174, 105, 208, 46, 109, 18, 77, 21, 177, 59, 73, 150,
1107                228, 26, 244, 134, 187, 217, 172, 34, 2, 1, 229, 123, 105, 202, 132, 233, 72, 41,
1108                243, 138, 127, 107, 135, 95, 139, 19, 121, 179, 170, 27
1109            ]
1110        );
1111        assert_eq!(
1112            sig[1].as_ref(),
1113            [
1114                44, 80, 117, 90, 18, 137, 36, 190, 8, 60, 201, 235, 242, 168, 164, 245, 119, 136,
1115                207, 178, 237, 64, 117, 69, 218, 189, 209, 110, 2, 9, 191, 194, 70, 50, 227, 47, 6,
1116                34, 8, 135, 43, 188, 236, 192, 184, 227, 59, 40
1117            ]
1118        );
1119
1120        Ok(())
1121    }
1122
1123    #[test]
1124    fn parse_p521_signature_produces_valid_data() -> TestResult {
1125        let sig = parse_signature(
1126            crate::SignatureType::EcdsaP521,
1127            &[
1128                48, 129, 136, 2, 66, 0, 203, 246, 21, 57, 217, 6, 101, 73, 103, 113, 98, 39, 223,
1129                246, 199, 136, 238, 213, 134, 163, 153, 151, 116, 237, 207, 181, 107, 183, 204,
1130                110, 97, 160, 95, 160, 193, 3, 219, 46, 105, 191, 0, 139, 124, 234, 90, 125, 114,
1131                115, 205, 109, 15, 193, 166, 100, 224, 108, 87, 143, 240, 65, 41, 93, 164, 166, 2,
1132                2, 66, 1, 203, 115, 121, 219, 49, 18, 3, 101, 130, 153, 95, 80, 27, 148, 249, 221,
1133                198, 251, 149, 118, 119, 32, 44, 160, 24, 125, 72, 161, 168, 71, 48, 138, 223, 200,
1134                37, 124, 234, 17, 237, 246, 13, 123, 102, 151, 83, 95, 186, 161, 112, 41, 158, 138,
1135                144, 55, 23, 110, 100, 185, 237, 13, 174, 83, 4, 153, 34,
1136            ],
1137        )?;
1138        assert_eq!(sig.len(), 2);
1139        assert_eq!(
1140            sig[0].as_ref(),
1141            [
1142                203, 246, 21, 57, 217, 6, 101, 73, 103, 113, 98, 39, 223, 246, 199, 136, 238, 213,
1143                134, 163, 153, 151, 116, 237, 207, 181, 107, 183, 204, 110, 97, 160, 95, 160, 193,
1144                3, 219, 46, 105, 191, 0, 139, 124, 234, 90, 125, 114, 115, 205, 109, 15, 193, 166,
1145                100, 224, 108, 87, 143, 240, 65, 41, 93, 164, 166, 2
1146            ]
1147        );
1148        assert_eq!(
1149            sig[1].as_ref(),
1150            [
1151                1, 203, 115, 121, 219, 49, 18, 3, 101, 130, 153, 95, 80, 27, 148, 249, 221, 198,
1152                251, 149, 118, 119, 32, 44, 160, 24, 125, 72, 161, 168, 71, 48, 138, 223, 200, 37,
1153                124, 234, 17, 237, 246, 13, 123, 102, 151, 83, 95, 186, 161, 112, 41, 158, 138,
1154                144, 55, 23, 110, 100, 185, 237, 13, 174, 83, 4, 153, 34
1155            ]
1156        );
1157
1158        Ok(())
1159    }
1160
1161    #[test]
1162    fn private_key_import_ed25199_is_correctly_zero_padded() -> TestResult {
1163        let mut key_data = vec![];
1164        SignedSecretKey::from_armor_single(std::fs::File::open(
1165            "tests/fixtures/ed25519-key-with-31-byte-private-key-scalar.asc",
1166        )?)?
1167        .0
1168        .to_writer(&mut key_data)?;
1169
1170        let import: nethsm_sdk_rs::models::KeyPrivateData =
1171            tsk_to_private_key_import(&key_data)?.0.into();
1172
1173        let data = Base64::decode_vec(&import.data.unwrap())?;
1174
1175        // data needs to be zero-padded for NetHSM import even if the
1176        // input is *not* zero-padded
1177        assert_eq!(data.len(), 32);
1178        assert_eq!(data[0], 0x00);
1179
1180        Ok(())
1181    }
1182
1183    #[test]
1184    fn private_key_import_rsa_key_with_nonstandard_moduli_is_read_correctly() -> TestResult {
1185        let mut key_data = vec![];
1186        SignedSecretKey::from_armor_single(std::fs::File::open(
1187            "tests/fixtures/rsa-key-with-modulus-e-257.asc",
1188        )?)?
1189        .0
1190        .to_writer(&mut key_data)?;
1191
1192        let import: nethsm_sdk_rs::models::KeyPrivateData =
1193            tsk_to_private_key_import(&key_data)?.0.into();
1194
1195        let data = Base64::decode_vec(&import.public_exponent.unwrap())?;
1196
1197        // this key used a non-standard modulus (e) of 257
1198        assert_eq!(data, vec![0x01, 0x01]); // 257 in hex
1199
1200        Ok(())
1201    }
1202
1203    #[test]
1204    fn rsa_digest_info_is_wrapped() -> TestResult {
1205        let data = prepare_digest_data(crate::SignatureType::Pkcs1, HashAlgorithm::Sha1, &[0; 20])?;
1206
1207        assert_eq!(
1208            data,
1209            vec![
1210                48, 33, 48, 9, 6, 5, 43, 14, 3, 2, 26, 5, 0, 4, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1211                0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1212            ]
1213        );
1214
1215        Ok(())
1216    }
1217
1218    #[rstest]
1219    #[case(crate::SignatureType::EcdsaP256, 32)]
1220    #[case(crate::SignatureType::EcdsaP384, 48)]
1221    #[case(crate::SignatureType::EcdsaP521, 64)]
1222    fn ecdsa_wrapped_up_to_max_len(
1223        #[case] sig_type: crate::SignatureType,
1224        #[case] max_len: usize,
1225        #[values(HashAlgorithm::Sha1, HashAlgorithm::Sha256, HashAlgorithm::Sha512)]
1226        hash_algo: HashAlgorithm,
1227    ) -> TestResult {
1228        // the digest value is irrelevant - just the size of the digest
1229        let digest = hash_algo.new_hasher()?.finalize();
1230        let data = prepare_digest_data(sig_type, hash_algo, &digest)?;
1231
1232        // The data to be signed size needs to be truncated to the value specific the the curve
1233        // being used. If the digest is short enough to be smaller than the curve specific field
1234        // size the digest is used as a whole.
1235        assert_eq!(
1236            data.len(),
1237            usize::min(max_len, digest.len()),
1238            "the data to be signed's length ({}) cannot exceed maximum length imposed by the curve ({})",
1239            data.len(),
1240            max_len
1241        );
1242
1243        Ok(())
1244    }
1245
1246    #[rstest]
1247    fn eddsa_is_not_wrapped(
1248        #[values(HashAlgorithm::Sha1, HashAlgorithm::Sha256, HashAlgorithm::Sha512)]
1249        hash_algo: HashAlgorithm,
1250    ) -> TestResult {
1251        // the digest value is irrelevant - just the size of the digest
1252        let digest = &hash_algo.new_hasher()?.finalize()[..];
1253
1254        let data = prepare_digest_data(crate::SignatureType::EdDsa, hash_algo, digest)?;
1255
1256        assert_eq!(data, digest);
1257
1258        Ok(())
1259    }
1260}