Tschuss Status Quo - Hallo, Zukunft!
This commit is contained in:
Alexey
2025-12-30 05:08:05 +03:00
parent 44169441b4
commit 3d9150a074
33 changed files with 6079 additions and 0 deletions

351
src/crypto/aes.rs Normal file
View File

@@ -0,0 +1,351 @@
//! AES
use aes::Aes256;
use ctr::{Ctr128BE, cipher::{KeyIvInit, StreamCipher}};
use cbc::{Encryptor as CbcEncryptor, Decryptor as CbcDecryptor};
use cbc::cipher::{BlockEncryptMut, BlockDecryptMut, block_padding::NoPadding};
use crate::error::{ProxyError, Result};
type Aes256Ctr = Ctr128BE<Aes256>;
type Aes256CbcEnc = CbcEncryptor<Aes256>;
type Aes256CbcDec = CbcDecryptor<Aes256>;
/// AES-256-CTR encryptor/decryptor
pub struct AesCtr {
cipher: Aes256Ctr,
}
impl AesCtr {
pub fn new(key: &[u8; 32], iv: u128) -> Self {
let iv_bytes = iv.to_be_bytes();
Self {
cipher: Aes256Ctr::new(key.into(), (&iv_bytes).into()),
}
}
pub fn from_key_iv(key: &[u8], iv: &[u8]) -> Result<Self> {
if key.len() != 32 {
return Err(ProxyError::InvalidKeyLength { expected: 32, got: key.len() });
}
if iv.len() != 16 {
return Err(ProxyError::InvalidKeyLength { expected: 16, got: iv.len() });
}
let key: [u8; 32] = key.try_into().unwrap();
let iv = u128::from_be_bytes(iv.try_into().unwrap());
Ok(Self::new(&key, iv))
}
/// Encrypt/decrypt data in-place (CTR mode is symmetric)
pub fn apply(&mut self, data: &mut [u8]) {
self.cipher.apply_keystream(data);
}
/// Encrypt data, returning new buffer
pub fn encrypt(&mut self, data: &[u8]) -> Vec<u8> {
let mut output = data.to_vec();
self.apply(&mut output);
output
}
/// Decrypt data (for CTR, identical to encrypt)
pub fn decrypt(&mut self, data: &[u8]) -> Vec<u8> {
self.encrypt(data)
}
}
/// AES-256-CBC Ciphermagic
pub struct AesCbc {
key: [u8; 32],
iv: [u8; 16],
}
impl AesCbc {
pub fn new(key: [u8; 32], iv: [u8; 16]) -> Self {
Self { key, iv }
}
pub fn from_slices(key: &[u8], iv: &[u8]) -> Result<Self> {
if key.len() != 32 {
return Err(ProxyError::InvalidKeyLength { expected: 32, got: key.len() });
}
if iv.len() != 16 {
return Err(ProxyError::InvalidKeyLength { expected: 16, got: iv.len() });
}
Ok(Self {
key: key.try_into().unwrap(),
iv: iv.try_into().unwrap(),
})
}
/// Encrypt data using CBC mode
pub fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
if data.len() % 16 != 0 {
return Err(ProxyError::Crypto(
format!("CBC data must be aligned to 16 bytes, got {}", data.len())
));
}
if data.is_empty() {
return Ok(Vec::new());
}
let mut buffer = data.to_vec();
let mut encryptor = Aes256CbcEnc::new((&self.key).into(), (&self.iv).into());
for chunk in buffer.chunks_mut(16) {
encryptor.encrypt_block_mut(chunk.into());
}
Ok(buffer)
}
/// Decrypt data using CBC mode
pub fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
if data.len() % 16 != 0 {
return Err(ProxyError::Crypto(
format!("CBC data must be aligned to 16 bytes, got {}", data.len())
));
}
if data.is_empty() {
return Ok(Vec::new());
}
let mut buffer = data.to_vec();
let mut decryptor = Aes256CbcDec::new((&self.key).into(), (&self.iv).into());
for chunk in buffer.chunks_mut(16) {
decryptor.decrypt_block_mut(chunk.into());
}
Ok(buffer)
}
/// Encrypt data in-place
pub fn encrypt_in_place(&self, data: &mut [u8]) -> Result<()> {
if data.len() % 16 != 0 {
return Err(ProxyError::Crypto(
format!("CBC data must be aligned to 16 bytes, got {}", data.len())
));
}
if data.is_empty() {
return Ok(());
}
let mut encryptor = Aes256CbcEnc::new((&self.key).into(), (&self.iv).into());
for chunk in data.chunks_mut(16) {
encryptor.encrypt_block_mut(chunk.into());
}
Ok(())
}
/// Decrypt data in-place
pub fn decrypt_in_place(&self, data: &mut [u8]) -> Result<()> {
if data.len() % 16 != 0 {
return Err(ProxyError::Crypto(
format!("CBC data must be aligned to 16 bytes, got {}", data.len())
));
}
if data.is_empty() {
return Ok(());
}
let mut decryptor = Aes256CbcDec::new((&self.key).into(), (&self.iv).into());
for chunk in data.chunks_mut(16) {
decryptor.decrypt_block_mut(chunk.into());
}
Ok(())
}
}
/// Trait for unified encryption interface
pub trait Encryptor: Send + Sync {
fn encrypt(&mut self, data: &[u8]) -> Vec<u8>;
}
/// Trait for unified decryption interface
pub trait Decryptor: Send + Sync {
fn decrypt(&mut self, data: &[u8]) -> Vec<u8>;
}
impl Encryptor for AesCtr {
fn encrypt(&mut self, data: &[u8]) -> Vec<u8> {
AesCtr::encrypt(self, data)
}
}
impl Decryptor for AesCtr {
fn decrypt(&mut self, data: &[u8]) -> Vec<u8> {
AesCtr::decrypt(self, data)
}
}
/// No-op encryptor for fast mode
pub struct PassthroughEncryptor;
impl Encryptor for PassthroughEncryptor {
fn encrypt(&mut self, data: &[u8]) -> Vec<u8> {
data.to_vec()
}
}
impl Decryptor for PassthroughEncryptor {
fn decrypt(&mut self, data: &[u8]) -> Vec<u8> {
data.to_vec()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_aes_ctr_roundtrip() {
let key = [0u8; 32];
let iv = 12345u128;
let original = b"Hello, MTProto!";
let mut enc = AesCtr::new(&key, iv);
let encrypted = enc.encrypt(original);
let mut dec = AesCtr::new(&key, iv);
let decrypted = dec.decrypt(&encrypted);
assert_eq!(original.as_slice(), decrypted.as_slice());
}
#[test]
fn test_aes_cbc_roundtrip() {
let key = [0u8; 32];
let iv = [0u8; 16];
// Must be aligned to 16 bytes
let original = [0u8; 32];
let cipher = AesCbc::new(key, iv);
let encrypted = cipher.encrypt(&original).unwrap();
let decrypted = cipher.decrypt(&encrypted).unwrap();
assert_eq!(original.as_slice(), decrypted.as_slice());
}
#[test]
fn test_aes_cbc_chaining_works() {
let key = [0x42u8; 32];
let iv = [0x00u8; 16];
let plaintext = [0xAA_u8; 32];
let cipher = AesCbc::new(key, iv);
let ciphertext = cipher.encrypt(&plaintext).unwrap();
// CBC Corrections
let block1 = &ciphertext[0..16];
let block2 = &ciphertext[16..32];
assert_ne!(block1, block2, "CBC chaining broken: identical plaintext blocks produced identical ciphertext");
}
#[test]
fn test_aes_cbc_known_vector() {
let key = [0u8; 32];
let iv = [0u8; 16];
// 3 Datablocks
let plaintext = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
// Block 2
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
// Block 3 - different
0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88,
0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00,
];
let cipher = AesCbc::new(key, iv);
let ciphertext = cipher.encrypt(&plaintext).unwrap();
// Decrypt + Verify
let decrypted = cipher.decrypt(&ciphertext).unwrap();
assert_eq!(plaintext.as_slice(), decrypted.as_slice());
// Verify Ciphertexts Block 1 != Block 2
assert_ne!(&ciphertext[0..16], &ciphertext[16..32]);
}
#[test]
fn test_aes_cbc_in_place() {
let key = [0x12u8; 32];
let iv = [0x34u8; 16];
let original = [0x56u8; 48]; // 3 blocks
let mut buffer = original.clone();
let cipher = AesCbc::new(key, iv);
cipher.encrypt_in_place(&mut buffer).unwrap();
assert_ne!(&buffer[..], &original[..]);
cipher.decrypt_in_place(&mut buffer).unwrap();
assert_eq!(&buffer[..], &original[..]);
}
#[test]
fn test_aes_cbc_empty_data() {
let cipher = AesCbc::new([0u8; 32], [0u8; 16]);
let encrypted = cipher.encrypt(&[]).unwrap();
assert!(encrypted.is_empty());
let decrypted = cipher.decrypt(&[]).unwrap();
assert!(decrypted.is_empty());
}
#[test]
fn test_aes_cbc_unaligned_error() {
let cipher = AesCbc::new([0u8; 32], [0u8; 16]);
// 15 bytes
let result = cipher.encrypt(&[0u8; 15]);
assert!(result.is_err());
// 17 bytes
let result = cipher.encrypt(&[0u8; 17]);
assert!(result.is_err());
}
#[test]
fn test_aes_cbc_avalanche_effect() {
// Cipherplane
let key = [0xAB; 32];
let iv = [0xCD; 16];
let mut plaintext1 = [0u8; 32];
let mut plaintext2 = [0u8; 32];
plaintext2[0] = 0x01; // Один бит отличается
let cipher = AesCbc::new(key, iv);
let ciphertext1 = cipher.encrypt(&plaintext1).unwrap();
let ciphertext2 = cipher.encrypt(&plaintext2).unwrap();
// First Blocks Diff
assert_ne!(&ciphertext1[0..16], &ciphertext2[0..16]);
// Second Blocks Diff
assert_ne!(&ciphertext1[16..32], &ciphertext2[16..32]);
}
}

