1use derive_where::derive_where;
4pub use spideroak_crypto::default::Rng;
5use spideroak_crypto::{
6 aead::{Aead, Nonce, Tag},
7 csprng::{Csprng, Random as _},
8 ed25519,
9 generic_array::GenericArray,
10 import::Import,
11 kdf::{Kdf, Prk},
12 kem::Kem,
13 keys::{SecretKey, SecretKeyBytes},
14 mac::Mac,
15 oid::Identified as _,
16 rust,
17 signer::Signer,
18 typenum::U64,
19};
20
21use crate::{
22 ciphersuite::{CipherSuite, CipherSuiteExt as _},
23 engine::{
24 self, AlgId, Engine, RawSecret, RawSecretWrap, UnwrapError, UnwrappedKey, WrapError,
25 WrongKeyType,
26 },
27 id::{BaseId, IdError, Identified},
28};
29
30pub struct DefaultCipherSuite;
41
42impl CipherSuite for DefaultCipherSuite {
43 type Aead = rust::Aes256Gcm;
44 type Hash = rust::Sha256;
45 type Kdf = rust::HkdfSha512;
46 type Kem = DhKemP256HkdfSha256;
47 type Mac = rust::HmacSha512;
48 type Signer = ed25519::Ed25519;
49}
50
51mod __private {
53 use spideroak_crypto::{oid::consts::DHKEM_P256_HKDF_SHA256, rust};
54
55 crate::kem_with_oid! {
56 #[derive(Debug)]
58 pub struct DhKemP256HkdfSha256(rust::DhKemP256HkdfSha256) => DHKEM_P256_HKDF_SHA256
59 }
60}
61pub(crate) use __private::DhKemP256HkdfSha256;
62
63pub struct DefaultEngine<R: Csprng = Rng, S: CipherSuite = DefaultCipherSuite> {
65 aead: S::Aead,
66 rng: R,
67}
68
69impl<S: CipherSuite> Clone for DefaultEngine<Rng, S>
70where
71 S::Aead: Clone,
72{
73 fn clone(&self) -> Self {
74 Self {
75 aead: self.aead.clone(),
76 rng: Rng,
77 }
78 }
79}
80
81impl<R: Csprng, S: CipherSuite> DefaultEngine<R, S> {
82 pub fn new(key: &<S::Aead as Aead>::Key, rng: R) -> Self {
84 Self {
85 aead: S::Aead::new(key),
86 rng,
87 }
88 }
89
90 pub fn from_entropy(rng: R) -> (Self, <S::Aead as Aead>::Key) {
93 let key = <S::Aead as Aead>::Key::random(&rng);
94 let eng = Self::new(&key, rng);
95 (eng, key)
96 }
97}
98
99impl<R: Csprng, S: CipherSuite> Csprng for DefaultEngine<R, S> {
100 fn fill_bytes(&self, dst: &mut [u8]) {
101 self.rng.fill_bytes(dst);
102 }
103}
104
105impl<R: Csprng, S: CipherSuite> Engine for DefaultEngine<R, S> {
106 type CS = S;
107
108 type WrappedKey = WrappedKey<S>;
109}
110
111impl<R: Csprng, S: CipherSuite> RawSecretWrap<Self> for DefaultEngine<R, S> {
112 fn wrap_secret<T>(
113 &self,
114 id: &<T as Identified>::Id,
115 secret: RawSecret<S>,
116 ) -> Result<<Self as Engine>::WrappedKey, WrapError>
117 where
118 T: UnwrappedKey<S>,
119 {
120 let id = *id.as_ref();
121 let mut tag = Tag::<S::Aead>::default();
122 let nonce = Nonce::<_>::random(&self.rng);
125
126 let ad = S::tuple_hash(b"DefaultEngine", [T::ID.as_bytes(), id.as_bytes()]);
127
128 let mut secret = match secret {
129 RawSecret::Aead(sk) => Ciphertext::Aead(sk.try_export_secret()?.into_bytes()),
130 RawSecret::Decap(sk) => Ciphertext::Decap(sk.try_export_secret()?.into_bytes()),
131 RawSecret::Mac(sk) => Ciphertext::Mac(sk.try_export_secret()?.into_bytes()),
132 RawSecret::Prk(sk) => Ciphertext::Prk(sk.into_bytes().into_bytes()),
133 RawSecret::Seed(sk) => Ciphertext::Seed(sk.into()),
134 RawSecret::Signing(sk) => Ciphertext::Signing(sk.try_export_secret()?.into_bytes()),
135 };
136 self.aead.seal_in_place(
137 nonce.as_ref(),
138 secret.as_bytes_mut(),
139 &mut tag,
140 ad.as_bytes(),
141 )?;
142 Ok(WrappedKey {
145 id,
146 nonce: nonce.into_inner(),
147 ciphertext: secret,
148 tag,
149 })
150 }
151
152 fn unwrap_secret<T>(
153 &self,
154 key: &<Self as Engine>::WrappedKey,
155 ) -> Result<RawSecret<S>, UnwrapError>
156 where
157 T: UnwrappedKey<S>,
158 {
159 let mut data = key.ciphertext.clone();
160 let ad = S::tuple_hash(b"DefaultEngine", [T::ID.as_bytes(), key.id.as_bytes()]);
161
162 self.aead.open_in_place(
163 key.nonce.as_ref(),
164 data.as_bytes_mut(),
165 &key.tag,
166 ad.as_bytes(),
167 )?;
168 let secret = match (T::ID, &data) {
171 (AlgId::Aead(_), Ciphertext::Aead(data)) => {
172 RawSecret::Aead(Import::<_>::import(data.as_slice())?)
173 }
174 (AlgId::Decap(_), Ciphertext::Decap(data)) => {
175 RawSecret::Decap(Import::<_>::import(data.as_slice())?)
176 }
177 (AlgId::Mac(_), Ciphertext::Mac(data)) => {
178 RawSecret::Mac(Import::<_>::import(data.as_slice())?)
179 }
180 (AlgId::Prk(_), Ciphertext::Prk(data)) => {
181 RawSecret::Prk(Prk::new(SecretKeyBytes::new(data.clone())))
182 }
183 (AlgId::Seed(()), Ciphertext::Seed(data)) => {
184 RawSecret::Seed(Import::<_>::import(data.as_slice())?)
185 }
186 (AlgId::Signing(_), Ciphertext::Signing(data)) => {
187 RawSecret::Signing(Import::<_>::import(data.as_slice())?)
188 }
189 _ => {
190 return Err(WrongKeyType {
191 got: data.name(),
192 expected: T::ID.name(),
193 }
194 .into());
195 }
196 };
197 Ok(secret)
198 }
199}
200
201#[derive_where(Clone, Serialize, Deserialize)]
203enum Ciphertext<CS: CipherSuite> {
204 Aead(GenericArray<u8, <<CS::Aead as Aead>::Key as SecretKey>::Size>),
205 Decap(GenericArray<u8, <<CS::Kem as Kem>::DecapKey as SecretKey>::Size>),
206 Mac(GenericArray<u8, <<CS::Mac as Mac>::Key as SecretKey>::Size>),
207 Prk(GenericArray<u8, <CS::Kdf as Kdf>::PrkSize>),
208 Seed(GenericArray<u8, U64>),
211 Signing(GenericArray<u8, <<CS::Signer as Signer>::SigningKey as SecretKey>::Size>),
212}
213
214impl<CS: CipherSuite> Ciphertext<CS> {
215 const fn name(&self) -> &'static str {
216 self.alg_id().name()
217 }
218
219 const fn alg_id(&self) -> AlgId {
220 match self {
221 Self::Aead(_) => AlgId::Aead(CS::Aead::OID),
222 Self::Decap(_) => AlgId::Decap(CS::Kem::OID),
223 Self::Mac(_) => AlgId::Mac(CS::Mac::OID),
224 Self::Prk(_) => AlgId::Prk(CS::Kdf::OID),
225 Self::Seed(_) => AlgId::Seed(()),
226 Self::Signing(_) => AlgId::Signing(CS::Signer::OID),
227 }
228 }
229}
230
231impl<CS: CipherSuite> Ciphertext<CS> {
232 fn as_bytes_mut(&mut self) -> &mut [u8] {
233 match self {
234 Self::Aead(v) => v.as_mut_slice(),
235 Self::Decap(v) => v.as_mut_slice(),
236 Self::Mac(v) => v.as_mut_slice(),
237 Self::Prk(v) => v.as_mut_slice(),
238 Self::Seed(v) => v.as_mut_slice(),
239 Self::Signing(v) => v.as_mut_slice(),
240 }
241 }
242}
243
244#[derive_where(Clone, Serialize, Deserialize)]
246pub struct WrappedKey<CS: CipherSuite> {
247 id: BaseId,
248 nonce: GenericArray<u8, <CS::Aead as Aead>::NonceSize>,
249 ciphertext: Ciphertext<CS>,
250 tag: Tag<CS::Aead>,
251}
252
253impl<CS: CipherSuite> engine::WrappedKey for WrappedKey<CS> {}
254
255impl<CS: CipherSuite> Identified for WrappedKey<CS> {
256 type Id = BaseId;
257
258 fn id(&self) -> Result<Self::Id, IdError> {
259 Ok(self.id)
260 }
261}
262
263#[cfg(test)]
264#[allow(clippy::wildcard_imports)]
265mod test {
266 use super::*;
267 use crate::{Rng, test_engine, test_util::test_ciphersuite};
268
269 test_engine!(
270 default_engine,
271 || -> DefaultEngine<Rng, DefaultCipherSuite> {
272 let (eng, _) = DefaultEngine::<Rng>::from_entropy(Rng);
273 eng
274 }
275 );
276
277 test_ciphersuite!(default_ciphersuite, DefaultCipherSuite);
278}