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