Skip to main content

signstar_crypto/key/
setup.rs

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