Skip to main content

signstar_crypto/key/base/
mod.rs

1//! Cryptographic key handling.
2
3#[cfg(feature = "nethsm")]
4pub mod nethsm;
5
6#[cfg(feature = "yubihsm2")]
7pub mod yubihsm2;
8
9use std::{collections::BTreeMap, fmt::Display};
10
11use pgp::{
12    composed::SignedPublicKey,
13    types::{KeyDetails as _, Timestamp},
14};
15use serde::{Deserialize, Serialize};
16use strum::{EnumIter, EnumString, IntoStaticStr};
17
18use crate::{
19    key::error::Error,
20    openpgp::{OpenPgpUserId, OpenPgpUserIdList, OpenPgpVersion},
21    signer::openpgp::{EmptyEd25519Signer, Notation, generate_certificate},
22};
23
24/// A mode for decrypting a message
25#[derive(
26    Clone,
27    Copy,
28    Debug,
29    Default,
30    Deserialize,
31    strum::Display,
32    strum::EnumString,
33    strum::EnumIter,
34    strum::IntoStaticStr,
35    Eq,
36    Hash,
37    Ord,
38    PartialEq,
39    PartialOrd,
40    Serialize,
41)]
42#[strum(ascii_case_insensitive)]
43pub enum DecryptMode {
44    /// Decryption using the Advanced Encryption Standard (AES) with Cipher Block Chaining (CBC)
45    AesCbc,
46
47    /// RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using an MD-5 hash
48    OaepMd5,
49
50    /// RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-1 hash
51    OaepSha1,
52
53    /// RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-224 hash
54    OaepSha224,
55
56    /// RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-256 hash
57    OaepSha256,
58
59    /// RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-384 hash
60    OaepSha384,
61
62    /// RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-512 hash
63    OaepSha512,
64
65    /// RSA decryption following the PKCS#1 standard
66    Pkcs1,
67
68    /// Raw RSA decryption
69    #[default]
70    Raw,
71}
72
73/// A mode for encrypting a message
74#[derive(
75    Clone,
76    Copy,
77    Debug,
78    Default,
79    Deserialize,
80    strum::Display,
81    strum::EnumString,
82    strum::EnumIter,
83    strum::IntoStaticStr,
84    Eq,
85    Hash,
86    Ord,
87    PartialEq,
88    PartialOrd,
89    Serialize,
90)]
91#[strum(ascii_case_insensitive)]
92pub enum EncryptMode {
93    /// Encryption using the Advanced Encryption Standard (AES) with Cipher Block Chaining (CBC)
94    #[default]
95    AesCbc,
96}
97
98/// The format of a key
99#[derive(
100    Clone,
101    Copy,
102    Debug,
103    Default,
104    Deserialize,
105    strum::Display,
106    EnumString,
107    EnumIter,
108    IntoStaticStr,
109    Eq,
110    Hash,
111    Ord,
112    PartialEq,
113    PartialOrd,
114    Serialize,
115)]
116#[strum(ascii_case_insensitive)]
117pub enum KeyFormat {
118    /// Privacy-Enhanced Mail (PEM) format.
119    Pem,
120
121    /// ASN.1 DER binary format.
122    #[default]
123    Der,
124}
125
126/// The minimum bit length for an RSA key
127///
128/// This follows recommendations from [NIST Special Publication 800-57 Part 3 Revision 1](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57Pt3r1.pdf) (January 2015).
129pub const MIN_RSA_BIT_LENGTH: u32 = 2048;
130
131/// The algorithm type of a key
132#[derive(
133    Clone,
134    Copy,
135    Debug,
136    Default,
137    Deserialize,
138    strum::Display,
139    EnumString,
140    EnumIter,
141    IntoStaticStr,
142    Eq,
143    Hash,
144    Ord,
145    PartialEq,
146    PartialOrd,
147    Serialize,
148)]
149#[strum(ascii_case_insensitive)]
150pub enum KeyType {
151    /// A Montgomery curve key over a prime field for the prime number 2^255-19
152    #[default]
153    Curve25519,
154
155    /// An elliptic (Brainpool) curve key over a prime field for a prime of size 256 bit
156    EcBp256,
157
158    /// An elliptic (Brainpool) curve key over a prime field for a prime of size 384 bit
159    EcBp384,
160
161    /// An elliptic (Brainpool) curve key over a prime field for a prime of size 512 bit
162    EcBp512,
163
164    /// An elliptic (Koblitz) curve key over a prime field for a prime of size 256 bit
165    EcK256,
166
167    /// An elliptic-curve key over a prime field for a prime of size 224 bit
168    EcP224,
169
170    /// An elliptic-curve key over a prime field for a prime of size 256 bit
171    EcP256,
172
173    /// An elliptic-curve key over a prime field for a prime of size 384 bit
174    EcP384,
175
176    /// An elliptic-curve key over a prime field for a prime of size 521 bit
177    EcP521,
178
179    /// A generic key used for block ciphers
180    Generic,
181
182    /// An RSA key
183    Rsa,
184}
185
186/// A mechanism which can be used with a key
187#[derive(
188    Clone,
189    Copy,
190    Debug,
191    Default,
192    Deserialize,
193    strum::Display,
194    EnumString,
195    EnumIter,
196    IntoStaticStr,
197    Hash,
198    Eq,
199    Ord,
200    PartialEq,
201    PartialOrd,
202    Serialize,
203)]
204#[strum(ascii_case_insensitive)]
205pub enum KeyMechanism {
206    /// Decryption using the Advanced Encryption Standard (AES) with Cipher Block Chaining (CBC)
207    AesDecryptionCbc,
208
209    /// Encryption using the Advanced Encryption Standard (AES) with Cipher Block Chaining (CBC)
210    AesEncryptionCbc,
211
212    /// Signing following the Elliptic Curve Digital Signature Algorithm (ECDSA)
213    EcdsaSignature,
214
215    /// Signing following the Edwards-curve Digital Signature Algorithm (EdDSA)
216    #[default]
217    EdDsaSignature,
218
219    /// RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using an MD-5 hash
220    RsaDecryptionOaepMd5,
221
222    /// RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-1 hash
223    RsaDecryptionOaepSha1,
224
225    /// RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-224 hash
226    RsaDecryptionOaepSha224,
227
228    /// RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-256 hash
229    RsaDecryptionOaepSha256,
230
231    /// RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-384 hash
232    RsaDecryptionOaepSha384,
233
234    /// RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-512 hash
235    RsaDecryptionOaepSha512,
236
237    /// RSA decryption following the PKCS#1 standard
238    RsaDecryptionPkcs1,
239
240    /// Raw RSA decryption
241    RsaDecryptionRaw,
242
243    /// RSA signing following the PKCS#1 standard
244    RsaSignaturePkcs1,
245
246    /// RSA signing following a "probabilistic signature scheme" (PSS) using a SHA-1 hash
247    RsaSignaturePssSha1,
248
249    /// RSA signing following a "probabilistic signature scheme" (PSS) using a SHA-224 hash
250    RsaSignaturePssSha224,
251
252    /// RSA signing following a "probabilistic signature scheme" (PSS) using a SHA-256 hash
253    RsaSignaturePssSha256,
254
255    /// RSA signing following a "probabilistic signature scheme" (PSS) using a SHA-384 hash
256    RsaSignaturePssSha384,
257
258    /// RSA signing following a "probabilistic signature scheme" (PSS) using a SHA-512 hash
259    RsaSignaturePssSha512,
260}
261
262impl KeyMechanism {
263    /// Returns key mechanisms specific to Curve25519 key types
264    pub fn curve25519_mechanisms() -> Vec<KeyMechanism> {
265        vec![KeyMechanism::EdDsaSignature]
266    }
267
268    /// Returns key mechanisms specific to elliptic curve key types
269    pub fn elliptic_curve_mechanisms() -> Vec<KeyMechanism> {
270        vec![KeyMechanism::EcdsaSignature]
271    }
272
273    /// Returns key mechanisms specific to generic key types
274    pub fn generic_mechanisms() -> Vec<KeyMechanism> {
275        vec![
276            KeyMechanism::AesDecryptionCbc,
277            KeyMechanism::AesEncryptionCbc,
278        ]
279    }
280
281    /// Returns key mechanisms specific to RSA key types
282    pub fn rsa_mechanisms() -> Vec<KeyMechanism> {
283        vec![
284            KeyMechanism::RsaDecryptionRaw,
285            KeyMechanism::RsaDecryptionPkcs1,
286            KeyMechanism::RsaDecryptionOaepMd5,
287            KeyMechanism::RsaDecryptionOaepSha1,
288            KeyMechanism::RsaDecryptionOaepSha224,
289            KeyMechanism::RsaDecryptionOaepSha256,
290            KeyMechanism::RsaDecryptionOaepSha384,
291            KeyMechanism::RsaDecryptionOaepSha512,
292            KeyMechanism::RsaSignaturePkcs1,
293            KeyMechanism::RsaSignaturePssSha1,
294            KeyMechanism::RsaSignaturePssSha224,
295            KeyMechanism::RsaSignaturePssSha256,
296            KeyMechanism::RsaSignaturePssSha384,
297            KeyMechanism::RsaSignaturePssSha512,
298        ]
299    }
300}
301
302/// The type of a signature.
303#[derive(
304    Clone,
305    Copy,
306    Debug,
307    Deserialize,
308    strum::Display,
309    EnumString,
310    EnumIter,
311    IntoStaticStr,
312    Eq,
313    PartialEq,
314    Ord,
315    PartialOrd,
316    Hash,
317    Serialize,
318)]
319#[strum(ascii_case_insensitive)]
320pub enum SignatureType {
321    /// Elliptic Curve Digital Signature Algorithm (ECDSA) signing using a (Koblitz) key over a
322    /// prime field for a prime of size 256 bit
323    EcdsaK256,
324
325    /// Elliptic Curve Digital Signature Algorithm (ECDSA) signing using a key over a prime field
326    /// for a prime of size 224 bit
327    EcdsaP224,
328
329    /// Elliptic Curve Digital Signature Algorithm (ECDSA) signing using a key over a prime field
330    /// for a prime of size 256 bit
331    EcdsaP256,
332
333    /// Elliptic Curve Digital Signature Algorithm (ECDSA) signing using a key over a prime field
334    /// for a prime of size 384 bit
335    EcdsaP384,
336
337    /// Elliptic Curve Digital Signature Algorithm (ECDSA) signing using a key over a prime field
338    /// for a prime of size 521 bit
339    EcdsaP521,
340
341    /// Signing following the Edwards-curve Digital Signature Algorithm (EdDSA)
342    EdDsa,
343
344    /// RSA signing following the PKCS#1 standard
345    Pkcs1,
346
347    /// RSA signing following a "probabilistic signature scheme" (PSS) using a SHA-1 hash
348    PssSha1,
349
350    /// RSA signing following a "probabilistic signature scheme" (PSS) using a SHA-224 hash
351    PssSha224,
352
353    /// RSA signing following a "probabilistic signature scheme" (PSS) using a SHA-256 hash
354    PssSha256,
355
356    /// RSA signing following a "probabilistic signature scheme" (PSS) using a SHA-384 hash
357    PssSha384,
358
359    /// RSA signing following a "probabilistic signature scheme" (PSS) using a SHA-512 hash
360    PssSha512,
361}
362
363/// The cryptographic context in which a key is used.
364#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
365pub enum CryptographicKeyContext {
366    /// A key is used in an OpenPGP context
367    #[serde(rename = "openpgp")]
368    OpenPgp {
369        /// List of OpenPGP User IDs for the certificate.
370        user_ids: OpenPgpUserIdList,
371
372        /// OpenPGP version for the certificate.
373        version: OpenPgpVersion,
374
375        /// OpenPGP notations to attach to created signatures.
376        #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
377        notations: BTreeMap<String, String>,
378    },
379
380    /// A key is used in a raw cryptographic context
381    #[serde(rename = "raw")]
382    Raw,
383}
384
385impl CryptographicKeyContext {
386    /// Validates the cryptographic context against a signing key setup
387    ///
388    /// # Errors
389    ///
390    /// Returns an error if the key setup can not be used for signing operations in the respective
391    /// cryptographic context.
392    ///
393    /// # Examples
394    ///
395    /// ```
396    /// use signstar_crypto::key::{CryptographicKeyContext, KeyMechanism, KeyType, SignatureType};
397    /// use signstar_crypto::openpgp::{OpenPgpUserIdList, OpenPgpVersion};
398    ///
399    /// # fn main() -> testresult::TestResult {
400    /// CryptographicKeyContext::Raw.validate_signing_key_setup(
401    ///     KeyType::Curve25519,
402    ///     &[KeyMechanism::EdDsaSignature],
403    ///     SignatureType::EdDsa,
404    /// )?;
405    ///
406    /// CryptographicKeyContext::OpenPgp {
407    ///     user_ids: OpenPgpUserIdList::new(vec!["Foobar McFooface <foobar@mcfooface.org>".parse()?])?,
408    ///     version: OpenPgpVersion::V4,
409    ///     notations: Default::default(),
410    /// }
411    /// .validate_signing_key_setup(
412    ///     KeyType::Curve25519,
413    ///     &[KeyMechanism::EdDsaSignature],
414    ///     SignatureType::EdDsa,
415    /// )?;
416    /// # Ok(())
417    /// # }
418    /// ```
419    pub fn validate_signing_key_setup(
420        &self,
421        key_type: KeyType,
422        key_mechanisms: &[KeyMechanism],
423        signature_type: SignatureType,
424    ) -> Result<(), crate::Error> {
425        match self {
426            Self::Raw => match (key_type, signature_type) {
427                (KeyType::Curve25519, SignatureType::EdDsa)
428                    if key_mechanisms.contains(&KeyMechanism::EdDsaSignature) => {}
429                (KeyType::EcP256, SignatureType::EcdsaP256)
430                    if key_mechanisms.contains(&KeyMechanism::EcdsaSignature) => {}
431                (KeyType::EcP384, SignatureType::EcdsaP384)
432                    if key_mechanisms.contains(&KeyMechanism::EcdsaSignature) => {}
433                (KeyType::EcP521, SignatureType::EcdsaP521)
434                    if key_mechanisms.contains(&KeyMechanism::EcdsaSignature) => {}
435                (KeyType::Rsa, SignatureType::Pkcs1)
436                    if key_mechanisms.contains(&KeyMechanism::RsaSignaturePkcs1) => {}
437                (KeyType::Rsa, SignatureType::PssSha1)
438                    if key_mechanisms.contains(&KeyMechanism::RsaSignaturePssSha1) => {}
439                (KeyType::Rsa, SignatureType::PssSha224)
440                    if key_mechanisms.contains(&KeyMechanism::RsaSignaturePssSha224) => {}
441                (KeyType::Rsa, SignatureType::PssSha256)
442                    if key_mechanisms.contains(&KeyMechanism::RsaSignaturePssSha256) => {}
443                (KeyType::Rsa, SignatureType::PssSha384)
444                    if key_mechanisms.contains(&KeyMechanism::RsaSignaturePssSha384) => {}
445                (KeyType::Rsa, SignatureType::PssSha512)
446                    if key_mechanisms.contains(&KeyMechanism::RsaSignaturePssSha512) => {}
447                _ => {
448                    return Err(Error::InvalidRawSigningKeySetup {
449                        key_type,
450                        key_mechanisms: key_mechanisms.to_vec(),
451                        signature_type,
452                    }
453                    .into());
454                }
455            },
456            Self::OpenPgp {
457                user_ids: _,
458                version: _,
459                notations: _,
460            } => match (key_type, signature_type) {
461                (KeyType::Curve25519, SignatureType::EdDsa)
462                    if key_mechanisms.contains(&KeyMechanism::EdDsaSignature) => {}
463                (KeyType::EcP256, SignatureType::EcdsaP256)
464                    if key_mechanisms.contains(&KeyMechanism::EcdsaSignature) => {}
465                (KeyType::EcP384, SignatureType::EcdsaP384)
466                    if key_mechanisms.contains(&KeyMechanism::EcdsaSignature) => {}
467                (KeyType::EcP521, SignatureType::EcdsaP521)
468                    if key_mechanisms.contains(&KeyMechanism::EcdsaSignature) => {}
469                (KeyType::Rsa, SignatureType::Pkcs1)
470                    if key_mechanisms.contains(&KeyMechanism::RsaSignaturePkcs1) => {}
471                _ => {
472                    return Err(Error::InvalidOpenPgpSigningKeySetup {
473                        key_type,
474                        key_mechanisms: key_mechanisms.to_vec(),
475                        signature_type,
476                    }
477                    .into());
478                }
479            },
480        }
481        Ok(())
482    }
483
484    /// Estimates the OpenPGP certificate size of this [`CryptographicKeyContext`].
485    ///
486    /// For non OpenPGP contexts this function returns [`Option::None`].
487    ///
488    /// # Errors
489    ///
490    /// Returns an error if the certificate creation fails.
491    ///
492    /// # Examples
493    ///
494    /// ```
495    /// use signstar_crypto::key::CryptographicKeyContext;
496    /// use signstar_crypto::openpgp::{OpenPgpUserIdList, OpenPgpVersion};
497    ///
498    /// # fn main() -> testresult::TestResult {
499    /// let cert_size = CryptographicKeyContext::OpenPgp {
500    ///     notations: Default::default(),
501    ///     user_ids: OpenPgpUserIdList::new(vec!["Foobar McFooface <foobar@mcfooface.org>".parse()?])?,
502    ///     version: OpenPgpVersion::V4,
503    /// }
504    /// .openpgp_cert_size()?;
505    ///
506    /// assert_eq!(cert_size, Some(155));
507    ///
508    /// let cert_size = CryptographicKeyContext::Raw.openpgp_cert_size()?;
509    ///
510    /// assert_eq!(cert_size, None);
511    /// # Ok(())
512    /// # }
513    /// ```
514    pub fn openpgp_cert_size(&self) -> Result<Option<usize>, crate::Error> {
515        if let CryptographicKeyContext::OpenPgp {
516            user_ids,
517            notations,
518            ..
519        } = self
520        {
521            match generate_certificate(
522                &EmptyEd25519Signer,
523                Default::default(),
524                user_ids.as_ref(),
525                notations
526                    .iter()
527                    .map(|(name, value)| Notation { name, value })
528                    .collect::<Vec<_>>()
529                    .as_slice(),
530                Timestamp::now(),
531                Default::default(),
532            ) {
533                Err(error) => Err(error),
534                Ok(cert) => Ok(Some(cert.len())),
535            }
536        } else {
537            Ok(None)
538        }
539    }
540}
541
542impl Display for CryptographicKeyContext {
543    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
544        match self {
545            Self::OpenPgp {
546                user_ids,
547                version,
548                notations,
549            } => {
550                write!(
551                    f,
552                    "OpenPGP (Version: {version}; User IDs: {}",
553                    user_ids
554                        .iter()
555                        .map(|user_id| format!("\"{user_id}\""))
556                        .collect::<Vec<String>>()
557                        .join(", ")
558                )?;
559                if !notations.is_empty() {
560                    write!(
561                        f,
562                        "; Notations: {}",
563                        notations
564                            .iter()
565                            .map(|(key, value)| format!("\"{key}={value}\""))
566                            .collect::<Vec<String>>()
567                            .join(", ")
568                    )?;
569                }
570                write!(f, ")")
571            }
572            Self::Raw => {
573                write!(f, "Raw")
574            }
575        }
576    }
577}
578
579impl TryFrom<SignedPublicKey> for CryptographicKeyContext {
580    type Error = crate::Error;
581
582    /// Creates a [`CryptographicKeyContext`] from [`SignedPublicKey`].
583    ///
584    /// Drops any invalid OpenPGP User ID (e.g. non-UTF-8).
585    ///
586    /// # Errors
587    ///
588    /// Returns an error if
589    ///
590    /// - duplicate OpenPGP User IDs are encountered in `value`,
591    /// - or no valid OpenPGP version can be derived from the OpenPGP primary key in `value`.
592    fn try_from(value: SignedPublicKey) -> Result<Self, Self::Error> {
593        let user_ids: Vec<OpenPgpUserId> = value
594            .details
595            .users
596            .iter()
597            .filter_map(|signed_user| signed_user.try_into().ok())
598            .collect();
599
600        let notations = value
601            .details
602            .users
603            .iter()
604            .flat_map(|user| &user.signatures)
605            .flat_map(|sig| sig.notations())
606            .fold(BTreeMap::new(), |mut acc, notation| {
607                acc.entry(String::from_utf8_lossy(&notation.name).into())
608                    .or_insert(String::from_utf8_lossy(&notation.value).into());
609                acc
610            });
611
612        Ok(Self::OpenPgp {
613            user_ids: OpenPgpUserIdList::new(user_ids)?,
614            version: value.primary_key.version().try_into()?,
615            notations,
616        })
617    }
618}
619
620/// Ensures that a [`KeyType`] is compatible with a list of [`KeyMechanism`]s
621///
622/// # Errors
623///
624/// Returns an error if any of the `mechanisms` is incompatible with the `key_type`.
625///
626/// # Examples
627///
628/// ```
629/// use signstar_crypto::key::{KeyMechanism, KeyType, key_type_matches_mechanisms};
630///
631/// # fn main() -> testresult::TestResult {
632/// key_type_matches_mechanisms(KeyType::Curve25519, &[KeyMechanism::EdDsaSignature])?;
633/// key_type_matches_mechanisms(
634///     KeyType::Rsa,
635///     &[
636///         KeyMechanism::RsaDecryptionPkcs1,
637///         KeyMechanism::RsaSignaturePkcs1,
638///     ],
639/// )?;
640/// key_type_matches_mechanisms(
641///     KeyType::Generic,
642///     &[
643///         KeyMechanism::AesDecryptionCbc,
644///         KeyMechanism::AesEncryptionCbc,
645///     ],
646/// )?;
647///
648/// // this fails because Curve25519 is not compatible with the Elliptic Curve Digital Signature Algorithm (ECDSA),
649/// // but instead requires the use of the Edwards-curve Digital Signature Algorithm (EdDSA)
650/// assert!(
651///     key_type_matches_mechanisms(KeyType::Curve25519, &[KeyMechanism::EcdsaSignature]).is_err()
652/// );
653///
654/// // this fails because RSA key mechanisms are not compatible with block ciphers
655/// assert!(key_type_matches_mechanisms(
656///     KeyType::Generic,
657///     &[
658///         KeyMechanism::RsaDecryptionPkcs1,
659///         KeyMechanism::RsaSignaturePkcs1,
660///     ]
661/// )
662/// .is_err());
663///
664/// // this fails because RSA keys do not support Curve25519's Edwards-curve Digital Signature Algorithm (EdDSA)
665/// assert!(key_type_matches_mechanisms(
666///     KeyType::Rsa,
667///     &[
668///         KeyMechanism::AesDecryptionCbc,
669///         KeyMechanism::AesEncryptionCbc,
670///         KeyMechanism::EcdsaSignature
671///     ]
672/// )
673/// .is_err());
674/// # Ok(())
675/// # }
676/// ```
677pub fn key_type_matches_mechanisms(
678    key_type: KeyType,
679    mechanisms: &[KeyMechanism],
680) -> Result<(), crate::Error> {
681    let valid_mechanisms: &[KeyMechanism] = match key_type {
682        KeyType::Curve25519 => &KeyMechanism::curve25519_mechanisms(),
683        KeyType::EcBp256
684        | KeyType::EcBp384
685        | KeyType::EcBp512
686        | KeyType::EcK256
687        | KeyType::EcP224
688        | KeyType::EcP256
689        | KeyType::EcP384
690        | KeyType::EcP521 => &KeyMechanism::elliptic_curve_mechanisms(),
691        KeyType::Generic => &KeyMechanism::generic_mechanisms(),
692        KeyType::Rsa => &KeyMechanism::rsa_mechanisms(),
693    };
694
695    let invalid_mechanisms = mechanisms
696        .iter()
697        .filter(|mechanism| !valid_mechanisms.contains(mechanism))
698        .cloned()
699        .collect::<Vec<KeyMechanism>>();
700
701    if invalid_mechanisms.is_empty() {
702        Ok(())
703    } else {
704        Err(Error::InvalidKeyMechanism {
705            key_type,
706            invalid_mechanisms,
707        }
708        .into())
709    }
710}
711
712/// Ensures that a [`KeyType`] and a list of [`KeyMechanism`]s is compatible with a
713/// [`SignatureType`]
714///
715/// # Errors
716///
717/// Returns an error if the provided `signature_type` is incompatible with the `key_type` or
718/// `mechanisms`.
719///
720/// # Examples
721///
722/// ```
723/// use signstar_crypto::key::{KeyMechanism, KeyType, SignatureType, key_type_and_mechanisms_match_signature_type};
724///
725/// # fn main() -> testresult::TestResult {
726/// key_type_and_mechanisms_match_signature_type(KeyType::Curve25519, &[KeyMechanism::EdDsaSignature], SignatureType::EdDsa)?;
727/// key_type_and_mechanisms_match_signature_type(KeyType::EcP256, &[KeyMechanism::EcdsaSignature], SignatureType::EcdsaP256)?;
728/// key_type_and_mechanisms_match_signature_type(KeyType::Rsa, &[KeyMechanism::RsaSignaturePkcs1],SignatureType::Pkcs1)?;
729///
730/// // this fails because Curve25519 is not compatible with the Elliptic Curve Digital Signature Algorithm (ECDSA),
731/// // but instead requires the use of the Edwards-curve Digital Signature Algorithm (EdDSA)
732/// assert!(
733///     key_type_and_mechanisms_match_signature_type(KeyType::Curve25519, &[KeyMechanism::EdDsaSignature], SignatureType::EcdsaP256).is_err()
734/// );
735/// # Ok(())
736/// # }
737/// ```
738pub fn key_type_and_mechanisms_match_signature_type(
739    key_type: KeyType,
740    mechanisms: &[KeyMechanism],
741    signature_type: SignatureType,
742) -> Result<(), crate::Error> {
743    match signature_type {
744        SignatureType::EcdsaK256 => {
745            if key_type != KeyType::EcK256 {
746                return Err(Error::InvalidKeyTypeForSignatureType {
747                    key_type,
748                    signature_type,
749                }
750                .into());
751            } else if !mechanisms.contains(&KeyMechanism::EcdsaSignature) {
752                return Err(Error::InvalidKeyMechanismsForSignatureType {
753                    required_key_mechanism: KeyMechanism::EcdsaSignature,
754                    signature_type,
755                }
756                .into());
757            }
758        }
759        SignatureType::EcdsaP224 => {
760            if key_type != KeyType::EcP224 {
761                return Err(Error::InvalidKeyTypeForSignatureType {
762                    key_type,
763                    signature_type,
764                }
765                .into());
766            } else if !mechanisms.contains(&KeyMechanism::EcdsaSignature) {
767                return Err(Error::InvalidKeyMechanismsForSignatureType {
768                    required_key_mechanism: KeyMechanism::EcdsaSignature,
769                    signature_type,
770                }
771                .into());
772            }
773        }
774        SignatureType::EcdsaP256 => {
775            if key_type != KeyType::EcP256 {
776                return Err(Error::InvalidKeyTypeForSignatureType {
777                    key_type,
778                    signature_type,
779                }
780                .into());
781            } else if !mechanisms.contains(&KeyMechanism::EcdsaSignature) {
782                return Err(Error::InvalidKeyMechanismsForSignatureType {
783                    required_key_mechanism: KeyMechanism::EcdsaSignature,
784                    signature_type,
785                }
786                .into());
787            }
788        }
789        SignatureType::EcdsaP384 => {
790            if key_type != KeyType::EcP384 {
791                return Err(Error::InvalidKeyTypeForSignatureType {
792                    key_type,
793                    signature_type,
794                }
795                .into());
796            } else if !mechanisms.contains(&KeyMechanism::EcdsaSignature) {
797                return Err(Error::InvalidKeyMechanismsForSignatureType {
798                    required_key_mechanism: KeyMechanism::EcdsaSignature,
799                    signature_type,
800                }
801                .into());
802            }
803        }
804        SignatureType::EcdsaP521 => {
805            if key_type != KeyType::EcP521 {
806                return Err(Error::InvalidKeyTypeForSignatureType {
807                    key_type,
808                    signature_type,
809                }
810                .into());
811            } else if !mechanisms.contains(&KeyMechanism::EcdsaSignature) {
812                return Err(Error::InvalidKeyMechanismsForSignatureType {
813                    required_key_mechanism: KeyMechanism::EcdsaSignature,
814                    signature_type,
815                }
816                .into());
817            }
818        }
819        SignatureType::EdDsa => {
820            if key_type != KeyType::Curve25519 {
821                return Err(Error::InvalidKeyTypeForSignatureType {
822                    key_type,
823                    signature_type,
824                }
825                .into());
826            } else if !mechanisms.contains(&KeyMechanism::EdDsaSignature) {
827                return Err(Error::InvalidKeyMechanismsForSignatureType {
828                    required_key_mechanism: KeyMechanism::EdDsaSignature,
829                    signature_type,
830                }
831                .into());
832            }
833        }
834        SignatureType::Pkcs1 => {
835            if key_type != KeyType::Rsa {
836                return Err(Error::InvalidKeyTypeForSignatureType {
837                    key_type,
838                    signature_type,
839                }
840                .into());
841            } else if !mechanisms.contains(&KeyMechanism::RsaSignaturePkcs1) {
842                return Err(Error::InvalidKeyMechanismsForSignatureType {
843                    required_key_mechanism: KeyMechanism::RsaSignaturePkcs1,
844                    signature_type,
845                }
846                .into());
847            }
848        }
849        SignatureType::PssSha1 => {
850            if key_type != KeyType::Rsa {
851                return Err(Error::InvalidKeyTypeForSignatureType {
852                    key_type,
853                    signature_type,
854                }
855                .into());
856            } else if !mechanisms.contains(&KeyMechanism::RsaSignaturePssSha1) {
857                return Err(Error::InvalidKeyMechanismsForSignatureType {
858                    required_key_mechanism: KeyMechanism::RsaSignaturePssSha1,
859                    signature_type,
860                }
861                .into());
862            }
863        }
864        SignatureType::PssSha224 => {
865            if key_type != KeyType::Rsa {
866                return Err(Error::InvalidKeyTypeForSignatureType {
867                    key_type,
868                    signature_type,
869                }
870                .into());
871            } else if !mechanisms.contains(&KeyMechanism::RsaSignaturePssSha224) {
872                return Err(Error::InvalidKeyMechanismsForSignatureType {
873                    required_key_mechanism: KeyMechanism::RsaSignaturePssSha224,
874                    signature_type,
875                }
876                .into());
877            }
878        }
879        SignatureType::PssSha256 => {
880            if key_type != KeyType::Rsa {
881                return Err(Error::InvalidKeyTypeForSignatureType {
882                    key_type,
883                    signature_type,
884                }
885                .into());
886            } else if !mechanisms.contains(&KeyMechanism::RsaSignaturePssSha256) {
887                return Err(Error::InvalidKeyMechanismsForSignatureType {
888                    required_key_mechanism: KeyMechanism::RsaSignaturePssSha256,
889                    signature_type,
890                }
891                .into());
892            }
893        }
894        SignatureType::PssSha384 => {
895            if key_type != KeyType::Rsa {
896                return Err(Error::InvalidKeyTypeForSignatureType {
897                    key_type,
898                    signature_type,
899                }
900                .into());
901            } else if !mechanisms.contains(&KeyMechanism::RsaSignaturePssSha384) {
902                return Err(Error::InvalidKeyMechanismsForSignatureType {
903                    required_key_mechanism: KeyMechanism::RsaSignaturePssSha384,
904                    signature_type,
905                }
906                .into());
907            }
908        }
909        SignatureType::PssSha512 => {
910            if key_type != KeyType::Rsa {
911                return Err(Error::InvalidKeyTypeForSignatureType {
912                    key_type,
913                    signature_type,
914                }
915                .into());
916            } else if !mechanisms.contains(&KeyMechanism::RsaSignaturePssSha512) {
917                return Err(Error::InvalidKeyMechanismsForSignatureType {
918                    required_key_mechanism: KeyMechanism::RsaSignaturePssSha512,
919                    signature_type,
920                }
921                .into());
922            }
923        }
924    }
925    Ok(())
926}
927
928/// Ensures that a [`KeyType`] is compatible with an optional key length
929///
930/// # Errors
931///
932/// Returns an error if
933/// * `key_type` is one of [`KeyType::Curve25519`], [`KeyType::EcP256`], [`KeyType::EcP384`] or
934///   [`KeyType::EcP521`] and `length` is [`Some`].
935/// * `key_type` is [`KeyType::Generic`] or [`KeyType::Rsa`] and `length` is [`None`].
936/// * `key_type` is [`KeyType::Generic`] and `length` is not [`Some`] value of `128`, `192` or
937///   `256`.
938/// * `key_type` is [`KeyType::Rsa`] and `length` is not [`Some`] value equal to or greater than
939///   [`MIN_RSA_BIT_LENGTH`].
940///
941/// # Examples
942///
943/// ```
944/// use signstar_crypto::key::{KeyType, key_type_matches_length};
945///
946/// # fn main() -> testresult::TestResult {
947/// key_type_matches_length(KeyType::Curve25519, None)?;
948/// key_type_matches_length(KeyType::EcP256, None)?;
949/// key_type_matches_length(KeyType::Rsa, Some(2048))?;
950/// key_type_matches_length(KeyType::Generic, Some(256))?;
951///
952/// // this fails because elliptic curve keys have their length set intrinsically
953/// assert!(key_type_matches_length(KeyType::Curve25519, Some(2048)).is_err());
954/// // this fails because a bit length of 2048 is not defined for AES block ciphers
955/// assert!(key_type_matches_length(KeyType::Generic, Some(2048)).is_err());
956/// // this fails because a bit length of 1024 is unsafe to use for RSA keys
957/// assert!(key_type_matches_length(KeyType::Rsa, Some(1024)).is_err());
958/// # Ok(())
959/// # }
960/// ```
961pub fn key_type_matches_length(key_type: KeyType, length: Option<u32>) -> Result<(), crate::Error> {
962    match key_type {
963        KeyType::Curve25519
964        | KeyType::EcBp256
965        | KeyType::EcBp384
966        | KeyType::EcBp512
967        | KeyType::EcK256
968        | KeyType::EcP224
969        | KeyType::EcP256
970        | KeyType::EcP384
971        | KeyType::EcP521 => {
972            if length.is_some() {
973                Err(Error::KeyLengthUnsupported { key_type }.into())
974            } else {
975                Ok(())
976            }
977        }
978        KeyType::Generic => match length {
979            None => Err(Error::KeyLengthRequired { key_type }.into()),
980            Some(length) => {
981                if ![128, 192, 256].contains(&length) {
982                    Err(Error::InvalidKeyLengthAes { key_length: length }.into())
983                } else {
984                    Ok(())
985                }
986            }
987        },
988        KeyType::Rsa => match length {
989            None => Err(Error::KeyLengthRequired { key_type }.into()),
990            Some(length) => {
991                if length < MIN_RSA_BIT_LENGTH {
992                    Err(Error::InvalidKeyLengthRsa { key_length: length }.into())
993                } else {
994                    Ok(())
995                }
996            }
997        },
998    }
999}
1000
1001#[cfg(test)]
1002mod tests {
1003    use std::str::FromStr;
1004
1005    use rstest::rstest;
1006    use testresult::TestResult;
1007
1008    use super::*;
1009
1010    #[rstest]
1011    #[case(KeyType::Curve25519, &[KeyMechanism::EdDsaSignature], SignatureType::EdDsa, None)]
1012    #[case(KeyType::EcP256, &[KeyMechanism::EcdsaSignature], SignatureType::EcdsaP256, None)]
1013    #[case(KeyType::EcP384, &[KeyMechanism::EcdsaSignature], SignatureType::EcdsaP384, None)]
1014    #[case(KeyType::EcP521, &[KeyMechanism::EcdsaSignature], SignatureType::EcdsaP521, None)]
1015    #[case(KeyType::Rsa, &[KeyMechanism::RsaSignaturePkcs1], SignatureType::Pkcs1, None)]
1016    #[case(KeyType::Rsa, &[KeyMechanism::RsaSignaturePssSha1], SignatureType::PssSha1, None)]
1017    #[case(KeyType::Rsa, &[KeyMechanism::RsaSignaturePssSha224], SignatureType::PssSha224, None)]
1018    #[case(KeyType::Rsa, &[KeyMechanism::RsaSignaturePssSha256], SignatureType::PssSha256, None)]
1019    #[case(KeyType::Rsa, &[KeyMechanism::RsaSignaturePssSha384], SignatureType::PssSha384, None)]
1020    #[case(KeyType::Rsa, &[KeyMechanism::RsaSignaturePssSha512], SignatureType::PssSha512, None)]
1021    #[case(
1022        KeyType::Curve25519,
1023        &[KeyMechanism::EdDsaSignature],
1024        SignatureType::EcdsaP256,
1025        Some(Box::new(crate::Error::Key(Error::InvalidKeyTypeForSignatureType {
1026            key_type: KeyType::Curve25519,
1027            signature_type: SignatureType::EcdsaP256
1028        }))
1029    ))]
1030    #[case(
1031        KeyType::Curve25519,
1032        &[KeyMechanism::EcdsaSignature],
1033        SignatureType::EdDsa,
1034        Some(Box::new(crate::Error::Key(Error::InvalidKeyMechanismsForSignatureType {
1035            signature_type: SignatureType::EdDsa,
1036            required_key_mechanism: KeyMechanism::EdDsaSignature,
1037        }))
1038    ))]
1039    #[case(
1040        KeyType::EcP256,
1041        &[KeyMechanism::EcdsaSignature],
1042        SignatureType::EdDsa,
1043        Some(Box::new(crate::Error::Key(Error::InvalidKeyTypeForSignatureType {
1044            key_type: KeyType::EcP256,
1045            signature_type: SignatureType::EdDsa,
1046        }))
1047    ))]
1048    #[case(
1049        KeyType::EcP256,
1050        &[KeyMechanism::EdDsaSignature],
1051        SignatureType::EcdsaP256,
1052        Some(Box::new(crate::Error::Key(Error::InvalidKeyMechanismsForSignatureType {
1053            signature_type: SignatureType::EcdsaP256,
1054            required_key_mechanism: KeyMechanism::EcdsaSignature,
1055        }))
1056    ))]
1057    #[case(
1058        KeyType::EcP384,
1059        &[KeyMechanism::EcdsaSignature],
1060        SignatureType::EdDsa,
1061        Some(Box::new(crate::Error::Key(Error::InvalidKeyTypeForSignatureType {
1062            key_type: KeyType::EcP384,
1063            signature_type: SignatureType::EdDsa,
1064        }))
1065    ))]
1066    #[case(
1067        KeyType::EcP384,
1068        &[KeyMechanism::EdDsaSignature],
1069        SignatureType::EcdsaP384,
1070        Some(Box::new(crate::Error::Key(Error::InvalidKeyMechanismsForSignatureType {
1071            signature_type: SignatureType::EcdsaP384,
1072            required_key_mechanism: KeyMechanism::EcdsaSignature,
1073        }))
1074    ))]
1075    #[case(
1076        KeyType::EcP521,
1077        &[KeyMechanism::EcdsaSignature],
1078        SignatureType::EdDsa,
1079        Some(Box::new(crate::Error::Key(Error::InvalidKeyTypeForSignatureType {
1080            key_type: KeyType::EcP521,
1081            signature_type: SignatureType::EdDsa,
1082        }))
1083    ))]
1084    #[case(
1085        KeyType::EcP521,
1086        &[KeyMechanism::EdDsaSignature],
1087        SignatureType::EcdsaP521,
1088        Some(Box::new(crate::Error::Key(Error::InvalidKeyMechanismsForSignatureType {
1089            signature_type: SignatureType::EcdsaP521,
1090            required_key_mechanism: KeyMechanism::EcdsaSignature,
1091        }))
1092    ))]
1093    #[case(
1094        KeyType::Rsa,
1095        &[KeyMechanism::RsaSignaturePkcs1],
1096        SignatureType::EdDsa,
1097        Some(Box::new(crate::Error::Key(Error::InvalidKeyTypeForSignatureType {
1098            key_type: KeyType::Rsa,
1099            signature_type: SignatureType::EdDsa,
1100        }))
1101    ))]
1102    fn test_key_type_and_mechanisms_match_signature_type(
1103        #[case] key_type: KeyType,
1104        #[case] key_mechanisms: &[KeyMechanism],
1105        #[case] signature_type: SignatureType,
1106        #[case] result: Option<Box<crate::Error>>,
1107    ) -> TestResult {
1108        if let Some(error) = result {
1109            if let Err(fn_error) = key_type_and_mechanisms_match_signature_type(
1110                key_type,
1111                key_mechanisms,
1112                signature_type,
1113            ) {
1114                assert_eq!(fn_error.to_string(), error.to_string());
1115            } else {
1116                panic!("Did not return an Error!");
1117            }
1118        } else {
1119            key_type_and_mechanisms_match_signature_type(key_type, key_mechanisms, signature_type)?;
1120        }
1121
1122        Ok(())
1123    }
1124
1125    #[rstest]
1126    #[case("raw", Some(DecryptMode::Raw))]
1127    #[case("pkcs1", Some(DecryptMode::Pkcs1))]
1128    #[case("oaepmd5", Some(DecryptMode::OaepMd5))]
1129    #[case("oaepsha1", Some(DecryptMode::OaepSha1))]
1130    #[case("oaepsha224", Some(DecryptMode::OaepSha224))]
1131    #[case("oaepsha256", Some(DecryptMode::OaepSha256))]
1132    #[case("oaepsha384", Some(DecryptMode::OaepSha384))]
1133    #[case("oaepsha512", Some(DecryptMode::OaepSha512))]
1134    #[case("aescbc", Some(DecryptMode::AesCbc))]
1135    #[case("foo", None)]
1136    fn decryptmode_fromstr(
1137        #[case] input: &str,
1138        #[case] expected: Option<DecryptMode>,
1139    ) -> TestResult {
1140        if let Some(expected) = expected {
1141            assert_eq!(DecryptMode::from_str(input)?, expected);
1142        } else {
1143            assert!(DecryptMode::from_str(input).is_err());
1144        }
1145        Ok(())
1146    }
1147
1148    #[rstest]
1149    #[case("aescbc", Some(EncryptMode::AesCbc))]
1150    #[case("foo", None)]
1151    fn encryptmode_fromstr(
1152        #[case] input: &str,
1153        #[case] expected: Option<EncryptMode>,
1154    ) -> TestResult {
1155        if let Some(expected) = expected {
1156            assert_eq!(EncryptMode::from_str(input)?, expected);
1157        } else {
1158            assert!(EncryptMode::from_str(input).is_err());
1159        }
1160        Ok(())
1161    }
1162
1163    #[rstest]
1164    #[case("rsadecryptionraw", Some(KeyMechanism::RsaDecryptionRaw))]
1165    #[case("rsadecryptionpkcs1", Some(KeyMechanism::RsaDecryptionPkcs1))]
1166    #[case("rsadecryptionoaepmd5", Some(KeyMechanism::RsaDecryptionOaepMd5))]
1167    #[case("rsadecryptionoaepsha1", Some(KeyMechanism::RsaDecryptionOaepSha1))]
1168    #[case("rsadecryptionoaepsha224", Some(KeyMechanism::RsaDecryptionOaepSha224))]
1169    #[case("rsadecryptionoaepsha256", Some(KeyMechanism::RsaDecryptionOaepSha256))]
1170    #[case("rsadecryptionoaepsha384", Some(KeyMechanism::RsaDecryptionOaepSha384))]
1171    #[case("rsadecryptionoaepsha512", Some(KeyMechanism::RsaDecryptionOaepSha512))]
1172    #[case("rsadecryptionoaepsha512", Some(KeyMechanism::RsaDecryptionOaepSha512))]
1173    #[case("rsasignaturepkcs1", Some(KeyMechanism::RsaSignaturePkcs1))]
1174    #[case("rsasignaturepsssha1", Some(KeyMechanism::RsaSignaturePssSha1))]
1175    #[case("rsasignaturepsssha224", Some(KeyMechanism::RsaSignaturePssSha224))]
1176    #[case("rsasignaturepsssha256", Some(KeyMechanism::RsaSignaturePssSha256))]
1177    #[case("rsasignaturepsssha384", Some(KeyMechanism::RsaSignaturePssSha384))]
1178    #[case("rsasignaturepsssha512", Some(KeyMechanism::RsaSignaturePssSha512))]
1179    #[case("eddsasignature", Some(KeyMechanism::EdDsaSignature))]
1180    #[case("ecdsasignature", Some(KeyMechanism::EcdsaSignature))]
1181    #[case("aesencryptioncbc", Some(KeyMechanism::AesEncryptionCbc))]
1182    #[case("aesdecryptioncbc", Some(KeyMechanism::AesDecryptionCbc))]
1183    #[case("foo", None)]
1184    fn keymechanism_fromstr(
1185        #[case] input: &str,
1186        #[case] expected: Option<KeyMechanism>,
1187    ) -> TestResult {
1188        if let Some(expected) = expected {
1189            assert_eq!(KeyMechanism::from_str(input)?, expected);
1190        } else {
1191            assert!(KeyMechanism::from_str(input).is_err());
1192        }
1193        Ok(())
1194    }
1195
1196    #[rstest]
1197    #[case("rsa", Some(KeyType::Rsa))]
1198    #[case("curve25519", Some(KeyType::Curve25519))]
1199    #[case("ecp256", Some(KeyType::EcP256))]
1200    #[case("ecp384", Some(KeyType::EcP384))]
1201    #[case("ecp521", Some(KeyType::EcP521))]
1202    #[case("generic", Some(KeyType::Generic))]
1203    #[case("foo", None)]
1204    fn keytype_fromstr(#[case] input: &str, #[case] expected: Option<KeyType>) -> TestResult {
1205        if let Some(expected) = expected {
1206            assert_eq!(KeyType::from_str(input)?, expected);
1207        } else {
1208            assert!(KeyType::from_str(input).is_err());
1209        }
1210        Ok(())
1211    }
1212
1213    #[rstest]
1214    #[case("ecdsap256", Some(SignatureType::EcdsaP256))]
1215    #[case("ecdsap384", Some(SignatureType::EcdsaP384))]
1216    #[case("ecdsap521", Some(SignatureType::EcdsaP521))]
1217    #[case("eddsa", Some(SignatureType::EdDsa))]
1218    #[case("pkcs1", Some(SignatureType::Pkcs1))]
1219    #[case("psssha1", Some(SignatureType::PssSha1))]
1220    #[case("psssha224", Some(SignatureType::PssSha224))]
1221    #[case("psssha256", Some(SignatureType::PssSha256))]
1222    #[case("psssha384", Some(SignatureType::PssSha384))]
1223    #[case("psssha512", Some(SignatureType::PssSha512))]
1224    #[case("foo", None)]
1225    fn signaturetype_fromstr(
1226        #[case] input: &str,
1227        #[case] expected: Option<SignatureType>,
1228    ) -> TestResult {
1229        if let Some(expected) = expected {
1230            assert_eq!(SignatureType::from_str(input)?, expected);
1231        } else {
1232            assert!(SignatureType::from_str(input).is_err());
1233        }
1234        Ok(())
1235    }
1236}