1use crate::error::PdfError;
8use crate::objects::PdfDict;
9
10pub struct EncryptionState {
12 key: Vec<u8>,
14 #[allow(dead_code)]
16 version: i32,
17 stm_method: CryptMethod,
19 str_method: CryptMethod,
21}
22
23#[derive(Clone, Copy, PartialEq)]
24enum CryptMethod {
25 None,
26 RC4,
27 AesV2, AesV3, }
30
31impl EncryptionState {
32 pub fn try_open(
38 encrypt_dict: &PdfDict,
39 trailer: &PdfDict,
40 file_id: &[u8],
41 ) -> Result<Self, PdfError> {
42 Self::try_open_with_password(encrypt_dict, trailer, file_id, b"")
43 }
44
45 pub fn try_open_with_password(
49 encrypt_dict: &PdfDict,
50 _trailer: &PdfDict,
51 file_id: &[u8],
52 password: &[u8],
53 ) -> Result<Self, PdfError> {
54 let v = encrypt_dict.get_int(b"V").unwrap_or(0) as i32;
55 let r = encrypt_dict.get_int(b"R").unwrap_or(0) as i32;
56 let key_length = if let Some(len) = encrypt_dict.get_int(b"Length") {
60 len as usize / 8
61 } else if v >= 4 {
62 let cf_len = encrypt_dict.get_dict(b"CF").and_then(|cf| {
64 let filter_name = encrypt_dict.get_name(b"StmF").unwrap_or(b"StdCF");
65 cf.get_dict(filter_name)
66 .and_then(|f| f.get_int(b"Length").map(|n| n as usize))
67 });
68 cf_len.unwrap_or(16) } else {
70 5 };
72
73 let o_value = encrypt_dict
74 .get(b"O")
75 .and_then(|o| o.as_str())
76 .ok_or(PdfError::Other("Encrypt missing /O".into()))?
77 .to_vec();
78 let u_value = encrypt_dict
79 .get(b"U")
80 .and_then(|o| o.as_str())
81 .ok_or(PdfError::Other("Encrypt missing /U".into()))?
82 .to_vec();
83 let p_value = encrypt_dict.get_int(b"P").unwrap_or(0) as i32;
84
85 if v == 5 {
86 return Self::try_open_v5(encrypt_dict, &u_value, r, password);
88 }
89
90 if v >= 4 {
94 let stm_name = encrypt_dict.get_name(b"StmF").unwrap_or(b"Identity");
95 let str_name = encrypt_dict.get_name(b"StrF").unwrap_or(b"Identity");
96 if stm_name == b"Identity" && str_name == b"Identity" {
97 return Ok(Self {
98 key: Vec::new(),
99 version: v,
100 stm_method: CryptMethod::None,
101 str_method: CryptMethod::None,
102 });
103 }
104 }
105
106 let encrypt_metadata = encrypt_dict
108 .get(b"EncryptMetadata")
109 .and_then(|o| match o {
110 crate::objects::PdfObj::Bool(b) => Some(*b),
111 _ => None,
112 })
113 .unwrap_or(true);
114 let key = compute_encryption_key(
115 password,
116 &o_value,
117 p_value,
118 file_id,
119 key_length,
120 r,
121 encrypt_metadata,
122 );
123
124 if !verify_user_password(&key, &u_value, file_id, r) {
126 return Err(PdfError::PasswordRequired);
127 }
128
129 let key = if v >= 4 && key.len() < 16 {
136 let mut padded = key;
137 padded.resize(16, 0);
138 padded
139 } else {
140 key
141 };
142
143 let (stm_method, str_method) = if v >= 4 {
144 let cf = encrypt_dict.get_dict(b"CF");
146 let stm_name = encrypt_dict.get_name(b"StmF").unwrap_or(b"Identity");
147 let str_name = encrypt_dict.get_name(b"StrF").unwrap_or(b"Identity");
148 (
149 parse_crypt_method(cf, stm_name),
150 parse_crypt_method(cf, str_name),
151 )
152 } else {
153 (CryptMethod::RC4, CryptMethod::RC4)
154 };
155
156 Ok(Self {
157 key,
158 version: v,
159 stm_method,
160 str_method,
161 })
162 }
163
164 fn try_open_v5(
165 encrypt_dict: &PdfDict,
166 u_value: &[u8],
167 r: i32,
168 password: &[u8],
169 ) -> Result<Self, PdfError> {
170 if u_value.len() < 48 {
172 return Err(PdfError::Other("AES-256: /U too short".into()));
173 }
174
175 let validation_salt = &u_value[32..40];
176 let key_salt = &u_value[40..48];
177
178 let hash = if r >= 6 {
180 compute_hash_r6(password, validation_salt, b"")
181 } else {
182 sha256(&[password, validation_salt])
183 };
184 if hash[..] != u_value[..32] {
185 return Err(PdfError::PasswordRequired);
186 }
187
188 let key_hash = if r >= 6 {
190 compute_hash_r6(password, key_salt, b"")
191 } else {
192 sha256(&[password, key_salt])
193 };
194
195 let ue = encrypt_dict
197 .get(b"UE")
198 .and_then(|o| o.as_str())
199 .ok_or(PdfError::Other("Encrypt missing /UE".into()))?;
200
201 if ue.len() < 32 {
202 return Err(PdfError::Other("AES-256: /UE too short".into()));
203 }
204
205 let file_key = aes_cbc_decrypt_no_pad(&key_hash, &[0u8; 16], &ue[..32]);
207
208 Ok(Self {
209 key: file_key,
210 version: 5,
211 stm_method: CryptMethod::AesV3,
212 str_method: CryptMethod::AesV3,
213 })
214 }
215
216 pub fn decrypt_string(&self, data: &[u8], obj_num: u32, gen_num: u16) -> Vec<u8> {
218 self.decrypt_data(data, obj_num, gen_num, self.str_method)
219 }
220
221 pub fn decrypt_stream(&self, data: &[u8], obj_num: u32, gen_num: u16) -> Vec<u8> {
223 self.decrypt_data(data, obj_num, gen_num, self.stm_method)
224 }
225
226 fn decrypt_data(
227 &self,
228 data: &[u8],
229 obj_num: u32,
230 gen_num: u16,
231 method: CryptMethod,
232 ) -> Vec<u8> {
233 match method {
234 CryptMethod::None => data.to_vec(),
235 CryptMethod::RC4 => {
236 let obj_key = compute_object_key(&self.key, obj_num, gen_num, false);
237 rc4(&obj_key, data)
238 }
239 CryptMethod::AesV2 => {
240 let obj_key = compute_object_key(&self.key, obj_num, gen_num, true);
241 if data.len() < 16 {
242 return data.to_vec();
243 }
244 aes_cbc_decrypt(&obj_key, &data[..16], &data[16..])
245 .unwrap_or_else(|_| data.to_vec())
246 }
247 CryptMethod::AesV3 => {
248 if data.len() < 16 {
249 return data.to_vec();
250 }
251 aes_cbc_decrypt(&self.key, &data[..16], &data[16..])
252 .unwrap_or_else(|_| data.to_vec())
253 }
254 }
255 }
256}
257
258fn parse_crypt_method(cf: Option<&PdfDict>, name: &[u8]) -> CryptMethod {
259 if name == b"Identity" {
260 return CryptMethod::None;
261 }
262 let cfm = cf
263 .and_then(|d| d.get_dict(name))
264 .and_then(|d| d.get_name(b"CFM"));
265 match cfm {
266 Some(b"None") => CryptMethod::None,
267 Some(b"V2") => CryptMethod::RC4,
268 Some(b"AESV2") => CryptMethod::AesV2,
269 Some(b"AESV3") => CryptMethod::AesV3,
270 _ => CryptMethod::RC4,
271 }
272}
273
274fn compute_encryption_key(
276 password: &[u8],
277 o_value: &[u8],
278 p: i32,
279 file_id: &[u8],
280 key_length: usize,
281 revision: i32,
282 encrypt_metadata: bool,
283) -> Vec<u8> {
284 let padded = pad_password(password);
286
287 let mut digest = md5::Context::new();
288 digest.consume(padded);
289 digest.consume(o_value);
290 digest.consume(p.to_le_bytes());
291 digest.consume(file_id);
292
293 if revision >= 4 && !encrypt_metadata {
295 digest.consume([0xFFu8; 4]);
296 }
297
298 let mut hash = digest.compute().0;
299
300 if revision >= 3 {
301 for _ in 0..50 {
302 hash = md5::compute(&hash[..key_length]).0;
303 }
304 }
305
306 hash[..key_length].to_vec()
307}
308
309fn verify_user_password(key: &[u8], u_value: &[u8], file_id: &[u8], revision: i32) -> bool {
311 if revision <= 2 {
312 let encrypted = rc4(key, &PASSWORD_PADDING);
314 encrypted == u_value
315 } else {
316 let mut digest = md5::Context::new();
318 digest.consume(PASSWORD_PADDING);
319 digest.consume(file_id);
320 let hash = digest.compute().0;
321
322 let mut result = rc4(key, &hash);
323 for i in 1..=19u8 {
324 let derived_key: Vec<u8> = key.iter().map(|&b| b ^ i).collect();
325 result = rc4(&derived_key, &result);
326 }
327
328 u_value.len() >= 16 && result[..16] == u_value[..16]
330 }
331}
332
333fn compute_object_key(key: &[u8], obj_num: u32, gen_num: u16, is_aes: bool) -> Vec<u8> {
335 let mut digest = md5::Context::new();
336 digest.consume(key);
337 digest.consume(&obj_num.to_le_bytes()[..3]);
338 digest.consume(gen_num.to_le_bytes());
339 if is_aes {
340 digest.consume(b"sAlT");
341 }
342 let hash = digest.compute().0;
343 let len = (key.len() + 5).min(16);
344 hash[..len].to_vec()
345}
346
347fn rc4(key: &[u8], data: &[u8]) -> Vec<u8> {
349 let mut s: Vec<u8> = (0..=255u8).collect();
350 let mut j: u8 = 0;
351 for i in 0..256 {
352 j = j.wrapping_add(s[i]).wrapping_add(key[i % key.len()]);
353 s.swap(i, j as usize);
354 }
355
356 let mut i: u8 = 0;
357 j = 0;
358 let mut result = Vec::with_capacity(data.len());
359 for &byte in data {
360 i = i.wrapping_add(1);
361 j = j.wrapping_add(s[i as usize]);
362 s.swap(i as usize, j as usize);
363 let k = s[(s[i as usize].wrapping_add(s[j as usize])) as usize];
364 result.push(byte ^ k);
365 }
366 result
367}
368
369fn aes_cbc_decrypt_no_pad(key: &[u8], iv: &[u8], data: &[u8]) -> Vec<u8> {
371 if data.is_empty() || !data.len().is_multiple_of(16) {
372 return data.to_vec();
373 }
374 let mut prev_block = [0u8; 16];
375 let iv_len = iv.len().min(16);
376 prev_block[..iv_len].copy_from_slice(&iv[..iv_len]);
377 let round_keys = aes_key_expansion(key);
378 let mut result = Vec::with_capacity(data.len());
379 for chunk in data.chunks(16) {
380 let decrypted = aes_decrypt_block(chunk, &round_keys);
381 for i in 0..16 {
382 result.push(decrypted[i] ^ prev_block[i]);
383 }
384 prev_block.copy_from_slice(chunk);
385 }
386 result
387}
388
389fn aes_cbc_decrypt(key: &[u8], iv: &[u8], data: &[u8]) -> Result<Vec<u8>, PdfError> {
391 if data.is_empty() {
392 return Ok(Vec::new());
393 }
394 if !data.len().is_multiple_of(16) {
395 return Err(PdfError::Other("AES data not block-aligned".into()));
396 }
397
398 let mut prev_block = [0u8; 16];
399 let iv_len = iv.len().min(16);
400 prev_block[..iv_len].copy_from_slice(&iv[..iv_len]);
401
402 let round_keys = aes_key_expansion(key);
403
404 let mut result = Vec::with_capacity(data.len());
405 for chunk in data.chunks(16) {
406 let decrypted = aes_decrypt_block(chunk, &round_keys);
407 for i in 0..16 {
408 result.push(decrypted[i] ^ prev_block[i]);
409 }
410 prev_block.copy_from_slice(chunk);
411 }
412
413 if let Some(&pad_len) = result.last() {
415 let pad_len = pad_len as usize;
416 if pad_len > 0
417 && pad_len <= 16
418 && result.len() >= pad_len
419 && result[result.len() - pad_len..]
420 .iter()
421 .all(|&b| b == pad_len as u8)
422 {
423 result.truncate(result.len() - pad_len);
424 }
425 }
426
427 Ok(result)
428}
429
430fn aes_cbc_encrypt(key: &[u8], iv: &[u8], data: &[u8]) -> Vec<u8> {
432 let round_keys = aes_key_expansion(key);
433 let mut prev = [0u8; 16];
434 let iv_len = iv.len().min(16);
435 prev[..iv_len].copy_from_slice(&iv[..iv_len]);
436
437 let mut result = Vec::with_capacity(data.len());
438 for chunk in data.chunks(16) {
439 let mut block = [0u8; 16];
440 let n = chunk.len().min(16);
441 block[..n].copy_from_slice(&chunk[..n]);
442 for i in 0..16 {
443 block[i] ^= prev[i];
444 }
445 let encrypted = aes_encrypt_block(&block, &round_keys);
446 result.extend_from_slice(&encrypted);
447 prev = encrypted;
448 }
449 result
450}
451
452fn aes_encrypt_block(block: &[u8; 16], round_keys: &[[u8; 16]]) -> [u8; 16] {
454 let nr = round_keys.len() - 1;
455 let mut state = *block;
456
457 for i in 0..16 {
459 state[i] ^= round_keys[0][i];
460 }
461
462 for round in 1..nr {
463 for b in &mut state {
464 *b = SBOX[*b as usize];
465 }
466 shift_rows(&mut state);
467 mix_columns(&mut state);
468 for i in 0..16 {
469 state[i] ^= round_keys[round][i];
470 }
471 }
472
473 for b in &mut state {
475 *b = SBOX[*b as usize];
476 }
477 shift_rows(&mut state);
478 for i in 0..16 {
479 state[i] ^= round_keys[nr][i];
480 }
481
482 state
483}
484
485fn shift_rows(state: &mut [u8; 16]) {
486 let t = state[1];
488 state[1] = state[5];
489 state[5] = state[9];
490 state[9] = state[13];
491 state[13] = t;
492 let (t0, t1) = (state[2], state[6]);
494 state[2] = state[10];
495 state[6] = state[14];
496 state[10] = t0;
497 state[14] = t1;
498 let t = state[15];
500 state[15] = state[11];
501 state[11] = state[7];
502 state[7] = state[3];
503 state[3] = t;
504}
505
506fn mix_columns(state: &mut [u8; 16]) {
507 for col in 0..4 {
508 let i = col * 4;
509 let (s0, s1, s2, s3) = (state[i], state[i + 1], state[i + 2], state[i + 3]);
510 state[i] = gmul(0x02, s0) ^ gmul(0x03, s1) ^ s2 ^ s3;
511 state[i + 1] = s0 ^ gmul(0x02, s1) ^ gmul(0x03, s2) ^ s3;
512 state[i + 2] = s0 ^ s1 ^ gmul(0x02, s2) ^ gmul(0x03, s3);
513 state[i + 3] = gmul(0x03, s0) ^ s1 ^ s2 ^ gmul(0x02, s3);
514 }
515}
516
517fn compute_hash_r6(password: &[u8], salt: &[u8], u_key: &[u8]) -> [u8; 32] {
519 use sha2::Digest;
520
521 let mut hasher = sha2::Sha256::new();
523 hasher.update(password);
524 hasher.update(salt);
525 if !u_key.is_empty() {
526 hasher.update(u_key);
527 }
528 let initial: [u8; 32] = hasher.finalize().into();
529 let mut k: Vec<u8> = initial.to_vec();
531
532 let mut round: u32 = 0;
533 loop {
534 let segment_len = password.len() + k.len() + u_key.len();
536 let mut k1 = Vec::with_capacity(segment_len * 64);
537 for _ in 0..64 {
538 k1.extend_from_slice(password);
539 k1.extend_from_slice(&k);
540 if !u_key.is_empty() {
541 k1.extend_from_slice(u_key);
542 }
543 }
544
545 let e = aes_cbc_encrypt(&k[..16], &k[16..32], &k1);
547
548 let mut mod3: u32 = 0;
550 for &b in &e[..16] {
551 mod3 = (mod3 * 256 + b as u32) % 3;
552 }
553
554 k = match mod3 {
556 0 => {
557 let mut h = sha2::Sha256::new();
558 h.update(&e);
559 h.finalize().to_vec()
560 }
561 1 => {
562 let mut h = sha2::Sha384::new();
563 h.update(&e);
564 h.finalize().to_vec()
565 }
566 _ => {
567 let mut h = sha2::Sha512::new();
568 h.update(&e);
569 h.finalize().to_vec()
570 }
571 };
572
573 round += 1;
575 let last_byte = *e.last().unwrap_or(&0);
576 if round >= 64 && (last_byte as u32) <= round - 32 {
577 break;
578 }
579 if round > 1000 {
580 break;
581 }
582 }
583
584 let mut result = [0u8; 32];
585 result.copy_from_slice(&k[..32]);
586 result
587}
588
589fn sha256(inputs: &[&[u8]]) -> [u8; 32] {
591 sha256_impl(inputs)
592}
593
594const SBOX: [u8; 256] = [
598 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
599 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
600 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
601 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
602 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
603 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
604 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
605 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
606 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
607 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
608 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
609 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
610 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
611 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
612 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
613 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
614];
615
616const INV_SBOX: [u8; 256] = [
617 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
618 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
619 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
620 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
621 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
622 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
623 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
624 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
625 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
626 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
627 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
628 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
629 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
630 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
631 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
632 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
633];
634
635const RCON: [u8; 11] = [
636 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
637];
638
639const PASSWORD_PADDING: [u8; 32] = [
640 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
641 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A,
642];
643
644fn pad_password(password: &[u8]) -> [u8; 32] {
645 let mut padded = [0u8; 32];
646 let len = password.len().min(32);
647 padded[..len].copy_from_slice(&password[..len]);
648 let pad_len = 32 - len;
649 padded[len..].copy_from_slice(&PASSWORD_PADDING[..pad_len]);
650 padded
651}
652
653fn gmul(a: u8, b: u8) -> u8 {
655 let mut result = 0u8;
656 let mut a = a;
657 let mut b = b;
658 for _ in 0..8 {
659 if b & 1 != 0 {
660 result ^= a;
661 }
662 let hi = a & 0x80;
663 a <<= 1;
664 if hi != 0 {
665 a ^= 0x1b;
666 }
667 b >>= 1;
668 }
669 result
670}
671
672fn aes_key_expansion(key: &[u8]) -> Vec<[u8; 16]> {
673 let nk = key.len() / 4; let nr = nk + 6; let nw = 4 * (nr + 1);
676
677 let mut w = vec![0u32; nw];
678 for i in 0..nk {
679 w[i] = u32::from_be_bytes([key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]]);
680 }
681
682 for i in nk..nw {
683 let mut temp = w[i - 1];
684 if i % nk == 0 {
685 temp = temp.rotate_left(8);
687 let bytes = temp.to_be_bytes();
688 temp = u32::from_be_bytes([
689 SBOX[bytes[0] as usize],
690 SBOX[bytes[1] as usize],
691 SBOX[bytes[2] as usize],
692 SBOX[bytes[3] as usize],
693 ]);
694 temp ^= (RCON[i / nk] as u32) << 24;
695 } else if nk > 6 && i % nk == 4 {
696 let bytes = temp.to_be_bytes();
697 temp = u32::from_be_bytes([
698 SBOX[bytes[0] as usize],
699 SBOX[bytes[1] as usize],
700 SBOX[bytes[2] as usize],
701 SBOX[bytes[3] as usize],
702 ]);
703 }
704 w[i] = w[i - nk] ^ temp;
705 }
706
707 let mut round_keys = Vec::with_capacity(nr + 1);
709 for i in 0..=nr {
710 let mut rk = [0u8; 16];
711 for j in 0..4 {
712 let bytes = w[4 * i + j].to_be_bytes();
713 rk[4 * j..4 * j + 4].copy_from_slice(&bytes);
714 }
715 round_keys.push(rk);
716 }
717 round_keys
718}
719
720fn aes_decrypt_block(block: &[u8], round_keys: &[[u8; 16]]) -> [u8; 16] {
721 let nr = round_keys.len() - 1;
722 let mut state = [0u8; 16];
723 state.copy_from_slice(&block[..16]);
724
725 for i in 0..16 {
727 state[i] ^= round_keys[nr][i];
728 }
729
730 for round in (1..nr).rev() {
731 inv_shift_rows(&mut state);
732 for b in &mut state {
733 *b = INV_SBOX[*b as usize];
734 }
735 for i in 0..16 {
736 state[i] ^= round_keys[round][i];
737 }
738 inv_mix_columns(&mut state);
739 }
740
741 inv_shift_rows(&mut state);
743 for b in &mut state {
744 *b = INV_SBOX[*b as usize];
745 }
746 for i in 0..16 {
747 state[i] ^= round_keys[0][i];
748 }
749
750 state
751}
752
753fn inv_shift_rows(state: &mut [u8; 16]) {
754 let t = state[13];
756 state[13] = state[9];
757 state[9] = state[5];
758 state[5] = state[1];
759 state[1] = t;
760 let (t0, t1) = (state[2], state[6]);
762 state[2] = state[10];
763 state[6] = state[14];
764 state[10] = t0;
765 state[14] = t1;
766 let t = state[3];
768 state[3] = state[7];
769 state[7] = state[11];
770 state[11] = state[15];
771 state[15] = t;
772}
773
774fn inv_mix_columns(state: &mut [u8; 16]) {
775 for col in 0..4 {
776 let i = col * 4;
777 let (s0, s1, s2, s3) = (state[i], state[i + 1], state[i + 2], state[i + 3]);
778 state[i] = gmul(0x0e, s0) ^ gmul(0x0b, s1) ^ gmul(0x0d, s2) ^ gmul(0x09, s3);
779 state[i + 1] = gmul(0x09, s0) ^ gmul(0x0e, s1) ^ gmul(0x0b, s2) ^ gmul(0x0d, s3);
780 state[i + 2] = gmul(0x0d, s0) ^ gmul(0x09, s1) ^ gmul(0x0e, s2) ^ gmul(0x0b, s3);
781 state[i + 3] = gmul(0x0b, s0) ^ gmul(0x0d, s1) ^ gmul(0x09, s2) ^ gmul(0x0e, s3);
782 }
783}
784
785fn sha256_impl(inputs: &[&[u8]]) -> [u8; 32] {
787 let mut h: [u32; 8] = [
788 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
789 0x5be0cd19,
790 ];
791
792 const K: [u32; 64] = [
793 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
794 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
795 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
796 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
797 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
798 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
799 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
800 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
801 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
802 0xc67178f2,
803 ];
804
805 let mut message = Vec::new();
807 for input in inputs {
808 message.extend_from_slice(input);
809 }
810 let bit_len = (message.len() as u64) * 8;
811
812 message.push(0x80);
814 while message.len() % 64 != 56 {
815 message.push(0);
816 }
817 message.extend_from_slice(&bit_len.to_be_bytes());
818
819 for block in message.chunks(64) {
821 let mut w = [0u32; 64];
822 for i in 0..16 {
823 w[i] = u32::from_be_bytes([
824 block[4 * i],
825 block[4 * i + 1],
826 block[4 * i + 2],
827 block[4 * i + 3],
828 ]);
829 }
830 for i in 16..64 {
831 let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
832 let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
833 w[i] = w[i - 16]
834 .wrapping_add(s0)
835 .wrapping_add(w[i - 7])
836 .wrapping_add(s1);
837 }
838
839 let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh] = h;
840 for i in 0..64 {
841 let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
842 let ch = (e & f) ^ (!e & g);
843 let t1 = hh
844 .wrapping_add(s1)
845 .wrapping_add(ch)
846 .wrapping_add(K[i])
847 .wrapping_add(w[i]);
848 let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
849 let maj = (a & b) ^ (a & c) ^ (b & c);
850 let t2 = s0.wrapping_add(maj);
851 hh = g;
852 g = f;
853 f = e;
854 e = d.wrapping_add(t1);
855 d = c;
856 c = b;
857 b = a;
858 a = t1.wrapping_add(t2);
859 }
860 h[0] = h[0].wrapping_add(a);
861 h[1] = h[1].wrapping_add(b);
862 h[2] = h[2].wrapping_add(c);
863 h[3] = h[3].wrapping_add(d);
864 h[4] = h[4].wrapping_add(e);
865 h[5] = h[5].wrapping_add(f);
866 h[6] = h[6].wrapping_add(g);
867 h[7] = h[7].wrapping_add(hh);
868 }
869
870 let mut result = [0u8; 32];
871 for (i, &val) in h.iter().enumerate() {
872 result[4 * i..4 * i + 4].copy_from_slice(&val.to_be_bytes());
873 }
874 result
875}
876
877#[cfg(test)]
878mod tests {
879 use super::*;
880
881 #[test]
882 fn rc4_roundtrip() {
883 let key = b"secret";
884 let plaintext = b"Hello, World!";
885 let encrypted = rc4(key, plaintext);
886 let decrypted = rc4(key, &encrypted);
887 assert_eq!(&decrypted, plaintext);
888 }
889
890 #[test]
891 fn aes128_known_vector() {
892 let key = [
894 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
895 0x4f, 0x3c,
896 ];
897 let plaintext = [
898 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37,
899 0x07, 0x34,
900 ];
901 let expected_cipher = [
902 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a,
903 0x0b, 0x32,
904 ];
905
906 let round_keys = aes_key_expansion(&key);
908 let decrypted = aes_decrypt_block(&expected_cipher, &round_keys);
909 assert_eq!(decrypted, plaintext);
910 }
911
912 #[test]
913 fn sha256_empty() {
914 let hash = sha256(&[b""]);
915 let expected = [
917 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
918 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
919 0x78, 0x52, 0xb8, 0x55,
920 ];
921 assert_eq!(hash, expected);
922 }
923
924 #[test]
925 fn sha256_abc() {
926 let hash = sha256(&[b"abc"]);
927 let expected = [
928 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae,
929 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61,
930 0xf2, 0x00, 0x15, 0xad,
931 ];
932 assert_eq!(hash, expected);
933 }
934
935 #[test]
936 fn password_padding() {
937 let padded = pad_password(b"");
938 assert_eq!(padded, PASSWORD_PADDING);
939
940 let padded = pad_password(b"test");
941 assert_eq!(&padded[..4], b"test");
942 assert_eq!(&padded[4..], &PASSWORD_PADDING[..28]);
943 }
944
945 #[test]
946 fn r6_hash_user_password() {
947 let validation_salt = [0x94, 0x92, 0x7c, 0x84, 0x96, 0xaf, 0xd8, 0x92];
949 let expected_hash = [
950 0x20, 0x23, 0xa0, 0xa6, 0x05, 0x6b, 0x38, 0x54, 0x42, 0xb0, 0xcb, 0x1a, 0x16, 0x5c,
951 0xb4, 0xf9, 0x29, 0x31, 0xec, 0xd9, 0xaf, 0x4b, 0xd5, 0xc2, 0x32, 0xdf, 0xb6, 0xbc,
952 0x8b, 0xe7, 0xb4, 0x05,
953 ];
954 let result = compute_hash_r6(b"", &validation_salt, b"");
955 assert_eq!(result, expected_hash, "R=6 validation hash mismatch");
956 }
957
958 #[test]
959 fn r6_key_derivation() {
960 let key_salt = [0xa2, 0x33, 0xb1, 0x19, 0xb7, 0x3f, 0xe6, 0xc2];
962 let expected_key_hash: [u8; 32] = [
963 0x0b, 0xdb, 0xaf, 0x8c, 0xa6, 0x25, 0x6c, 0xcf, 0xb1, 0xba, 0x29, 0x4d, 0xeb, 0x54,
964 0xc3, 0x83, 0xfd, 0xcc, 0x11, 0x90, 0xcf, 0x6c, 0xf5, 0x9d, 0x4a, 0x7b, 0xc0, 0x38,
965 0x4f, 0x73, 0x9a, 0x62,
966 ];
967 let result = compute_hash_r6(b"", &key_salt, b"");
968 assert_eq!(result, expected_key_hash, "R=6 key hash mismatch");
969 }
970
971 #[test]
972 fn aes_cbc_encrypt_known_vector() {
973 let key = [0u8; 16];
975 let iv = [0u8; 16];
976 let data = [0u8; 32];
977 let result = aes_cbc_encrypt(&key, &iv, &data);
978 let expected = "66e94bd4ef8a2c3b884cfa59ca342b2ef795bd4a52e29ed713d313fa20e98dbc";
979 let got: String = result.iter().map(|b| format!("{:02x}", b)).collect();
980 assert_eq!(got, expected);
981 }
982
983 #[test]
984 fn aes_cbc_roundtrip() {
985 let key = [0u8; 16]; let iv = [0u8; 16]; let ciphertext = [0u8; 16];
992 let result = aes_cbc_decrypt(&key, &iv, &ciphertext);
993 assert!(result.is_ok());
994 }
995}