90
src/crypto/hash.rs Normal file
View File

@@ -0,0 +1,90 @@
use hmac::{Hmac, Mac};
use sha2::Sha256;
use md5::Md5;
use sha1::Sha1;
use sha2::Digest;
type HmacSha256 = Hmac<Sha256>;
/// SHA-256
pub fn sha256(data: &[u8]) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(data);
hasher.finalize().into()
}
/// SHA-256 HMAC
pub fn sha256_hmac(key: &[u8], data: &[u8]) -> [u8; 32] {
let mut mac = HmacSha256::new_from_slice(key)
.expect("HMAC accepts any key length");
mac.update(data);
mac.finalize().into_bytes().into()
}
/// SHA-1
pub fn sha1(data: &[u8]) -> [u8; 20] {
let mut hasher = Sha1::new();
hasher.update(data);
hasher.finalize().into()
}
/// MD5
pub fn md5(data: &[u8]) -> [u8; 16] {
let mut hasher = Md5::new();
hasher.update(data);
hasher.finalize().into()
}
/// CRC32
pub fn crc32(data: &[u8]) -> u32 {
crc32fast::hash(data)
}
/// Middle Proxy Keygen
pub fn derive_middleproxy_keys(
nonce_srv: &[u8; 16],
nonce_clt: &[u8; 16],
clt_ts: &[u8; 4],
srv_ip: Option<&[u8]>,
clt_port: &[u8; 2],
purpose: &[u8],
clt_ip: Option<&[u8]>,
srv_port: &[u8; 2],
secret: &[u8],
clt_ipv6: Option<&[u8; 16]>,
srv_ipv6: Option<&[u8; 16]>,
) -> ([u8; 32], [u8; 16]) {
const EMPTY_IP: [u8; 4] = [0, 0, 0, 0];
let srv_ip = srv_ip.unwrap_or(&EMPTY_IP);
let clt_ip = clt_ip.unwrap_or(&EMPTY_IP);
let mut s = Vec::with_capacity(256);
s.extend_from_slice(nonce_srv);
s.extend_from_slice(nonce_clt);
s.extend_from_slice(clt_ts);
s.extend_from_slice(srv_ip);
s.extend_from_slice(clt_port);
s.extend_from_slice(purpose);
s.extend_from_slice(clt_ip);
s.extend_from_slice(srv_port);
s.extend_from_slice(secret);
s.extend_from_slice(nonce_srv);
if let (Some(clt_v6), Some(srv_v6)) = (clt_ipv6, srv_ipv6) {
s.extend_from_slice(clt_v6);
s.extend_from_slice(srv_v6);
}
s.extend_from_slice(nonce_clt);
let md5_1 = md5(&s[1..]);
let sha1_sum = sha1(&s);
let md5_2 = md5(&s[2..]);
let mut key = [0u8; 32];
key[..12].copy_from_slice(&md5_1[..12]);
key[12..].copy_from_slice(&sha1_sum);
(key, md5_2)
}

