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