1use core::mem;
8
9use ctr::cipher::{self, KeySizeUser};
10use hmac::{
11 Mac,
12 digest::{
13 OutputSizeUser,
14 typenum::{U32, Unsigned},
15 },
16};
17use rand::{Rng, SeedableRng, rngs::StdRng};
18use sha2::{Digest, Sha256};
19
20use crate::{Aes256Ctr128BE, Error, HmacSha256, HmacSha256Key, HmacSha256Output, Params, Result};
21
22type MagicNumber = [u8; 6];
24
25type Salt = [u8; 32];
27
28type Checksum = [u8; 16];
30
31type HeaderMac = HmacSha256;
33
34type HeaderMacOutput = HmacSha256Output;
36
37type HeaderMacKey = HmacSha256Key;
39
40type Aes256Ctr128BEKey = cipher::Key<Aes256Ctr128BE>;
42
43pub const HEADER_SIZE: usize = Header::SIZE;
58
59pub const TAG_SIZE: usize = <HmacSha256 as OutputSizeUser>::OutputSize::USIZE;
75
76#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
78enum Version {
79 #[default]
81 V0,
82
83 #[allow(dead_code)]
84 #[doc(hidden)]
86 V1,
87}
88
89impl From<Version> for u8 {
90 #[inline]
91 fn from(version: Version) -> Self {
92 version as Self
93 }
94}
95
96#[derive(Clone, Debug)]
98pub struct Header {
99 magic_number: MagicNumber,
100 version: Version,
101 params: Params,
102 salt: Salt,
103 checksum: Checksum,
104 mac: HeaderMacOutput,
105}
106
107impl Header {
108 const MAGIC_NUMBER: MagicNumber = *b"scrypt";
112
113 const SIZE: usize = mem::size_of::<MagicNumber>()
115 + mem::size_of::<Version>()
116 + (mem::size_of::<Params>() - (mem::align_of::<Params>() - mem::size_of::<u8>()))
117 + mem::size_of::<Salt>()
118 + mem::size_of::<Checksum>()
119 + <HeaderMac as OutputSizeUser>::OutputSize::USIZE;
120
121 pub fn new(params: scrypt::Params) -> Self {
123 let magic_number = Self::MAGIC_NUMBER;
124 let version = Version::default();
125 let params = params.into();
126 let salt = StdRng::from_entropy().r#gen();
127 let checksum = Checksum::default();
128 let mac = HeaderMacOutput::default();
129 Self {
130 magic_number,
131 version,
132 params,
133 salt,
134 checksum,
135 mac,
136 }
137 }
138
139 pub fn parse(data: &[u8]) -> Result<Self> {
141 if data.len() < Self::SIZE + TAG_SIZE {
142 return Err(Error::InvalidLength);
143 }
144
145 let Some(magic_number) = Some(Self::MAGIC_NUMBER).filter(|mn| &data[..6] == mn) else {
146 return Err(Error::InvalidMagicNumber);
147 };
148 let version = match data[6] {
149 0 => Version::V0,
150 v => return Err(Error::UnknownVersion(v)),
151 };
152 let log_n = data[7];
153 let r = u32::from_be_bytes(
154 data[8..12]
155 .try_into()
156 .expect("size of `r` parameter should be 4 bytes"),
157 );
158 let p = u32::from_be_bytes(
159 data[12..16]
160 .try_into()
161 .expect("size of `p` parameter should be 4 bytes"),
162 );
163 let params =
164 scrypt::Params::new(log_n, r, p, scrypt::Params::RECOMMENDED_LEN).map(Params::from)?;
165 let salt = data[16..48]
166 .try_into()
167 .expect("size of salt should be 32 bytes");
168 let checksum = Checksum::default();
169 let mac = HeaderMacOutput::default();
170 Ok(Self {
171 magic_number,
172 version,
173 params,
174 salt,
175 checksum,
176 mac,
177 })
178 }
179
180 #[inline]
182 pub fn compute_checksum(&mut self) {
183 let result = Sha256::digest(&self.as_bytes()[..48]);
184 self.checksum.copy_from_slice(&result[..16]);
185 }
186
187 #[inline]
189 pub fn verify_checksum(&mut self, checksum: &[u8]) -> Result<()> {
190 self.compute_checksum();
191 if self.checksum == checksum {
192 Ok(())
193 } else {
194 Err(Error::InvalidChecksum)
195 }
196 }
197
198 #[inline]
200 pub fn compute_mac(&mut self, key: &HeaderMacKey) {
201 let mut mac =
202 HmacSha256::new_from_slice(key).expect("HMAC-SHA-256 key size should be 256 bits");
203 mac.update(&self.as_bytes()[..64]);
204 self.mac.copy_from_slice(&mac.finalize().into_bytes());
205 }
206
207 pub fn verify_mac(&mut self, key: &HeaderMacKey, tag: &HeaderMacOutput) -> Result<()> {
209 let mut mac =
210 HmacSha256::new_from_slice(key).expect("HMAC-SHA-256 key size should be 256 bits");
211 mac.update(&self.as_bytes()[..64]);
212 mac.verify(tag).map_err(Error::InvalidHeaderMac)?;
213 self.mac.copy_from_slice(tag);
214 Ok(())
215 }
216
217 pub fn as_bytes(&self) -> [u8; Self::SIZE] {
219 let mut header = [u8::default(); Self::SIZE];
220 header[..6].copy_from_slice(&self.magic_number);
221 header[6] = self.version.into();
222 header[7] = self.params.log_n();
223 header[8..12].copy_from_slice(&self.params.r().to_be_bytes());
224 header[12..16].copy_from_slice(&self.params.p().to_be_bytes());
225 header[16..48].copy_from_slice(&self.salt);
226 header[48..64].copy_from_slice(&self.checksum);
227 header[64..].copy_from_slice(&self.mac);
228 header
229 }
230
231 #[inline]
233 pub const fn params(&self) -> Params {
234 self.params
235 }
236
237 #[inline]
239 pub const fn salt(&self) -> Salt {
240 self.salt
241 }
242}
243
244#[derive(Clone, Debug)]
246pub struct DerivedKey {
247 encrypt: Aes256Ctr128BEKey,
248 mac: HmacSha256Key,
249}
250
251impl DerivedKey {
252 pub const SIZE: usize = <Aes256Ctr128BE as KeySizeUser>::KeySize::USIZE + U32::USIZE;
254
255 #[inline]
257 pub fn new(dk: [u8; Self::SIZE]) -> Self {
258 let encrypt = *Aes256Ctr128BEKey::from_slice(&dk[..32]);
259 let mac = *HmacSha256Key::from_slice(&dk[32..]);
260 Self { encrypt, mac }
261 }
262
263 #[inline]
265 pub const fn encrypt(&self) -> Aes256Ctr128BEKey {
266 self.encrypt
267 }
268
269 #[inline]
271 pub const fn mac(&self) -> HmacSha256Key {
272 self.mac
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use core::str;
279
280 use super::*;
281
282 #[test]
283 fn header_size() {
284 assert_eq!(HEADER_SIZE, 96);
285 assert_eq!(HEADER_SIZE, Header::SIZE);
286 }
287
288 #[test]
289 fn tag_size() {
290 assert_eq!(TAG_SIZE, 32);
291 assert_eq!(TAG_SIZE, <HmacSha256 as OutputSizeUser>::OutputSize::USIZE);
292 }
293
294 #[test]
295 fn version() {
296 assert_eq!(Version::V0 as u8, 0);
297 assert_eq!(Version::V1 as u8, 1);
298 }
299
300 #[test]
301 fn size_of_version() {
302 assert_eq!(mem::size_of::<Version>(), mem::size_of::<u8>());
303 }
304
305 #[test]
306 fn clone_version() {
307 assert_eq!(Version::V0.clone(), Version::V0);
308 assert_eq!(Version::V1.clone(), Version::V1);
309 }
310
311 #[test]
312 fn copy_version() {
313 {
314 let a = Version::V0;
315 let b = a;
316 assert_eq!(a, b);
317 }
318
319 {
320 let a = Version::V1;
321 let b = a;
322 assert_eq!(a, b);
323 }
324 }
325
326 #[cfg(feature = "alloc")]
327 #[test]
328 fn debug_version() {
329 assert_eq!(format!("{:?}", Version::V0), "V0");
330 assert_eq!(format!("{:?}", Version::V1), "V1");
331 }
332
333 #[test]
334 fn default_version() {
335 assert_eq!(Version::default(), Version::V0);
336 }
337
338 #[test]
339 fn version_equality() {
340 assert_eq!(Version::V0, Version::V0);
341 assert_ne!(Version::V0, Version::V1);
342 assert_ne!(Version::V1, Version::V0);
343 assert_eq!(Version::V1, Version::V1);
344 }
345
346 #[test]
347 fn from_version_to_u8() {
348 assert_eq!(u8::from(Version::V0), 0);
349 assert_eq!(u8::from(Version::V1), 1);
350 }
351
352 #[test]
353 fn magic_number() {
354 assert_eq!(str::from_utf8(&Header::MAGIC_NUMBER).unwrap(), "scrypt");
355 }
356
357 #[test]
358 fn derived_key_size() {
359 assert_eq!(DerivedKey::SIZE, 64);
360 }
361}