9
src/crypto/mod.rs Normal file
View File

@@ -0,0 +1,9 @@
//! Crypto
pub mod aes;
pub mod hash;
pub mod random;
pub use aes::{AesCtr, AesCbc};
pub use hash::{sha256, sha256_hmac, sha1, md5, crc32};
pub use random::{SecureRandom, SECURE_RANDOM};

212
src/crypto/random.rs Normal file
View File

@@ -0,0 +1,212 @@
//! Pseudorandom
use rand::{Rng, RngCore, SeedableRng};
use rand::rngs::StdRng;
use parking_lot::Mutex;
use crate::crypto::AesCtr;
use once_cell::sync::Lazy;
/// Global secure random instance
pub static SECURE_RANDOM: Lazy<SecureRandom> = Lazy::new(SecureRandom::new);
/// Cryptographically secure PRNG with AES-CTR
pub struct SecureRandom {
inner: Mutex<SecureRandomInner>,
}
struct SecureRandomInner {
rng: StdRng,
cipher: AesCtr,
buffer: Vec<u8>,
}
impl SecureRandom {
pub fn new() -> Self {
let mut rng = StdRng::from_entropy();
let mut key = [0u8; 32];
rng.fill_bytes(&mut key);
let iv: u128 = rng.gen();
Self {
inner: Mutex::new(SecureRandomInner {
rng,
cipher: AesCtr::new(&key, iv),
buffer: Vec::with_capacity(1024),
}),
}
}
/// Generate random bytes
pub fn bytes(&self, len: usize) -> Vec<u8> {
let mut inner = self.inner.lock();
const CHUNK_SIZE: usize = 512;
while inner.buffer.len() < len {
let mut chunk = vec![0u8; CHUNK_SIZE];
inner.rng.fill_bytes(&mut chunk);
inner.cipher.apply(&mut chunk);
inner.buffer.extend_from_slice(&chunk);
}
inner.buffer.drain(..len).collect()
}
/// Generate random number in range [0, max)
pub fn range(&self, max: usize) -> usize {
if max == 0 {
return 0;
}
let mut inner = self.inner.lock();
inner.rng.gen_range(0..max)
}
/// Generate random bits
pub fn bits(&self, k: usize) -> u64 {
if k == 0 {
return 0;
}
let bytes_needed = (k + 7) / 8;
let bytes = self.bytes(bytes_needed.min(8));
let mut result = 0u64;
for (i, &b) in bytes.iter().enumerate() {
if i >= 8 {
break;
}
result |= (b as u64) << (i * 8);
}
// Mask extra bits
if k < 64 {
result &= (1u64 << k) - 1;
}
result
}
/// Choose random element from slice
pub fn choose<'a, T>(&self, slice: &'a [T]) -> Option<&'a T> {
if slice.is_empty() {
None
} else {
Some(&slice[self.range(slice.len())])
}
}
/// Shuffle slice in place
pub fn shuffle<T>(&self, slice: &mut [T]) {
let mut inner = self.inner.lock();
for i in (1..slice.len()).rev() {
let j = inner.rng.gen_range(0..=i);
slice.swap(i, j);
}
}
/// Generate random u32
pub fn u32(&self) -> u32 {
let mut inner = self.inner.lock();
inner.rng.gen()
}
/// Generate random u64
pub fn u64(&self) -> u64 {
let mut inner = self.inner.lock();
inner.rng.gen()
}
}
impl Default for SecureRandom {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn test_bytes_uniqueness() {
let rng = SecureRandom::new();
let a = rng.bytes(32);
let b = rng.bytes(32);
assert_ne!(a, b);
}
#[test]
fn test_bytes_length() {
let rng = SecureRandom::new();
assert_eq!(rng.bytes(0).len(), 0);
assert_eq!(rng.bytes(1).len(), 1);
assert_eq!(rng.bytes(100).len(), 100);
assert_eq!(rng.bytes(1000).len(), 1000);
}
#[test]
fn test_range() {
let rng = SecureRandom::new();
for _ in 0..1000 {
let n = rng.range(10);
assert!(n < 10);
}
assert_eq!(rng.range(1), 0);
assert_eq!(rng.range(0), 0);
}
#[test]
fn test_bits() {
let rng = SecureRandom::new();
// Single bit should be 0 or 1
for _ in 0..100 {
assert!(rng.bits(1) <= 1);
}
// 8 bits should be 0-255
for _ in 0..100 {
assert!(rng.bits(8) <= 255);
}
}
#[test]
fn test_choose() {
let rng = SecureRandom::new();
let items = vec![1, 2, 3, 4, 5];
let mut seen = HashSet::new();
for _ in 0..1000 {
if let Some(&item) = rng.choose(&items) {
seen.insert(item);
}
}
// Should have seen all items
assert_eq!(seen.len(), 5);
// Empty slice should return None
let empty: Vec<i32> = vec![];
assert!(rng.choose(&empty).is_none());
}
#[test]
fn test_shuffle() {
let rng = SecureRandom::new();
let original = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let mut shuffled = original.clone();
rng.shuffle(&mut shuffled);
// Should contain same elements
let mut sorted = shuffled.clone();
sorted.sort();
assert_eq!(sorted, original);
// Should be different order (with very high probability)
assert_ne!(shuffled, original);
}
}