Skip to main content

tightbeam/crypto/
key.rs

1//! Pluggable key backend abstraction for tightbeam transport encryption.
2//!
3//! This module provides the [`KeyProvider`] trait, which abstracts cryptographic
4//! key operations to enable flexible backend integration (in-memory, HSM, KMS, enclave).
5//!
6//! The trait is algorithm-agnostic, using byte representations for all values.
7//! Concrete implementations (e.g., [`InMemoryKeyProvider`]) handle algorithm-specific
8//! encoding/decoding.
9
10use core::fmt::Debug;
11
12#[cfg(not(feature = "std"))]
13extern crate alloc;
14
15#[cfg(all(not(feature = "std"), any(feature = "signature", feature = "aead")))]
16use alloc::{boxed::Box, sync::Arc, vec::Vec};
17
18#[cfg(any(feature = "signature", feature = "aead"))]
19use core::marker::PhantomData;
20
21#[cfg(any(feature = "signature", feature = "aead"))]
22use core::future::Future;
23#[cfg(any(feature = "signature", feature = "aead"))]
24use core::pin::Pin;
25#[cfg(all(feature = "std", any(feature = "signature", feature = "aead")))]
26use std::sync::Arc;
27
28#[cfg(feature = "signature")]
29mod signing {
30	pub use crate::crypto::sign::ecdsa::{
31		DigestPrimitive, Secp256k1, Secp256k1Signature, Secp256k1SigningKey, SignPrimitive, Signature, SignatureSize,
32		SigningKey, VerifyPrimitive,
33	};
34	pub use crate::crypto::sign::elliptic_curve::generic_array::{ArrayLength, GenericArray};
35	pub use crate::crypto::sign::elliptic_curve::ops::{Invert, Reduce};
36	pub use crate::crypto::sign::elliptic_curve::point::PointCompression;
37	pub use crate::crypto::sign::elliptic_curve::scalar::Scalar;
38	pub use crate::crypto::sign::elliptic_curve::sec1::ModulusSize;
39	pub use crate::crypto::sign::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
40	pub use crate::crypto::sign::elliptic_curve::subtle::CtOption;
41	pub use crate::crypto::sign::elliptic_curve::{
42		AffinePoint, CurveArithmetic, Error as EllipticCurveError, FieldBytesSize, PrimeCurve,
43	};
44	pub use crate::crypto::sign::{
45		Error as SignatureError, Keypair, PrehashSigner, SignatureAlgorithmIdentifier, SignatureEncoding,
46	};
47
48	#[cfg(feature = "ecdh")]
49	pub use crate::crypto::sign::elliptic_curve::ecdh::diffie_hellman;
50	#[cfg(feature = "ecdh")]
51	pub use crate::crypto::sign::elliptic_curve::PublicKey;
52}
53
54#[cfg(feature = "signature")]
55use signing::*;
56
57#[cfg(feature = "aead")]
58mod encryption {
59	pub use crate::crypto::aead::{
60		Aead, AeadCore, Aes128Gcm, Aes128GcmOid, Aes256Gcm, Aes256GcmOid, Error as AeadError, Nonce,
61	};
62	pub use crate::crypto::common::typenum::Unsigned;
63}
64
65#[cfg(feature = "aead")]
66use encryption::*;
67
68#[cfg(any(feature = "signature", feature = "aead"))]
69mod common {
70	pub use crate::der::oid::AssociatedOid;
71	pub use crate::spki::AlgorithmIdentifierOwned;
72
73	#[cfg(feature = "signature")]
74	pub use crate::spki::EncodePublicKey;
75}
76
77#[cfg(any(feature = "signature", feature = "aead"))]
78use common::*;
79
80#[cfg(feature = "signature")]
81use crate::crypto::secret::SecretSlice;
82
83// =============================================================================
84// KeyError
85// =============================================================================
86
87/// Errors from key provider operations.
88///
89/// Deliberately does not derive `Errorizable`: this module builds without
90/// the `derive` feature, so the message strings live in exactly one place --
91/// the `impl_error_display!` block below.
92#[derive(Debug)]
93pub enum KeyError {
94	/// SPKI encoding/decoding error
95	SpkiError(crate::spki::Error),
96
97	/// Elliptic curve operation error
98	#[cfg(feature = "signature")]
99	EllipticCurveError(EllipticCurveError),
100
101	/// Signature/ECDSA error (e.g., invalid key bytes)
102	#[cfg(feature = "signature")]
103	SignatureError(SignatureError),
104
105	/// AEAD encryption/decryption error
106	#[cfg(feature = "aead")]
107	AeadError(AeadError),
108
109	/// Nonce length mismatch
110	#[cfg(feature = "aead")]
111	NonceLengthError(crate::error::ReceivedExpectedError<usize, usize>),
112
113	/// Operation not supported by this key provider
114	UnsupportedOperation,
115}
116
117crate::impl_error_display!(unconditional KeyError {
118	SpkiError(e) => "SPKI error: {e}",
119	#[cfg(feature = "signature")]
120	EllipticCurveError(e) => "Elliptic curve error: {e}",
121	#[cfg(feature = "signature")]
122	SignatureError(e) => "Signature error: {e}",
123	#[cfg(feature = "aead")]
124	AeadError(e) => "AEAD error: {e}",
125	#[cfg(feature = "aead")]
126	NonceLengthError(e) => "Nonce length mismatch: {e}",
127	UnsupportedOperation => "Operation not supported by this key provider",
128});
129
130crate::impl_from!(crate::spki::Error => KeyError::SpkiError);
131crate::impl_from!(#[cfg(feature = "signature")] EllipticCurveError => KeyError::EllipticCurveError);
132crate::impl_from!(#[cfg(feature = "signature")] SignatureError => KeyError::SignatureError);
133crate::impl_from!(#[cfg(feature = "aead")] AeadError => KeyError::AeadError);
134
135/// Specification for providing a cryptographic signing key in various formats.
136///
137/// This enum allows keys to be specified in multiple ways for flexible
138/// configuration in const contexts (e.g., servlet! macro).
139#[cfg(feature = "signature")]
140#[derive(Debug, Clone)]
141pub enum SigningKeySpec {
142	/// Raw key bytes (e.g., secp256k1 scalar - 32 bytes)
143	Bytes(&'static [u8]),
144
145	/// Key provider instance (for HSM/KMS)
146	Provider(Arc<dyn SigningKeyProvider>),
147}
148
149#[cfg(feature = "signature")]
150impl SigningKeySpec {
151	/// Convert this key specification to a key provider for the given ECDSA curve.
152	///
153	/// For `KeySpec::Bytes`, constructs an ECDSA signing key from the raw bytes
154	/// and wraps it in an `InMemoryKeyProvider`. For `KeySpec::Provider`, returns
155	/// a clone of the existing provider Arc.
156	///
157	/// # Type Parameters
158	///
159	/// * `C` - The elliptic curve type (e.g., `k256::Secp256k1`)
160	pub fn to_provider<C>(&self) -> Result<Arc<dyn SigningKeyProvider>, KeyError>
161	where
162		C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression + AssociatedOid + Send + Sync + 'static,
163		Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C> + Reduce<C::Uint>,
164		SignatureSize<C>: ArrayLength<u8>,
165		FieldBytesSize<C>: ModulusSize,
166		AffinePoint<C>: VerifyPrimitive<C> + FromEncodedPoint<C> + ToEncodedPoint<C>,
167		SigningKey<C>: PrehashSigner<Signature<C>> + Keypair + Send + Sync + Debug + 'static,
168		<SigningKey<C> as Keypair>::VerifyingKey: EncodePublicKey,
169		Signature<C>: SignatureEncoding + SignatureAlgorithmIdentifier + Send + Sync + 'static,
170	{
171		match self {
172			SigningKeySpec::Bytes(bytes) => {
173				let field_bytes = GenericArray::from_slice(bytes);
174				let signing_key = SigningKey::<C>::from_bytes(field_bytes)?;
175				Ok(Arc::new(EcdsaKeyProvider::from(signing_key)))
176			}
177			SigningKeySpec::Provider(provider) => Ok(Arc::clone(provider)),
178		}
179	}
180}
181
182/// Trait for pluggable cryptographic key backends.
183///
184/// Implementations of this trait provide access to private key operations
185/// (key agreement, signing) without exposing the raw key material. This
186/// enables integration with Hardware Security Modules (HSMs), Key Management
187/// Services (KMS), and secure enclaves where private keys cannot leave the
188/// secure boundary.
189///
190/// # Security Properties
191///
192/// - **Key Encapsulation**: Private keys never leave the provider boundary
193/// - **Uniform Interface**: In-memory and remote backends use identical APIs
194/// - **Async by Default**: All operations async for maximum flexibility
195/// - **Algorithm Agnostic**: Byte encoding allows any signature/key algorithm
196#[cfg(feature = "signature")]
197pub trait SigningKeyProvider: Send + Sync + Debug {
198	/// Returns the algorithm identifier for this key.
199	fn algorithm(&self) -> AlgorithmIdentifierOwned;
200
201	/// Returns the public key as DER-encoded bytes.
202	///
203	/// # Errors
204	///
205	/// Returns [`KeyError`] if the backend cannot retrieve the public key.
206	fn to_public_key_bytes(&self) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>>;
207
208	/// Signs a precomputed digest (prehash) using this provider's private key.
209	///
210	/// The canonical tightbeam convention hashes content exactly once (see
211	/// `crypto::sign::sign_canonical`); providers MUST sign the given prehash
212	/// directly and MUST NOT rehash it, so the produced signature matches the
213	/// advertised signature-algorithm OID regardless of backend.
214	///
215	/// # Arguments
216	///
217	/// * `prehash` - The digest of the content to sign
218	///
219	/// # Returns
220	///
221	/// DER-encoded signature bytes.
222	fn sign_prehash(&self, prehash: &[u8]) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>>;
223
224	/// Performs key agreement (ECDH, X25519, etc).
225	///
226	/// Computes a shared secret from this provider's private key and the peer's
227	/// public key. The shared secret is used for session key derivation.
228	///
229	/// # Arguments
230	///
231	/// * `peer_public_key` - The peer's public key bytes (SEC1 or DER encoded)
232	///
233	/// # Returns
234	///
235	/// The computed shared secret, wrapped in [`SecretSlice`] so it is
236	/// zeroized on drop (CWE-212).
237	///
238	/// # Default
239	///
240	/// Returns `UnsupportedOperation` - not all key types support key agreement.
241	fn key_agreement(
242		&self,
243		_peer_public_key: &[u8],
244	) -> Pin<Box<dyn Future<Output = Result<SecretSlice<u8>, KeyError>> + Send + '_>> {
245		Box::pin(async { Err(KeyError::UnsupportedOperation) })
246	}
247}
248
249/// In-memory key provider generic over any RustCrypto signing key.
250///
251/// This is the reference implementation for [`KeyProvider`], storing the private
252/// key directly in memory. Suitable for development, testing, and applications
253/// where HSM/KMS integration is not required.
254///
255/// # Type Parameters
256///
257/// * `K` - The signing key type (e.g., `Secp256k1SigningKey`, `Ed25519SigningKey`)
258/// * `S` - The signature type produced by `K`
259///
260/// # Security
261///
262/// For zeroization on drop, use keys that implement `ZeroizeOnDrop`
263/// (e.g., k256's `SigningKey`).
264#[cfg(feature = "signature")]
265pub struct InMemorySigningKeyProvider<K, S>
266where
267	K: PrehashSigner<S> + Keypair,
268	S: SignatureEncoding,
269{
270	signing_key: K,
271	_sig: PhantomData<S>,
272}
273
274#[cfg(feature = "signature")]
275impl<K, S> From<K> for InMemorySigningKeyProvider<K, S>
276where
277	K: PrehashSigner<S> + Keypair,
278	S: SignatureEncoding,
279{
280	fn from(signing_key: K) -> Self {
281		InMemorySigningKeyProvider { signing_key, _sig: PhantomData }
282	}
283}
284
285#[cfg(feature = "signature")]
286impl<K, S> Debug for InMemorySigningKeyProvider<K, S>
287where
288	K: PrehashSigner<S> + Keypair + Debug,
289	S: SignatureEncoding,
290{
291	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
292		f.debug_struct("InMemoryKeyProvider")
293			.field("signing_key", &self.signing_key)
294			.finish()
295	}
296}
297
298#[cfg(feature = "signature")]
299impl<K, S> SigningKeyProvider for InMemorySigningKeyProvider<K, S>
300where
301	K: PrehashSigner<S> + Keypair + Send + Sync + Debug + 'static,
302	K::VerifyingKey: EncodePublicKey,
303	S: SignatureEncoding + SignatureAlgorithmIdentifier + Send + Sync + 'static,
304{
305	fn algorithm(&self) -> AlgorithmIdentifierOwned {
306		AlgorithmIdentifierOwned { oid: S::ALGORITHM_OID, parameters: None }
307	}
308
309	fn to_public_key_bytes(&self) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>> {
310		let result = self
311			.signing_key
312			.verifying_key()
313			.to_public_key_der()
314			.map(|der| der.into_vec())
315			.map_err(KeyError::from);
316
317		Box::pin(async move { result })
318	}
319
320	fn sign_prehash(&self, prehash: &[u8]) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>> {
321		let result = self
322			.signing_key
323			.sign_prehash(prehash)
324			.map(|signature: S| signature.to_bytes().as_ref().to_vec())
325			.map_err(KeyError::from);
326
327		Box::pin(async move { result })
328	}
329}
330
331// Implement KeyProvider for Arc<InMemoryKeyProvider<K, S>> for convenience
332#[cfg(feature = "signature")]
333impl<K, S> SigningKeyProvider for Arc<InMemorySigningKeyProvider<K, S>>
334where
335	K: PrehashSigner<S> + Keypair + Send + Sync + Debug + 'static,
336	K::VerifyingKey: EncodePublicKey,
337	S: SignatureEncoding + SignatureAlgorithmIdentifier + Send + Sync + 'static,
338{
339	fn algorithm(&self) -> AlgorithmIdentifierOwned {
340		self.as_ref().algorithm()
341	}
342
343	fn to_public_key_bytes(&self) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>> {
344		self.as_ref().to_public_key_bytes()
345	}
346
347	fn sign_prehash(&self, prehash: &[u8]) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>> {
348		self.as_ref().sign_prehash(prehash)
349	}
350}
351
352/// Type alias for secp256k1 key provider (signing only, no ECDH)
353#[cfg(all(feature = "signature", feature = "secp256k1"))]
354pub type Secp256k1Provider = InMemorySigningKeyProvider<Secp256k1SigningKey, Secp256k1Signature>;
355
356// ============================================================================
357// ECDSA Key Provider with ECDH Support (Generic)
358// ============================================================================
359
360/// Generic ECDSA key provider with signing and key agreement (ECDH) support.
361///
362/// This provider wraps an ECDSA signing key for any curve `C` and provides both
363/// signing and ECDH operations. This is the recommended provider for TLS handshakes.
364///
365/// # Type Parameters
366///
367/// * `C` - The elliptic curve type (e.g., `k256::Secp256k1`, `p256::NistP256`)
368#[cfg(all(feature = "signature", feature = "secp256k1"))]
369pub struct EcdsaKeyProvider<C>
370where
371	C: PrimeCurve + CurveArithmetic,
372	Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
373	SignatureSize<C>: ArrayLength<u8>,
374{
375	signing_key: SigningKey<C>,
376}
377
378#[cfg(all(feature = "signature", feature = "secp256k1"))]
379impl<C> From<SigningKey<C>> for EcdsaKeyProvider<C>
380where
381	C: PrimeCurve + CurveArithmetic,
382	Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
383	SignatureSize<C>: ArrayLength<u8>,
384{
385	fn from(signing_key: SigningKey<C>) -> Self {
386		EcdsaKeyProvider { signing_key }
387	}
388}
389
390#[cfg(all(feature = "signature", feature = "secp256k1"))]
391impl<C> Debug for EcdsaKeyProvider<C>
392where
393	C: PrimeCurve + CurveArithmetic,
394	Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
395	SignatureSize<C>: ArrayLength<u8>,
396{
397	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
398		f.debug_struct("EcdsaKeyProvider")
399			.field("curve", &core::any::type_name::<C>())
400			.finish_non_exhaustive()
401	}
402}
403
404#[cfg(all(feature = "signature", feature = "secp256k1"))]
405impl<C> SigningKeyProvider for EcdsaKeyProvider<C>
406where
407	C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression + AssociatedOid + Send + Sync + 'static,
408	Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C> + Reduce<C::Uint>,
409	SignatureSize<C>: ArrayLength<u8>,
410	FieldBytesSize<C>: ModulusSize,
411	AffinePoint<C>: VerifyPrimitive<C> + FromEncodedPoint<C> + ToEncodedPoint<C>,
412	SigningKey<C>: PrehashSigner<Signature<C>> + Keypair + Send + Sync + Debug,
413	<SigningKey<C> as Keypair>::VerifyingKey: EncodePublicKey,
414	Signature<C>: SignatureEncoding + SignatureAlgorithmIdentifier + Send + Sync,
415{
416	fn algorithm(&self) -> AlgorithmIdentifierOwned {
417		AlgorithmIdentifierOwned { oid: Signature::<C>::ALGORITHM_OID, parameters: None }
418	}
419
420	fn to_public_key_bytes(&self) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>> {
421		let result = self
422			.signing_key
423			.verifying_key()
424			.to_public_key_der()
425			.map(|der| der.into_vec())
426			.map_err(KeyError::from);
427
428		Box::pin(async move { result })
429	}
430
431	fn sign_prehash(&self, prehash: &[u8]) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>> {
432		let result = self
433			.signing_key
434			.sign_prehash(prehash)
435			.map(|signature: Signature<C>| signature.to_bytes().as_ref().to_vec())
436			.map_err(KeyError::from);
437
438		Box::pin(async move { result })
439	}
440
441	#[cfg(feature = "ecdh")]
442	fn key_agreement(
443		&self,
444		peer_public_key: &[u8],
445	) -> Pin<Box<dyn Future<Output = Result<SecretSlice<u8>, KeyError>> + Send + '_>> {
446		let pk_result = PublicKey::<C>::from_sec1_bytes(peer_public_key);
447		let secret_key = *self.signing_key.as_nonzero_scalar();
448
449		Box::pin(async move {
450			let pk = pk_result?;
451			let shared_secret = diffie_hellman(secret_key, pk.as_affine());
452
453			Ok(SecretSlice::from(shared_secret.raw_secret_bytes().to_vec()))
454		})
455	}
456}
457
458#[cfg(all(feature = "signature", feature = "secp256k1"))]
459impl<C> SigningKeyProvider for Arc<EcdsaKeyProvider<C>>
460where
461	C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression + AssociatedOid + Send + Sync + 'static,
462	Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C> + Reduce<C::Uint>,
463	SignatureSize<C>: ArrayLength<u8>,
464	FieldBytesSize<C>: ModulusSize,
465	AffinePoint<C>: VerifyPrimitive<C> + FromEncodedPoint<C> + ToEncodedPoint<C>,
466	SigningKey<C>: PrehashSigner<Signature<C>> + Keypair + Send + Sync + Debug,
467	<SigningKey<C> as Keypair>::VerifyingKey: EncodePublicKey,
468	Signature<C>: SignatureEncoding + SignatureAlgorithmIdentifier + Send + Sync,
469{
470	fn algorithm(&self) -> AlgorithmIdentifierOwned {
471		self.as_ref().algorithm()
472	}
473
474	fn to_public_key_bytes(&self) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>> {
475		self.as_ref().to_public_key_bytes()
476	}
477
478	fn sign_prehash(&self, prehash: &[u8]) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>> {
479		self.as_ref().sign_prehash(prehash)
480	}
481
482	fn key_agreement(
483		&self,
484		peer_public_key: &[u8],
485	) -> Pin<Box<dyn Future<Output = Result<SecretSlice<u8>, KeyError>> + Send + '_>> {
486		self.as_ref().key_agreement(peer_public_key)
487	}
488}
489
490/// Type alias for secp256k1-specific ECDSA key provider with ECDH
491#[cfg(feature = "signature")]
492pub type Secp256k1KeyProvider = EcdsaKeyProvider<Secp256k1>;
493
494// ============================================================================
495// EncryptingKeyProvider Trait
496// ============================================================================
497
498/// Trait for pluggable symmetric encryption key backends.
499///
500/// Implementations of this trait provide access to symmetric encryption
501/// and decryption operations without exposing the raw key material. This
502/// enables integration with Hardware Security Modules (HSMs), Key Management
503/// Services (KMS), and secure enclaves where encryption keys cannot leave the
504/// secure boundary.
505///
506/// # Security Properties
507///
508/// - **Key Encapsulation**: Encryption keys never leave the provider boundary
509/// - **Uniform Interface**: In-memory and remote backends use identical APIs
510/// - **Async by Default**: All operations async for maximum flexibility
511/// - **Algorithm Agnostic**: Byte encoding allows any AEAD cipher
512#[cfg(feature = "aead")]
513pub trait EncryptingKeyProvider: Send + Sync + Debug {
514	/// Returns the algorithm identifier for this encryption key.
515	fn algorithm(&self) -> AlgorithmIdentifierOwned;
516
517	/// Encrypts plaintext using the provided nonce.
518	///
519	/// # Arguments
520	///
521	/// * `nonce` - The nonce/IV for this encryption operation. The caller MUST
522	///   ensure the `(key, nonce)` pair is never reused for AEAD ciphers.
523	/// * `plaintext` - The data to encrypt
524	///
525	/// # Returns
526	///
527	/// Encrypted ciphertext bytes (includes authentication tag for AEAD ciphers).
528	fn encrypt(
529		&self,
530		nonce: &[u8],
531		plaintext: &[u8],
532	) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>>;
533
534	/// Decrypts ciphertext using the provided nonce.
535	///
536	/// # Arguments
537	///
538	/// * `nonce` - The nonce/IV used for encryption
539	/// * `ciphertext` - The encrypted data to decrypt
540	///
541	/// # Returns
542	///
543	/// Decrypted plaintext bytes.
544	fn decrypt(
545		&self,
546		nonce: &[u8],
547		ciphertext: &[u8],
548	) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>>;
549}
550
551// =============================================================================
552// InMemoryEncryptingKeyProvider
553// =============================================================================
554
555/// In-memory encryption key provider generic over any RustCrypto AEAD cipher.
556///
557/// This is the reference implementation for [`EncryptingKeyProvider`], storing the
558/// encryption key directly in memory. Suitable for development, testing, and applications
559/// where HSM/KMS integration is not required.
560///
561/// # Type Parameters
562///
563/// * `A` - The AEAD cipher type (e.g., `Aes256Gcm`, `Aes128Gcm`)
564/// * `O` - The OID type associated with this cipher (e.g., `Aes256GcmOid`)
565///
566/// # Security
567///
568/// For zeroization on drop, use keys that implement `ZeroizeOnDrop`.
569#[cfg(feature = "aead")]
570pub struct InMemoryEncryptingKeyProvider<A, O>
571where
572	A: Aead + Send + Sync + 'static,
573	O: AssociatedOid + Send + Sync,
574{
575	cipher: A,
576	_oid: PhantomData<O>,
577}
578
579#[cfg(feature = "aead")]
580impl<A, O> From<A> for InMemoryEncryptingKeyProvider<A, O>
581where
582	A: Aead + Send + Sync + 'static,
583	O: AssociatedOid + Send + Sync,
584{
585	fn from(cipher: A) -> Self {
586		InMemoryEncryptingKeyProvider { cipher, _oid: PhantomData }
587	}
588}
589
590#[cfg(feature = "aead")]
591impl<A, O> Debug for InMemoryEncryptingKeyProvider<A, O>
592where
593	A: Aead + Send + Sync + 'static,
594	O: AssociatedOid + Send + Sync,
595{
596	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
597		f.debug_struct("InMemoryEncryptingKeyProvider")
598			.field("algorithm", &O::OID)
599			.finish_non_exhaustive()
600	}
601}
602
603#[cfg(feature = "aead")]
604impl<A, O> EncryptingKeyProvider for InMemoryEncryptingKeyProvider<A, O>
605where
606	A: Aead + Send + Sync + 'static,
607	O: AssociatedOid + Send + Sync,
608{
609	fn algorithm(&self) -> AlgorithmIdentifierOwned {
610		AlgorithmIdentifierOwned { oid: O::OID, parameters: None }
611	}
612
613	fn encrypt(
614		&self,
615		nonce: &[u8],
616		plaintext: &[u8],
617	) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>> {
618		let nonce_size = <<A as AeadCore>::NonceSize as Unsigned>::USIZE;
619		let received_len = nonce.len();
620		if received_len != nonce_size {
621			return Box::pin(async move {
622				Err(KeyError::NonceLengthError(crate::error::ReceivedExpectedError::from((
623					received_len,
624					nonce_size,
625				))))
626			});
627		}
628
629		let nonce_ref = Nonce::<A>::from_slice(nonce);
630		let result = self.cipher.encrypt(nonce_ref, plaintext).map_err(KeyError::from);
631		Box::pin(async move { result })
632	}
633
634	fn decrypt(
635		&self,
636		nonce: &[u8],
637		ciphertext: &[u8],
638	) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>> {
639		let nonce_size = <<A as AeadCore>::NonceSize as Unsigned>::USIZE;
640		let received_len = nonce.len();
641		if received_len != nonce_size {
642			return Box::pin(async move {
643				Err(KeyError::NonceLengthError(crate::error::ReceivedExpectedError::from((
644					received_len,
645					nonce_size,
646				))))
647			});
648		}
649
650		let nonce_ref = Nonce::<A>::from_slice(nonce);
651		let result = self.cipher.decrypt(nonce_ref, ciphertext).map_err(KeyError::from);
652		Box::pin(async move { result })
653	}
654}
655
656#[cfg(feature = "aead")]
657impl<A, O> EncryptingKeyProvider for Arc<InMemoryEncryptingKeyProvider<A, O>>
658where
659	A: Aead + Send + Sync + 'static,
660	O: AssociatedOid + Send + Sync,
661{
662	fn algorithm(&self) -> AlgorithmIdentifierOwned {
663		self.as_ref().algorithm()
664	}
665
666	fn encrypt(
667		&self,
668		nonce: &[u8],
669		plaintext: &[u8],
670	) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>> {
671		self.as_ref().encrypt(nonce, plaintext)
672	}
673
674	fn decrypt(
675		&self,
676		nonce: &[u8],
677		ciphertext: &[u8],
678	) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, KeyError>> + Send + '_>> {
679		self.as_ref().decrypt(nonce, ciphertext)
680	}
681}
682
683// =============================================================================
684// AES Type Aliases
685// =============================================================================
686
687#[cfg(all(feature = "aead", feature = "aes-gcm"))]
688/// Type alias for AES-256-GCM encryption key provider
689pub type Aes256GcmKeyProvider = InMemoryEncryptingKeyProvider<Aes256Gcm, Aes256GcmOid>;
690
691#[cfg(all(feature = "aead", feature = "aes-gcm"))]
692/// Type alias for AES-128-GCM encryption key provider
693pub type Aes128GcmKeyProvider = InMemoryEncryptingKeyProvider<Aes128Gcm, Aes128GcmOid>;
694
695#[cfg(test)]
696mod tests {
697	use rand_core::OsRng;
698
699	use super::*;
700	use crate::crypto::hash::{Digest, Sha3_256};
701	use crate::crypto::secret::ToInsecure;
702	use crate::crypto::sign::ecdsa::k256::ecdsa::SigningKey;
703	use crate::crypto::sign::PrehashVerifier;
704
705	fn prehash(data: &[u8]) -> Vec<u8> {
706		let mut hasher = Sha3_256::new();
707		hasher.update(data);
708		hasher.finalize().to_vec()
709	}
710
711	#[tokio::test]
712	async fn test_secp256k1_provider_public_key() -> Result<(), Box<dyn std::error::Error>> {
713		let signing_key = SigningKey::random(&mut OsRng);
714		let provider = Secp256k1KeyProvider::from(signing_key);
715
716		let public_key_bytes = provider.to_public_key_bytes().await?;
717		// DER-encoded SPKI for secp256k1 is 88 bytes
718		assert_eq!(public_key_bytes.len(), 88);
719		Ok(())
720	}
721
722	#[tokio::test]
723	async fn test_secp256k1_provider_sign() -> Result<(), Box<dyn std::error::Error>> {
724		let signing_key = SigningKey::random(&mut OsRng);
725		let provider = Secp256k1KeyProvider::from(signing_key.clone());
726
727		let digest = prehash(b"test data to sign");
728		let signature_bytes = provider.sign_prehash(&digest).await?;
729
730		// Verify signature using the public key
731		let signature = Secp256k1Signature::from_slice(&signature_bytes)?;
732		signing_key.verifying_key().verify_prehash(&digest, &signature)?;
733
734		Ok(())
735	}
736
737	#[tokio::test]
738	async fn test_secp256k1_provider_key_agreement() -> Result<(), Box<dyn std::error::Error>> {
739		let signing_key1 = SigningKey::random(&mut OsRng);
740		let signing_key2 = SigningKey::random(&mut OsRng);
741
742		let provider1 = Secp256k1KeyProvider::from(signing_key1.clone());
743		let provider2 = Secp256k1KeyProvider::from(signing_key2.clone());
744
745		// key_agreement expects SEC1 encoded public keys (not DER/SPKI)
746		let public1 = signing_key1.verifying_key().to_encoded_point(false).as_bytes().to_vec();
747		let public2 = signing_key2.verifying_key().to_encoded_point(false).as_bytes().to_vec();
748
749		// Both sides should compute the same shared secret
750		let shared1 = provider1.key_agreement(&public2).await?.to_insecure()?;
751		let shared2 = provider2.key_agreement(&public1).await?.to_insecure()?;
752
753		assert_eq!(shared1, shared2);
754		assert_eq!(shared1.len(), 32); // secp256k1 shared secret is 32 bytes
755		Ok(())
756	}
757
758	#[tokio::test]
759	async fn test_generic_provider_sign() -> Result<(), Box<dyn std::error::Error>> {
760		let signing_key = SigningKey::random(&mut OsRng);
761		let provider: Secp256k1Provider = InMemorySigningKeyProvider::from(signing_key.clone());
762
763		let digest = prehash(b"test data to sign");
764		let signature_bytes = provider.sign_prehash(&digest).await?;
765
766		// Verify signature using the public key
767		let signature = Secp256k1Signature::from_slice(&signature_bytes)?;
768		signing_key.verifying_key().verify_prehash(&digest, &signature)?;
769
770		Ok(())
771	}
772
773	#[tokio::test]
774	async fn test_arc_secp256k1_provider() -> Result<(), Box<dyn std::error::Error>> {
775		let signing_key = SigningKey::random(&mut OsRng);
776		let provider = Arc::new(Secp256k1KeyProvider::from(signing_key.clone()));
777
778		// Test that Arc<Secp256k1KeyProvider> implements KeyProvider
779		let public_key_bytes = provider.to_public_key_bytes().await?;
780		// DER-encoded SPKI for secp256k1 is 88 bytes
781		assert_eq!(public_key_bytes.len(), 88);
782
783		let digest = prehash(b"test");
784		let signature_bytes = provider.sign_prehash(&digest).await?;
785
786		let signature = Secp256k1Signature::from_slice(&signature_bytes)?;
787		signing_key.verifying_key().verify_prehash(&digest, &signature)?;
788
789		Ok(())
790	}
791
792	#[tokio::test]
793	async fn test_algorithm_identifier() -> Result<(), Box<dyn std::error::Error>> {
794		let signing_key = SigningKey::random(&mut OsRng);
795		let provider = Secp256k1KeyProvider::from(signing_key);
796
797		let alg = provider.algorithm();
798		assert_eq!(alg.oid, Secp256k1Signature::ALGORITHM_OID);
799		Ok(())
800	}
801}