browser_crypto/algorithm.rs
1use std::marker::PhantomData;
2
3use wasm_bindgen::{JsCast, JsValue};
4use web_sys::DomException;
5
6/// Errors that can occur during nonce (number used once) operations.
7///
8/// These errors handle both Web Crypto API random generation errors and
9/// nonce validation errors.
10///
11/// See [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#exceptions)
12#[derive(Debug, Clone, thiserror::Error)]
13pub enum NonceError {
14 /// Indicates that the requested nonce length exceeds the maximum allowed
15 /// size.
16 ///
17 /// This error occurs when trying to generate a nonce larger than 65536
18 /// bytes, which is the maximum size allowed by the Web Crypto API's
19 /// getRandomValues(). This limit exists as a security measure to
20 /// prevent excessive entropy extraction.
21 ///
22 /// Note: Most cryptographic algorithms use much smaller nonces
23 /// (typically 12 or 16 bytes), so this error should rarely occur in
24 /// practice.
25 #[error("the requested nonce length exceeds 65536")]
26 QuotaExceeded,
27 /// Indicates that the provided nonce size doesn't match the algorithm's
28 /// requirements.
29 ///
30 /// This error occurs when:
31 /// - Creating a nonce from existing data
32 /// - The provided data length doesn't match the algorithm's specified nonce
33 /// size
34 ///
35 /// # Fields
36 /// * `expected` - The nonce size required by the algorithm
37 /// * `received` - The actual size of the provided nonce data
38 ///
39 /// For example, if AES-GCM requires a 12-byte nonce but 16 bytes were
40 /// provided, this error would be returned with expected=12,
41 /// received=16.
42 #[error("invalid nonce size provided, expected {expected}, received {received}")]
43 InvalidSize { expected: u32, received: u32 },
44 /// A wrapper for other types of errors that may occur during nonce
45 /// operations.
46 ///
47 /// This includes general Web Crypto API errors and other unexpected
48 /// failures that might occur during nonce generation or handling.
49 #[error(transparent)]
50 Generic(#[from] crate::Error),
51}
52
53impl From<wasm_bindgen::JsValue> for NonceError {
54 fn from(value: wasm_bindgen::JsValue) -> Self {
55 if let Some(exception) = value.dyn_ref::<web_sys::DomException>() {
56 if exception.name().as_str() == "QuotaExceededError" {
57 return Self::QuotaExceeded;
58 }
59 }
60 Self::Generic(crate::Error::from(value))
61 }
62}
63
64/// Errors that can occur during encryption operations.
65///
66/// These errors map to the exceptions defined in the Web Crypto API
67/// specification for encryption operations.
68///
69/// See [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#exceptions)
70#[derive(Debug, Clone, thiserror::Error)]
71pub enum EncryptionError {
72 /// Indicates that the requested operation is not valid for the provided
73 /// key. This typically occurs when:
74 /// - The key doesn't support the encryption operation
75 /// - The key's algorithm doesn't match the specified algorithm
76 /// - The key's usages don't include "encrypt"
77 #[error("requested operation is not valid for the provided key")]
78 InvalidAccess,
79 /// Indicates that the operation failed for an algorithm-specific reason.
80 /// This can occur when:
81 /// - The input data is too large
82 /// - The algorithm parameters are invalid
83 /// - There's an internal error in the cryptographic implementation
84 #[error("operation failed for an operation-specific reason")]
85 Operation,
86 /// A wrapper for other types of errors that may occur during encryption
87 #[error(transparent)]
88 Generic(#[from] crate::Error),
89}
90
91impl From<JsValue> for EncryptionError {
92 fn from(value: JsValue) -> Self {
93 if let Some(exception) = value.dyn_ref::<DomException>() {
94 match exception.name().as_str() {
95 "InvalidAccessError" => {
96 return Self::InvalidAccess;
97 }
98 "OperationError" => {
99 return Self::Operation;
100 }
101 _ => {}
102 }
103 }
104 Self::Generic(crate::Error::from(value))
105 }
106}
107
108/// Errors that can occur during decryption operations.
109///
110/// These errors map to the exceptions defined in the Web Crypto API
111/// specification for decryption operations.
112///
113/// See [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/decrypt#exceptions)
114#[derive(Debug, Clone, thiserror::Error)]
115pub enum DecryptionError {
116 /// Indicates that the requested operation is not valid for the provided
117 /// key. This typically occurs when:
118 /// - The key doesn't support the decryption operation
119 /// - The key's algorithm doesn't match the specified algorithm
120 /// - The key's usages don't include "decrypt"
121 #[error("requested operation is not valid for the provided key")]
122 InvalidAccess,
123 /// Indicates that the operation failed for an algorithm-specific reason.
124 /// This can occur when:
125 /// - The ciphertext is corrupted or malformed
126 /// - The authentication tag is invalid (for authenticated encryption)
127 /// - The algorithm parameters (like nonce) don't match those used for
128 /// encryption
129 #[error("operation failed for an operation-specific reason")]
130 Operation,
131 /// A wrapper for other types of errors that may occur during decryption
132 #[error(transparent)]
133 Generic(#[from] crate::Error),
134}
135
136impl From<JsValue> for DecryptionError {
137 fn from(value: JsValue) -> Self {
138 if let Some(exception) = value.dyn_ref::<DomException>() {
139 match exception.name().as_str() {
140 "InvalidAccessError" => {
141 return Self::InvalidAccess;
142 }
143 "OperationError" => {
144 return Self::Operation;
145 }
146 _ => {}
147 }
148 }
149 Self::Generic(crate::Error::from(value))
150 }
151}
152
153/// Nonce handling for cryptographic operations
154#[derive(Debug, Clone)]
155pub struct Nonce<A> {
156 algo: PhantomData<A>,
157 inner: js_sys::Uint8Array,
158}
159
160impl<A> AsRef<js_sys::Uint8Array> for Nonce<A> {
161 fn as_ref(&self) -> &js_sys::Uint8Array {
162 &self.inner
163 }
164}
165
166impl<A> Nonce<A>
167where
168 A: Algorithm,
169 A: Sized,
170{
171 /// Generates a new random nonce
172 ///
173 /// # Returns
174 /// Result containing generated Nonce or NonceError
175 pub fn generate() -> Result<Nonce<A>, NonceError> {
176 let crypto = crate::crypto()?;
177 let inner = js_sys::Uint8Array::new_with_length(A::NONCE_SIZE);
178 crypto.get_random_values_with_js_u8_array(&inner)?;
179 Ok(Nonce {
180 algo: PhantomData,
181 inner,
182 })
183 }
184
185 /// Creates a nonce from existing bytes
186 ///
187 /// # Arguments
188 /// * `data` - Byte slice containing nonce data
189 ///
190 /// # Returns
191 /// Result containing Nonce or NonceError
192 ///
193 /// # Errors
194 /// Returns `NonceError::InvalidSize` if data length doesn't match algorithm
195 /// requirements
196 pub fn from_slice(data: &[u8]) -> Result<Self, NonceError> {
197 let size = data.len() as u32;
198 if size != A::NONCE_SIZE {
199 return Err(NonceError::InvalidSize {
200 expected: A::NONCE_SIZE,
201 received: size,
202 });
203 }
204 Ok(Self {
205 algo: PhantomData,
206 inner: js_sys::Uint8Array::from(data),
207 })
208 }
209
210 pub fn iter(&self) -> impl Iterator<Item = u8> + '_ {
211 (0..self.inner.length()).map(|idx| self.inner.get_index(idx))
212 }
213
214 /// Returns nonce bytes as a vector
215 pub fn to_vec(&self) -> Vec<u8> {
216 crate::array_to_vec(&self.inner)
217 }
218}
219
220/// Core cryptographic algorithm trait
221pub trait Algorithm: Sized {
222 /// Required nonce size in bytes for this algorithm
223 const NONCE_SIZE: u32;
224
225 /// Generates a new random nonce suitable for this algorithm
226 ///
227 /// # Returns
228 /// Result containing the generated Nonce or a NonceError
229 ///
230 /// # Errors
231 /// - `NonceError::QuotaExceeded` if requested length > 65536 bytes
232 /// - `NonceError::InvalidSize` if nonce size doesn't match algorithm
233 /// requirements
234 fn generate_nonce() -> Result<Nonce<Self>, NonceError> {
235 Nonce::<Self>::generate()
236 }
237
238 /// Encrypts data using this algorithm
239 ///
240 /// # Arguments
241 /// * `nonce` - Nonce to use for encryption
242 /// * `payload` - Data to encrypt
243 ///
244 /// # Returns
245 /// Result containing encrypted bytes or an EncryptionError
246 ///
247 /// # Errors
248 /// - `EncryptionError::InvalidAccess` if operation invalid for provided key
249 /// - `EncryptionError::Operation` if encryption fails for
250 /// algorithm-specific reasons
251 fn encrypt(
252 &self,
253 nonce: &Nonce<Self>,
254 payload: &[u8],
255 ) -> impl std::future::Future<Output = Result<Vec<u8>, EncryptionError>>;
256
257 /// Decrypts data using this algorithm
258 ///
259 /// # Arguments
260 /// * `nonce` - Nonce used for encryption
261 /// * `payload` - Encrypted data to decrypt
262 ///
263 /// # Returns
264 /// Result containing decrypted bytes or a DecryptionError
265 ///
266 /// # Errors
267 /// - `DecryptionError::InvalidAccess` if operation invalid for provided key
268 /// - `DecryptionError::Operation` if decryption fails for
269 /// algorithm-specific reasons
270 fn decrypt(
271 &self,
272 nonce: &Nonce<Self>,
273 payload: &[u8],
274 ) -> impl std::future::Future<Output = Result<Vec<u8>, DecryptionError>>;
275}