claim169_core/crypto/
traits.rs

1//! Cryptographic traits for pluggable crypto backends.
2//!
3//! These traits define the interface between the Claim 169 decoder and
4//! cryptographic operations. Implement these traits to integrate with
5//! external crypto providers such as:
6//!
7//! - Hardware Security Modules (HSMs)
8//! - Cloud KMS (AWS KMS, Google Cloud KMS, Azure Key Vault)
9//! - Remote signing services
10//! - Smart cards and TPMs
11//! - Custom software keystores
12//!
13//! # Verification vs Signing
14//!
15//! - [`SignatureVerifier`] and [`Decryptor`]: Used during credential verification
16//! - [`Signer`] and [`Encryptor`]: Used for credential issuance
17//!
18//! # Thread Safety
19//!
20//! All traits require `Send + Sync` for use in multi-threaded contexts.
21
22use coset::iana;
23
24use crate::error::CryptoResult;
25
26/// Trait for signature verification.
27///
28/// Implement this trait to provide custom signature verification
29/// using external crypto providers (HSM, cloud KMS, etc.).
30pub trait SignatureVerifier: Send + Sync {
31    /// Verify a signature over the given data
32    ///
33    /// # Arguments
34    /// * `algorithm` - The COSE algorithm identifier
35    /// * `key_id` - Optional key identifier from the COSE header
36    /// * `data` - The data that was signed (Sig_structure)
37    /// * `signature` - The signature bytes to verify
38    ///
39    /// # Returns
40    /// * `Ok(())` if the signature is valid
41    /// * `Err(CryptoError::VerificationFailed)` if the signature is invalid
42    /// * `Err(...)` for other errors (key not found, unsupported algorithm, etc.)
43    fn verify(
44        &self,
45        algorithm: iana::Algorithm,
46        key_id: Option<&[u8]>,
47        data: &[u8],
48        signature: &[u8],
49    ) -> CryptoResult<()>;
50}
51
52/// Trait for decryption.
53///
54/// Implement this trait to provide custom decryption
55/// using external crypto providers (HSM, cloud KMS, etc.).
56pub trait Decryptor: Send + Sync {
57    /// Decrypt ciphertext using AEAD
58    ///
59    /// # Arguments
60    /// * `algorithm` - The COSE algorithm identifier
61    /// * `key_id` - Optional key identifier from the COSE header
62    /// * `nonce` - The IV/nonce for decryption
63    /// * `aad` - Additional authenticated data (Enc_structure)
64    /// * `ciphertext` - The ciphertext to decrypt (includes auth tag for AEAD)
65    ///
66    /// # Returns
67    /// * `Ok(plaintext)` if decryption succeeds
68    /// * `Err(CryptoError::DecryptionFailed)` if decryption fails
69    fn decrypt(
70        &self,
71        algorithm: iana::Algorithm,
72        key_id: Option<&[u8]>,
73        nonce: &[u8],
74        aad: &[u8],
75        ciphertext: &[u8],
76    ) -> CryptoResult<Vec<u8>>;
77}
78
79/// Trait for signing.
80///
81/// Implement this trait to provide custom signing
82/// using external crypto providers (HSM, cloud KMS, etc.).
83pub trait Signer: Send + Sync {
84    /// Sign data
85    ///
86    /// # Arguments
87    /// * `algorithm` - The COSE algorithm identifier
88    /// * `key_id` - Optional key identifier
89    /// * `data` - The data to sign (Sig_structure)
90    ///
91    /// # Returns
92    /// * `Ok(signature)` containing the signature bytes
93    fn sign(
94        &self,
95        algorithm: iana::Algorithm,
96        key_id: Option<&[u8]>,
97        data: &[u8],
98    ) -> CryptoResult<Vec<u8>>;
99
100    /// Get the key ID for this signer
101    fn key_id(&self) -> Option<&[u8]> {
102        None
103    }
104}
105
106/// Trait for encryption.
107///
108/// Implement this trait to provide custom encryption
109/// using external crypto providers (HSM, cloud KMS, etc.).
110pub trait Encryptor: Send + Sync {
111    /// Encrypt plaintext using AEAD
112    ///
113    /// # Arguments
114    /// * `algorithm` - The COSE algorithm identifier
115    /// * `key_id` - Optional key identifier
116    /// * `nonce` - The IV/nonce for encryption
117    /// * `aad` - Additional authenticated data
118    /// * `plaintext` - The plaintext to encrypt
119    ///
120    /// # Returns
121    /// * `Ok(ciphertext)` containing ciphertext with auth tag
122    fn encrypt(
123        &self,
124        algorithm: iana::Algorithm,
125        key_id: Option<&[u8]>,
126        nonce: &[u8],
127        aad: &[u8],
128        plaintext: &[u8],
129    ) -> CryptoResult<Vec<u8>>;
130}
131
132/// Key resolver trait for looking up keys by key ID.
133///
134/// Implement this trait to provide key lookup functionality
135/// using external key management systems (HSM, cloud KMS, etc.).
136pub trait KeyResolver: Send + Sync {
137    /// Resolve a verifier for the given key ID and algorithm
138    fn resolve_verifier(
139        &self,
140        key_id: Option<&[u8]>,
141        algorithm: iana::Algorithm,
142    ) -> CryptoResult<Box<dyn SignatureVerifier>>;
143
144    /// Resolve a decryptor for the given key ID and algorithm
145    fn resolve_decryptor(
146        &self,
147        key_id: Option<&[u8]>,
148        algorithm: iana::Algorithm,
149    ) -> CryptoResult<Box<dyn Decryptor>>;
150}
151
152/// Blanket implementation allowing `&T` where `T: SignatureVerifier`.
153impl<T: SignatureVerifier + ?Sized> SignatureVerifier for &T {
154    fn verify(
155        &self,
156        algorithm: iana::Algorithm,
157        key_id: Option<&[u8]>,
158        data: &[u8],
159        signature: &[u8],
160    ) -> CryptoResult<()> {
161        (*self).verify(algorithm, key_id, data, signature)
162    }
163}
164
165/// Blanket implementation allowing `Box<T>` where `T: SignatureVerifier`.
166impl<T: SignatureVerifier + ?Sized> SignatureVerifier for Box<T> {
167    fn verify(
168        &self,
169        algorithm: iana::Algorithm,
170        key_id: Option<&[u8]>,
171        data: &[u8],
172        signature: &[u8],
173    ) -> CryptoResult<()> {
174        self.as_ref().verify(algorithm, key_id, data, signature)
175    }
176}
177
178/// Blanket implementation allowing `&T` where `T: Decryptor`.
179impl<T: Decryptor + ?Sized> Decryptor for &T {
180    fn decrypt(
181        &self,
182        algorithm: iana::Algorithm,
183        key_id: Option<&[u8]>,
184        nonce: &[u8],
185        aad: &[u8],
186        ciphertext: &[u8],
187    ) -> CryptoResult<Vec<u8>> {
188        (*self).decrypt(algorithm, key_id, nonce, aad, ciphertext)
189    }
190}
191
192/// Blanket implementation allowing `Box<T>` where `T: Decryptor`.
193impl<T: Decryptor + ?Sized> Decryptor for Box<T> {
194    fn decrypt(
195        &self,
196        algorithm: iana::Algorithm,
197        key_id: Option<&[u8]>,
198        nonce: &[u8],
199        aad: &[u8],
200        ciphertext: &[u8],
201    ) -> CryptoResult<Vec<u8>> {
202        self.as_ref()
203            .decrypt(algorithm, key_id, nonce, aad, ciphertext)
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210    use crate::error::CryptoError;
211
212    /// Mock verifier for testing blanket implementations
213    struct MockVerifier {
214        expected_result: bool,
215    }
216
217    impl SignatureVerifier for MockVerifier {
218        fn verify(
219            &self,
220            _algorithm: iana::Algorithm,
221            _key_id: Option<&[u8]>,
222            _data: &[u8],
223            _signature: &[u8],
224        ) -> CryptoResult<()> {
225            if self.expected_result {
226                Ok(())
227            } else {
228                Err(CryptoError::VerificationFailed)
229            }
230        }
231    }
232
233    /// Mock decryptor for testing blanket implementations
234    struct MockDecryptor {
235        plaintext: Vec<u8>,
236    }
237
238    impl Decryptor for MockDecryptor {
239        fn decrypt(
240            &self,
241            _algorithm: iana::Algorithm,
242            _key_id: Option<&[u8]>,
243            _nonce: &[u8],
244            _aad: &[u8],
245            _ciphertext: &[u8],
246        ) -> CryptoResult<Vec<u8>> {
247            Ok(self.plaintext.clone())
248        }
249    }
250
251    /// Mock signer for testing default key_id implementation
252    struct MockSigner;
253
254    impl Signer for MockSigner {
255        fn sign(
256            &self,
257            _algorithm: iana::Algorithm,
258            _key_id: Option<&[u8]>,
259            _data: &[u8],
260        ) -> CryptoResult<Vec<u8>> {
261            Ok(vec![1, 2, 3, 4])
262        }
263        // Uses default key_id() implementation
264    }
265
266    #[test]
267    fn test_signature_verifier_ref_blanket_impl() {
268        let verifier = MockVerifier {
269            expected_result: true,
270        };
271        let verifier_ref: &dyn SignatureVerifier = &verifier;
272
273        let result = verifier_ref.verify(iana::Algorithm::EdDSA, None, b"data", b"sig");
274        assert!(result.is_ok());
275    }
276
277    #[test]
278    fn test_signature_verifier_box_blanket_impl() {
279        let verifier = MockVerifier {
280            expected_result: true,
281        };
282        let boxed: Box<dyn SignatureVerifier> = Box::new(verifier);
283
284        let result = boxed.verify(iana::Algorithm::EdDSA, None, b"data", b"sig");
285        assert!(result.is_ok());
286    }
287
288    #[test]
289    fn test_signature_verifier_box_failure() {
290        let verifier = MockVerifier {
291            expected_result: false,
292        };
293        let boxed: Box<dyn SignatureVerifier> = Box::new(verifier);
294
295        let result = boxed.verify(iana::Algorithm::EdDSA, None, b"data", b"sig");
296        assert!(result.is_err());
297        assert!(matches!(
298            result.unwrap_err(),
299            CryptoError::VerificationFailed
300        ));
301    }
302
303    #[test]
304    fn test_decryptor_ref_blanket_impl() {
305        let decryptor = MockDecryptor {
306            plaintext: vec![1, 2, 3],
307        };
308        let decryptor_ref: &dyn Decryptor = &decryptor;
309
310        let result = decryptor_ref.decrypt(iana::Algorithm::A256GCM, None, b"nonce", b"aad", b"ct");
311        assert!(result.is_ok());
312        assert_eq!(result.unwrap(), vec![1, 2, 3]);
313    }
314
315    #[test]
316    fn test_decryptor_box_blanket_impl() {
317        let decryptor = MockDecryptor {
318            plaintext: vec![4, 5, 6],
319        };
320        let boxed: Box<dyn Decryptor> = Box::new(decryptor);
321
322        let result = boxed.decrypt(iana::Algorithm::A256GCM, None, b"nonce", b"aad", b"ct");
323        assert!(result.is_ok());
324        assert_eq!(result.unwrap(), vec![4, 5, 6]);
325    }
326
327    #[test]
328    fn test_signer_default_key_id() {
329        let signer = MockSigner;
330        assert!(signer.key_id().is_none());
331    }
332
333    #[test]
334    fn test_signature_verifier_with_key_id() {
335        let verifier = MockVerifier {
336            expected_result: true,
337        };
338        let key_id = b"test-key-id";
339
340        let result = verifier.verify(iana::Algorithm::EdDSA, Some(key_id), b"data", b"sig");
341        assert!(result.is_ok());
342    }
343
344    #[test]
345    fn test_decryptor_with_key_id() {
346        let decryptor = MockDecryptor {
347            plaintext: vec![7, 8, 9],
348        };
349        let key_id = b"decrypt-key-id";
350
351        let result = decryptor.decrypt(
352            iana::Algorithm::A256GCM,
353            Some(key_id),
354            b"nonce",
355            b"aad",
356            b"ciphertext",
357        );
358        assert!(result.is_ok());
359        assert_eq!(result.unwrap(), vec![7, 8, 9]);
360    }
361}