signstar_crypto/key/
setup.rs

1//! Setup for signing keys.
2
3use serde::{Deserialize, Serialize};
4
5use crate::key::Error;
6use crate::key::{
7    CryptographicKeyContext,
8    KeyMechanism,
9    KeyType,
10    SignatureType,
11    key_type_and_mechanisms_match_signature_type,
12    key_type_matches_length,
13    key_type_matches_mechanisms,
14};
15
16/// The setup of a cryptographic signing key.
17///
18/// This covers the type of key, its supported mechanisms, its optional length, its signature type
19/// and the context in which it is used.
20#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
21pub struct SigningKeySetup {
22    key_type: KeyType,
23    key_mechanisms: Vec<KeyMechanism>,
24    #[serde(skip_serializing_if = "Option::is_none")]
25    key_length: Option<u32>,
26    signature_type: SignatureType,
27    key_context: CryptographicKeyContext,
28}
29
30impl SigningKeySetup {
31    /// Creates a new [`SigningKeySetup`].
32    ///
33    /// # Examples
34    ///
35    /// ```
36    /// use signstar_crypto::{
37    ///     key::{CryptographicKeyContext, KeyMechanism, KeyType, SignatureType, SigningKeySetup},
38    ///     openpgp::OpenPgpUserIdList,
39    /// };
40    ///
41    /// # fn main() -> testresult::TestResult {
42    /// SigningKeySetup::new(
43    ///     KeyType::Curve25519,
44    ///     vec![KeyMechanism::EdDsaSignature],
45    ///     None,
46    ///     SignatureType::EdDsa,
47    ///     CryptographicKeyContext::Raw,
48    /// )?;
49    ///
50    /// SigningKeySetup::new(
51    ///     KeyType::Curve25519,
52    ///     vec![KeyMechanism::EdDsaSignature],
53    ///     None,
54    ///     SignatureType::EdDsa,
55    ///     CryptographicKeyContext::OpenPgp {
56    ///         user_ids: OpenPgpUserIdList::new(vec![
57    ///             "Foobar McFooface <foobar@mcfooface.org>".parse()?,
58    ///         ])?,
59    ///         version: "v4".parse()?,
60    ///     },
61    /// )?;
62    ///
63    /// // this fails because Curve25519 does not support the ECDSA key mechanism
64    /// assert!(
65    ///     SigningKeySetup::new(
66    ///         KeyType::Curve25519,
67    ///         vec![KeyMechanism::EcdsaSignature],
68    ///         None,
69    ///         SignatureType::EdDsa,
70    ///         CryptographicKeyContext::OpenPgp {
71    ///             user_ids: OpenPgpUserIdList::new(vec![
72    ///                 "Foobar McFooface <foobar@mcfooface.org>".parse()?
73    ///             ])?,
74    ///             version: "v4".parse()?,
75    ///         },
76    ///     )
77    ///     .is_err()
78    /// );
79    /// # Ok(())
80    /// # }
81    /// ```
82    ///
83    /// # Errors
84    ///
85    /// Returns an error if
86    ///
87    /// - the `key_type` and `key_mechanisms` are incompatible,
88    /// - the `key_type` and `key_length` are incompatible,
89    /// - the `key_type`, `key_mechanisms` and `signature_type` are incompatible,
90    /// - or the `cryptographic_key_context` is not valid.
91    pub fn new(
92        key_type: KeyType,
93        key_mechanisms: Vec<KeyMechanism>,
94        key_length: Option<u32>,
95        signature_type: SignatureType,
96        cryptographic_key_context: CryptographicKeyContext,
97    ) -> Result<Self, Error> {
98        key_type_matches_mechanisms(key_type, &key_mechanisms)?;
99        key_type_matches_length(key_type, key_length)?;
100        key_type_and_mechanisms_match_signature_type(key_type, &key_mechanisms, signature_type)?;
101        cryptographic_key_context.validate_signing_key_setup(
102            key_type,
103            &key_mechanisms,
104            signature_type,
105        )?;
106
107        Ok(Self {
108            key_type,
109            key_mechanisms,
110            key_length,
111            signature_type,
112            key_context: cryptographic_key_context,
113        })
114    }
115
116    /// Returns the [`KeyType`].
117    pub fn key_type(&self) -> KeyType {
118        self.key_type
119    }
120
121    /// Returns a reference to the list of [`KeyMechanism`]s.
122    pub fn key_mechanisms(&self) -> &[KeyMechanism] {
123        &self.key_mechanisms
124    }
125
126    /// Returns the optional key length.
127    pub fn key_length(&self) -> Option<u32> {
128        self.key_length
129    }
130
131    /// Returns the [`SignatureType`].
132    pub fn signature_type(&self) -> SignatureType {
133        self.signature_type
134    }
135
136    /// Returns a reference to the [`CryptographicKeyContext`].
137    pub fn key_context(&self) -> &CryptographicKeyContext {
138        &self.key_context
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use rstest::rstest;
145    use testresult::TestResult;
146
147    use super::*;
148
149    #[test]
150    fn signing_key_setup_new_succeeds() -> TestResult {
151        let setup = SigningKeySetup::new(
152            KeyType::Curve25519,
153            vec![KeyMechanism::EdDsaSignature],
154            None,
155            SignatureType::EdDsa,
156            CryptographicKeyContext::Raw,
157        )?;
158
159        assert_eq!(setup.key_type(), KeyType::Curve25519);
160        assert_eq!(setup.key_mechanisms(), [KeyMechanism::EdDsaSignature]);
161        assert_eq!(setup.key_length(), None);
162        assert_eq!(setup.signature_type(), SignatureType::EdDsa);
163        assert_eq!(setup.key_context(), &CryptographicKeyContext::Raw);
164
165        Ok(())
166    }
167
168    #[rstest]
169    #[case::curve25519_ecdsa(KeyType::Curve25519, vec![KeyMechanism::EcdsaSignature])]
170    #[case::rsa_ecdsa(KeyType::Rsa, vec![KeyMechanism::EcdsaSignature])]
171    fn signing_key_setup_new_fails_on_key_type_mechanism_mismatch(
172        #[case] key_type: KeyType,
173        #[case] key_mechanisms: Vec<KeyMechanism>,
174    ) -> TestResult {
175        let result = SigningKeySetup::new(
176            key_type,
177            key_mechanisms,
178            None,
179            SignatureType::EdDsa,
180            CryptographicKeyContext::Raw,
181        );
182
183        match result {
184            Err(Error::InvalidKeyMechanism { .. }) => {}
185            Err(error) => {
186                panic!("Expected an Error::InvalidKeyMechanism, but got {error}");
187            }
188            Ok(setup) => {
189                panic!(
190                    "Should have failed, but succeeded in creating a SigningKeySetup: {setup:?}"
191                );
192            }
193        }
194
195        Ok(())
196    }
197
198    #[rstest]
199    #[case::curve25519_with_length(KeyType::Curve25519, vec![KeyMechanism::EdDsaSignature], Some(1024))]
200    #[case::ecp521_with_length(KeyType::EcP521, vec![KeyMechanism::EcdsaSignature], Some(1024))]
201    fn signing_key_setup_new_fails_on_key_length_unsupported(
202        #[case] key_type: KeyType,
203        #[case] key_mechanisms: Vec<KeyMechanism>,
204        #[case] key_length: Option<u32>,
205    ) -> TestResult {
206        let result = SigningKeySetup::new(
207            key_type,
208            key_mechanisms,
209            key_length,
210            SignatureType::EdDsa,
211            CryptographicKeyContext::Raw,
212        );
213
214        match result {
215            Err(Error::KeyLengthUnsupported { .. }) => {}
216            Err(error) => {
217                panic!("Expected an Error::KeyLengthUnsupported, but got {error}");
218            }
219            Ok(setup) => {
220                panic!(
221                    "Should have failed, but succeeded in creating a SigningKeySetup: {setup:?}"
222                );
223            }
224        }
225
226        Ok(())
227    }
228
229    #[rstest]
230    #[case::rsa_too_short(KeyType::Rsa, vec![KeyMechanism::RsaSignaturePkcs1], Some(1024))]
231    #[case::rsa_no_length(KeyType::Rsa, vec![KeyMechanism::RsaSignaturePkcs1], None)]
232    fn signing_key_setup_new_fails_on_key_length_required_or_too_short(
233        #[case] key_type: KeyType,
234        #[case] key_mechanisms: Vec<KeyMechanism>,
235        #[case] key_length: Option<u32>,
236    ) -> TestResult {
237        let result = SigningKeySetup::new(
238            key_type,
239            key_mechanisms,
240            key_length,
241            SignatureType::EdDsa,
242            CryptographicKeyContext::Raw,
243        );
244
245        match result {
246            Err(Error::KeyLengthRequired { .. }) | Err(Error::InvalidKeyLengthRsa { .. }) => {}
247            Err(error) => {
248                panic!(
249                    "Expected an Error::KeyLengthRequired or Error::InvalidKeyLengthRsa, but got {error}"
250                );
251            }
252            Ok(setup) => {
253                panic!(
254                    "Should have failed, but succeeded in creating a SigningKeySetup: {setup:?}"
255                );
256            }
257        }
258
259        Ok(())
260    }
261
262    #[rstest]
263    #[case::curve25519_ecdsap521(KeyType::Curve25519, vec![KeyMechanism::EdDsaSignature], SignatureType::EcdsaP521)]
264    #[case::ecdsap521_eddsa(KeyType::EcP521, vec![KeyMechanism::EcdsaSignature], SignatureType::EdDsa)]
265    fn signing_key_setup_new_fails_signature_type_mismatch(
266        #[case] key_type: KeyType,
267        #[case] key_mechanisms: Vec<KeyMechanism>,
268        #[case] signature_type: SignatureType,
269    ) -> TestResult {
270        let result = SigningKeySetup::new(
271            key_type,
272            key_mechanisms,
273            None,
274            signature_type,
275            CryptographicKeyContext::Raw,
276        );
277
278        match result {
279            Err(Error::InvalidKeyTypeForSignatureType { .. })
280            | Err(Error::InvalidKeyMechanismsForSignatureType { .. }) => {}
281            Err(error) => {
282                panic!(
283                    "Expected an Error::InvalidKeyTypeForSignatureType or Error::InvalidKeyMechanismsForSignatureType, but got {error}"
284                )
285            }
286            Ok(setup) => {
287                panic!("Should have failed, but succeeded in creating a SigningKeySetup: {setup:?}")
288            }
289        }
290
291        Ok(())
292    }
293}