1use crate::error::Unspecified;
9use native_ossl::cipher::{AeadDecryptCtx, AeadEncryptCtx, CipherAlg};
10use native_ossl::util::SecretBuf;
11use std::ffi::CStr;
12
13pub const MAX_TAG_LEN: usize = 16;
14pub const NONCE_LEN: usize = 12;
15
16#[derive(Debug)]
18pub struct Algorithm {
19 cipher_name: &'static CStr,
20 key_len: usize,
21 tag_len: usize,
22}
23
24pub static AES_128_GCM: Algorithm = Algorithm {
25 cipher_name: c"AES-128-GCM",
26 key_len: 16,
27 tag_len: 16,
28};
29pub static AES_256_GCM: Algorithm = Algorithm {
30 cipher_name: c"AES-256-GCM",
31 key_len: 32,
32 tag_len: 16,
33};
34pub static CHACHA20_POLY1305: Algorithm = Algorithm {
35 cipher_name: c"ChaCha20-Poly1305",
36 key_len: 32,
37 tag_len: 16,
38};
39
40impl Algorithm {
41 #[must_use]
42 pub fn key_len(&self) -> usize {
43 self.key_len
44 }
45
46 #[must_use]
47 pub fn tag_len(&self) -> usize {
48 self.tag_len
49 }
50
51 #[must_use]
52 pub fn nonce_len(&self) -> usize {
53 NONCE_LEN
54 }
55}
56
57#[derive(Clone, Copy, Debug)]
59pub struct Nonce(pub [u8; NONCE_LEN]);
60
61impl Nonce {
62 #[must_use]
63 pub fn assume_unique_for_key(bytes: [u8; NONCE_LEN]) -> Self {
64 Self(bytes)
65 }
66
67 pub fn try_assume_unique_for_key(bytes: &[u8]) -> Result<Self, Unspecified> {
71 let arr: [u8; NONCE_LEN] = bytes.try_into().map_err(|_| Unspecified)?;
72 Ok(Self(arr))
73 }
74}
75
76pub struct Aad<A>(pub A);
78
79impl<A: AsRef<[u8]>> Aad<A> {
80 pub fn from(val: A) -> Self {
81 Aad(val)
82 }
83}
84
85impl Aad<[u8; 0]> {
86 #[must_use]
87 pub fn empty() -> Self {
88 Aad([])
89 }
90}
91
92pub struct UnboundKey {
94 alg: &'static Algorithm,
95 key: SecretBuf,
96}
97
98impl std::fmt::Debug for UnboundKey {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 f.debug_struct("UnboundKey")
101 .field("alg", &self.alg)
102 .finish_non_exhaustive()
103 }
104}
105
106impl std::fmt::Debug for LessSafeKey {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 f.debug_struct("LessSafeKey")
109 .field("alg", &self.alg)
110 .finish_non_exhaustive()
111 }
112}
113
114impl UnboundKey {
115 pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self, Unspecified> {
119 if key_bytes.len() != algorithm.key_len {
120 return Err(Unspecified);
121 }
122 Ok(Self {
123 alg: algorithm,
124 key: SecretBuf::from_slice(key_bytes),
125 })
126 }
127
128 #[must_use]
129 pub fn algorithm(&self) -> &'static Algorithm {
130 self.alg
131 }
132}
133
134pub struct LessSafeKey {
137 alg: &'static Algorithm,
138 key: SecretBuf,
139}
140
141impl LessSafeKey {
142 #[must_use]
143 pub fn new(key: UnboundKey) -> Self {
144 Self {
145 alg: key.alg,
146 key: key.key,
147 }
148 }
149
150 #[must_use]
151 pub fn algorithm(&self) -> &'static Algorithm {
152 self.alg
153 }
154
155 pub fn seal_in_place_append_tag<A: AsRef<[u8]>>(
161 &self,
162 nonce: Nonce,
163 aad: &Aad<A>,
164 in_out: &mut Vec<u8>,
165 ) -> Result<(), Unspecified> {
166 let cipher_alg = CipherAlg::fetch(self.alg.cipher_name, None).map_err(|_| Unspecified)?;
167 let mut enc = AeadEncryptCtx::new(&cipher_alg, self.key.as_ref(), &nonce.0, None)
168 .map_err(|_| Unspecified)?;
169 enc.set_aad(aad.0.as_ref()).map_err(|_| Unspecified)?;
170
171 let plaintext_len = in_out.len();
172 let mut ciphertext = vec![0u8; plaintext_len + 32];
174 let n = enc
175 .update(in_out, &mut ciphertext)
176 .map_err(|_| Unspecified)?;
177 let n2 = enc
178 .finalize(&mut ciphertext[n..])
179 .map_err(|_| Unspecified)?;
180 let ct_len = n + n2;
181
182 let mut tag = [0u8; 16];
183 enc.tag(&mut tag[..self.alg.tag_len])
184 .map_err(|_| Unspecified)?;
185
186 in_out.clear();
187 in_out.extend_from_slice(&ciphertext[..ct_len]);
188 in_out.extend_from_slice(&tag[..self.alg.tag_len]);
189 Ok(())
190 }
191
192 pub fn open_in_place<'in_out, A: AsRef<[u8]>>(
200 &self,
201 nonce: Nonce,
202 aad: &Aad<A>,
203 in_out: &'in_out mut [u8],
204 ) -> Result<&'in_out mut [u8], Unspecified> {
205 let tag_len = self.alg.tag_len;
206 if in_out.len() < tag_len {
207 return Err(Unspecified);
208 }
209 let ciphertext_len = in_out.len() - tag_len;
210 let (ciphertext, tag) = in_out.split_at_mut(ciphertext_len);
211 let tag = &*tag; let cipher_alg = CipherAlg::fetch(self.alg.cipher_name, None).map_err(|_| Unspecified)?;
214 let mut dec = AeadDecryptCtx::new(&cipher_alg, self.key.as_ref(), &nonce.0, None)
215 .map_err(|_| Unspecified)?;
216 dec.set_aad(aad.0.as_ref()).map_err(|_| Unspecified)?;
217 dec.set_tag(tag).map_err(|_| Unspecified)?;
218
219 let mut plaintext = vec![0u8; ciphertext_len + 32];
220 let n = dec
221 .update(ciphertext, &mut plaintext)
222 .map_err(|_| Unspecified)?;
223 let n2 = dec.finalize(&mut plaintext[n..]).map_err(|_| Unspecified)?;
224 let pt_len = n + n2;
225
226 in_out[..pt_len].copy_from_slice(&plaintext[..pt_len]);
227 Ok(&mut in_out[..pt_len])
228 }
229
230 pub fn open_within<'in_out, A: AsRef<[u8]>>(
236 &self,
237 nonce: Nonce,
238 aad: &Aad<A>,
239 in_out: &'in_out mut [u8],
240 in_prefix_len: usize,
241 ) -> Result<&'in_out mut [u8], Unspecified> {
242 let tag_len = self.alg.tag_len;
243 let total = in_out.len();
244 if total < in_prefix_len + tag_len {
245 return Err(Unspecified);
246 }
247 let ciphertext_and_tag = &mut in_out[in_prefix_len..];
248 let result = self.open_in_place(nonce, aad, ciphertext_and_tag)?;
249 let pt_len = result.len();
250 in_out.copy_within(in_prefix_len..in_prefix_len + pt_len, 0);
252 Ok(&mut in_out[..pt_len])
253 }
254}