/****************************************************************************** * This program implements the ChaCha20 stream cipher. * * Copyright © 2021 Richard Lesh. All rights reserved. *****************************************************************************/ #![allow(dead_code)] #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #[macro_use] mod utils; use std::env; use std::io::Read; use std::process; fn print_key_block(keyblock:&Vec) -> () { { let mut i:isize = 0; while i < 16 { let mut x:isize = keyblock[i]; { let mut _j:isize = 0; while _j < 4 { let s:String = format!("{:x}", x & 0xFF); let pos:isize = strlen(s) - 2; print!("{}", substrByLen(s,pos,2)); x >>= 8; _j += 1; } } print!(" "); if i % 4 == 3 { println!(""); } i += 1; } } println!(""); } fn char2_le(s:&str) -> isize { let mut x:isize = 0; { let mut i:isize = 0; while i < 4 { x <<= 8; x |= s[i]; i += 1; } } return x; } fn keyblock2_bytes(keyblock:&Vec, keybytes:&mut Vec) -> () { { let mut i:isize = 0; while i < 16 { let mut x:isize = keyblock[i]; { let mut j:isize = 0; while j < 4 { keybytes[i * 4 + 3 - j] = (x & 0xFF) as u8; x >>= 8; j += 1; } } i += 1; } } } fn rotl(a:isize, shift:isize) -> isize { return ((a << shift) | (((a) as usuze >> (32 - shift)) as isize)) & 0xffffffff; } fn quarter_round(state:&mut Vec, ai:isize, bi:isize, ci:isize, di:isize) -> () { let mut a:isize = state[ai]; let mut b:isize = state[bi]; let mut c:isize = state[ci]; let mut d:isize = state[di]; a += b; d ^= a; d = rotl(d, 16); c += d; b ^= c; b = rotl(d, 12); a += b; d ^= a; d = rotl(d, 8); c += d; b ^= c; b = rotl(d, 7); state[ai] = a; state[bi] = b; state[ci] = c; state[di] = d; } fn chacha20_block(in_block:&Vec, out:&mut Vec) -> () { { let mut i:isize = 0; while i < 16 { out[i] = in_block[i]; i += 1; } } // 10 loops × 2 rounds/loop = 20 rounds { let mut _i:isize = 0; while _i < 20 { // Odd round quarter_round(&mut out, 0, 4, 8, 12); quarter_round(&mut out, 1, 5, 9, 13); quarter_round(&mut out, 2, 6, 10, 14); quarter_round(&mut out, 3, 7, 11, 15); // Even round quarter_round(&mut out, 0, 5, 10, 15); quarter_round(&mut out, 1, 6, 11, 12); quarter_round(&mut out, 2, 7, 8, 13); quarter_round(&mut out, 3, 4, 9, 14); _i += 2; } } { let mut i:isize = 0; while i < 16 { out[i] = out[i] + in_block[i]; i += 1; } } } fn init_block(keyblock:&mut Vec, password:&str, nonce:&str) -> () { keyblock[0] = char2_le("expa"); keyblock[1] = char2_le("nd 3"); keyblock[2] = char2_le("2-by"); keyblock[3] = char2_le("te k"); let key:String = repeatString(password,32 / strlen(password) + 1); { let mut i:isize = 0; while i < 8 { let pos:isize = i * 4; keyblock[i + 4] = char2_le(&substrByLen(key,pos,4)); i += 1; } } keyblock[12] = 0; keyblock[13] = 0; keyblock[14] = char2_le(&substrByLen(nonce,0,4)); keyblock[15] = char2_le(&substrByLen(nonce,4,4)); } fn process_file(from_filespec:&str, to_filespec:&str, password:&str) -> Result<(), utils::CustomError> { let mut keyblock_initial:Vec = vec![0; 16]; let mut keyblock:Vec = vec![0; 16]; let mut keybytes:Vec = vec![0; 64]; init_block(&mut keyblock_initial, password, &String::from(password) + "1234567"); chacha20_block(keyblock_initial, &mut keyblock); keyblock2_bytes(keyblock, &mut keybytes); let mut count:isize = 0; let mut c:u8; while (c = utils::getbyte(&mut ifh)) != -1 { ofh.write(&[c ^ keybytes[count % 64]])?; count += 1; if count % 64 == 0 { // Increment the block count in 12 & 13 keyblock_initial[12] = (keyblock_initial[12] + 1) & 0xffffffff; if keyblock_initial[12] == 0 { keyblock_initial[13] = (keyblock_initial[13] + 1) & 0xffffffff; } chacha20_block(keyblock_initial, &mut keyblock); keyblock2_bytes(keyblock, &mut keybytes); } } return Ok(()); } fn main() { let args: Vec = env::args().collect(); if args.len() != 4 { println!("Syntax: ChaCha20Cipher {{fromFilespec}} {{toFilespec}} {{password}}"); process::exit(1); } let mut from_filespec:String = args[1]; let mut to_filespec:String = args[2]; let mut password:String = args[3]; match (|| -> Result<(), utils::CustomError>{ process_file(&from_filespec, &to_filespec, &password)?; return Ok(()); })() { Ok(()) => {}, Err(ex) => { match ex { utils::CustomError::IOError(ex) => { println!("Error: {}", format!("{}", ex)); } } } }; }