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    ///     },
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, crate::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    use crate::key::Error;
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(crate::Error::Key(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(crate::Error::Key(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(crate::Error::Key(Error::KeyLengthRequired { .. }))
247            | Err(crate::Error::Key(Error::InvalidKeyLengthRsa { .. })) => {}
248            Err(error) => {
249                panic!(
250                    "Expected an Error::KeyLengthRequired or Error::InvalidKeyLengthRsa, but got {error}"
251                );
252            }
253            Ok(setup) => {
254                panic!(
255                    "Should have failed, but succeeded in creating a SigningKeySetup: {setup:?}"
256                );
257            }
258        }
259
260        Ok(())
261    }
262
263    #[rstest]
264    #[case::curve25519_ecdsap521(KeyType::Curve25519, vec![KeyMechanism::EdDsaSignature], SignatureType::EcdsaP521)]
265    #[case::ecdsap521_eddsa(KeyType::EcP521, vec![KeyMechanism::EcdsaSignature], SignatureType::EdDsa)]
266    fn signing_key_setup_new_fails_signature_type_mismatch(
267        #[case] key_type: KeyType,
268        #[case] key_mechanisms: Vec<KeyMechanism>,
269        #[case] signature_type: SignatureType,
270    ) -> TestResult {
271        let result = SigningKeySetup::new(
272            key_type,
273            key_mechanisms,
274            None,
275            signature_type,
276            CryptographicKeyContext::Raw,
277        );
278
279        match result {
280            Err(crate::Error::Key(Error::InvalidKeyTypeForSignatureType { .. }))
281            | Err(crate::Error::Key(Error::InvalidKeyMechanismsForSignatureType { .. })) => {}
282            Err(error) => {
283                panic!(
284                    "Expected an Error::InvalidKeyTypeForSignatureType or Error::InvalidKeyMechanismsForSignatureType, but got {error}"
285                )
286            }
287            Ok(setup) => {
288                panic!("Should have failed, but succeeded in creating a SigningKeySetup: {setup:?}")
289            }
290        }
291
292        Ok(())
293    }
294}