nethsm/
openpgp.rs

1//! OpenPGP-related functions.
2
3use std::{
4    borrow::{Borrow, Cow},
5    collections::HashSet,
6    fmt::{Debug, Display},
7    str::FromStr,
8};
9
10use base64ct::{Base64, Encoding as _};
11use chrono::{DateTime, Utc};
12use email_address::{EmailAddress, Options};
13use log::error;
14use pgp::{
15    ArmorOptions,
16    Deserializable,
17    KeyDetails,
18    SignedPublicKey,
19    SignedSecretKey,
20    StandaloneSignature,
21    crypto::{ecc_curve::ECCCurve, hash::HashAlgorithm, public_key::PublicKeyAlgorithm},
22    packet::{
23        KeyFlags,
24        Notation,
25        PublicKey,
26        SignatureConfig,
27        SignatureType,
28        Subpacket,
29        SubpacketData,
30        UserId,
31    },
32    ser::Serialize,
33    types::{
34        CompressionAlgorithm,
35        EcdsaPublicParams,
36        EskType,
37        KeyId,
38        KeyVersion,
39        Mpi,
40        PkeskBytes,
41        PlainSecretParams,
42        PublicKeyTrait,
43        PublicParams,
44        SecretKeyTrait,
45        SecretParams,
46        SignatureBytes,
47        Version,
48    },
49};
50use picky_asn1_x509::{
51    AlgorithmIdentifier,
52    DigestInfo,
53    ShaVariant,
54    signature::EcdsaSignatureValue,
55};
56use rand::prelude::{CryptoRng, Rng};
57
58use crate::{KeyMechanism, KeyType, NetHsm, PrivateKeyImport, key_type_matches_length};
59
60#[derive(Debug, thiserror::Error)]
61pub enum Error {
62    /// A Base64 encoded string can not be decode
63    #[error("Decoding Base64 string failed: {0}")]
64    Base64Decode(#[from] base64ct::Error),
65
66    /// Elliptic curve error
67    #[error("Elliptic curve error: {0}")]
68    EllipticCurve(#[from] p256::elliptic_curve::Error),
69
70    /// Duplicate OpenPGP User ID
71    #[error("The OpenPGP User ID {user_id} is used more than once!")]
72    DuplicateUserId { user_id: OpenPgpUserId },
73
74    /// Provided OpenPGP version is invalid
75    #[error("Invalid OpenPGP version: {0}")]
76    InvalidOpenPgpVersion(String),
77
78    /// Provided key data is invalid
79    #[error("Key data invalid: {0}")]
80    KeyData(String),
81
82    /// NetHsm error
83    #[error("NetHSM error: {0}")]
84    NetHsm(String),
85
86    /// OpenPGP error
87    #[error("rPGP error: {0}")]
88    Pgp(#[from] pgp::errors::Error),
89
90    /// The Transferable Secret Key is passphrase protected
91    #[error("Transferable Secret Key is passphrase protected")]
92    PrivateKeyPassphraseProtected,
93
94    /// Multiple component keys are unsupported
95    #[error("Unsupported multiple component keys")]
96    UnsupportedMultipleComponentKeys,
97
98    /// The key format used is unsupported
99    #[error("Unsupported key format: {public_params:?}")]
100    UnsupportedKeyFormat { public_params: Box<PublicParams> },
101
102    /// The User ID is too large
103    #[error("The OpenPGP User ID is too large: {user_id}")]
104    UserIdTooLarge { user_id: String },
105}
106
107/// The OpenPGP version
108#[derive(
109    Clone,
110    Copy,
111    Debug,
112    Default,
113    serde::Deserialize,
114    strum::Display,
115    strum::EnumIter,
116    Hash,
117    strum::IntoStaticStr,
118    Eq,
119    PartialEq,
120    serde::Serialize,
121)]
122#[serde(into = "String", try_from = "String")]
123pub enum OpenPgpVersion {
124    /// OpenPGP version 4 as defined in [RFC 4880]
125    ///
126    /// [RFC 4880]: https://www.rfc-editor.org/rfc/rfc4880.html
127    #[default]
128    #[strum(to_string = "4")]
129    V4,
130
131    /// OpenPGP version 6 as defined in [RFC 9580]
132    ///
133    /// [RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html
134    #[strum(to_string = "6")]
135    V6,
136}
137
138impl AsRef<str> for OpenPgpVersion {
139    fn as_ref(&self) -> &str {
140        match self {
141            Self::V4 => "4",
142            Self::V6 => "6",
143        }
144    }
145}
146
147impl FromStr for OpenPgpVersion {
148    type Err = Error;
149
150    /// Creates an [`OpenPgpVersion`] from a string slice
151    ///
152    /// Only valid OpenPGP versions are considered:
153    /// * [RFC 4880] aka "v4"
154    /// * [RFC 9580] aka "v6"
155    ///
156    /// # Errors
157    ///
158    /// Returns an error if the provided string slice does not represent a valid OpenPGP version.
159    ///
160    /// # Examples
161    ///
162    /// ```
163    /// use std::str::FromStr;
164    ///
165    /// use nethsm::OpenPgpVersion;
166    ///
167    /// # fn main() -> testresult::TestResult {
168    /// assert_eq!(OpenPgpVersion::from_str("4")?, OpenPgpVersion::V4);
169    /// assert_eq!(OpenPgpVersion::from_str("6")?, OpenPgpVersion::V6);
170    ///
171    /// assert!(OpenPgpVersion::from_str("5").is_err());
172    /// # Ok(())
173    /// # }
174    /// ```
175    /// [RFC 4880]: https://www.rfc-editor.org/rfc/rfc4880.html
176    /// [RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html
177    fn from_str(s: &str) -> Result<Self, Self::Err> {
178        match s {
179            "4" | "v4" | "V4" | "OpenPGPv4" => Ok(Self::V4),
180            "5" | "v5" | "V5" | "OpenPGPv5" => Err(Error::InvalidOpenPgpVersion(format!(
181                "{s} (\"we don't do these things around here\")"
182            ))),
183            "6" | "v6" | "V6" | "OpenPGPv6" => Ok(Self::V6),
184            _ => Err(Error::InvalidOpenPgpVersion(s.to_string())),
185        }
186    }
187}
188
189impl From<OpenPgpVersion> for String {
190    fn from(value: OpenPgpVersion) -> Self {
191        value.to_string()
192    }
193}
194
195impl TryFrom<String> for OpenPgpVersion {
196    type Error = Error;
197
198    fn try_from(value: String) -> Result<Self, Self::Error> {
199        Self::from_str(&value)
200    }
201}
202
203/// A distinction between types of OpenPGP User IDs
204#[derive(Clone, Debug, Hash, Eq, PartialEq)]
205enum OpenPgpUserIdType {
206    /// An OpenPGP User ID that contains a valid e-mail address (e.g. "John Doe
207    /// <john@example.org>")
208    ///
209    /// The e-mail address must use a top-level domain (TLD) and no domain literal (e.g. an IP
210    /// address) is allowed.
211    Email(EmailAddress),
212
213    /// A plain OpenPGP User ID
214    ///
215    /// The User ID may contain any UTF-8 character, but does not represent a valid e-mail address.
216    Plain(String),
217}
218
219/// A basic representation of a User ID for OpenPGP
220///
221/// While [OpenPGP User IDs] are loosely defined to be UTF-8 strings, they do not enforce
222/// particular rules around the use of e-mail addresses or their general length.
223/// This type allows to distinguish between plain UTF-8 strings and valid e-mail addresses.
224/// Valid e-mail addresses must provide a display part, use a top-level domain (TLD) and not rely on
225/// domain literals (e.g. IP address).
226/// The length of a User ID is implicitly limited by the maximum length of an OpenPGP packet (8192
227/// bytes).
228/// As such, this type only allows a maximum length of 4096 bytes as middle ground.
229///
230/// [OpenPGP User IDs]: https://www.rfc-editor.org/rfc/rfc9580.html#name-user-id-packet-type-id-13
231#[derive(Clone, Debug, serde::Deserialize, Hash, Eq, PartialEq, serde::Serialize)]
232#[serde(into = "String", try_from = "String")]
233pub struct OpenPgpUserId(OpenPgpUserIdType);
234
235impl OpenPgpUserId {
236    /// Creates a new [`OpenPgpUserId`] from a String
237    ///
238    /// # Errors
239    ///
240    /// Returns an [`Error::Key`][`crate::Error::Key`] if the chars of the provided String exceed
241    /// 4096 bytes. This ensures to stay below the valid upper limit defined by the maximum OpenPGP
242    /// packet size of 8192 bytes.
243    ///
244    /// # Examples
245    ///
246    /// ```
247    /// use std::str::FromStr;
248    ///
249    /// use nethsm::OpenPgpUserId;
250    ///
251    /// # fn main() -> testresult::TestResult {
252    /// assert!(!OpenPgpUserId::new("🤡".to_string())?.is_email());
253    ///
254    /// assert!(OpenPgpUserId::new("🤡 <foo@xn--rl8h.org>".to_string())?.is_email());
255    ///
256    /// // an e-mail without a display name is not considered a valid e-mail
257    /// assert!(!OpenPgpUserId::new("<foo@xn--rl8h.org>".to_string())?.is_email());
258    ///
259    /// // this fails because the provided String is too long
260    /// assert!(OpenPgpUserId::new("U".repeat(4097)).is_err());
261    /// # Ok(())
262    /// # }
263    /// ```
264    pub fn new(user_id: String) -> Result<Self, Error> {
265        if user_id.len() > 4096 {
266            return Err(Error::UserIdTooLarge { user_id });
267        }
268        if let Ok(email) = EmailAddress::parse_with_options(
269            &user_id,
270            Options::default()
271                .with_required_tld()
272                .without_domain_literal(),
273        ) {
274            Ok(Self(OpenPgpUserIdType::Email(email)))
275        } else {
276            Ok(Self(OpenPgpUserIdType::Plain(user_id)))
277        }
278    }
279
280    /// Returns whether the [`OpenPgpUserId`] is a valid e-mail address
281    ///
282    /// # Examples
283    ///
284    /// ```
285    /// use nethsm::OpenPgpUserId;
286    ///
287    /// # fn main() -> testresult::TestResult {
288    /// assert!(!OpenPgpUserId::new("🤡".to_string())?.is_email());
289    ///
290    /// assert!(OpenPgpUserId::new("🤡 <foo@xn--rl8h.org>".to_string())?.is_email());
291    /// # Ok(())
292    /// # }
293    /// ```
294    pub fn is_email(&self) -> bool {
295        matches!(self.0, OpenPgpUserIdType::Email(..))
296    }
297}
298
299impl AsRef<str> for OpenPgpUserId {
300    fn as_ref(&self) -> &str {
301        match self.0.borrow() {
302            OpenPgpUserIdType::Email(user_id) => user_id.as_str(),
303            OpenPgpUserIdType::Plain(user_id) => user_id.as_str(),
304        }
305    }
306}
307
308impl Display for OpenPgpUserId {
309    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
310        write!(f, "{}", self.as_ref())
311    }
312}
313
314impl FromStr for OpenPgpUserId {
315    type Err = Error;
316
317    fn from_str(s: &str) -> Result<Self, Self::Err> {
318        Self::new(s.to_string())
319    }
320}
321
322impl From<OpenPgpUserId> for String {
323    fn from(value: OpenPgpUserId) -> Self {
324        value.to_string()
325    }
326}
327
328impl TryFrom<String> for OpenPgpUserId {
329    type Error = Error;
330
331    fn try_from(value: String) -> Result<Self, Self::Error> {
332        Self::new(value)
333    }
334}
335
336/// A list of [`OpenPgpUserId`]
337///
338/// The items of the list are guaranteed to be unique.
339#[derive(Clone, Debug, serde::Deserialize, Hash, Eq, PartialEq, serde::Serialize)]
340#[serde(into = "Vec<String>", try_from = "Vec<String>")]
341pub struct OpenPgpUserIdList(Vec<OpenPgpUserId>);
342
343impl OpenPgpUserIdList {
344    /// Creates a new [`OpenPgpUserIdList`]
345    ///
346    /// # Errors
347    ///
348    /// Returns an error, if one of the provided [`OpenPgpUserId`]s is a duplicate.
349    ///
350    /// # Examples
351    ///
352    /// ```
353    /// use nethsm::OpenPgpUserIdList;
354    ///
355    /// # fn main() -> testresult::TestResult {
356    /// OpenPgpUserIdList::new(vec![
357    ///     "🤡 <foo@xn--rl8h.org>".parse()?,
358    ///     "🤡 <bar@xn--rl8h.org>".parse()?,
359    /// ])?;
360    ///
361    /// // this fails because the two OpenPgpUserIds are the same
362    /// assert!(
363    ///     OpenPgpUserIdList::new(vec![
364    ///         "🤡 <foo@xn--rl8h.org>".parse()?,
365    ///         "🤡 <foo@xn--rl8h.org>".parse()?,
366    ///     ])
367    ///     .is_err()
368    /// );
369    /// # Ok(())
370    /// # }
371    /// ```
372    pub fn new(user_ids: Vec<OpenPgpUserId>) -> Result<Self, Error> {
373        let mut set = HashSet::new();
374        for user_id in user_ids.iter() {
375            if !set.insert(user_id) {
376                return Err(Error::DuplicateUserId {
377                    user_id: user_id.to_owned(),
378                });
379            }
380        }
381        Ok(Self(user_ids))
382    }
383
384    /// Iterator for OpenPGP User IDs contained in this list.
385    pub fn iter(&self) -> impl Iterator<Item = &OpenPgpUserId> {
386        self.0.iter()
387    }
388}
389
390impl AsRef<[OpenPgpUserId]> for OpenPgpUserIdList {
391    fn as_ref(&self) -> &[OpenPgpUserId] {
392        &self.0
393    }
394}
395
396impl From<OpenPgpUserIdList> for Vec<String> {
397    fn from(value: OpenPgpUserIdList) -> Self {
398        value
399            .iter()
400            .map(|user_id| user_id.to_string())
401            .collect::<Vec<String>>()
402    }
403}
404
405impl TryFrom<Vec<String>> for OpenPgpUserIdList {
406    type Error = Error;
407
408    fn try_from(value: Vec<String>) -> Result<Self, Self::Error> {
409        let user_ids = {
410            let mut user_ids: Vec<OpenPgpUserId> = vec![];
411            for user_id in value {
412                user_ids.push(OpenPgpUserId::new(user_id)?)
413            }
414            user_ids
415        };
416        OpenPgpUserIdList::new(user_ids)
417    }
418}
419
420/// PGP-adapter for a NetHSM key.
421///
422/// All PGP-related operations executed on objects of this type will be forwarded to
423/// the NetHSM instance.
424struct HsmKey<'a, 'b> {
425    public_key: PublicKey,
426    nethsm: &'a NetHsm,
427    key_id: &'b crate::KeyId,
428}
429
430impl Debug for HsmKey<'_, '_> {
431    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
432        f.debug_struct("HsmKey")
433            .field("public_key", &self.public_key)
434            .field("key_id", &self.key_id)
435            .finish()
436    }
437}
438
439/// Wraps an [`Error`] in a [`std::io::Error`] and returns it as a [`pgp::errors::Error`].
440///
441/// Since it is currently not possible to wrap the arbitrary [`Error`] of an external function
442/// cleanly in a [`pgp::errors::Error`], this function first wraps it in a [`std::io::Error`].
443/// This behavior has been suggested upstream in <https://github.com/rpgp/rpgp/issues/517#issuecomment-2778245199>
444#[inline]
445fn to_rpgp_error(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> pgp::errors::Error {
446    pgp::errors::Error::IOError(std::io::Error::other(e))
447}
448
449/// Parse signature bytes into algorithm-specific vector of MPIs.
450fn parse_signature(sig_type: crate::SignatureType, sig: &[u8]) -> pgp::errors::Result<Vec<Mpi>> {
451    use crate::SignatureType::*;
452    Ok(match sig_type {
453        EcdsaP256 | EcdsaP384 | EcdsaP521 => {
454            let sig: EcdsaSignatureValue = picky_asn1_der::from_bytes(sig).map_err(|e| {
455                error!("DER decoding error when parsing ECDSA signature: {e:?}");
456                to_rpgp_error(e)
457            })?;
458            vec![
459                Mpi::from_slice(sig.r.as_unsigned_bytes_be()),
460                Mpi::from_slice(sig.s.as_unsigned_bytes_be()),
461            ]
462        }
463        EdDsa => {
464            if sig.len() != 64 {
465                return Err(pgp::errors::Error::InvalidKeyLength);
466            }
467
468            vec![Mpi::from_slice(&sig[..32]), Mpi::from_slice(&sig[32..])]
469        }
470        Pkcs1 => {
471            // RSA
472            vec![Mpi::from_slice(sig)]
473        }
474        param => {
475            return Err(pgp::errors::Error::Unsupported(format!(
476                "Unsupoprted key type: {param:?}"
477            )));
478        }
479    })
480}
481
482impl<'a, 'b> HsmKey<'a, 'b> {
483    /// Creates a new remote signing key which will use `key_id` key for signing.
484    fn new(nethsm: &'a NetHsm, public_key: PublicKey, key_id: &'b crate::KeyId) -> Self {
485        Self {
486            nethsm,
487            public_key,
488            key_id,
489        }
490    }
491
492    /// Returns correct mode to use for signatures which depend on the public key.
493    fn sign_mode(&self) -> pgp::errors::Result<crate::SignatureType> {
494        Ok(match self.public_key.public_params() {
495            PublicParams::ECDSA(ecdsa) => match ecdsa {
496                EcdsaPublicParams::P256 { .. } => crate::SignatureType::EcdsaP256,
497                EcdsaPublicParams::P384 { .. } => crate::SignatureType::EcdsaP384,
498                EcdsaPublicParams::P521 { .. } => crate::SignatureType::EcdsaP521,
499                param => {
500                    return Err(pgp::errors::Error::Unsupported(format!(
501                        "Unsupported EC key type: {param:?}"
502                    )));
503                }
504            },
505            PublicParams::EdDSALegacy { .. } => crate::SignatureType::EdDsa,
506            PublicParams::RSA { .. } => crate::SignatureType::Pkcs1,
507            param => {
508                return Err(pgp::errors::Error::Unsupported(format!(
509                    "Unsupported key type: {param:?}"
510                )));
511            }
512        })
513    }
514}
515
516impl PublicKeyTrait for HsmKey<'_, '_> {
517    fn verify_signature(
518        &self,
519        hash: pgp::crypto::hash::HashAlgorithm,
520        data: &[u8],
521        sig: &SignatureBytes,
522    ) -> pgp::errors::Result<()> {
523        self.public_key.verify_signature(hash, data, sig)
524    }
525
526    fn encrypt<R: CryptoRng + Rng>(
527        &self,
528        rng: R,
529        plain: &[u8],
530        esk_type: EskType,
531    ) -> pgp::errors::Result<PkeskBytes> {
532        self.public_key.encrypt(rng, plain, esk_type)
533    }
534
535    fn serialize_for_hashing(&self, writer: &mut impl std::io::Write) -> pgp::errors::Result<()> {
536        self.public_key.serialize_for_hashing(writer)
537    }
538
539    fn version(&self) -> KeyVersion {
540        self.public_key.version()
541    }
542
543    fn fingerprint(&self) -> pgp::types::Fingerprint {
544        self.public_key.fingerprint()
545    }
546
547    fn key_id(&self) -> KeyId {
548        self.public_key.key_id()
549    }
550
551    fn algorithm(&self) -> PublicKeyAlgorithm {
552        self.public_key.algorithm()
553    }
554
555    fn created_at(&self) -> &chrono::DateTime<chrono::Utc> {
556        self.public_key.created_at()
557    }
558
559    fn expiration(&self) -> Option<u16> {
560        self.public_key.expiration()
561    }
562
563    fn public_params(&self) -> &PublicParams {
564        self.public_key.public_params()
565    }
566}
567
568/// Transforms the raw digest data for cryptographic signing.
569///
570/// Raw cryptographic signing primitives have special provisions that
571/// need to be taken care of when using certain combinations of
572/// signing schemes and hashing algorithms.
573///
574/// This function transforms the digest into bytes that are ready to
575/// be passed to raw cryptographic functions. The exact specifics of
576/// the transformations are documented inside the function.
577fn prepare_digest_data(
578    signature_type: crate::SignatureType,
579    hash: HashAlgorithm,
580    digest: &[u8],
581) -> pgp::errors::Result<Cow<'_, [u8]>> {
582    Ok(match signature_type {
583        // RSA-PKCS#1 signing scheme needs to wrap the digest value
584        // in an DER-encoded ASN.1 DigestInfo structure which captures
585        // the hash used.
586        // See: https://www.rfc-editor.org/rfc/rfc8017#appendix-A.2.4
587        crate::SignatureType::Pkcs1 => picky_asn1_der::to_vec(&DigestInfo {
588            oid: hash_to_oid(hash)?,
589            digest: digest.to_vec().into(),
590        })
591        .map_err(|e| {
592            error!("Encoding signature to PKCS#1 format failed: {e:?}");
593            to_rpgp_error(e)
594        })?
595        .into(),
596
597        // ECDSA may need to truncate the digest if it's too long
598        // See: https://www.rfc-editor.org/rfc/rfc9580#section-5.2.3.2
599        crate::SignatureType::EcdsaP224 => digest[..usize::min(28, digest.len())].into(),
600        crate::SignatureType::EcdsaP256 => digest[..usize::min(32, digest.len())].into(),
601        crate::SignatureType::EcdsaP384 => digest[..usize::min(48, digest.len())].into(),
602
603        // All other schemes that we use will not need any kind of
604        // digest transformations.
605        _ => digest.into(),
606    })
607}
608
609impl SecretKeyTrait for HsmKey<'_, '_> {
610    type PublicKey = PublicKey;
611
612    type Unlocked = Self;
613
614    fn unlock<F, G, T>(&self, _pw: F, work: G) -> pgp::errors::Result<T>
615    where
616        F: FnOnce() -> String,
617        G: FnOnce(&Self::Unlocked) -> pgp::errors::Result<T>,
618    {
619        work(self)
620    }
621
622    fn create_signature<F>(
623        &self,
624        _key_pw: F,
625        hash: HashAlgorithm,
626        data: &[u8],
627    ) -> pgp::errors::Result<SignatureBytes>
628    where
629        F: FnOnce() -> String,
630    {
631        let signature_type = self.sign_mode()?;
632        let request_data = prepare_digest_data(signature_type, hash, data)?;
633
634        let sig = self
635            .nethsm
636            .sign_digest(self.key_id, signature_type, &request_data)
637            .map_err(|e| {
638                error!("NetHsm::sign_digest failed: {e:?}");
639                to_rpgp_error(e)
640            })?;
641
642        Ok(parse_signature(signature_type, &sig)?.into())
643    }
644
645    fn public_key(&self) -> Self::PublicKey {
646        self.public_key.clone()
647    }
648
649    fn hash_alg(&self) -> HashAlgorithm {
650        HashAlgorithm::SHA2_512
651    }
652}
653
654/// Generates an OpenPGP certificate for the given NetHSM key.
655pub fn add_certificate(
656    nethsm: &NetHsm,
657    flags: KeyUsageFlags,
658    key_id: &crate::KeyId,
659    user_id: OpenPgpUserId,
660    created_at: DateTime<Utc>,
661    version: OpenPgpVersion,
662) -> Result<Vec<u8>, crate::Error> {
663    if version != OpenPgpVersion::V4 {
664        unimplemented!(
665            "Support for creating OpenPGP {version} certificates is not yet implemented!"
666        );
667    }
668
669    let public_key = nethsm.get_key(key_id)?;
670    let signer = HsmKey::new(nethsm, hsm_pk_to_pgp_pk(public_key, created_at)?, key_id);
671    let mut keyflags: KeyFlags = flags.into();
672    // the primary key always need to be certifying
673    keyflags.set_certify(true);
674
675    let composed_pk = pgp::PublicKey::new(
676        signer.public_key(),
677        KeyDetails::new(
678            UserId::from_str(Default::default(), user_id.as_ref()),
679            vec![],
680            vec![],
681            keyflags,
682            Default::default(),
683            Default::default(),
684            vec![CompressionAlgorithm::Uncompressed].into(),
685            vec![].into(),
686            None,
687        ),
688        vec![],
689    );
690    let signed_pk = composed_pk
691        .sign(rand::thread_rng(), &signer, String::new)
692        .map_err(Error::Pgp)?;
693    let mut buffer = vec![];
694    signed_pk.to_writer(&mut buffer).map_err(Error::Pgp)?;
695    Ok(buffer)
696}
697
698/// Converts OpenPGP hash algorithm into an OID form for PKCS#1 signing.
699fn hash_to_oid(hash: HashAlgorithm) -> pgp::errors::Result<AlgorithmIdentifier> {
700    Ok(AlgorithmIdentifier::new_sha(match hash {
701        HashAlgorithm::SHA1 => ShaVariant::SHA1,
702        HashAlgorithm::SHA2_256 => ShaVariant::SHA2_256,
703        HashAlgorithm::SHA2_384 => ShaVariant::SHA2_384,
704        HashAlgorithm::SHA2_512 => ShaVariant::SHA2_512,
705        HashAlgorithm::SHA2_224 => ShaVariant::SHA2_224,
706        HashAlgorithm::SHA3_256 => ShaVariant::SHA3_256,
707        HashAlgorithm::SHA3_512 => ShaVariant::SHA3_512,
708        hash => {
709            return Err(pgp::errors::Error::Unsupported(format!(
710                "Unsupported hash: {hash:?}"
711            )));
712        }
713    }))
714}
715
716/// Converts an OpenPGP Transferable Secret Key into [`PrivateKeyImport`] object.
717///
718/// # Errors
719///
720/// Returns an [`crate::Error::OpenPgp`] if creating a [`PrivateKeyImport`] from `key_data` is not
721/// possible.
722///
723/// Returns an [`crate::Error::Key`] if an RSA public key is shorter than
724/// [`crate::MIN_RSA_BIT_LENGTH`].
725pub fn tsk_to_private_key_import(
726    key_data: &[u8],
727) -> Result<(PrivateKeyImport, KeyMechanism), crate::Error> {
728    let key = SignedSecretKey::from_bytes(key_data).map_err(Error::Pgp)?;
729    if !key.secret_subkeys.is_empty() {
730        return Err(crate::Error::OpenPgp(
731            Error::UnsupportedMultipleComponentKeys,
732        ));
733    }
734    let SecretParams::Plain(secret) = key.primary_key.secret_params() else {
735        return Err(crate::Error::OpenPgp(Error::PrivateKeyPassphraseProtected));
736    };
737    Ok(match (secret, key.public_params()) {
738        (PlainSecretParams::RSA { p, q, .. }, PublicParams::RSA { n, e }) => {
739            // ensure, that we have sufficient bit length
740            key_type_matches_length(KeyType::Rsa, Some(n.as_bytes().len() as u32 * 8))?;
741
742            (
743                PrivateKeyImport::from_rsa(
744                    p.as_bytes().to_vec(),
745                    q.as_bytes().to_vec(),
746                    e.as_bytes().to_vec(),
747                ),
748                KeyMechanism::RsaSignaturePkcs1,
749            )
750        }
751        (PlainSecretParams::ECDSA(bytes), _) => {
752            let ec = if let PublicParams::ECDSA(pp) = key.primary_key.public_params() {
753                match pp {
754                    EcdsaPublicParams::P256 { .. } => crate::KeyType::EcP256,
755                    EcdsaPublicParams::P384 { .. } => crate::KeyType::EcP384,
756                    EcdsaPublicParams::P521 { .. } => crate::KeyType::EcP521,
757                    _ => {
758                        return Err(crate::Error::OpenPgp(Error::UnsupportedKeyFormat {
759                            public_params: Box::new(key.public_params().clone()),
760                        }));
761                    }
762                }
763            } else {
764                return Err(crate::Error::OpenPgp(Error::UnsupportedKeyFormat {
765                    public_params: Box::new(key.public_params().clone()),
766                }));
767            };
768
769            (
770                PrivateKeyImport::from_raw_bytes(ec, bytes)?,
771                KeyMechanism::EcdsaSignature,
772            )
773        }
774        (PlainSecretParams::EdDSALegacy(bytes), _) => (
775            PrivateKeyImport::from_raw_bytes(crate::KeyType::Curve25519, bytes)?,
776            KeyMechanism::EdDsaSignature,
777        ),
778        (_, public_params) => {
779            return Err(crate::Error::OpenPgp(Error::UnsupportedKeyFormat {
780                public_params: Box::new(public_params.clone()),
781            }));
782        }
783    })
784}
785
786/// Generates an OpenPGP signature using a given NetHSM key for the message.
787pub fn sign(
788    nethsm: &NetHsm,
789    key_id: &crate::KeyId,
790    message: &[u8],
791) -> Result<Vec<u8>, crate::Error> {
792    let public_key = nethsm.get_key_certificate(key_id)?;
793
794    let signer = HsmKey::new(
795        nethsm,
796        SignedPublicKey::from_bytes(&*public_key)
797            .map_err(Error::Pgp)?
798            .primary_key,
799        key_id,
800    );
801
802    let mut sig_config =
803        SignatureConfig::v4(SignatureType::Binary, signer.algorithm(), signer.hash_alg());
804    sig_config.hashed_subpackets = vec![
805        Subpacket::regular(SubpacketData::SignatureCreationTime(
806            std::time::SystemTime::now().into(),
807        )),
808        Subpacket::regular(SubpacketData::Issuer(signer.key_id())),
809        Subpacket::regular(SubpacketData::IssuerFingerprint(signer.fingerprint())),
810    ];
811
812    let mut hasher = sig_config.hash_alg.new_hasher().map_err(Error::Pgp)?;
813    sig_config
814        .hash_data_to_sign(&mut *hasher, message)
815        .map_err(Error::Pgp)?;
816
817    let len = sig_config
818        .hash_signature_data(&mut hasher)
819        .map_err(Error::Pgp)?;
820
821    hasher.update(&sig_config.trailer(len).map_err(Error::Pgp)?);
822
823    let hash = &hasher.finish()[..];
824
825    let signed_hash_value = [hash[0], hash[1]];
826    let raw_sig = signer
827        .create_signature(String::new, sig_config.hash_alg, hash)
828        .map_err(Error::Pgp)?;
829
830    let signature = pgp::Signature::from_config(sig_config, signed_hash_value, raw_sig);
831
832    let mut out = vec![];
833    pgp::packet::write_packet(&mut out, &signature).map_err(Error::Pgp)?;
834
835    Ok(out)
836}
837
838/// Generates an armored OpenPGP signature based on provided hasher state.
839///
840/// Signs the hasher `state` using the key identified by `key_id`
841/// and returns a binary [OpenPGP data signature].
842///
843/// This call requires using a user in the [`Operator`][`crate::UserRole::Operator`] [role], which
844/// carries a tag (see [`add_user_tag`][`NetHsm::add_user_tag`]) matching one of the tags of
845/// the targeted key (see [`add_key_tag`][`NetHsm::add_key_tag`]).
846///
847/// ## Namespaces
848///
849/// * [`Operator`][`crate::UserRole::Operator`] users in a [namespace] only have access to keys in
850///   their own [namespace].
851/// * System-wide [`Operator`][`crate::UserRole::Operator`] users only have access to system-wide
852///   keys.
853///
854/// # Errors
855///
856/// Returns an [`crate::Error::Api`] if creating an [OpenPGP signature] for the hasher state fails:
857/// * the NetHSM is not in [`Operational`][`crate::SystemState::Operational`] [state]
858/// * no key identified by `key_id` exists on the NetHSM
859/// * the [`Operator`][`crate::UserRole::Operator`] user does not have access to the key (e.g.
860///   different [namespace])
861/// * the [`Operator`][`crate::UserRole::Operator`] user does not carry a tag matching one of the
862///   key tags
863/// * the used [`Credentials`][`crate::Credentials`] are not correct
864/// * the used [`Credentials`][`crate::Credentials`] are not those of a user in the
865///   [`Operator`][`crate::UserRole::Operator`] [role]
866///
867/// [OpenPGP signature]: https://openpgp.dev/book/signing_data.html
868/// [OpenPGP data signature]: https://openpgp.dev/book/signing_data.html
869/// [namespace]: https://docs.nitrokey.com/nethsm/administration#namespaces
870/// [role]: https://docs.nitrokey.com/nethsm/administration#roles
871/// [state]: https://docs.nitrokey.com/nethsm/administration#state
872pub fn sign_hasher_state(
873    nethsm: &NetHsm,
874    key_id: &crate::KeyId,
875    state: impl sha2::Digest + Clone + std::io::Write,
876) -> Result<String, crate::Error> {
877    let public_key = nethsm.get_key_certificate(key_id)?;
878
879    let signer = HsmKey::new(
880        nethsm,
881        SignedPublicKey::from_bytes(public_key.as_slice())
882            .map_err(Error::Pgp)?
883            .primary_key,
884        key_id,
885    );
886
887    let hasher = state.clone();
888    let file_hash = hasher.finalize();
889
890    let sig_config = {
891        let mut sig_config =
892            SignatureConfig::v4(SignatureType::Binary, signer.algorithm(), signer.hash_alg());
893        sig_config.hashed_subpackets = vec![
894            Subpacket::regular(SubpacketData::SignatureCreationTime(
895                std::time::SystemTime::now().into(),
896            )),
897            Subpacket::regular(SubpacketData::Issuer(signer.key_id())),
898            Subpacket::regular(SubpacketData::IssuerFingerprint(signer.fingerprint())),
899            Subpacket::regular(SubpacketData::Notation(Notation {
900                readable: false,
901                name: "data-digest@archlinux.org".into(),
902                value: file_hash[..].into(),
903            })),
904        ];
905        sig_config
906    };
907
908    let hasher = {
909        let mut hasher = state.clone();
910        let write: &mut dyn std::io::Write = &mut hasher;
911
912        let len = sig_config.hash_signature_data(write).map_err(Error::Pgp)?;
913
914        hasher.update(&sig_config.trailer(len).map_err(Error::Pgp)?);
915        hasher
916    };
917
918    let hash = &hasher.finalize()[..];
919
920    let signed_hash_value = [hash[0], hash[1]];
921
922    let raw_sig = signer
923        .create_signature(String::new, sig_config.hash_alg, hash)
924        .map_err(Error::Pgp)?;
925
926    let signature = pgp::Signature::from_config(sig_config, signed_hash_value, raw_sig);
927
928    let signature = StandaloneSignature { signature };
929    Ok(signature
930        .to_armored_string(ArmorOptions::default())
931        .map_err(Error::Pgp)?)
932}
933
934/// Converts NetHSM public key to OpenPGP public key.
935///
936/// Since OpenPGP public keys have a date of creation (which is used
937/// for fingerprint calculation) this is an additional, explicit
938/// parameter.
939fn hsm_pk_to_pgp_pk(
940    pk: nethsm_sdk_rs::models::PublicKey,
941    created_at: DateTime<Utc>,
942) -> Result<PublicKey, Error> {
943    let public = pk
944        .public
945        .ok_or(Error::KeyData("missing public key data".into()))?;
946    let key_type: KeyType = pk.r#type.into();
947    Ok(match key_type {
948        KeyType::Rsa => PublicKey::new(
949            Version::New,
950            KeyVersion::V4,
951            PublicKeyAlgorithm::RSA,
952            created_at,
953            None,
954            PublicParams::RSA {
955                n: Mpi::from_raw(Base64::decode_vec(
956                    &public
957                        .modulus
958                        .ok_or(Error::KeyData("missing RSA modulus".into()))?,
959                )?),
960                e: Mpi::from_raw(Base64::decode_vec(
961                    &public
962                        .public_exponent
963                        .ok_or(Error::KeyData("missing RSA exponent".into()))?,
964                )?),
965            },
966        )?,
967        KeyType::Curve25519 => {
968            let pubkey = Base64::decode_vec(
969                &public
970                    .data
971                    .ok_or(Error::KeyData("missing ed25519 public key data".into()))?,
972            )?;
973            let mut bytes = vec![0x40];
974            bytes.extend(pubkey);
975
976            PublicKey::new(
977                Version::New,
978                KeyVersion::V4,
979                PublicKeyAlgorithm::EdDSALegacy,
980                created_at,
981                None,
982                PublicParams::EdDSALegacy {
983                    curve: ECCCurve::Ed25519,
984                    q: Mpi::from_raw(bytes),
985                },
986            )?
987        }
988        curve @ (KeyType::EcP256 | KeyType::EcP384 | KeyType::EcP521) => {
989            let pubkey = Base64::decode_vec(
990                &public
991                    .data
992                    .ok_or(Error::KeyData("missing EC public key data".into()))?,
993            )?;
994            let key = match curve {
995                KeyType::EcP256 => EcdsaPublicParams::P256 {
996                    key: p256::PublicKey::from_sec1_bytes(&pubkey)?,
997                    p: Mpi::from_raw(pubkey),
998                },
999                KeyType::EcP384 => EcdsaPublicParams::P384 {
1000                    key: p384::PublicKey::from_sec1_bytes(&pubkey)?,
1001                    p: Mpi::from_raw(pubkey),
1002                },
1003                KeyType::EcP521 => EcdsaPublicParams::P521 {
1004                    key: p521::PublicKey::from_sec1_bytes(&pubkey)?,
1005                    p: Mpi::from_raw(pubkey),
1006                },
1007                _ => unreachable!(),
1008            };
1009
1010            PublicKey::new(
1011                Version::New,
1012                KeyVersion::V4,
1013                PublicKeyAlgorithm::ECDSA,
1014                created_at,
1015                None,
1016                PublicParams::ECDSA(key),
1017            )?
1018        }
1019
1020        _ => {
1021            return Err(pgp::errors::Error::Unsupported(
1022                "unsupported key type".into(),
1023            ))?;
1024        }
1025    })
1026}
1027
1028/// Extracts certificate (public key) from an OpenPGP TSK.
1029pub fn extract_certificate(key_data: &[u8]) -> Result<Vec<u8>, crate::Error> {
1030    let key = SignedSecretKey::from_bytes(key_data).map_err(Error::Pgp)?;
1031    let public: SignedPublicKey = key.into();
1032    let mut buffer = vec![];
1033    public.to_writer(&mut buffer).map_err(Error::Pgp)?;
1034    Ok(buffer)
1035}
1036
1037/// Key usage flags that can be set on the generated certificate.
1038#[derive(Debug, Default)]
1039pub struct KeyUsageFlags(KeyFlags);
1040
1041impl KeyUsageFlags {
1042    /// Makes it possible for this key to issue data signatures.
1043    pub fn set_sign(&mut self) {
1044        self.0.set_sign(true);
1045    }
1046
1047    /// Makes it impossible for this key to issue data signatures.
1048    pub fn clear_sign(&mut self) {
1049        self.0.set_sign(false);
1050    }
1051}
1052
1053impl From<KeyUsageFlags> for KeyFlags {
1054    fn from(value: KeyUsageFlags) -> Self {
1055        value.0
1056    }
1057}
1058
1059#[cfg(test)]
1060mod tests {
1061    use nethsm_sdk_rs::models::{KeyMechanism, KeyPublicData, KeyRestrictions, KeyType};
1062    use pgp::{
1063        crypto::ecc_curve::ECCCurve,
1064        types::{EcdsaPublicParams, PublicParams},
1065    };
1066    use rstest::rstest;
1067    use testresult::TestResult;
1068
1069    use super::*;
1070
1071    #[test]
1072    fn convert_ed25519_to_pgp() -> TestResult {
1073        let hsm_key = nethsm_sdk_rs::models::PublicKey {
1074            mechanisms: vec![KeyMechanism::EdDsaSignature],
1075            r#type: KeyType::Curve25519,
1076            restrictions: Box::new(KeyRestrictions {
1077                tags: Some(vec!["signing1".into()]),
1078            }),
1079            public: Some(Box::new(KeyPublicData {
1080                modulus: None,
1081                public_exponent: None,
1082                data: Some("/ODoaDzX9xDjpx2LfR0DCIgdxqOndY9tukEFLVCObQo=".into()),
1083            })),
1084            operations: 1,
1085        };
1086
1087        let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
1088        let PublicParams::EdDSALegacy { curve, q } = pgp_key.public_params() else {
1089            panic!("Wrong type of public params");
1090        };
1091        assert_eq!(curve, &ECCCurve::Ed25519);
1092        assert_eq!(
1093            q.to_vec(),
1094            [
1095                64, 252, 224, 232, 104, 60, 215, 247, 16, 227, 167, 29, 139, 125, 29, 3, 8, 136,
1096                29, 198, 163, 167, 117, 143, 109, 186, 65, 5, 45, 80, 142, 109, 10
1097            ]
1098        );
1099
1100        Ok(())
1101    }
1102
1103    #[test]
1104    fn convert_p256_to_pgp() -> TestResult {
1105        let hsm_key = nethsm_sdk_rs::models::PublicKey {
1106            mechanisms: vec![KeyMechanism::EcdsaSignature],
1107            r#type: KeyType::EcP256,
1108            restrictions: Box::new(KeyRestrictions {
1109                tags: Some(vec!["signing2".into()]),
1110            }),
1111            public: Some(Box::new(KeyPublicData {
1112                modulus: None,
1113                public_exponent: None,
1114                data: Some(
1115                    "BN5q7GCR8w1RtXdMBR1IcIaCqbbn92vM5LItTcRbdXo5RfDwhnKK6D8tjWakqXbWY9eKelkCtALtD/hoU44WuYU="
1116                        .into(),
1117                ),
1118            })),
1119            operations: 1,
1120        };
1121        let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
1122        let PublicParams::ECDSA(EcdsaPublicParams::P256 { p, .. }) = pgp_key.public_params() else {
1123            panic!("Wrong type of public params");
1124        };
1125        assert_eq!(
1126            p.to_vec(),
1127            [
1128                4, 222, 106, 236, 96, 145, 243, 13, 81, 181, 119, 76, 5, 29, 72, 112, 134, 130,
1129                169, 182, 231, 247, 107, 204, 228, 178, 45, 77, 196, 91, 117, 122, 57, 69, 240,
1130                240, 134, 114, 138, 232, 63, 45, 141, 102, 164, 169, 118, 214, 99, 215, 138, 122,
1131                89, 2, 180, 2, 237, 15, 248, 104, 83, 142, 22, 185, 133
1132            ]
1133        );
1134
1135        Ok(())
1136    }
1137
1138    #[test]
1139    fn convert_p384_to_pgp() -> TestResult {
1140        let hsm_key = nethsm_sdk_rs::models::PublicKey {
1141            mechanisms: vec![KeyMechanism::EcdsaSignature],
1142            r#type: KeyType::EcP384,
1143            restrictions: Box::new(KeyRestrictions {
1144                tags: Some(vec!["signing2".into()]),
1145            }),
1146            public: Some(Box::new(KeyPublicData {
1147                modulus: None,
1148                public_exponent: None,
1149                data: Some(
1150                    "BH+Ik2+7v4NUpnZDTGs0jq9I+kDFTJqiMNOHP5k81agoKW8ICEJ13aL06dLNzkZAdB5iulgRCEuX/Htitii3BhxuHTUPWuN0uVKGhgYRddpTteaaauv0cOPni9la3O+/lA=="
1151                        .into(),
1152                ),
1153            })),
1154            operations: 3,
1155        };
1156        let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
1157        let PublicParams::ECDSA(EcdsaPublicParams::P384 { p, .. }) = pgp_key.public_params() else {
1158            panic!("Wrong type of public params");
1159        };
1160        assert_eq!(
1161            p.to_vec(),
1162            [
1163                4, 127, 136, 147, 111, 187, 191, 131, 84, 166, 118, 67, 76, 107, 52, 142, 175, 72,
1164                250, 64, 197, 76, 154, 162, 48, 211, 135, 63, 153, 60, 213, 168, 40, 41, 111, 8, 8,
1165                66, 117, 221, 162, 244, 233, 210, 205, 206, 70, 64, 116, 30, 98, 186, 88, 17, 8,
1166                75, 151, 252, 123, 98, 182, 40, 183, 6, 28, 110, 29, 53, 15, 90, 227, 116, 185, 82,
1167                134, 134, 6, 17, 117, 218, 83, 181, 230, 154, 106, 235, 244, 112, 227, 231, 139,
1168                217, 90, 220, 239, 191, 148
1169            ]
1170        );
1171
1172        Ok(())
1173    }
1174
1175    #[test]
1176    fn convert_p521_to_pgp() -> TestResult {
1177        let hsm_key = nethsm_sdk_rs::models::PublicKey {
1178            mechanisms: vec![KeyMechanism::EcdsaSignature],
1179            r#type: KeyType::EcP521,
1180            restrictions: Box::new(KeyRestrictions {
1181                tags: Some(vec!["signing2".into()]),
1182            }),
1183            public: Some(Box::new(KeyPublicData {
1184                modulus: None,
1185                public_exponent: None,
1186                data: Some(
1187                    "BAEhJ8HuyTN/DBjAoXD3H7jTdl+TwOwJ3taKwq2q+HsBislgZjeg1JZlOus1Mh4viKv0iuwaviid0D9cqsO2UHLN/QHTWGbzQw6fLiNZvCaGuNDf1c5+aiFMxvAgbDB8qp4eBAsl6f6ro5kKQXbpT7NauRVHYxUv32TgxG5mcRpnf+ovUQ=="
1188                        .into(),
1189                ),
1190            })),
1191            operations: 2,
1192        };
1193        let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
1194        let PublicParams::ECDSA(EcdsaPublicParams::P521 { p, .. }) = pgp_key.public_params() else {
1195            panic!("Wrong type of public params");
1196        };
1197        assert_eq!(
1198            p.to_vec(),
1199            [
1200                4, 1, 33, 39, 193, 238, 201, 51, 127, 12, 24, 192, 161, 112, 247, 31, 184, 211,
1201                118, 95, 147, 192, 236, 9, 222, 214, 138, 194, 173, 170, 248, 123, 1, 138, 201, 96,
1202                102, 55, 160, 212, 150, 101, 58, 235, 53, 50, 30, 47, 136, 171, 244, 138, 236, 26,
1203                190, 40, 157, 208, 63, 92, 170, 195, 182, 80, 114, 205, 253, 1, 211, 88, 102, 243,
1204                67, 14, 159, 46, 35, 89, 188, 38, 134, 184, 208, 223, 213, 206, 126, 106, 33, 76,
1205                198, 240, 32, 108, 48, 124, 170, 158, 30, 4, 11, 37, 233, 254, 171, 163, 153, 10,
1206                65, 118, 233, 79, 179, 90, 185, 21, 71, 99, 21, 47, 223, 100, 224, 196, 110, 102,
1207                113, 26, 103, 127, 234, 47, 81
1208            ]
1209        );
1210
1211        Ok(())
1212    }
1213
1214    #[test]
1215    fn convert_rsa_to_pgp() -> TestResult {
1216        let hsm_key = nethsm_sdk_rs::models::PublicKey {
1217            mechanisms: vec![KeyMechanism::RsaSignaturePkcs1],
1218            r#type: KeyType::Rsa,
1219            restrictions: Box::new(KeyRestrictions {
1220                tags: Some(vec!["signing8".into()]) }),
1221                public: Some(Box::new(KeyPublicData {
1222                    modulus: Some("4386l1aC1e4N93rxM+Npj+dy0CGY0W3PNbOTBGRj7tTEflkEl2qx2xW7kyme8sLQQ/yxhyJ4mqo/ggR9ODfvYytzxsS/n/MNZwdATGC4QDBjPv74s/51nC/gZHq9VzvYq3bmF0e0WNiXRT3p53Zofmv1CBDPBEDrrJq3Mq+O3+TH8/ur3OOMgvNx2CDgwwQ1WGSW3XITN9ekZpoj/h8cwxFkz5ljmygCLRtXdNWrzVJGW3G5L/Jz9sdSfE2tyb8+312IVFJ57zcvRygqAkkS11uYIPxuoabT6IJ8SpScfqltGsU3jiALKyFRV58I91KUlXegjUVR31ExFc0eADuhuw==".into()),
1223                    public_exponent: Some("AQAB".into()),
1224                    data: None })),
1225            operations: 2 };
1226        let pgp_key = hsm_pk_to_pgp_pk(hsm_key, DateTime::UNIX_EPOCH)?;
1227        let PublicParams::RSA { e, n } = pgp_key.public_params() else {
1228            panic!("Wrong type of public params");
1229        };
1230        assert_eq!(e.to_vec(), [1, 0, 1]);
1231        assert_eq!(
1232            n.to_vec(),
1233            [
1234                227, 127, 58, 151, 86, 130, 213, 238, 13, 247, 122, 241, 51, 227, 105, 143, 231,
1235                114, 208, 33, 152, 209, 109, 207, 53, 179, 147, 4, 100, 99, 238, 212, 196, 126, 89,
1236                4, 151, 106, 177, 219, 21, 187, 147, 41, 158, 242, 194, 208, 67, 252, 177, 135, 34,
1237                120, 154, 170, 63, 130, 4, 125, 56, 55, 239, 99, 43, 115, 198, 196, 191, 159, 243,
1238                13, 103, 7, 64, 76, 96, 184, 64, 48, 99, 62, 254, 248, 179, 254, 117, 156, 47, 224,
1239                100, 122, 189, 87, 59, 216, 171, 118, 230, 23, 71, 180, 88, 216, 151, 69, 61, 233,
1240                231, 118, 104, 126, 107, 245, 8, 16, 207, 4, 64, 235, 172, 154, 183, 50, 175, 142,
1241                223, 228, 199, 243, 251, 171, 220, 227, 140, 130, 243, 113, 216, 32, 224, 195, 4,
1242                53, 88, 100, 150, 221, 114, 19, 55, 215, 164, 102, 154, 35, 254, 31, 28, 195, 17,
1243                100, 207, 153, 99, 155, 40, 2, 45, 27, 87, 116, 213, 171, 205, 82, 70, 91, 113,
1244                185, 47, 242, 115, 246, 199, 82, 124, 77, 173, 201, 191, 62, 223, 93, 136, 84, 82,
1245                121, 239, 55, 47, 71, 40, 42, 2, 73, 18, 215, 91, 152, 32, 252, 110, 161, 166, 211,
1246                232, 130, 124, 74, 148, 156, 126, 169, 109, 26, 197, 55, 142, 32, 11, 43, 33, 81,
1247                87, 159, 8, 247, 82, 148, 149, 119, 160, 141, 69, 81, 223, 81, 49, 21, 205, 30, 0,
1248                59, 161, 187
1249            ]
1250        );
1251
1252        Ok(())
1253    }
1254
1255    #[test]
1256    fn parse_rsa_signature_produces_valid_data() -> TestResult {
1257        let sig = parse_signature(crate::SignatureType::Pkcs1, &[0, 1, 2])?;
1258        assert_eq!(sig.len(), 1);
1259        assert_eq!(&sig[0][..], &[1, 2]);
1260
1261        Ok(())
1262    }
1263
1264    #[test]
1265    fn parse_ed25519_signature_produces_valid_data() -> TestResult {
1266        let sig = parse_signature(
1267            crate::SignatureType::EdDsa,
1268            &[
1269                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,
1270                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,
1271                1, 1, 1, 1, 1, 1, 1, 1,
1272            ],
1273        )?;
1274        assert_eq!(sig.len(), 2);
1275        assert_eq!(sig[0].as_bytes(), vec![2; 32]);
1276        assert_eq!(sig[1].as_bytes(), vec![1; 32]);
1277
1278        Ok(())
1279    }
1280
1281    #[test]
1282    fn parse_p256_signature_produces_valid_data() -> TestResult {
1283        let sig = parse_signature(
1284            crate::SignatureType::EcdsaP256,
1285            &[
1286                48, 70, 2, 33, 0, 193, 176, 219, 0, 133, 254, 212, 239, 236, 122, 85, 239, 73, 161,
1287                179, 53, 100, 172, 103, 45, 123, 21, 169, 28, 59, 150, 72, 92, 242, 9, 53, 143, 2,
1288                33, 0, 165, 1, 144, 97, 102, 109, 66, 50, 185, 234, 211, 150, 253, 228, 210, 126,
1289                26, 0, 189, 184, 230, 163, 36, 203, 232, 161, 12, 75, 121, 171, 45, 107,
1290            ],
1291        )?;
1292        assert_eq!(sig.len(), 2);
1293        assert_eq!(
1294            sig[0].as_bytes(),
1295            [
1296                193, 176, 219, 0, 133, 254, 212, 239, 236, 122, 85, 239, 73, 161, 179, 53, 100,
1297                172, 103, 45, 123, 21, 169, 28, 59, 150, 72, 92, 242, 9, 53, 143
1298            ]
1299        );
1300        assert_eq!(
1301            sig[1].as_bytes(),
1302            [
1303                165, 1, 144, 97, 102, 109, 66, 50, 185, 234, 211, 150, 253, 228, 210, 126, 26, 0,
1304                189, 184, 230, 163, 36, 203, 232, 161, 12, 75, 121, 171, 45, 107
1305            ]
1306        );
1307
1308        Ok(())
1309    }
1310
1311    #[test]
1312    fn parse_p384_signature_produces_valid_data() -> TestResult {
1313        let sig = parse_signature(
1314            crate::SignatureType::EcdsaP384,
1315            &[
1316                48, 101, 2, 49, 0, 134, 13, 108, 74, 135, 234, 174, 105, 208, 46, 109, 18, 77, 21,
1317                177, 59, 73, 150, 228, 26, 244, 134, 187, 217, 172, 34, 2, 1, 229, 123, 105, 202,
1318                132, 233, 72, 41, 243, 138, 127, 107, 135, 95, 139, 19, 121, 179, 170, 27, 2, 48,
1319                44, 80, 117, 90, 18, 137, 36, 190, 8, 60, 201, 235, 242, 168, 164, 245, 119, 136,
1320                207, 178, 237, 64, 117, 69, 218, 189, 209, 110, 2, 9, 191, 194, 70, 50, 227, 47, 6,
1321                34, 8, 135, 43, 188, 236, 192, 184, 227, 59, 40,
1322            ],
1323        )?;
1324        assert_eq!(sig.len(), 2);
1325        assert_eq!(
1326            sig[0].as_bytes(),
1327            [
1328                134, 13, 108, 74, 135, 234, 174, 105, 208, 46, 109, 18, 77, 21, 177, 59, 73, 150,
1329                228, 26, 244, 134, 187, 217, 172, 34, 2, 1, 229, 123, 105, 202, 132, 233, 72, 41,
1330                243, 138, 127, 107, 135, 95, 139, 19, 121, 179, 170, 27
1331            ]
1332        );
1333        assert_eq!(
1334            sig[1].as_bytes(),
1335            [
1336                44, 80, 117, 90, 18, 137, 36, 190, 8, 60, 201, 235, 242, 168, 164, 245, 119, 136,
1337                207, 178, 237, 64, 117, 69, 218, 189, 209, 110, 2, 9, 191, 194, 70, 50, 227, 47, 6,
1338                34, 8, 135, 43, 188, 236, 192, 184, 227, 59, 40
1339            ]
1340        );
1341
1342        Ok(())
1343    }
1344
1345    #[test]
1346    fn parse_p521_signature_produces_valid_data() -> TestResult {
1347        let sig = parse_signature(
1348            crate::SignatureType::EcdsaP521,
1349            &[
1350                48, 129, 136, 2, 66, 0, 203, 246, 21, 57, 217, 6, 101, 73, 103, 113, 98, 39, 223,
1351                246, 199, 136, 238, 213, 134, 163, 153, 151, 116, 237, 207, 181, 107, 183, 204,
1352                110, 97, 160, 95, 160, 193, 3, 219, 46, 105, 191, 0, 139, 124, 234, 90, 125, 114,
1353                115, 205, 109, 15, 193, 166, 100, 224, 108, 87, 143, 240, 65, 41, 93, 164, 166, 2,
1354                2, 66, 1, 203, 115, 121, 219, 49, 18, 3, 101, 130, 153, 95, 80, 27, 148, 249, 221,
1355                198, 251, 149, 118, 119, 32, 44, 160, 24, 125, 72, 161, 168, 71, 48, 138, 223, 200,
1356                37, 124, 234, 17, 237, 246, 13, 123, 102, 151, 83, 95, 186, 161, 112, 41, 158, 138,
1357                144, 55, 23, 110, 100, 185, 237, 13, 174, 83, 4, 153, 34,
1358            ],
1359        )?;
1360        assert_eq!(sig.len(), 2);
1361        assert_eq!(
1362            sig[0].as_bytes(),
1363            [
1364                203, 246, 21, 57, 217, 6, 101, 73, 103, 113, 98, 39, 223, 246, 199, 136, 238, 213,
1365                134, 163, 153, 151, 116, 237, 207, 181, 107, 183, 204, 110, 97, 160, 95, 160, 193,
1366                3, 219, 46, 105, 191, 0, 139, 124, 234, 90, 125, 114, 115, 205, 109, 15, 193, 166,
1367                100, 224, 108, 87, 143, 240, 65, 41, 93, 164, 166, 2
1368            ]
1369        );
1370        assert_eq!(
1371            sig[1].as_bytes(),
1372            [
1373                1, 203, 115, 121, 219, 49, 18, 3, 101, 130, 153, 95, 80, 27, 148, 249, 221, 198,
1374                251, 149, 118, 119, 32, 44, 160, 24, 125, 72, 161, 168, 71, 48, 138, 223, 200, 37,
1375                124, 234, 17, 237, 246, 13, 123, 102, 151, 83, 95, 186, 161, 112, 41, 158, 138,
1376                144, 55, 23, 110, 100, 185, 237, 13, 174, 83, 4, 153, 34
1377            ]
1378        );
1379
1380        Ok(())
1381    }
1382
1383    #[test]
1384    fn private_key_import_ed25199_is_correctly_zero_padded() -> TestResult {
1385        let mut key_data = vec![];
1386        SignedSecretKey::from_armor_single(std::fs::File::open(
1387            "tests/fixtures/ed25519-key-with-31-byte-private-key-scalar.asc",
1388        )?)?
1389        .0
1390        .to_writer(&mut key_data)?;
1391
1392        let import: nethsm_sdk_rs::models::KeyPrivateData =
1393            tsk_to_private_key_import(&key_data)?.0.into();
1394
1395        let data = Base64::decode_vec(&import.data.unwrap())?;
1396
1397        // data needs to be zero-padded for NetHSM import even if the
1398        // input is *not* zero-padded
1399        assert_eq!(data.len(), 32);
1400        assert_eq!(data[0], 0x00);
1401
1402        Ok(())
1403    }
1404
1405    #[test]
1406    fn private_key_import_rsa_key_with_nonstandard_moduli_is_read_correctly() -> TestResult {
1407        let mut key_data = vec![];
1408        SignedSecretKey::from_armor_single(std::fs::File::open(
1409            "tests/fixtures/rsa-key-with-modulus-e-257.asc",
1410        )?)?
1411        .0
1412        .to_writer(&mut key_data)?;
1413
1414        let import: nethsm_sdk_rs::models::KeyPrivateData =
1415            tsk_to_private_key_import(&key_data)?.0.into();
1416
1417        let data = Base64::decode_vec(&import.public_exponent.unwrap())?;
1418
1419        // this key used a non-standard modulus (e) of 257
1420        assert_eq!(data, vec![0x01, 0x01]); // 257 in hex
1421
1422        Ok(())
1423    }
1424
1425    #[test]
1426    fn rsa_digest_info_is_wrapped() -> TestResult {
1427        let data = prepare_digest_data(crate::SignatureType::Pkcs1, HashAlgorithm::SHA1, &[0; 20])?;
1428
1429        assert_eq!(
1430            data,
1431            vec![
1432                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,
1433                0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1434            ]
1435        );
1436
1437        Ok(())
1438    }
1439
1440    #[rstest]
1441    #[case(crate::SignatureType::EcdsaP224, 28)]
1442    #[case(crate::SignatureType::EcdsaP256, 32)]
1443    #[case(crate::SignatureType::EcdsaP384, 48)]
1444    #[case(crate::SignatureType::EcdsaP521, 64)]
1445    fn ecdsa_wrapped_up_to_max_len(
1446        #[case] sig_type: crate::SignatureType,
1447        #[case] max_len: usize,
1448        #[values(HashAlgorithm::SHA1, HashAlgorithm::SHA2_256, HashAlgorithm::SHA2_512)] hash_algo: HashAlgorithm,
1449    ) -> TestResult {
1450        // the digest value is irrelevant - just the size of the digest
1451        let digest = hash_algo.new_hasher()?.finish();
1452        let data = prepare_digest_data(sig_type, hash_algo, &digest)?;
1453
1454        // The data to be signed size needs to be truncated to the value specific the the curve
1455        // being used. If the digest is short enough to be smaller than the curve specific field
1456        // size the digest is used as a whole.
1457        assert_eq!(
1458            data.len(),
1459            usize::min(max_len, digest.len()),
1460            "the data to be signed's length ({}) cannot exceed maximum length imposed by the curve ({})",
1461            data.len(),
1462            max_len
1463        );
1464
1465        Ok(())
1466    }
1467
1468    #[rstest]
1469    fn eddsa_is_not_wrapped(
1470        #[values(HashAlgorithm::SHA1, HashAlgorithm::SHA2_256, HashAlgorithm::SHA2_512)] hash_algo: HashAlgorithm,
1471    ) -> TestResult {
1472        // the digest value is irrelevant - just the size of the digest
1473        let digest = hash_algo.new_hasher()?.finish();
1474
1475        let data = prepare_digest_data(crate::SignatureType::EdDsa, hash_algo, &digest)?;
1476
1477        assert_eq!(data, digest);
1478
1479        Ok(())
1480    }
1481}