1use rustls::pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer};
15use tracing::debug;
16
17use crate::error::{NetError, NetResult};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum EncryptedPemFormat {
26 NotEncrypted,
28 Pkcs8Encrypted,
30 LegacyEncrypted,
32}
33
34pub fn detect_encrypted_pem(pem_str: &str) -> EncryptedPemFormat {
36 if pem_str.contains("-----BEGIN ENCRYPTED PRIVATE KEY-----") {
37 EncryptedPemFormat::Pkcs8Encrypted
38 } else if pem_str.contains("Proc-Type: 4,ENCRYPTED") {
39 EncryptedPemFormat::LegacyEncrypted
40 } else {
41 EncryptedPemFormat::NotEncrypted
42 }
43}
44
45pub(crate) fn resolve_password(password: &str) -> NetResult<String> {
47 if !password.is_empty() {
48 return Ok(password.to_string());
49 }
50
51 match std::env::var("AMATERS_KEY_PASSWORD") {
52 Ok(env_pw) if !env_pw.is_empty() => {
53 debug!("Using password from AMATERS_KEY_PASSWORD environment variable");
54 Ok(env_pw)
55 }
56 _ => Err(NetError::InvalidCertificate(
57 "Key is encrypted but no password provided. Set AMATERS_KEY_PASSWORD or pass a password."
58 .to_string(),
59 )),
60 }
61}
62
63pub fn parse_dek_info(pem_str: &str) -> NetResult<(String, Vec<u8>)> {
67 for line in pem_str.lines() {
68 let trimmed = line.trim();
69 if let Some(rest) = trimmed.strip_prefix("DEK-Info:") {
70 let rest = rest.trim();
71 let parts: Vec<&str> = rest.splitn(2, ',').collect();
72 if parts.len() != 2 {
73 return Err(NetError::InvalidCertificate(
74 "Malformed DEK-Info header: expected 'algorithm,IV'".to_string(),
75 ));
76 }
77 let algorithm = parts[0].trim().to_string();
78 let iv_hex = parts[1].trim();
79 let iv = hex_decode(iv_hex).map_err(|e| {
80 NetError::InvalidCertificate(format!("Invalid IV hex in DEK-Info: {e}"))
81 })?;
82 return Ok((algorithm, iv));
83 }
84 }
85 Err(NetError::InvalidCertificate(
86 "No DEK-Info header found in legacy encrypted PEM".to_string(),
87 ))
88}
89
90pub(crate) fn decrypt_pkcs8_encrypted_pem(
99 pem_str: &str,
100 password: &str,
101) -> NetResult<PrivateKeyDer<'static>> {
102 let der_data = extract_pem_body(pem_str, "ENCRYPTED PRIVATE KEY")?;
103 let decrypted = decrypt_pkcs8_der(&der_data, password.as_bytes())?;
104 Ok(PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(decrypted)))
105}
106
107pub(crate) fn decrypt_legacy_encrypted_pem(
109 pem_str: &str,
110 password: &str,
111) -> NetResult<PrivateKeyDer<'static>> {
112 let (algorithm, iv) = parse_dek_info(pem_str)?;
113
114 let key_len = match algorithm.as_str() {
116 "AES-256-CBC" => 32,
117 "AES-128-CBC" => 16,
118 "AES-192-CBC" => 24,
119 "DES-EDE3-CBC" => 24,
120 other => {
121 return Err(NetError::InvalidCertificate(format!(
122 "Unsupported encryption algorithm: {other}"
123 )));
124 }
125 };
126
127 let body = extract_legacy_pem_body(pem_str)?;
129
130 let derived_key = evp_bytes_to_key_md5(password.as_bytes(), &iv[..8], key_len);
132
133 let decrypted = if algorithm.starts_with("AES") {
135 aes_cbc_decrypt(&body, &derived_key, &iv)?
136 } else if algorithm == "DES-EDE3-CBC" {
137 return Err(NetError::InvalidCertificate(
138 "DES-EDE3-CBC is not supported in pure Rust mode. Please re-encrypt with AES-256-CBC."
139 .to_string(),
140 ));
141 } else {
142 return Err(NetError::InvalidCertificate(format!(
143 "Unsupported encryption algorithm: {algorithm}"
144 )));
145 };
146
147 let unpadded = remove_pkcs7_padding(&decrypted)?;
149
150 if is_pkcs8_key_der(unpadded) {
154 Ok(PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(
155 unpadded.to_vec(),
156 )))
157 } else {
158 Ok(PrivateKeyDer::Pkcs1(unpadded.to_vec().into()))
160 }
161}
162
163fn extract_pem_body(pem_str: &str, label: &str) -> NetResult<Vec<u8>> {
165 let begin_marker = format!("-----BEGIN {label}-----");
166 let end_marker = format!("-----END {label}-----");
167
168 let start = pem_str.find(&begin_marker).ok_or_else(|| {
169 NetError::InvalidCertificate(format!("Missing PEM header: {begin_marker}"))
170 })? + begin_marker.len();
171
172 let end = pem_str
173 .find(&end_marker)
174 .ok_or_else(|| NetError::InvalidCertificate(format!("Missing PEM footer: {end_marker}")))?;
175
176 let body = &pem_str[start..end];
177 let b64: String = body.chars().filter(|c| !c.is_whitespace()).collect();
178 base64_decode_pure(&b64)
179 .map_err(|e| NetError::InvalidCertificate(format!("Invalid base64 in PEM: {e}")))
180}
181
182fn extract_legacy_pem_body(pem_str: &str) -> NetResult<Vec<u8>> {
184 let mut in_body = false;
185 let mut past_headers = false;
186 let mut b64 = String::new();
187
188 for line in pem_str.lines() {
189 let trimmed = line.trim();
190 if trimmed.starts_with("-----BEGIN ") && trimmed.ends_with("-----") {
191 in_body = true;
192 continue;
193 }
194 if trimmed.starts_with("-----END ") && trimmed.ends_with("-----") {
195 break;
196 }
197 if !in_body {
198 continue;
199 }
200 if trimmed.starts_with("Proc-Type:") || trimmed.starts_with("DEK-Info:") {
202 continue;
203 }
204 if !past_headers && trimmed.is_empty() {
206 past_headers = true;
207 continue;
208 }
209 past_headers = true;
210 b64.push_str(trimmed);
211 }
212
213 base64_decode_pure(&b64)
214 .map_err(|e| NetError::InvalidCertificate(format!("Invalid base64 in legacy PEM: {e}")))
215}
216
217fn is_pkcs8_key_der(data: &[u8]) -> bool {
219 if data.len() < 4 {
222 return false;
223 }
224 if data[0] != 0x30 {
226 return false;
227 }
228 let (_, content_offset) = match parse_asn1_length(&data[1..]) {
230 Ok(v) => v,
231 Err(_) => return false,
232 };
233 let content_start = 1 + content_offset;
234 if content_start >= data.len() {
235 return false;
236 }
237 if data[content_start] == 0x02 {
239 let ver_start = content_start + 1;
241 if ver_start < data.len() {
242 let (ver_len, ver_len_size) = match parse_asn1_length(&data[ver_start..]) {
243 Ok(v) => v,
244 Err(_) => return false,
245 };
246 let after_ver = ver_start + ver_len_size + ver_len;
247 if after_ver < data.len() && data[after_ver] == 0x30 {
248 return true;
249 }
250 }
251 }
252 false
253}
254
255pub(crate) fn parse_asn1_length(data: &[u8]) -> NetResult<(usize, usize)> {
261 if data.is_empty() {
262 return Err(NetError::InvalidCertificate(
263 "ASN.1 parse error: unexpected end of data for length".to_string(),
264 ));
265 }
266 if data[0] < 0x80 {
267 Ok((data[0] as usize, 1))
268 } else if data[0] == 0x80 {
269 Err(NetError::InvalidCertificate(
270 "ASN.1 indefinite length not supported".to_string(),
271 ))
272 } else {
273 let num_bytes = (data[0] & 0x7f) as usize;
274 if num_bytes > 4 || num_bytes + 1 > data.len() {
275 return Err(NetError::InvalidCertificate(
276 "ASN.1 parse error: length too large or truncated".to_string(),
277 ));
278 }
279 let mut length = 0usize;
280 for &b in &data[1..=num_bytes] {
281 length = length
282 .checked_shl(8)
283 .ok_or_else(|| NetError::InvalidCertificate("ASN.1 length overflow".to_string()))?
284 .checked_add(b as usize)
285 .ok_or_else(|| NetError::InvalidCertificate("ASN.1 length overflow".to_string()))?;
286 }
287 Ok((length, num_bytes + 1))
288 }
289}
290
291fn skip_asn1_tlv(data: &[u8]) -> NetResult<usize> {
293 if data.is_empty() {
294 return Err(NetError::InvalidCertificate(
295 "ASN.1 parse error: empty TLV".to_string(),
296 ));
297 }
298 let (len, len_size) = parse_asn1_length(&data[1..])?;
299 Ok(1 + len_size + len)
300}
301
302fn decrypt_pkcs8_der(data: &[u8], password: &[u8]) -> NetResult<Vec<u8>> {
325 let mut pos = 0;
326
327 if data.get(pos) != Some(&0x30) {
329 return Err(NetError::InvalidCertificate(
330 "PKCS#8 encrypted: expected outer SEQUENCE".to_string(),
331 ));
332 }
333 pos += 1;
334 let (_outer_len, outer_len_size) = parse_asn1_length(&data[pos..])?;
335 pos += outer_len_size;
336
337 if data.get(pos) != Some(&0x30) {
339 return Err(NetError::InvalidCertificate(
340 "PKCS#8 encrypted: expected algorithm SEQUENCE".to_string(),
341 ));
342 }
343 pos += 1;
344 let (algo_seq_len, algo_len_size) = parse_asn1_length(&data[pos..])?;
345 pos += algo_len_size;
346 let algo_seq_end = pos + algo_seq_len;
347
348 let pbes2_oid = parse_asn1_oid(&data[pos..])?;
350 pos += skip_asn1_tlv(&data[pos..])?;
351
352 if pbes2_oid != [42, 134, 72, 134, 247, 13, 1, 5, 13] {
354 return Err(NetError::InvalidCertificate(format!(
355 "Unsupported encryption algorithm OID (expected PBES2): {:?}",
356 pbes2_oid
357 )));
358 }
359
360 if data.get(pos) != Some(&0x30) {
362 return Err(NetError::InvalidCertificate(
363 "PKCS#8 encrypted: expected PBES2-params SEQUENCE".to_string(),
364 ));
365 }
366 pos += 1;
367 let (_pbes2_len, pbes2_len_size) = parse_asn1_length(&data[pos..])?;
368 pos += pbes2_len_size;
369
370 if data.get(pos) != Some(&0x30) {
372 return Err(NetError::InvalidCertificate(
373 "PKCS#8 encrypted: expected KDF SEQUENCE".to_string(),
374 ));
375 }
376 pos += 1;
377 let (kdf_seq_len, kdf_len_size) = parse_asn1_length(&data[pos..])?;
378 pos += kdf_len_size;
379 let kdf_seq_end = pos + kdf_seq_len;
380
381 let pbkdf2_oid = parse_asn1_oid(&data[pos..])?;
383 pos += skip_asn1_tlv(&data[pos..])?;
384
385 if pbkdf2_oid != [42, 134, 72, 134, 247, 13, 1, 5, 12] {
387 return Err(NetError::InvalidCertificate(format!(
388 "Unsupported KDF OID (expected PBKDF2): {:?}",
389 pbkdf2_oid
390 )));
391 }
392
393 if data.get(pos) != Some(&0x30) {
395 return Err(NetError::InvalidCertificate(
396 "PKCS#8 encrypted: expected PBKDF2-params SEQUENCE".to_string(),
397 ));
398 }
399 pos += 1;
400 let (pbkdf2_params_len, pbkdf2_params_len_size) = parse_asn1_length(&data[pos..])?;
401 pos += pbkdf2_params_len_size;
402 let pbkdf2_params_end = pos + pbkdf2_params_len;
403
404 let salt = parse_asn1_octet_string(&data[pos..])?;
406 pos += skip_asn1_tlv(&data[pos..])?;
407
408 let iterations = parse_asn1_integer_value(&data[pos..])?;
410 pos += skip_asn1_tlv(&data[pos..])?;
411
412 let mut prf_is_sha256 = false;
415 while pos < pbkdf2_params_end {
416 let tag = data.get(pos).copied().unwrap_or(0);
417 if tag == 0x02 {
418 pos += skip_asn1_tlv(&data[pos..])?;
420 } else if tag == 0x30 {
421 let prf_inner_start = pos + 1;
423 let (prf_seq_len, prf_seq_len_size) = parse_asn1_length(&data[prf_inner_start..])?;
424 let prf_content_start = prf_inner_start + prf_seq_len_size;
425 let prf_oid = parse_asn1_oid(&data[prf_content_start..])?;
426 if prf_oid == [42, 134, 72, 134, 247, 13, 2, 9] {
428 prf_is_sha256 = true;
429 }
430 pos += skip_asn1_tlv(&data[pos..])?;
432 } else {
433 pos += skip_asn1_tlv(&data[pos..])?;
434 }
435 }
436 pos = kdf_seq_end;
437
438 if data.get(pos) != Some(&0x30) {
440 return Err(NetError::InvalidCertificate(
441 "PKCS#8 encrypted: expected encryption scheme SEQUENCE".to_string(),
442 ));
443 }
444 pos += 1;
445 let (enc_seq_len, enc_len_size) = parse_asn1_length(&data[pos..])?;
446 pos += enc_len_size;
447
448 let enc_oid = parse_asn1_oid(&data[pos..])?;
449 pos += skip_asn1_tlv(&data[pos..])?;
450
451 let key_len = match enc_oid.as_slice() {
456 [96, 134, 72, 1, 101, 3, 4, 1, 2] => 16,
457 [96, 134, 72, 1, 101, 3, 4, 1, 22] => 24,
458 [96, 134, 72, 1, 101, 3, 4, 1, 42] => 32,
459 _ => {
460 return Err(NetError::InvalidCertificate(format!(
461 "Unsupported encryption scheme OID: {:?}",
462 enc_oid
463 )));
464 }
465 };
466
467 let iv = parse_asn1_octet_string(&data[pos..])?;
469
470 let pos = algo_seq_end;
472
473 let encrypted_data = parse_asn1_octet_string(&data[pos..])?;
475
476 let derived_key = if prf_is_sha256 {
478 pbkdf2_hmac_sha256(password, &salt, iterations as u32, key_len)
479 } else {
480 pbkdf2_hmac_sha1(password, &salt, iterations as u32, key_len)
481 };
482
483 let decrypted = aes_cbc_decrypt(&encrypted_data, &derived_key, &iv)?;
485
486 let unpadded = remove_pkcs7_padding(&decrypted)?;
488
489 Ok(unpadded.to_vec())
490}
491
492fn parse_asn1_oid(data: &[u8]) -> NetResult<Vec<u8>> {
494 if data.is_empty() || data[0] != 0x06 {
495 return Err(NetError::InvalidCertificate(
496 "ASN.1 parse error: expected OID tag (0x06)".to_string(),
497 ));
498 }
499 let (len, len_size) = parse_asn1_length(&data[1..])?;
500 let start = 1 + len_size;
501 let end = start + len;
502 if end > data.len() {
503 return Err(NetError::InvalidCertificate(
504 "ASN.1 parse error: OID data truncated".to_string(),
505 ));
506 }
507 Ok(data[start..end].to_vec())
508}
509
510fn parse_asn1_octet_string(data: &[u8]) -> NetResult<Vec<u8>> {
512 if data.is_empty() || data[0] != 0x04 {
513 return Err(NetError::InvalidCertificate(
514 "ASN.1 parse error: expected OCTET STRING tag (0x04)".to_string(),
515 ));
516 }
517 let (len, len_size) = parse_asn1_length(&data[1..])?;
518 let start = 1 + len_size;
519 let end = start + len;
520 if end > data.len() {
521 return Err(NetError::InvalidCertificate(
522 "ASN.1 parse error: OCTET STRING data truncated".to_string(),
523 ));
524 }
525 Ok(data[start..end].to_vec())
526}
527
528fn parse_asn1_integer_value(data: &[u8]) -> NetResult<usize> {
530 if data.is_empty() || data[0] != 0x02 {
531 return Err(NetError::InvalidCertificate(
532 "ASN.1 parse error: expected INTEGER tag (0x02)".to_string(),
533 ));
534 }
535 let (len, len_size) = parse_asn1_length(&data[1..])?;
536 let start = 1 + len_size;
537 let end = start + len;
538 if end > data.len() {
539 return Err(NetError::InvalidCertificate(
540 "ASN.1 parse error: INTEGER data truncated".to_string(),
541 ));
542 }
543 let mut value = 0usize;
544 for &b in &data[start..end] {
545 value = value
546 .checked_shl(8)
547 .ok_or_else(|| NetError::InvalidCertificate("ASN.1 INTEGER overflow".to_string()))?
548 .checked_add(b as usize)
549 .ok_or_else(|| NetError::InvalidCertificate("ASN.1 INTEGER overflow".to_string()))?;
550 }
551 Ok(value)
552}
553
554pub fn pbkdf2_hmac_sha256(
560 password: &[u8],
561 salt: &[u8],
562 iterations: u32,
563 key_len: usize,
564) -> Vec<u8> {
565 let mut result = Vec::with_capacity(key_len);
566 let mut block_num = 1u32;
567
568 while result.len() < key_len {
569 let block = pbkdf2_f_sha256(password, salt, iterations, block_num);
570 let needed = key_len - result.len();
571 result.extend_from_slice(&block[..needed.min(32)]);
572 block_num += 1;
573 }
574
575 result.truncate(key_len);
576 result
577}
578
579fn pbkdf2_f_sha256(password: &[u8], salt: &[u8], iterations: u32, block_num: u32) -> [u8; 32] {
581 let mut msg = Vec::with_capacity(salt.len() + 4);
583 msg.extend_from_slice(salt);
584 msg.extend_from_slice(&block_num.to_be_bytes());
585
586 let mut u_prev = hmac_sha256(password, &msg);
587 let mut result = u_prev;
588
589 for _ in 1..iterations {
590 let u_curr = hmac_sha256(password, &u_prev);
591 for (r, c) in result.iter_mut().zip(u_curr.iter()) {
592 *r ^= c;
593 }
594 u_prev = u_curr;
595 }
596
597 result
598}
599
600fn hmac_sha256(key: &[u8], message: &[u8]) -> [u8; 32] {
602 const BLOCK_SIZE: usize = 64;
603
604 let normalized_key = if key.len() > BLOCK_SIZE {
605 let h = sha256(key);
606 let mut k = [0u8; BLOCK_SIZE];
607 k[..32].copy_from_slice(&h);
608 k
609 } else {
610 let mut k = [0u8; BLOCK_SIZE];
611 k[..key.len()].copy_from_slice(key);
612 k
613 };
614
615 let mut ipad = [0x36u8; BLOCK_SIZE];
616 let mut opad = [0x5cu8; BLOCK_SIZE];
617 for i in 0..BLOCK_SIZE {
618 ipad[i] ^= normalized_key[i];
619 opad[i] ^= normalized_key[i];
620 }
621
622 let mut inner_msg = Vec::with_capacity(BLOCK_SIZE + message.len());
624 inner_msg.extend_from_slice(&ipad);
625 inner_msg.extend_from_slice(message);
626 let inner_hash = sha256(&inner_msg);
627
628 let mut outer_msg = Vec::with_capacity(BLOCK_SIZE + 32);
630 outer_msg.extend_from_slice(&opad);
631 outer_msg.extend_from_slice(&inner_hash);
632 sha256(&outer_msg)
633}
634
635pub(crate) fn sha256(data: &[u8]) -> [u8; 32] {
637 const K: [u32; 64] = [
638 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
639 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
640 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
641 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
642 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
643 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
644 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
645 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
646 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
647 0xc67178f2,
648 ];
649
650 let mut h: [u32; 8] = [
651 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
652 0x5be0cd19,
653 ];
654
655 let bit_len = (data.len() as u64) * 8;
657 let mut padded = data.to_vec();
658 padded.push(0x80);
659 while (padded.len() % 64) != 56 {
660 padded.push(0x00);
661 }
662 padded.extend_from_slice(&bit_len.to_be_bytes());
663
664 for chunk in padded.chunks_exact(64) {
666 let mut w = [0u32; 64];
667 for i in 0..16 {
668 w[i] = u32::from_be_bytes([
669 chunk[i * 4],
670 chunk[i * 4 + 1],
671 chunk[i * 4 + 2],
672 chunk[i * 4 + 3],
673 ]);
674 }
675 for i in 16..64 {
676 let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
677 let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
678 w[i] = w[i - 16]
679 .wrapping_add(s0)
680 .wrapping_add(w[i - 7])
681 .wrapping_add(s1);
682 }
683
684 let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh] = h;
685
686 for i in 0..64 {
687 let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
688 let ch = (e & f) ^ ((!e) & g);
689 let temp1 = hh
690 .wrapping_add(s1)
691 .wrapping_add(ch)
692 .wrapping_add(K[i])
693 .wrapping_add(w[i]);
694 let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
695 let maj = (a & b) ^ (a & c) ^ (b & c);
696 let temp2 = s0.wrapping_add(maj);
697
698 hh = g;
699 g = f;
700 f = e;
701 e = d.wrapping_add(temp1);
702 d = c;
703 c = b;
704 b = a;
705 a = temp1.wrapping_add(temp2);
706 }
707
708 h[0] = h[0].wrapping_add(a);
709 h[1] = h[1].wrapping_add(b);
710 h[2] = h[2].wrapping_add(c);
711 h[3] = h[3].wrapping_add(d);
712 h[4] = h[4].wrapping_add(e);
713 h[5] = h[5].wrapping_add(f);
714 h[6] = h[6].wrapping_add(g);
715 h[7] = h[7].wrapping_add(hh);
716 }
717
718 let mut result = [0u8; 32];
719 for (i, val) in h.iter().enumerate() {
720 result[i * 4..i * 4 + 4].copy_from_slice(&val.to_be_bytes());
721 }
722 result
723}
724
725pub fn pbkdf2_hmac_sha1(password: &[u8], salt: &[u8], iterations: u32, key_len: usize) -> Vec<u8> {
727 let mut result = Vec::with_capacity(key_len);
728 let mut block_num = 1u32;
729
730 while result.len() < key_len {
731 let block = pbkdf2_f_sha1(password, salt, iterations, block_num);
732 let needed = key_len - result.len();
733 result.extend_from_slice(&block[..needed.min(20)]);
734 block_num += 1;
735 }
736
737 result.truncate(key_len);
738 result
739}
740
741fn pbkdf2_f_sha1(password: &[u8], salt: &[u8], iterations: u32, block_num: u32) -> [u8; 20] {
743 let mut msg = Vec::with_capacity(salt.len() + 4);
744 msg.extend_from_slice(salt);
745 msg.extend_from_slice(&block_num.to_be_bytes());
746
747 let mut u_prev = hmac_sha1(password, &msg);
748 let mut result = u_prev;
749
750 for _ in 1..iterations {
751 let u_curr = hmac_sha1(password, &u_prev);
752 for (r, c) in result.iter_mut().zip(u_curr.iter()) {
753 *r ^= c;
754 }
755 u_prev = u_curr;
756 }
757
758 result
759}
760
761fn hmac_sha1(key: &[u8], message: &[u8]) -> [u8; 20] {
763 const BLOCK_SIZE: usize = 64;
764
765 let normalized_key = if key.len() > BLOCK_SIZE {
766 let h = sha1(key);
767 let mut k = [0u8; BLOCK_SIZE];
768 k[..20].copy_from_slice(&h);
769 k
770 } else {
771 let mut k = [0u8; BLOCK_SIZE];
772 k[..key.len()].copy_from_slice(key);
773 k
774 };
775
776 let mut ipad = [0x36u8; BLOCK_SIZE];
777 let mut opad = [0x5cu8; BLOCK_SIZE];
778 for i in 0..BLOCK_SIZE {
779 ipad[i] ^= normalized_key[i];
780 opad[i] ^= normalized_key[i];
781 }
782
783 let mut inner_msg = Vec::with_capacity(BLOCK_SIZE + message.len());
784 inner_msg.extend_from_slice(&ipad);
785 inner_msg.extend_from_slice(message);
786 let inner_hash = sha1(&inner_msg);
787
788 let mut outer_msg = Vec::with_capacity(BLOCK_SIZE + 20);
789 outer_msg.extend_from_slice(&opad);
790 outer_msg.extend_from_slice(&inner_hash);
791 sha1(&outer_msg)
792}
793
794pub(crate) fn sha1(data: &[u8]) -> [u8; 20] {
796 let mut h0: u32 = 0x67452301;
797 let mut h1: u32 = 0xEFCDAB89;
798 let mut h2: u32 = 0x98BADCFE;
799 let mut h3: u32 = 0x10325476;
800 let mut h4: u32 = 0xC3D2E1F0;
801
802 let bit_len = (data.len() as u64) * 8;
803 let mut padded = data.to_vec();
804 padded.push(0x80);
805 while (padded.len() % 64) != 56 {
806 padded.push(0x00);
807 }
808 padded.extend_from_slice(&bit_len.to_be_bytes());
809
810 for chunk in padded.chunks_exact(64) {
811 let mut w = [0u32; 80];
812 for i in 0..16 {
813 w[i] = u32::from_be_bytes([
814 chunk[i * 4],
815 chunk[i * 4 + 1],
816 chunk[i * 4 + 2],
817 chunk[i * 4 + 3],
818 ]);
819 }
820 for i in 16..80 {
821 w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
822 }
823
824 let (mut a, mut b, mut c, mut d, mut e) = (h0, h1, h2, h3, h4);
825
826 for (i, w_i) in w.iter().enumerate() {
827 let (f_val, k) = match i {
828 0..=19 => ((b & c) | ((!b) & d), 0x5A827999u32),
829 20..=39 => (b ^ c ^ d, 0x6ED9EBA1u32),
830 40..=59 => ((b & c) | (b & d) | (c & d), 0x8F1BBCDCu32),
831 _ => (b ^ c ^ d, 0xCA62C1D6u32),
832 };
833 let temp = a
834 .rotate_left(5)
835 .wrapping_add(f_val)
836 .wrapping_add(e)
837 .wrapping_add(k)
838 .wrapping_add(*w_i);
839 e = d;
840 d = c;
841 c = b.rotate_left(30);
842 b = a;
843 a = temp;
844 }
845
846 h0 = h0.wrapping_add(a);
847 h1 = h1.wrapping_add(b);
848 h2 = h2.wrapping_add(c);
849 h3 = h3.wrapping_add(d);
850 h4 = h4.wrapping_add(e);
851 }
852
853 let mut result = [0u8; 20];
854 result[0..4].copy_from_slice(&h0.to_be_bytes());
855 result[4..8].copy_from_slice(&h1.to_be_bytes());
856 result[8..12].copy_from_slice(&h2.to_be_bytes());
857 result[12..16].copy_from_slice(&h3.to_be_bytes());
858 result[16..20].copy_from_slice(&h4.to_be_bytes());
859 result
860}
861
862pub(crate) fn evp_bytes_to_key_md5(password: &[u8], salt: &[u8], key_len: usize) -> Vec<u8> {
866 let mut result = Vec::with_capacity(key_len);
867 let mut d_prev: Option<[u8; 16]> = None;
868
869 while result.len() < key_len {
870 let mut input = Vec::new();
871 if let Some(prev) = d_prev {
872 input.extend_from_slice(&prev);
873 }
874 input.extend_from_slice(password);
875 input.extend_from_slice(&salt[..8.min(salt.len())]);
876 let hash = md5(&input);
877 let needed = key_len - result.len();
878 result.extend_from_slice(&hash[..needed.min(16)]);
879 d_prev = Some(hash);
880 }
881
882 result.truncate(key_len);
883 result
884}
885
886pub(crate) fn md5(data: &[u8]) -> [u8; 16] {
888 const S: [u32; 64] = [
889 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5,
890 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10,
891 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
892 ];
893 #[allow(clippy::unreadable_literal)]
894 const T: [u32; 64] = [
895 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613,
896 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193,
897 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d,
898 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
899 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122,
900 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa,
901 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244,
902 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
903 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb,
904 0xeb86d391,
905 ];
906
907 let mut a0: u32 = 0x67452301;
908 let mut b0: u32 = 0xefcdab89;
909 let mut c0: u32 = 0x98badcfe;
910 let mut d0: u32 = 0x10325476;
911
912 let bit_len = (data.len() as u64) * 8;
913 let mut padded = data.to_vec();
914 padded.push(0x80);
915 while (padded.len() % 64) != 56 {
916 padded.push(0x00);
917 }
918 padded.extend_from_slice(&bit_len.to_le_bytes());
919
920 for chunk in padded.chunks_exact(64) {
921 let mut m = [0u32; 16];
922 for i in 0..16 {
923 m[i] = u32::from_le_bytes([
924 chunk[i * 4],
925 chunk[i * 4 + 1],
926 chunk[i * 4 + 2],
927 chunk[i * 4 + 3],
928 ]);
929 }
930
931 let (mut a, mut b, mut c, mut d) = (a0, b0, c0, d0);
932
933 for i in 0..64 {
934 let (f_val, g) = match i {
935 0..=15 => ((b & c) | ((!b) & d), i),
936 16..=31 => ((d & b) | ((!d) & c), (5 * i + 1) % 16),
937 32..=47 => (b ^ c ^ d, (3 * i + 5) % 16),
938 _ => (c ^ (b | (!d)), (7 * i) % 16),
939 };
940 let f_val = f_val.wrapping_add(a).wrapping_add(T[i]).wrapping_add(m[g]);
941 a = d;
942 d = c;
943 c = b;
944 b = b.wrapping_add(f_val.rotate_left(S[i]));
945 }
946
947 a0 = a0.wrapping_add(a);
948 b0 = b0.wrapping_add(b);
949 c0 = c0.wrapping_add(c);
950 d0 = d0.wrapping_add(d);
951 }
952
953 let mut result = [0u8; 16];
954 result[0..4].copy_from_slice(&a0.to_le_bytes());
955 result[4..8].copy_from_slice(&b0.to_le_bytes());
956 result[8..12].copy_from_slice(&c0.to_le_bytes());
957 result[12..16].copy_from_slice(&d0.to_le_bytes());
958 result
959}
960
961pub(crate) const AES_SBOX: [u8; 256] = [
967 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
968 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
969 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
970 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
971 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
972 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
973 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
974 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
975 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
976 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
977 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
978 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
979 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
980 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
981 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
982 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
983];
984
985const AES_INV_SBOX: [u8; 256] = [
987 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
988 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
989 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
990 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
991 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
992 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
993 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
994 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
995 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
996 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
997 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
998 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
999 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
1000 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
1001 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
1002 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
1003];
1004
1005const AES_RCON: [u8; 11] = [
1007 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
1008];
1009
1010fn gf_mul2(a: u8) -> u8 {
1012 if a & 0x80 != 0 {
1013 (a << 1) ^ 0x1b
1014 } else {
1015 a << 1
1016 }
1017}
1018
1019pub(crate) fn gf_mul(mut a: u8, mut b: u8) -> u8 {
1021 let mut result: u8 = 0;
1022 while b != 0 {
1023 if b & 1 != 0 {
1024 result ^= a;
1025 }
1026 a = gf_mul2(a);
1027 b >>= 1;
1028 }
1029 result
1030}
1031
1032pub(crate) fn aes_key_expansion(key: &[u8]) -> NetResult<Vec<[u8; 4]>> {
1034 let nk = key.len() / 4; let nr = nk + 6; let total_words = 4 * (nr + 1);
1037
1038 let mut w: Vec<[u8; 4]> = Vec::with_capacity(total_words);
1039
1040 for i in 0..nk {
1042 w.push([key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]]);
1043 }
1044
1045 for i in nk..total_words {
1046 let mut temp = w[i - 1];
1047
1048 if i % nk == 0 {
1049 temp = [temp[1], temp[2], temp[3], temp[0]];
1051 temp = [
1053 AES_SBOX[temp[0] as usize],
1054 AES_SBOX[temp[1] as usize],
1055 AES_SBOX[temp[2] as usize],
1056 AES_SBOX[temp[3] as usize],
1057 ];
1058 let rcon_idx = i / nk;
1060 if rcon_idx >= AES_RCON.len() {
1061 return Err(NetError::InvalidCertificate(
1062 "AES key expansion: rcon index out of bounds".to_string(),
1063 ));
1064 }
1065 temp[0] ^= AES_RCON[rcon_idx];
1066 } else if nk > 6 && i % nk == 4 {
1067 temp = [
1069 AES_SBOX[temp[0] as usize],
1070 AES_SBOX[temp[1] as usize],
1071 AES_SBOX[temp[2] as usize],
1072 AES_SBOX[temp[3] as usize],
1073 ];
1074 }
1075
1076 let prev = w[i - nk];
1077 w.push([
1078 prev[0] ^ temp[0],
1079 prev[1] ^ temp[1],
1080 prev[2] ^ temp[2],
1081 prev[3] ^ temp[3],
1082 ]);
1083 }
1084
1085 Ok(w)
1086}
1087
1088fn aes_decrypt_block(block: &[u8; 16], round_keys: &[[u8; 4]]) -> [u8; 16] {
1090 let nr = round_keys.len() / 4 - 1;
1091 let mut state = [[0u8; 4]; 4];
1092
1093 for c in 0..4 {
1095 for r in 0..4 {
1096 state[r][c] = block[c * 4 + r];
1097 }
1098 }
1099
1100 for c in 0..4 {
1102 for r in 0..4 {
1103 state[r][c] ^= round_keys[nr * 4 + c][r];
1104 }
1105 }
1106
1107 for round in (1..nr).rev() {
1109 inv_shift_rows(&mut state);
1111 inv_sub_bytes(&mut state);
1113 for c in 0..4 {
1115 for r in 0..4 {
1116 state[r][c] ^= round_keys[round * 4 + c][r];
1117 }
1118 }
1119 inv_mix_columns(&mut state);
1121 }
1122
1123 inv_shift_rows(&mut state);
1125 inv_sub_bytes(&mut state);
1126 for c in 0..4 {
1127 for r in 0..4 {
1128 state[r][c] ^= round_keys[c][r];
1129 }
1130 }
1131
1132 let mut output = [0u8; 16];
1134 for c in 0..4 {
1135 for r in 0..4 {
1136 output[c * 4 + r] = state[r][c];
1137 }
1138 }
1139 output
1140}
1141
1142fn inv_sub_bytes(state: &mut [[u8; 4]; 4]) {
1143 for row in state.iter_mut() {
1144 for val in row.iter_mut() {
1145 *val = AES_INV_SBOX[*val as usize];
1146 }
1147 }
1148}
1149
1150fn inv_shift_rows(state: &mut [[u8; 4]; 4]) {
1151 let tmp = state[1][3];
1154 state[1][3] = state[1][2];
1155 state[1][2] = state[1][1];
1156 state[1][1] = state[1][0];
1157 state[1][0] = tmp;
1158 let (t0, t1) = (state[2][0], state[2][1]);
1160 state[2][0] = state[2][2];
1161 state[2][1] = state[2][3];
1162 state[2][2] = t0;
1163 state[2][3] = t1;
1164 let tmp = state[3][0];
1166 state[3][0] = state[3][1];
1167 state[3][1] = state[3][2];
1168 state[3][2] = state[3][3];
1169 state[3][3] = tmp;
1170}
1171
1172#[allow(clippy::needless_range_loop)]
1173fn inv_mix_columns(state: &mut [[u8; 4]; 4]) {
1174 for c in 0..4 {
1176 let s0 = state[0][c];
1177 let s1 = state[1][c];
1178 let s2 = state[2][c];
1179 let s3 = state[3][c];
1180
1181 state[0][c] = gf_mul(s0, 0x0e) ^ gf_mul(s1, 0x0b) ^ gf_mul(s2, 0x0d) ^ gf_mul(s3, 0x09);
1182 state[1][c] = gf_mul(s0, 0x09) ^ gf_mul(s1, 0x0e) ^ gf_mul(s2, 0x0b) ^ gf_mul(s3, 0x0d);
1183 state[2][c] = gf_mul(s0, 0x0d) ^ gf_mul(s1, 0x09) ^ gf_mul(s2, 0x0e) ^ gf_mul(s3, 0x0b);
1184 state[3][c] = gf_mul(s0, 0x0b) ^ gf_mul(s1, 0x0d) ^ gf_mul(s2, 0x09) ^ gf_mul(s3, 0x0e);
1185 }
1186}
1187
1188pub(crate) fn aes_cbc_decrypt(ciphertext: &[u8], key: &[u8], iv: &[u8]) -> NetResult<Vec<u8>> {
1190 if ciphertext.len() % 16 != 0 {
1191 return Err(NetError::InvalidCertificate(
1192 "Decryption failed: ciphertext length is not a multiple of 16".to_string(),
1193 ));
1194 }
1195 if iv.len() != 16 {
1196 return Err(NetError::InvalidCertificate(format!(
1197 "Decryption failed: IV must be 16 bytes, got {}",
1198 iv.len()
1199 )));
1200 }
1201 if key.len() != 16 && key.len() != 24 && key.len() != 32 {
1202 return Err(NetError::InvalidCertificate(format!(
1203 "Decryption failed: invalid key length {} (expected 16, 24, or 32)",
1204 key.len()
1205 )));
1206 }
1207
1208 let round_keys = aes_key_expansion(key)?;
1209 let mut result = Vec::with_capacity(ciphertext.len());
1210 let mut prev_cipher_block = [0u8; 16];
1211 prev_cipher_block.copy_from_slice(&iv[..16]);
1212
1213 for chunk in ciphertext.chunks_exact(16) {
1214 let mut block = [0u8; 16];
1215 block.copy_from_slice(chunk);
1216
1217 let decrypted_block = aes_decrypt_block(&block, &round_keys);
1218
1219 for i in 0..16 {
1221 result.push(decrypted_block[i] ^ prev_cipher_block[i]);
1222 }
1223
1224 prev_cipher_block = block;
1225 }
1226
1227 Ok(result)
1228}
1229
1230pub(crate) fn remove_pkcs7_padding(data: &[u8]) -> NetResult<&[u8]> {
1232 if data.is_empty() {
1233 return Err(NetError::InvalidCertificate(
1234 "Decryption failed: empty decrypted data".to_string(),
1235 ));
1236 }
1237
1238 let pad_len = *data.last().unwrap_or(&0) as usize;
1239 if pad_len == 0 || pad_len > 16 || pad_len > data.len() {
1240 return Err(NetError::InvalidCertificate(
1241 "Decryption failed: wrong password or corrupted key (invalid PKCS#7 padding)"
1242 .to_string(),
1243 ));
1244 }
1245
1246 for &b in &data[data.len() - pad_len..] {
1248 if b as usize != pad_len {
1249 return Err(NetError::InvalidCertificate(
1250 "Decryption failed: wrong password or corrupted key (invalid PKCS#7 padding)"
1251 .to_string(),
1252 ));
1253 }
1254 }
1255
1256 Ok(&data[..data.len() - pad_len])
1257}
1258
1259pub(crate) fn hex_decode(hex: &str) -> Result<Vec<u8>, String> {
1265 if hex.len() % 2 != 0 {
1266 return Err("Hex string has odd length".to_string());
1267 }
1268 let mut bytes = Vec::with_capacity(hex.len() / 2);
1269 let mut chars = hex.chars();
1270 while let (Some(h), Some(l)) = (chars.next(), chars.next()) {
1271 let high = h
1272 .to_digit(16)
1273 .ok_or_else(|| format!("Invalid hex character: {h}"))? as u8;
1274 let low = l
1275 .to_digit(16)
1276 .ok_or_else(|| format!("Invalid hex character: {l}"))? as u8;
1277 bytes.push((high << 4) | low);
1278 }
1279 Ok(bytes)
1280}
1281
1282pub(crate) fn base64_decode_pure(input: &str) -> Result<Vec<u8>, String> {
1284 let mut output = Vec::with_capacity(input.len() * 3 / 4);
1285 let mut buf: u32 = 0;
1286 let mut bits: u32 = 0;
1287 let mut pad_count = 0;
1288
1289 for c in input.chars() {
1290 if c == '=' {
1291 pad_count += 1;
1292 continue;
1293 }
1294 if pad_count > 0 {
1295 return Err("Invalid base64: data after padding".to_string());
1296 }
1297 let val = match c {
1298 'A'..='Z' => c as u32 - 'A' as u32,
1299 'a'..='z' => c as u32 - 'a' as u32 + 26,
1300 '0'..='9' => c as u32 - '0' as u32 + 52,
1301 '+' => 62,
1302 '/' => 63,
1303 _ => return Err(format!("Invalid base64 character: {c}")),
1304 };
1305 buf = (buf << 6) | val;
1306 bits += 6;
1307 if bits >= 8 {
1308 bits -= 8;
1309 output.push((buf >> bits) as u8);
1310 buf &= (1 << bits) - 1;
1311 }
1312 }
1313
1314 Ok(output)
1315}