diff --git a/CHANGELOG.md b/CHANGELOG.md index 7288583..33711db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## 0.8.0 (2023-08-15) + +### Breaking Change + +- The core and stream operations are moved into separate modules + ## 0.7.1 (2023-08-05) - Updated documentation diff --git a/README.md b/README.md index 897f5a6..a49590d 100644 --- a/README.md +++ b/README.md @@ -56,19 +56,16 @@ let decrypted = magma.decrypt(encrypted); println!("Decrypted:\n{:x}", decrypted); assert_eq!(decrypted, source); - ``` ### Text encryption sample: [encrypt_text.rs](https://github.com/sheroz/magma/tree/main/magma_samples/src/samples/encrypt_text.rs) ```rust -use cipher_magma::{CipherMode, CipherOperation, Magma}; - -let cipher_mode = CipherMode::CFB; +use cipher_magma::{CipherMode, MagmaStream}; let key = [0xab; 32]; println!("Key:\n{:x?}\n", key); -let mut magma = Magma::with_key(key); +let mut magma = MagmaStream::new(key, CipherMode::CFB); let source = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ Aenean ac sem leo. Morbi pretium neque eget felis finibus convallis. \ @@ -78,12 +75,12 @@ let source = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ println!("Source:\n{}\n", String::from_utf8(source.to_vec()).unwrap()); -let encrypted = magma.cipher(source, &CipherOperation::Encrypt, &cipher_mode); +let encrypted = magma.encrypt(source); println!("Encrypted:\n{:02x?}\n", encrypted); -let mut decrypted = magma.cipher(&encrypted, &CipherOperation::Decrypt, &cipher_mode); +let mut decrypted = magma.decrypt(&encrypted); -if cipher_mode.has_padding() { +if magma.get_mode().has_padding() { // remove padding bytes decrypted.truncate(source.len()); } @@ -95,7 +92,7 @@ println!("Decrypted:\n{}\n", String::from_utf8(decrypted).unwrap()); ### Message Authentication Code (MAC) sample: [calculate_mac.rs](https://github.com/sheroz/magma/tree/main/magma_samples/src/samples/calculate_mac.rs) ```rust -use cipher_magma::{mac, Magma}; +use cipher_magma::{mac, CipherMode, MagmaStream}; let key: [u8; 32] = [ 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, @@ -103,7 +100,6 @@ let key: [u8; 32] = [ 0xfe, 0xff, ]; println!("Key:\n{:x?}\n", key); -let mut magma = Magma::with_key(key); let message = [ 0x92, 0xde, 0xf0, 0x6b, 0x3c, 0x13, 0x0a, 0x59, 0xdb, 0x54, 0xc7, 0x04, 0xf8, 0x18, 0x9d, @@ -112,6 +108,8 @@ let message = [ ]; println!("Message:\n{:02x?}\n", message); +let mut magma = MagmaStream::new(key, CipherMode::MAC); + // update the context for chunk in message.chunks(8) { mac::update(&mut magma, &chunk); @@ -127,29 +125,29 @@ assert_eq!(mac, 0x154e7210); ### File encryption sample: [encrypt_file.rs](https://github.com/sheroz/magma/tree/main/magma_samples/src/samples/encrypt_file.rs) ```rust -use cipher_magma::{CipherMode, CipherOperation, Magma}; +use cipher_magma::{CipherMode, MagmaStream}; +use std::env; +use std::fs::File; use std::io::{Read, Seek, Write}; -let cipher_mode = CipherMode::CBC; - let key = [0xab; 32]; -let mut magma = Magma::with_key(key); +let mut magma = MagmaStream::new(key, CipherMode::CBC); -// opening file +// opening source file let source_filename = "README.md"; println!("Opening source file: {}", source_filename); -let mut source_file = std::fs::File::open(source_filename).expect("Could not open file."); +let mut source_file = File::open(source_filename).expect("Could not open file."); let source_len = source_file.metadata().unwrap().len(); -let temp_dir = std::env::temp_dir(); +let temp_dir = env::temp_dir(); // creating file for encrypted data let encrypted_filename = format!("{}.encrypted", source_filename); let encrypted_filepath = temp_dir.join(encrypted_filename); println!("Creating encrypted file: {:?}", encrypted_filepath); -let mut encrypted_file = std::fs::File::options() +let mut encrypted_file = File::options() .write(true) .read(true) .create(true) @@ -170,12 +168,13 @@ loop { break; } - let ciphertext = magma.cipher(&buf[0..read_count], &CipherOperation::Encrypt, &cipher_mode); + let ciphertext = magma.encrypt(&buf[0..read_count]); encrypted_file .write_all(&ciphertext) .expect("Could not write into encrypted file"); } + encrypted_file .flush() .expect("Could not flush the encrypted file"); @@ -185,10 +184,10 @@ println!("Encryption completed."); let decrypted_filename = format!("{}.decrypted", source_filename); let decrypted_filepath = temp_dir.join(decrypted_filename); -println!("Creating decrypted file: {:?}", decrypted_filepath); +println!("Creating file for decrypted data: {:?}", decrypted_filepath); let mut decrypted_file = - std::fs::File::create(decrypted_filepath).expect("Could not create decrypted file."); + File::create(decrypted_filepath).expect("Could not create decrypted file."); println!("Decrypting ..."); @@ -206,17 +205,18 @@ loop { break; } - let plaintext = magma.cipher(&buf[0..read_count], &CipherOperation::Decrypt, &cipher_mode); + let plaintext = magma.decrypt(&buf[0..read_count]); decrypted_file .write_all(&plaintext) .expect("Could not write into decrypted file"); } + decrypted_file .flush() .expect("Could not flush the decrypted file"); -if cipher_mode.has_padding() { +if magma.get_mode().has_padding() { // remove padding bytes decrypted_file .set_len(source_len) diff --git a/cipher_magma/CHANGELOG.md b/cipher_magma/CHANGELOG.md index 7288583..33711db 100644 --- a/cipher_magma/CHANGELOG.md +++ b/cipher_magma/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## 0.8.0 (2023-08-15) + +### Breaking Change + +- The core and stream operations are moved into separate modules + ## 0.7.1 (2023-08-05) - Updated documentation diff --git a/cipher_magma/Cargo.toml b/cipher_magma/Cargo.toml index da16248..c5c9a0e 100644 --- a/cipher_magma/Cargo.toml +++ b/cipher_magma/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cipher_magma" -version = "0.7.1" +version = "0.8.0" edition = "2021" authors = ["Sheroz Khaydarov"] description = "Block Cipher Magma (GOST R 34.12-2015, former GOST 28147-89)" diff --git a/cipher_magma/README.md b/cipher_magma/README.md index 897f5a6..a49590d 100644 --- a/cipher_magma/README.md +++ b/cipher_magma/README.md @@ -56,19 +56,16 @@ let decrypted = magma.decrypt(encrypted); println!("Decrypted:\n{:x}", decrypted); assert_eq!(decrypted, source); - ``` ### Text encryption sample: [encrypt_text.rs](https://github.com/sheroz/magma/tree/main/magma_samples/src/samples/encrypt_text.rs) ```rust -use cipher_magma::{CipherMode, CipherOperation, Magma}; - -let cipher_mode = CipherMode::CFB; +use cipher_magma::{CipherMode, MagmaStream}; let key = [0xab; 32]; println!("Key:\n{:x?}\n", key); -let mut magma = Magma::with_key(key); +let mut magma = MagmaStream::new(key, CipherMode::CFB); let source = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ Aenean ac sem leo. Morbi pretium neque eget felis finibus convallis. \ @@ -78,12 +75,12 @@ let source = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ println!("Source:\n{}\n", String::from_utf8(source.to_vec()).unwrap()); -let encrypted = magma.cipher(source, &CipherOperation::Encrypt, &cipher_mode); +let encrypted = magma.encrypt(source); println!("Encrypted:\n{:02x?}\n", encrypted); -let mut decrypted = magma.cipher(&encrypted, &CipherOperation::Decrypt, &cipher_mode); +let mut decrypted = magma.decrypt(&encrypted); -if cipher_mode.has_padding() { +if magma.get_mode().has_padding() { // remove padding bytes decrypted.truncate(source.len()); } @@ -95,7 +92,7 @@ println!("Decrypted:\n{}\n", String::from_utf8(decrypted).unwrap()); ### Message Authentication Code (MAC) sample: [calculate_mac.rs](https://github.com/sheroz/magma/tree/main/magma_samples/src/samples/calculate_mac.rs) ```rust -use cipher_magma::{mac, Magma}; +use cipher_magma::{mac, CipherMode, MagmaStream}; let key: [u8; 32] = [ 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, @@ -103,7 +100,6 @@ let key: [u8; 32] = [ 0xfe, 0xff, ]; println!("Key:\n{:x?}\n", key); -let mut magma = Magma::with_key(key); let message = [ 0x92, 0xde, 0xf0, 0x6b, 0x3c, 0x13, 0x0a, 0x59, 0xdb, 0x54, 0xc7, 0x04, 0xf8, 0x18, 0x9d, @@ -112,6 +108,8 @@ let message = [ ]; println!("Message:\n{:02x?}\n", message); +let mut magma = MagmaStream::new(key, CipherMode::MAC); + // update the context for chunk in message.chunks(8) { mac::update(&mut magma, &chunk); @@ -127,29 +125,29 @@ assert_eq!(mac, 0x154e7210); ### File encryption sample: [encrypt_file.rs](https://github.com/sheroz/magma/tree/main/magma_samples/src/samples/encrypt_file.rs) ```rust -use cipher_magma::{CipherMode, CipherOperation, Magma}; +use cipher_magma::{CipherMode, MagmaStream}; +use std::env; +use std::fs::File; use std::io::{Read, Seek, Write}; -let cipher_mode = CipherMode::CBC; - let key = [0xab; 32]; -let mut magma = Magma::with_key(key); +let mut magma = MagmaStream::new(key, CipherMode::CBC); -// opening file +// opening source file let source_filename = "README.md"; println!("Opening source file: {}", source_filename); -let mut source_file = std::fs::File::open(source_filename).expect("Could not open file."); +let mut source_file = File::open(source_filename).expect("Could not open file."); let source_len = source_file.metadata().unwrap().len(); -let temp_dir = std::env::temp_dir(); +let temp_dir = env::temp_dir(); // creating file for encrypted data let encrypted_filename = format!("{}.encrypted", source_filename); let encrypted_filepath = temp_dir.join(encrypted_filename); println!("Creating encrypted file: {:?}", encrypted_filepath); -let mut encrypted_file = std::fs::File::options() +let mut encrypted_file = File::options() .write(true) .read(true) .create(true) @@ -170,12 +168,13 @@ loop { break; } - let ciphertext = magma.cipher(&buf[0..read_count], &CipherOperation::Encrypt, &cipher_mode); + let ciphertext = magma.encrypt(&buf[0..read_count]); encrypted_file .write_all(&ciphertext) .expect("Could not write into encrypted file"); } + encrypted_file .flush() .expect("Could not flush the encrypted file"); @@ -185,10 +184,10 @@ println!("Encryption completed."); let decrypted_filename = format!("{}.decrypted", source_filename); let decrypted_filepath = temp_dir.join(decrypted_filename); -println!("Creating decrypted file: {:?}", decrypted_filepath); +println!("Creating file for decrypted data: {:?}", decrypted_filepath); let mut decrypted_file = - std::fs::File::create(decrypted_filepath).expect("Could not create decrypted file."); + File::create(decrypted_filepath).expect("Could not create decrypted file."); println!("Decrypting ..."); @@ -206,17 +205,18 @@ loop { break; } - let plaintext = magma.cipher(&buf[0..read_count], &CipherOperation::Decrypt, &cipher_mode); + let plaintext = magma.decrypt(&buf[0..read_count]); decrypted_file .write_all(&plaintext) .expect("Could not write into decrypted file"); } + decrypted_file .flush() .expect("Could not flush the decrypted file"); -if cipher_mode.has_padding() { +if magma.get_mode().has_padding() { // remove padding bytes decrypted_file .set_len(source_len) diff --git a/cipher_magma/src/core/cipher_key.rs b/cipher_magma/src/core/cipher_key.rs new file mode 100644 index 0000000..4a365ad --- /dev/null +++ b/cipher_magma/src/core/cipher_key.rs @@ -0,0 +1,19 @@ +/// Passing cipher keys in a polymorphic way +pub enum CipherKey { + ArrayU8([u8; 32]), + ArrayU32([u32; 8]), +} + +impl From<[u8; 32]> for CipherKey { + /// builds key from '[u8;32]' array + fn from(array_u8: [u8; 32]) -> Self { + Self::ArrayU8(array_u8) + } +} + +impl From<[u32; 8]> for CipherKey { + /// builds key from '[u32;8]' array + fn from(array_u32: [u32; 8]) -> Self { + Self::ArrayU32(array_u32) + } +} diff --git a/cipher_magma/src/core/constants.rs b/cipher_magma/src/core/constants.rs new file mode 100644 index 0000000..9bdd34c --- /dev/null +++ b/cipher_magma/src/core/constants.rs @@ -0,0 +1,63 @@ +/// Substitution Box (S-Box) data according to [Appendix C. RFC7836](https://datatracker.ietf.org/doc/html/rfc7836#appendix-C) +/// +/// Parameter set: id-tc26-gost-28147-param-Z +pub const SUBSTITUTION_BOX_RFC7836: [u8; 128] = [ + 0xC, 0x4, 0x6, 0x2, 0xA, 0x5, 0xB, 0x9, 0xE, 0x8, 0xD, 0x7, 0x0, 0x3, 0xF, 0x1, 0x6, 0x8, + 0x2, 0x3, 0x9, 0xA, 0x5, 0xC, 0x1, 0xE, 0x4, 0x7, 0xB, 0xD, 0x0, 0xF, 0xB, 0x3, 0x5, 0x8, + 0x2, 0xF, 0xA, 0xD, 0xE, 0x1, 0x7, 0x4, 0xC, 0x9, 0x6, 0x0, 0xC, 0x8, 0x2, 0x1, 0xD, 0x4, + 0xF, 0x6, 0x7, 0x0, 0xA, 0x5, 0x3, 0xE, 0x9, 0xB, 0x7, 0xF, 0x5, 0xA, 0x8, 0x1, 0x6, 0xD, + 0x0, 0x9, 0x3, 0xE, 0xB, 0x4, 0x2, 0xC, 0x5, 0xD, 0xF, 0x6, 0x9, 0x2, 0xC, 0xA, 0xB, 0x7, + 0x8, 0x1, 0x4, 0x3, 0xE, 0x0, 0x8, 0xE, 0x2, 0x5, 0x6, 0x9, 0x1, 0xC, 0xF, 0x4, 0xB, 0x0, + 0xD, 0xA, 0x3, 0x7, 0x1, 0x7, 0xE, 0xD, 0x0, 0x5, 0x8, 0x3, 0x4, 0xF, 0xA, 0x6, 0x9, 0xC, + 0xB, 0x2, +]; + +/// Substitution Box (S-Box) data according to [RFC5831](https://datatracker.ietf.org/doc/html/rfc5831#section-7.1) +/// +/// As per [Appendix B of RFC8891](https://datatracker.ietf.org/doc/html/rfc8891.html#section-appendix.b) data values converted +/// from little-endian to big-endian format. +/// +/// OID: 1.2.643.2.2.30.0 +pub const SUBSTITUTION_BOX_RFC5831: [u8; 128] = [ + 0x4, 0xA, 0x9, 0x2, 0xD, 0x8, 0x0, 0xE, 0x6, 0xB, 0x1, 0xC, 0x7, 0xF, 0x5, 0x3, 0xE, 0xB, + 0x4, 0xC, 0x6, 0xD, 0xF, 0xA, 0x2, 0x3, 0x8, 0x1, 0x0, 0x7, 0x5, 0x9, 0x5, 0x8, 0x1, 0xD, + 0xA, 0x3, 0x4, 0x2, 0xE, 0xF, 0xC, 0x7, 0x6, 0x0, 0x9, 0xB, 0x7, 0xD, 0xA, 0x1, 0x0, 0x8, + 0x9, 0xF, 0xE, 0x4, 0x6, 0xC, 0xB, 0x2, 0x5, 0x3, 0x6, 0xC, 0x7, 0x1, 0x5, 0xF, 0xD, 0x8, + 0x4, 0xA, 0x9, 0xE, 0x0, 0x3, 0xB, 0x2, 0x4, 0xB, 0xA, 0x0, 0x7, 0x2, 0x1, 0xD, 0x3, 0x6, + 0x8, 0x5, 0x9, 0xC, 0xF, 0xE, 0xD, 0xB, 0x4, 0x1, 0x3, 0xF, 0x5, 0x9, 0x0, 0xA, 0xE, 0x7, + 0x6, 0x8, 0x2, 0xC, 0x1, 0xF, 0xD, 0x0, 0x5, 0x7, 0xA, 0x4, 0x9, 0x2, 0x3, 0xE, 0x6, 0xB, + 0x8, 0xC, +]; + +/// Initialization Vector (IV) +/// +/// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) +/// +/// CTR Mode: Page 36, Section A.2.2, uses MSB(32) part of IV +/// +/// OFB Mode: Page 37, Section A.2.3, uses MSB(128) part of IV +/// +/// CFB Mode: Page 39, Section A.2.5, uses MSB(128) part of IV +pub const IV_GOST_R3413_2015: [u64; 3] = [ + 0x1234567890abcdef_u64, + 0x234567890abcdef1_u64, + 0x34567890abcdef12_u64, +]; + +/// Р 1323565.1.017—2018 +/// +/// Section size N +/// +/// Page 7, CTR-ACPKM +pub const CTR_ACPKM_SECTION_SIZE_N: usize = 128; + +/// Р 1323565.1.017—2018 +/// +/// Constant D for ACPKM function +/// +/// Page 8, CTR-ACPKM +pub const CTR_ACPKM_D: [u8; 32] = [ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, + 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, + 0x9E, 0x9F, +]; diff --git a/cipher_magma/src/core/magma.rs b/cipher_magma/src/core/magma.rs new file mode 100644 index 0000000..596462d --- /dev/null +++ b/cipher_magma/src/core/magma.rs @@ -0,0 +1,447 @@ +//! Block Cipher "Magma" +//! +//! Implemented and tested according to specifications: +//! 1. [RFC 8891](https://datatracker.ietf.org/doc/html/rfc8891.html) a.k.a **GOST R 34.12-2015** +//! 2. [RFC 5830](https://datatracker.ietf.org/doc/html/rfc5830) a.k.a **GOST 28147-89** + +use crate::core::*; +use crate::core::constants::*; +use crate::core::cipher_key::*; + +/// The core Magma block-cipher +pub struct Magma { + pub (crate) key: [u32; 8], + pub (crate) round_keys: [u32; 32], + pub (crate) substitution_box: [u8; 128], +} + +impl Magma { + /// Returns a new Magma by using RFC7836 based substitution box + /// + /// # Example + /// ``` + /// use cipher_magma::Magma; + /// let magma = Magma::new(); + /// ``` + pub fn new() -> Self { + Magma { + key: [0u32; 8], + round_keys: [0u32; 32], + substitution_box: SUBSTITUTION_BOX_RFC7836.clone(), + } + } + + /// Returns a new `Magma` initialized with given cipher key + /// + /// Uses RFC7836 based substitution box + /// + /// # Arguments + /// + /// * `key` - array `[u32;8]` or `[u8;32]` + /// + /// # Example + /// + /// ``` + /// use cipher_magma::Magma; + /// let key: [u32;8] = [ + /// 0xffeeddcc, 0xbbaa9988, 0x77665544, 0x33221100, 0xf0f1f2f3, 0xf4f5f6f7, 0xf8f9fafb, 0xfcfdfeff + /// ]; + /// + /// let magma = Magma::with_key(key); + /// ``` + /// Or + /// + /// ``` + /// use cipher_magma::Magma; + /// let key: [u8;32] = [ + /// 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, + /// 0x00, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, + /// 0xfe, 0xff, + /// ]; + /// + /// let magma = Magma::with_key(key); + /// ``` + pub fn with_key (key: T) -> Magma + where CipherKey: From { + let mut engine = Magma::new(); + engine.set_key(key); + engine + } + + /// Sets the cipher key from array + /// + /// # Arguments + /// + /// * `key` - a `[u8;32]' or `[u32;8]` array + pub fn set_key (&mut self, key: T) where CipherKey: From { + let cipher_key = CipherKey::from(key); + match cipher_key { + CipherKey::ArrayU8(k) => { self.set_key_u8(&k) } + CipherKey::ArrayU32(k) => { self.set_key_u32(&k) }, + }; + } + + /// Sets the cipher key from `[u32;8]` array + /// + /// # Arguments + /// + /// * `key` - A reference to `[u32;8]` array + pub(crate) fn set_key_u32(&mut self, key: &[u32; 8]) { + self.key.clone_from(key); + self.prepare_round_keys(); + } + + /// Sets the cipher key from slice of u8 bytes + /// + /// # Arguments + /// + /// * `bytes` - A `&[u8]` slice with 32 byte elements + pub(crate) fn set_key_u8(&mut self, bytes: &[u8]) { + self.set_key_u32(&Self::key_from_u8(bytes)); + } + + fn key_from_u8(bytes: &[u8]) -> [u32;8] { + assert!(bytes.len() == 32); + let mut key = [0_u32;8]; + let mut array_u8 = [0u8; 4]; + for (index, chunk) in bytes.chunks(4).enumerate() { + chunk.iter().enumerate().for_each(|t| array_u8[t.0] = *t.1); + key[index] = u32::from_be_bytes(array_u8); + } + key + } + + /// Prepares [round keys](https://datatracker.ietf.org/doc/html/rfc8891.html#section-4.3) from the cipher key + fn prepare_round_keys(&mut self) { + const ROUND_KEY_POSITION: [u8; 32] = [ + 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, + 2, 1, 0, + ]; + + for index in 0..32 { + let round_key_position = ROUND_KEY_POSITION[index] as usize; + self.round_keys[index] = self.key[round_key_position]; + } + } + + /// Sets the substitution box + /// + /// # Arguments + /// + /// * `substitution_box` - A reference to `[u8;128]` array + pub (crate) fn set_substitution_box(&mut self, substitution_box: &[u8; 128]) { + self.substitution_box.copy_from_slice(substitution_box); + } + + /// [Transformation](https://datatracker.ietf.org/doc/html/rfc8891.html#section-4.2) + /// + /// `t: V_32 -> V_32` + #[inline] + fn transformation_t(&self, a: u32) -> u32 { + let mut res: u32 = 0; + let mut shift_count = 0; + for i in 0..8 { + let v = (a >> shift_count) & 0xF; + let s = self.substitution_box[(i * 16 + v) as usize] as u32; + res |= s << shift_count; + shift_count += 4; + } + res + } + + /// [Transformation](https://datatracker.ietf.org/doc/html/rfc8891.html#section-4.2) + /// + /// `g[k]: V_32 -> V_32` + #[inline] + fn transformation_g(&self, k: u32, a: u32) -> u32 { + let res = self.transformation_t(((k as u64) + (a as u64)) as u32); + res.rotate_left(11) + } + + /// [Transformation](https://datatracker.ietf.org/doc/html/rfc8891.html#section-4.2) + /// + /// `G[k]: V_32[*]V_32 -> V_32[*]V_32` + #[inline] + fn transformation_big_g(&self, k: u32, a_1: u32, a_0: u32) -> (u32, u32) { + (a_0, self.transformation_g(k, a_0) ^ a_1) + } + + /// Returns [encrypted block](https://datatracker.ietf.org/doc/html/rfc8891.html#section-5.1) as `u64` value + /// + /// # Arguments + /// + /// * `block_in` - a plaintext value as `u64` + #[inline] + pub fn encrypt(&self, block_in: u64) -> u64 { + // split the input block into u32 parts + let (mut a_1, mut a_0) = utils::u64_split(block_in); + + // crypto transformations + let mut round = 0; + while round < 32 { + (a_1, a_0) = self.transformation_big_g(self.round_keys[round], a_1, a_0); + round += 1; + } + + // join u32 parts into u64 block + utils::u32_join(a_0, a_1) + } + + /// Returns [decrypted block](https://datatracker.ietf.org/doc/html/rfc8891.html#section-5.2) as `u64` value + /// + /// # Arguments + /// + /// * `block_in` - a ciphertext value as `u64` + #[inline] + pub fn decrypt(&self, block_in: u64) -> u64 { + // split the input block into u32 parts + let (mut b_1, mut b_0) = utils::u64_split(block_in); + + // crypto transformations + let mut round = 32; + while round != 0 { + round -= 1; + (b_1, b_0) = self.transformation_big_g(self.round_keys[round], b_1, b_0); + } + + // join u32 parts into u64 block + utils::u32_join(b_0, b_1) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn default_initialization() { + let core = Magma::new(); + assert_eq!(core.key, [0u32; 8]); + assert_eq!(core.round_keys, [0u32; 32]); + assert_eq!(core.substitution_box, SUBSTITUTION_BOX_RFC7836); + } + #[test] + fn with_key_generic_u32_rfc8891() { + use crypto_vectors::gost::rfc8891; + let magma = Magma::with_key(rfc8891::CIPHER_KEY.clone()); + assert_eq!(magma.key, rfc8891::CIPHER_KEY); + } + + #[test] + fn with_key_generic_u8_rfc8891() { + use crypto_vectors::gost::rfc8891; + let magma = Magma::with_key(rfc8891::CIPHER_KEY_U8_ARRAY.clone()); + assert_eq!(magma.key, rfc8891::CIPHER_KEY); + } + + #[test] + fn set_key_generic_u32() { + use crypto_vectors::gost::rfc8891; + let mut magma = Magma::new(); + magma.set_key(rfc8891::CIPHER_KEY.clone()); + assert_eq!(magma.key, rfc8891::CIPHER_KEY); + } + + #[test] + fn set_key_generic_u8() { + use crypto_vectors::gost::rfc8891; + let mut magma = Magma::new(); + magma.set_key(rfc8891::CIPHER_KEY_U8_ARRAY.clone()); + assert_eq!(magma.key, rfc8891::CIPHER_KEY); + } + + #[test] + fn set_key_u32_rfc8891() { + use crypto_vectors::gost::rfc8891; + let mut magma = Magma::new(); + magma.set_key_u32(&rfc8891::CIPHER_KEY); + assert_eq!(magma.key, rfc8891::CIPHER_KEY); + } + + #[test] + fn set_key_u8_rfc8891() { + use crypto_vectors::gost::rfc8891; + let mut magma = Magma::new(); + magma.set_key_u8(&rfc8891::CIPHER_KEY_U8_ARRAY); + assert_eq!(magma.key, rfc8891::CIPHER_KEY); + } + + #[test] + fn round_keys_rfc8891() { + // Test vectors RFC8891: + // https://datatracker.ietf.org/doc/html/rfc8891.html#section-a.3 + + use crypto_vectors::gost::rfc8891; + let mut magma = Magma::new(); + magma.set_key_u32(&rfc8891::CIPHER_KEY); + assert_eq!(magma.round_keys, rfc8891::ROUND_KEYS); + } + + #[test] + fn transformation_t_rfc8891() { + // Test vectors RFC8891: + // https://datatracker.ietf.org/doc/html/rfc8891.html#section-a.1 + + use crypto_vectors::gost::rfc8891; + let t = rfc8891::TRANSFORMATION_T; + + let magma = Magma::new(); + assert_eq!(magma.transformation_t(t[0].0), t[0].1); + assert_eq!(magma.transformation_t(t[1].0), t[1].1); + assert_eq!(magma.transformation_t(t[2].0), t[2].1); + assert_eq!(magma.transformation_t(t[3].0), t[3].1); + } + + #[test] + fn transformation_g_rfc8891() { + // Test vectors RFC8891: + // https://datatracker.ietf.org/doc/html/rfc8891.html#section-a.2 + + use crypto_vectors::gost::rfc8891; + let g = rfc8891::TRANSFORMATION_G; + + let magma = Magma::new(); + + assert_eq!(magma.transformation_g(g[0].0 .0, g[0].0 .1), g[0].1); + assert_eq!(magma.transformation_g(g[1].0 .0, g[1].0 .1), g[1].1); + assert_eq!(magma.transformation_g(g[2].0 .0, g[2].0 .1), g[2].1); + assert_eq!(magma.transformation_g(g[3].0 .0, g[3].0 .1), g[3].1); + } + + #[test] + fn transformation_big_g_rfc8891() { + // Test vectors RFC8891: + // https://datatracker.ietf.org/doc/html/rfc8891.html#section-a.4 + + use crypto_vectors::gost::rfc8891; + let big_g = rfc8891::TRANSFORMATION_BIG_G; + + let mut magma = Magma::new(); + magma.set_key_u32(&rfc8891::CIPHER_KEY); + + let (mut a_1, mut a_0) = utils::u64_split(rfc8891::PLAINTEXT); + + for round in 0..32 { + (a_1, a_0) = magma.transformation_big_g(magma.round_keys[round], a_1, a_0); + assert_eq!(big_g[round], (a_1, a_0)); + } + } + + #[test] + fn encrypt_rfc8891() { + // Test vectors RFC8891: + // https://datatracker.ietf.org/doc/html/rfc8891.html#name-test-encryption + + use crypto_vectors::gost::rfc8891; + let mut magma = Magma::new(); + magma.set_key_u32(&rfc8891::CIPHER_KEY); + assert_eq!(magma.encrypt(rfc8891::PLAINTEXT), rfc8891::CIPHERTEXT); + } + + #[test] + fn decrypt_rfc8891() { + // Test vectors RFC8891: + // https://datatracker.ietf.org/doc/html/rfc8891.html#name-test-decryption + + use crypto_vectors::gost::rfc8891; + let mut magma = Magma::new(); + magma.set_key_u32(&rfc8891::CIPHER_KEY); + assert_eq!(magma.decrypt(rfc8891::CIPHERTEXT), rfc8891::PLAINTEXT); + } + + #[test] + fn encrypt_rfc5830() { + // Test vectors for GOST 28147-89 + // https://www.rfc-editor.org/rfc/rfc5831#section-7 + + use crypto_vectors::gost::rfc5831; + + let mut magma = Magma::new(); + magma.set_substitution_box(&SUBSTITUTION_BOX_RFC5831); + + magma.set_key_u32(&rfc5831::CIPHER_KEY1); + assert_eq!(magma.encrypt(rfc5831::PLAINTEXT), rfc5831::CIPHERTEXT1); + + magma.set_key_u32(&rfc5831::CIPHER_KEY2); + assert_eq!(magma.encrypt(rfc5831::PLAINTEXT), rfc5831::CIPHERTEXT2); + + magma.set_key_u32(&rfc5831::CIPHER_KEY3); + assert_eq!(magma.encrypt(rfc5831::PLAINTEXT), rfc5831::CIPHERTEXT3); + + magma.set_key_u32(&rfc5831::CIPHER_KEY4); + assert_eq!(magma.encrypt(rfc5831::PLAINTEXT), rfc5831::CIPHERTEXT4); + } + + #[test] + fn decrypt_rfc5830() { + // Test vectors for GOST 28147-89 + // https://www.rfc-editor.org/rfc/rfc5831#section-7 + + use crypto_vectors::gost::rfc5831; + + let mut magma = Magma::new(); + magma.set_substitution_box(&SUBSTITUTION_BOX_RFC5831); + + magma.set_key_u32(&rfc5831::CIPHER_KEY1); + assert_eq!(magma.decrypt(rfc5831::CIPHERTEXT1), rfc5831::PLAINTEXT); + + magma.set_key_u32(&rfc5831::CIPHER_KEY2); + assert_eq!(magma.decrypt(rfc5831::CIPHERTEXT2), rfc5831::PLAINTEXT); + + magma.set_key_u32(&rfc5831::CIPHER_KEY3); + assert_eq!(magma.decrypt(rfc5831::CIPHERTEXT3), rfc5831::PLAINTEXT); + + magma.set_key_u32(&rfc5831::CIPHER_KEY4); + assert_eq!(magma.decrypt(rfc5831::CIPHERTEXT4), rfc5831::PLAINTEXT); + } + + #[test] + fn encrypt_gost_r_34_13_2015_ecb() { + use crypto_vectors::gost::r3413_2015; + + let mut magma = Magma::new(); + magma.set_key_u32(&r3413_2015::CIPHER_KEY); + assert_eq!( + magma.encrypt(r3413_2015::PLAINTEXT1), + r3413_2015::CIPHERTEXT1_ECB + ); + assert_eq!( + magma.encrypt(r3413_2015::PLAINTEXT2), + r3413_2015::CIPHERTEXT2_ECB + ); + assert_eq!( + magma.encrypt(r3413_2015::PLAINTEXT3), + r3413_2015::CIPHERTEXT3_ECB + ); + assert_eq!( + magma.encrypt(r3413_2015::PLAINTEXT4), + r3413_2015::CIPHERTEXT4_ECB + ); + } + + #[test] + fn decrypt_gost_r_34_13_2015_ecb() { + use crypto_vectors::gost::r3413_2015; + + let mut magma = Magma::new(); + magma.set_key_u32(&r3413_2015::CIPHER_KEY); + + assert_eq!( + magma.decrypt(r3413_2015::CIPHERTEXT1_ECB), + r3413_2015::PLAINTEXT1 + ); + assert_eq!( + magma.decrypt(r3413_2015::CIPHERTEXT2_ECB), + r3413_2015::PLAINTEXT2 + ); + assert_eq!( + magma.decrypt(r3413_2015::CIPHERTEXT3_ECB), + r3413_2015::PLAINTEXT3 + ); + assert_eq!( + magma.decrypt(r3413_2015::CIPHERTEXT4_ECB), + r3413_2015::PLAINTEXT4 + ); + } + +} diff --git a/cipher_magma/src/core/mod.rs b/cipher_magma/src/core/mod.rs new file mode 100644 index 0000000..db863fc --- /dev/null +++ b/cipher_magma/src/core/mod.rs @@ -0,0 +1,4 @@ +pub mod constants; +pub mod cipher_key; +pub mod utils; +pub mod magma; diff --git a/cipher_magma/src/magma/utils.rs b/cipher_magma/src/core/utils.rs similarity index 100% rename from cipher_magma/src/magma/utils.rs rename to cipher_magma/src/core/utils.rs diff --git a/cipher_magma/src/lib.rs b/cipher_magma/src/lib.rs index f76bd8c..280bfe2 100644 --- a/cipher_magma/src/lib.rs +++ b/cipher_magma/src/lib.rs @@ -14,13 +14,20 @@ //! * **CFB** - Cipher Feedback Mode //! * **MAC** - Message Authentication Code Generation Mode -pub mod magma; +pub mod core; +pub mod stream; -// re-export the magma core -pub use magma::Magma; +// re-export the core block-ciphering operations +pub use crate::core::magma::Magma; + +// re-export constants +pub use crate::core::constants; + +// re-export the stream ciphering operations +pub use stream::magma_stream::MagmaStream; // re-export the CipherOperation -pub use magma::cipher_operation::CipherOperation; +pub use stream::cipher_operation::CipherOperation; // re-export the cipher modes -pub use magma::cipher_mode::{CipherMode, ecb, ctr, ctr_acpkm, ofb, cbc, cfb, mac}; +pub use stream::cipher_mode::{CipherMode, ecb, ctr, ctr_acpkm, ofb, cbc, cfb, mac}; diff --git a/cipher_magma/src/magma.rs b/cipher_magma/src/magma.rs deleted file mode 100644 index a7ca72b..0000000 --- a/cipher_magma/src/magma.rs +++ /dev/null @@ -1,949 +0,0 @@ -//! Block Cipher "Magma" -//! -//! Implemented and tested according to specifications: -//! 1. [RFC 8891](https://datatracker.ietf.org/doc/html/rfc8891.html) a.k.a **GOST R 34.12-2015** -//! 2. [RFC 5830](https://datatracker.ietf.org/doc/html/rfc5830) a.k.a **GOST 28147-89** -//! 3. Block Cipher Modes: [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) -//! -//! [Cipher Modes](https://tc26.ru/standard/gost/GOST_R_3413-2015.pdf) -//! * **ECB** - Electronic Codebook Mode -//! * **CTR** - Counter Encryption Mode -//! * **CTR-ACPKM** - Counter Encryption Mode as per [RFC8645](https://www.rfc-editor.org/rfc/rfc8645.html), [P 1323565.1.017— 2018](https://standartgost.ru/g/%D0%A0_1323565.1.017-2018) -//! * **OFB** - Output Feedback Mode -//! * **CBC** - Cipher Block Chaining Mode -//! * **CFB** - Cipher Feedback Mode -//! * **MAC** - Message Authentication Code Generation Mode - -pub mod cipher_mode; -pub mod cipher_operation; -pub mod utils; - -use std::collections::VecDeque; - -use cipher_mode::{cbc, cfb, ctr, ctr_acpkm, ecb, mac, ofb, CipherMode}; -use cipher_operation::CipherOperation; - -/// Block Cipher "Magma" -pub struct Magma { - key: [u32; 8], - round_keys: [u32; 32], - substitution_box: [u8; 128], - iv: Vec, - context: Context, -} - -pub enum CipherKey { - ArrayU8([u8;32]), - ArrayU32([u32;8]), -} - -impl From<[u8;32]> for CipherKey { - fn from(array_u8: [u8;32]) -> Self { - Self::ArrayU8(array_u8) - } -} - -impl From<[u32;8]> for CipherKey { - fn from(array_u32: [u32;8]) -> Self { - Self::ArrayU32(array_u32) - } -} - -#[derive(Clone)] -struct Context { - operation: Option, - mode: Option, - padded: bool, - feedback: Feedback -} -impl Context { - fn new() -> Self { - Context { operation: None, mode: None, padded: false, feedback: Feedback::new() } - } -} - -#[derive(Clone)] -struct Feedback { - block: Option, - vector: Option> -} - -impl Feedback { - fn new() -> Self { - Feedback { block: None, vector: None } - } -} - -impl Magma { - /// Substitution Box (S-Box) data according to [Appendix C. RFC7836](https://datatracker.ietf.org/doc/html/rfc7836#appendix-C) - /// - /// Parameter set: id-tc26-gost-28147-param-Z - pub const SUBSTITUTION_BOX_RFC7836: [u8; 128] = [ - 0xC, 0x4, 0x6, 0x2, 0xA, 0x5, 0xB, 0x9, 0xE, 0x8, 0xD, 0x7, 0x0, 0x3, 0xF, 0x1, 0x6, 0x8, - 0x2, 0x3, 0x9, 0xA, 0x5, 0xC, 0x1, 0xE, 0x4, 0x7, 0xB, 0xD, 0x0, 0xF, 0xB, 0x3, 0x5, 0x8, - 0x2, 0xF, 0xA, 0xD, 0xE, 0x1, 0x7, 0x4, 0xC, 0x9, 0x6, 0x0, 0xC, 0x8, 0x2, 0x1, 0xD, 0x4, - 0xF, 0x6, 0x7, 0x0, 0xA, 0x5, 0x3, 0xE, 0x9, 0xB, 0x7, 0xF, 0x5, 0xA, 0x8, 0x1, 0x6, 0xD, - 0x0, 0x9, 0x3, 0xE, 0xB, 0x4, 0x2, 0xC, 0x5, 0xD, 0xF, 0x6, 0x9, 0x2, 0xC, 0xA, 0xB, 0x7, - 0x8, 0x1, 0x4, 0x3, 0xE, 0x0, 0x8, 0xE, 0x2, 0x5, 0x6, 0x9, 0x1, 0xC, 0xF, 0x4, 0xB, 0x0, - 0xD, 0xA, 0x3, 0x7, 0x1, 0x7, 0xE, 0xD, 0x0, 0x5, 0x8, 0x3, 0x4, 0xF, 0xA, 0x6, 0x9, 0xC, - 0xB, 0x2, - ]; - - /// Substitution Box (S-Box) data according to [RFC5831](https://datatracker.ietf.org/doc/html/rfc5831#section-7.1) - /// - /// As per [Appendix B of RFC8891](https://datatracker.ietf.org/doc/html/rfc8891.html#section-appendix.b) data values converted - /// from little-endian to big-endian format. - /// - /// OID: 1.2.643.2.2.30.0 - pub const SUBSTITUTION_BOX_RFC5831: [u8; 128] = [ - 0x4, 0xA, 0x9, 0x2, 0xD, 0x8, 0x0, 0xE, 0x6, 0xB, 0x1, 0xC, 0x7, 0xF, 0x5, 0x3, 0xE, 0xB, - 0x4, 0xC, 0x6, 0xD, 0xF, 0xA, 0x2, 0x3, 0x8, 0x1, 0x0, 0x7, 0x5, 0x9, 0x5, 0x8, 0x1, 0xD, - 0xA, 0x3, 0x4, 0x2, 0xE, 0xF, 0xC, 0x7, 0x6, 0x0, 0x9, 0xB, 0x7, 0xD, 0xA, 0x1, 0x0, 0x8, - 0x9, 0xF, 0xE, 0x4, 0x6, 0xC, 0xB, 0x2, 0x5, 0x3, 0x6, 0xC, 0x7, 0x1, 0x5, 0xF, 0xD, 0x8, - 0x4, 0xA, 0x9, 0xE, 0x0, 0x3, 0xB, 0x2, 0x4, 0xB, 0xA, 0x0, 0x7, 0x2, 0x1, 0xD, 0x3, 0x6, - 0x8, 0x5, 0x9, 0xC, 0xF, 0xE, 0xD, 0xB, 0x4, 0x1, 0x3, 0xF, 0x5, 0x9, 0x0, 0xA, 0xE, 0x7, - 0x6, 0x8, 0x2, 0xC, 0x1, 0xF, 0xD, 0x0, 0x5, 0x7, 0xA, 0x4, 0x9, 0x2, 0x3, 0xE, 0x6, 0xB, - 0x8, 0xC, - ]; - - /// Initialization Vector (IV) - /// - /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) - /// - /// CTR Mode: Page 36, Section A.2.2, uses MSB(32) part of IV - /// - /// OFB Mode: Page 37, Section A.2.3, uses MSB(128) part of IV - /// - /// CFB Mode: Page 39, Section A.2.5, uses MSB(128) part of IV - pub const IV_GOST_R3413_2015: [u64; 3] = [ - 0x1234567890abcdef_u64, - 0x234567890abcdef1_u64, - 0x34567890abcdef12_u64, - ]; - - /// Р 1323565.1.017—2018 - /// - /// Section size N - /// - /// Page 7, CTR-ACPKM - const CTR_ACPKM_SECTION_SIZE_N: usize = 128; - - /// Р 1323565.1.017—2018 - /// - /// Constant D for ACPKM function - /// - /// Page 8, CTR-ACPKM - const CTR_ACPKM_D: [u8; 32] = [ - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, - 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, - 0x9E, 0x9F, - ]; - - /// Returns a new Magma by using RFC7836 based substitution box - /// - /// # Example - /// ``` - /// use cipher_magma::Magma; - /// let magma = Magma::new(); - /// ``` - pub fn new() -> Magma { - Magma { - key: [0u32; 8], - round_keys: [0u32; 32], - substitution_box: Magma::SUBSTITUTION_BOX_RFC7836.clone(), - iv: Vec::from(Magma::IV_GOST_R3413_2015), - context: Context::new(), - } - } - - /// Returns a new Magma initialized with given cipher key - /// - /// Uses RFC7836 based substitution box - /// - /// # Arguments - /// - /// * `key` - array `[u32;8]` or `[u8;32]` - /// - /// # Example - /// - /// ``` - /// use cipher_magma::Magma; - /// let key: [u32;8] = [ - /// 0xffeeddcc, 0xbbaa9988, 0x77665544, 0x33221100, 0xf0f1f2f3, 0xf4f5f6f7, 0xf8f9fafb, 0xfcfdfeff - /// ]; - /// - /// let magma = Magma::with_key(key); - /// ``` - /// Or - /// - /// ``` - /// use cipher_magma::Magma; - /// let key: [u8;32] = [ - /// 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, - /// 0x00, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, - /// 0xfe, 0xff, - /// ]; - /// - /// let magma = Magma::with_key(key); - /// ``` - pub fn with_key (key: T) -> Magma - where CipherKey: From { - let mut engine = Magma::new(); - engine.set_key(key); - engine - } - - /// Returns a new Magma initialized with given cipher key - /// - /// Uses RFC7836 based substitution box - /// - /// # Arguments - /// - /// * `key` - A reference to `[u32;8]` array - /// - /// # Example - /// ``` - /// use cipher_magma::Magma; - /// let key: [u32;8] = [ - /// 0xffeeddcc, 0xbbaa9988, 0x77665544, 0x33221100, 0xf0f1f2f3, 0xf4f5f6f7, 0xf8f9fafb, 0xfcfdfeff - /// ]; - /// - /// let magma = Magma::with_key_u32(&key); - /// ``` - pub fn with_key_u32(key: &[u32; 8]) -> Magma { - let mut engine = Magma::new(); - engine.set_key_u32(key); - engine - } - - /// Returns a new Magma initialized with given cipher key - /// - /// Uses RFC7836 based substitution box - /// - /// # Arguments - /// - /// * `key` - A reference to `[u8;32]` array - /// - /// # Example - /// ``` - /// use cipher_magma::Magma; - /// let key: [u8; 32] = [ - /// 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, - /// 0x00, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, - /// 0xfe, 0xff, - /// ]; - /// - /// let magma = Magma::with_key_u8(&key); - /// ``` - pub fn with_key_u8(key: &[u8; 32]) -> Magma { - let mut engine = Magma::new(); - engine.set_key_u8(key); - engine - } - - /// Sets the substitution box - /// - /// # Arguments - /// - /// * `substitution_box` - A reference to `[u8;128]` array - pub fn set_substitution_box(&mut self, substitution_box: &[u8; 128]) { - self.substitution_box.copy_from_slice(substitution_box); - self.reset_feedback(); - } - - /// Sets the Initialization Vector (IV) - /// - /// # Arguments - /// - /// * `iv` - A slice to `&[u64]` array - /// - /// **Attention**: `CTR` Mode uses only the MSB(32) part of IV - pub fn set_iv(&mut self, iv: &[u64]) { - self.iv = Vec::from(iv); - self.reset_feedback(); - } - - #[inline] - fn prepare_vector_ctr(&self) -> u64 { - self.ensure_iv_not_empty(); - // [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) - // CTR Mode: Page 36, Section A.2.2, uses MSB(32) part of IV extended to 64bit with Initial Nonce - // Initial Nonce: 0x00000000 - self.iv[0] & 0xffffffff_00000000 - } - - #[inline] - fn ensure_iv_not_empty(&self) { - if self.iv.is_empty() { - panic!("Initialization vector is empty!"); - } - } - - /// Sets the cipher key from array - /// - /// # Arguments - /// - /// * `key` - a `[u8;32]' or `[u32;8]` array - pub fn set_key (&mut self, key: T) where CipherKey: From { - let cipher_key = CipherKey::from(key); - match cipher_key { - CipherKey::ArrayU8(k) => { self.set_key_u8(&k) } - CipherKey::ArrayU32(k) => { self.set_key_u32(&k) }, - }; - } - - /// Sets the cipher key from `[u32;8]` array - /// - /// # Arguments - /// - /// * `key` - A reference to `[u32;8]` array - pub fn set_key_u32(&mut self, key: &[u32; 8]) { - self.key.clone_from(key); - self.prepare_round_keys(); - self.reset_feedback(); - } - - /// Sets the cipher key from slice of u8 bytes - /// - /// # Arguments - /// - /// * `bytes` - A `&[u8]` slice with 32 byte elements - pub fn set_key_u8(&mut self, bytes: &[u8]) { - self.set_key_u32(&Self::key_from_u8(bytes)); - } - - fn key_from_u8(bytes: &[u8]) -> [u32;8] { - assert!(bytes.len() == 32); - let mut key = [0_u32;8]; - let mut array_u8 = [0u8; 4]; - for (index, chunk) in bytes.chunks(4).enumerate() { - chunk.iter().enumerate().for_each(|t| array_u8[t.0] = *t.1); - key[index] = u32::from_be_bytes(array_u8); - } - key - } - - /// Prepares [round keys](https://datatracker.ietf.org/doc/html/rfc8891.html#section-4.3) from the cipher key - fn prepare_round_keys(&mut self) { - const ROUND_KEY_POSITION: [u8; 32] = [ - 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, - 2, 1, 0, - ]; - - for index in 0..32 { - let round_key_position = ROUND_KEY_POSITION[index] as usize; - self.round_keys[index] = self.key[round_key_position]; - } - } - - /// Resets the context of stream ciphering - pub fn reset_context(&mut self) { - self.context = Context::new(); - } - - - /// Resets the feedback state of stream ciphering - pub fn reset_feedback(&mut self) { - self.context.feedback = Feedback::new(); - } - - /// [Transformation](https://datatracker.ietf.org/doc/html/rfc8891.html#section-4.2) - /// - /// `t: V_32 -> V_32` - #[inline] - fn transformation_t(&self, a: u32) -> u32 { - let mut res: u32 = 0; - let mut shift_count = 0; - for i in 0..8 { - let v = (a >> shift_count) & 0xF; - let s = self.substitution_box[(i * 16 + v) as usize] as u32; - res |= s << shift_count; - shift_count += 4; - } - res - } - - /// [Transformation](https://datatracker.ietf.org/doc/html/rfc8891.html#section-4.2) - /// - /// `g[k]: V_32 -> V_32` - #[inline] - fn transformation_g(&self, k: u32, a: u32) -> u32 { - let res = self.transformation_t(((k as u64) + (a as u64)) as u32); - res.rotate_left(11) - } - - /// [Transformation](https://datatracker.ietf.org/doc/html/rfc8891.html#section-4.2) - /// - /// `G[k]: V_32[*]V_32 -> V_32[*]V_32` - #[inline] - fn transformation_big_g(&self, k: u32, a_1: u32, a_0: u32) -> (u32, u32) { - (a_0, self.transformation_g(k, a_0) ^ a_1) - } - - /// Returns [encrypted block](https://datatracker.ietf.org/doc/html/rfc8891.html#section-5.1) as `u64` value - /// - /// # Arguments - /// - /// * `block_in` - a plaintext value as `u64` - #[inline] - pub fn encrypt(&self, block_in: u64) -> u64 { - // split the input block into u32 parts - let (mut a_1, mut a_0) = utils::u64_split(block_in); - - // crypto transformations - let mut round = 0; - while round < 32 { - (a_1, a_0) = self.transformation_big_g(self.round_keys[round], a_1, a_0); - round += 1; - } - - // join u32 parts into u64 block - utils::u32_join(a_0, a_1) - } - - /// Returns [decrypted block](https://datatracker.ietf.org/doc/html/rfc8891.html#section-5.2) as `u64` value - /// - /// # Arguments - /// - /// * `block_in` - a ciphertext value as `u64` - #[inline] - pub fn decrypt(&self, block_in: u64) -> u64 { - // split the input block into u32 parts - let (mut b_1, mut b_0) = utils::u64_split(block_in); - - // crypto transformations - let mut round = 32; - while round != 0 { - round -= 1; - (b_1, b_0) = self.transformation_big_g(self.round_keys[round], b_1, b_0); - } - - // join u32 parts into u64 block - utils::u32_join(b_0, b_1) - } - - // check and update cipher context - fn update_context(&mut self, cipher_operation: &CipherOperation, cipher_mode: &CipherMode) { - if self.context.operation.as_ref() != Some(&cipher_operation) - || self.context.mode.as_ref() != Some(&cipher_mode) - { - self.context.operation = Some(cipher_operation.clone()); - self.context.mode = Some(cipher_mode.clone()); - self.reset_feedback(); - } - } - - /// Returns resulting vector as `Vec` - /// - /// # Arguments - /// - /// * `buf` - a slice of `&[u8]` input data - /// * `cipher_operation` - reference to `CipherOperation` - /// * `cipher_mode` - reference to `CipherMode` - pub fn cipher( - &mut self, - buf: &[u8], - cipher_operation: &CipherOperation, - cipher_mode: &CipherMode, - ) -> Vec { - - // check and update feedback state - self.update_context(cipher_operation, cipher_mode); - - match cipher_operation { - CipherOperation::Encrypt => match cipher_mode { - CipherMode::ECB => ecb::encrypt(self, buf), - CipherMode::CTR => ctr::encrypt(self, buf), - CipherMode::CTR_ACPKM => ctr_acpkm::encrypt(self, buf), - CipherMode::OFB => ofb::encrypt(self, buf), - CipherMode::CBC => cbc::encrypt(self, buf), - CipherMode::CFB => cfb::encrypt(self, buf), - CipherMode::MAC => { - panic!("CipherMode::MAC can not be used in encrypting operation!") - } - }, - CipherOperation::Decrypt => match cipher_mode { - CipherMode::ECB => ecb::decrypt(self, buf), - CipherMode::CTR => ctr::decrypt(self, buf), - CipherMode::CTR_ACPKM => ctr_acpkm::decrypt(self, buf), - CipherMode::OFB => ofb::decrypt(self, buf), - CipherMode::CBC => cbc::decrypt(self, buf), - CipherMode::CFB => cfb::decrypt(self, buf), - CipherMode::MAC => { - panic!("CipherMode::MAC can not be used in decrypting operation!") - } - }, - CipherOperation::MessageAuthentication => match cipher_mode { - CipherMode::MAC => mac::calculate(self, buf).to_be_bytes().to_vec(), - _ => panic!("Only CipherMode::MAC can be used in MessageAuthentication!"), - }, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn has_padding() { - assert_eq!(CipherMode::ECB.has_padding(), true); - assert_eq!(CipherMode::CTR.has_padding(), false); - assert_eq!(CipherMode::CTR_ACPKM.has_padding(), false); - assert_eq!(CipherMode::OFB.has_padding(), false); - assert_eq!(CipherMode::CBC.has_padding(), true); - assert_eq!(CipherMode::CFB.has_padding(), false); - assert_eq!(CipherMode::MAC.has_padding(), true); - } - - #[test] - fn default_initialization() { - let magma = Magma::new(); - assert_eq!(magma.key, [0u32; 8]); - assert_eq!(magma.round_keys, [0u32; 32]); - assert_eq!(magma.substitution_box, Magma::SUBSTITUTION_BOX_RFC7836); - assert_eq!(magma.iv, Magma::IV_GOST_R3413_2015); - } - - #[test] - fn with_key_generic_u32_rfc8891() { - use crypto_vectors::gost::rfc8891; - let magma = Magma::with_key(rfc8891::CIPHER_KEY.clone()); - assert_eq!(magma.key, rfc8891::CIPHER_KEY); - } - - #[test] - fn with_key_generic_u8_rfc8891() { - use crypto_vectors::gost::rfc8891; - let magma = Magma::with_key(rfc8891::CIPHER_KEY_U8_ARRAY.clone()); - assert_eq!(magma.key, rfc8891::CIPHER_KEY); - } - - #[test] - fn with_key_u32_rfc8891() { - use crypto_vectors::gost::rfc8891; - let magma = Magma::with_key_u32(&rfc8891::CIPHER_KEY); - assert_eq!(magma.key, rfc8891::CIPHER_KEY); - } - - #[test] - fn with_key_u8_rfc8891() { - use crypto_vectors::gost::rfc8891; - let magma = Magma::with_key_u8(&rfc8891::CIPHER_KEY_U8_ARRAY); - assert_eq!(magma.key, rfc8891::CIPHER_KEY); - } - - #[test] - fn set_key_u32_rfc8891() { - use crypto_vectors::gost::rfc8891; - let mut magma = Magma::new(); - magma.set_key_u32(&rfc8891::CIPHER_KEY); - assert_eq!(magma.key, rfc8891::CIPHER_KEY); - } - - #[test] - fn set_key_u8_rfc8891() { - use crypto_vectors::gost::rfc8891; - let mut magma = Magma::new(); - magma.set_key_u8(&rfc8891::CIPHER_KEY_U8_ARRAY); - assert_eq!(magma.key, rfc8891::CIPHER_KEY); - } - - #[test] - fn set_key_generic_u32() { - use crypto_vectors::gost::rfc8891; - let mut magma = Magma::new(); - magma.set_key(rfc8891::CIPHER_KEY.clone()); - assert_eq!(magma.key, rfc8891::CIPHER_KEY); - } - - #[test] - fn set_key_generic_u8() { - use crypto_vectors::gost::rfc8891; - let mut magma = Magma::new(); - magma.set_key(rfc8891::CIPHER_KEY_U8_ARRAY.clone()); - assert_eq!(magma.key, rfc8891::CIPHER_KEY); - } - - #[test] - fn set_initialization_vector() { - let mut magma = Magma::new(); - let initialization_vector = vec![0x11223344_u64]; - magma.set_iv(&initialization_vector); - assert_eq!(magma.iv, initialization_vector); - } - - #[test] - fn round_keys_rfc8891() { - // Test vectors RFC8891: - // https://datatracker.ietf.org/doc/html/rfc8891.html#section-a.3 - - use crypto_vectors::gost::rfc8891; - let magma = Magma::with_key_u32(&rfc8891::CIPHER_KEY); - assert_eq!(magma.round_keys, rfc8891::ROUND_KEYS); - } - - #[test] - fn transformation_t_rfc8891() { - // Test vectors RFC8891: - // https://datatracker.ietf.org/doc/html/rfc8891.html#section-a.1 - - use crypto_vectors::gost::rfc8891; - let t = rfc8891::TRANSFORMATION_T; - - let magma = Magma::new(); - assert_eq!(magma.transformation_t(t[0].0), t[0].1); - assert_eq!(magma.transformation_t(t[1].0), t[1].1); - assert_eq!(magma.transformation_t(t[2].0), t[2].1); - assert_eq!(magma.transformation_t(t[3].0), t[3].1); - } - - #[test] - fn transformation_g_rfc8891() { - // Test vectors RFC8891: - // https://datatracker.ietf.org/doc/html/rfc8891.html#section-a.2 - - use crypto_vectors::gost::rfc8891; - let g = rfc8891::TRANSFORMATION_G; - - let magma = Magma::new(); - - assert_eq!(magma.transformation_g(g[0].0 .0, g[0].0 .1), g[0].1); - assert_eq!(magma.transformation_g(g[1].0 .0, g[1].0 .1), g[1].1); - assert_eq!(magma.transformation_g(g[2].0 .0, g[2].0 .1), g[2].1); - assert_eq!(magma.transformation_g(g[3].0 .0, g[3].0 .1), g[3].1); - } - - #[test] - fn transformation_big_g_rfc8891() { - // Test vectors RFC8891: - // https://datatracker.ietf.org/doc/html/rfc8891.html#section-a.4 - - use crypto_vectors::gost::rfc8891; - let big_g = rfc8891::TRANSFORMATION_BIG_G; - - let magma = Magma::with_key_u32(&rfc8891::CIPHER_KEY); - - let (mut a_1, mut a_0) = utils::u64_split(rfc8891::PLAINTEXT); - - for round in 0..32 { - (a_1, a_0) = magma.transformation_big_g(magma.round_keys[round], a_1, a_0); - assert_eq!(big_g[round], (a_1, a_0)); - } - } - - #[test] - fn encrypt_rfc8891() { - // Test vectors RFC8891: - // https://datatracker.ietf.org/doc/html/rfc8891.html#name-test-encryption - - use crypto_vectors::gost::rfc8891; - let magma = Magma::with_key_u32(&rfc8891::CIPHER_KEY); - assert_eq!(magma.encrypt(rfc8891::PLAINTEXT), rfc8891::CIPHERTEXT); - } - - #[test] - fn decrypt_rfc8891() { - // Test vectors RFC8891: - // https://datatracker.ietf.org/doc/html/rfc8891.html#name-test-decryption - - use crypto_vectors::gost::rfc8891; - let magma = Magma::with_key_u32(&rfc8891::CIPHER_KEY); - assert_eq!(magma.decrypt(rfc8891::CIPHERTEXT), rfc8891::PLAINTEXT); - } - - #[test] - fn encrypt_rfc5830() { - // Test vectors for GOST 28147-89 - // https://www.rfc-editor.org/rfc/rfc5831#section-7 - - use crypto_vectors::gost::rfc5831; - - let mut magma = Magma::new(); - magma.set_substitution_box(&Magma::SUBSTITUTION_BOX_RFC5831); - - magma.set_key_u32(&rfc5831::CIPHER_KEY1); - assert_eq!(magma.encrypt(rfc5831::PLAINTEXT), rfc5831::CIPHERTEXT1); - - magma.set_key_u32(&rfc5831::CIPHER_KEY2); - assert_eq!(magma.encrypt(rfc5831::PLAINTEXT), rfc5831::CIPHERTEXT2); - - magma.set_key_u32(&rfc5831::CIPHER_KEY3); - assert_eq!(magma.encrypt(rfc5831::PLAINTEXT), rfc5831::CIPHERTEXT3); - - magma.set_key_u32(&rfc5831::CIPHER_KEY4); - assert_eq!(magma.encrypt(rfc5831::PLAINTEXT), rfc5831::CIPHERTEXT4); - } - - #[test] - fn decrypt_rfc5830() { - // Test vectors for GOST 28147-89 - // https://www.rfc-editor.org/rfc/rfc5831#section-7 - - use crypto_vectors::gost::rfc5831; - - let mut magma = Magma::new(); - magma.set_substitution_box(&Magma::SUBSTITUTION_BOX_RFC5831); - - magma.set_key_u32(&rfc5831::CIPHER_KEY1); - assert_eq!(magma.decrypt(rfc5831::CIPHERTEXT1), rfc5831::PLAINTEXT); - - magma.set_key_u32(&rfc5831::CIPHER_KEY2); - assert_eq!(magma.decrypt(rfc5831::CIPHERTEXT2), rfc5831::PLAINTEXT); - - magma.set_key_u32(&rfc5831::CIPHER_KEY3); - assert_eq!(magma.decrypt(rfc5831::CIPHERTEXT3), rfc5831::PLAINTEXT); - - magma.set_key_u32(&rfc5831::CIPHER_KEY4); - assert_eq!(magma.decrypt(rfc5831::CIPHERTEXT4), rfc5831::PLAINTEXT); - } - - #[test] - fn encrypt_gost_r_34_13_2015_ecb() { - use crypto_vectors::gost::r3413_2015; - - let magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); - assert_eq!( - magma.encrypt(r3413_2015::PLAINTEXT1), - r3413_2015::CIPHERTEXT1_ECB - ); - assert_eq!( - magma.encrypt(r3413_2015::PLAINTEXT2), - r3413_2015::CIPHERTEXT2_ECB - ); - assert_eq!( - magma.encrypt(r3413_2015::PLAINTEXT3), - r3413_2015::CIPHERTEXT3_ECB - ); - assert_eq!( - magma.encrypt(r3413_2015::PLAINTEXT4), - r3413_2015::CIPHERTEXT4_ECB - ); - } - - #[test] - fn decrypt_gost_r_34_13_2015_ecb() { - use crypto_vectors::gost::r3413_2015; - let magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); - assert_eq!( - magma.decrypt(r3413_2015::CIPHERTEXT1_ECB), - r3413_2015::PLAINTEXT1 - ); - assert_eq!( - magma.decrypt(r3413_2015::CIPHERTEXT2_ECB), - r3413_2015::PLAINTEXT2 - ); - assert_eq!( - magma.decrypt(r3413_2015::CIPHERTEXT3_ECB), - r3413_2015::PLAINTEXT3 - ); - assert_eq!( - magma.decrypt(r3413_2015::CIPHERTEXT4_ECB), - r3413_2015::PLAINTEXT4 - ); - } - - #[test] - fn cipher_ecb_gost_r_34_13_2015() { - use crypto_vectors::gost::r3413_2015; - let mut source = Vec::::new(); - source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); - let encrypted = magma.cipher(&source, &CipherOperation::Encrypt, &CipherMode::ECB); - assert!(!encrypted.is_empty()); - - let mut expected = Vec::::new(); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT1_ECB.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT2_ECB.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT3_ECB.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT4_ECB.to_be_bytes()); - assert_eq!(encrypted, expected); - - let decrypted = magma.cipher(&encrypted, &CipherOperation::Decrypt, &CipherMode::ECB); - assert_eq!(decrypted, source); - } - - #[test] - fn cipher_ctr_gost_r_34_13_2015() { - // Test vectors GOST R 34.13-2015 - // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf - // Page 36, Section A.2.2 - - use crypto_vectors::gost::r3413_2015; - let mut source = Vec::::new(); - source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); - let encrypted = magma.cipher(&source, &CipherOperation::Encrypt, &CipherMode::CTR); - assert!(!encrypted.is_empty()); - - let mut expected = Vec::::new(); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT1_CTR.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT2_CTR.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT3_CTR.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT4_CTR.to_be_bytes()); - assert_eq!(encrypted, expected); - - let decrypted = magma.cipher(&encrypted, &CipherOperation::Decrypt, &CipherMode::CTR); - assert_eq!(decrypted, source); - } - - #[test] - fn cipher_ctr_acpkm_r_1323565_1_017_2018() { - // Test Vectors CTR-ACPKM - // Р 1323565.1.017—2018 - // https://standartgost.ru/g/%D0%A0_1323565.1.017-2018 - // Page 11 - - use crypto_vectors::gost::r1323565_1_017_2018::ctr_acpkm; - - let mut magma = Magma::new(); - magma.set_key_u8(&ctr_acpkm::CIPHER_KEY); - - let encrypted = magma.cipher( - &ctr_acpkm::PLAINTEXT, - &CipherOperation::Encrypt, - &CipherMode::CTR_ACPKM, - ); - assert!(!encrypted.is_empty()); - - assert_eq!(encrypted, ctr_acpkm::CIPHERTEXT); - - let decrypted = magma.cipher( - &encrypted, - &CipherOperation::Decrypt, - &CipherMode::CTR_ACPKM, - ); - assert_eq!(decrypted, ctr_acpkm::PLAINTEXT); - } - - #[test] - fn cipher_ofb_gost_r_34_13_2015() { - // Test vectors GOST R 34.13-2015 - // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf - // Page 37, Section A.2.3 - - use crypto_vectors::gost::r3413_2015; - let mut source = Vec::::new(); - source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); - - // [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) - // OFB Mode: Page 37, Section A.2.3, uses MSB(128) part of IV - magma.set_iv(&Magma::IV_GOST_R3413_2015[..2]); - - let encrypted = magma.cipher(&source, &CipherOperation::Encrypt, &CipherMode::OFB); - assert!(!encrypted.is_empty()); - - let mut expected = Vec::::new(); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT1_OFB.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT2_OFB.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT3_OFB.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT4_OFB.to_be_bytes()); - assert_eq!(encrypted, expected); - - let decrypted = magma.cipher(&encrypted, &CipherOperation::Decrypt, &CipherMode::OFB); - assert_eq!(decrypted, source); - } - - #[test] - fn cipher_cbc_gost_r_34_13_2015() { - // Test vectors GOST R 34.13-2015 - // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf - // Page 38, Section A.2.4 - use crypto_vectors::gost::r3413_2015; - let mut source = Vec::::new(); - source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); - let encrypted = magma.cipher(&source, &CipherOperation::Encrypt, &CipherMode::CBC); - assert!(!encrypted.is_empty()); - - let mut expected = Vec::::new(); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT1_CBC.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT2_CBC.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT3_CBC.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT4_CBC.to_be_bytes()); - assert_eq!(encrypted, expected); - - let decrypted = magma.cipher(&encrypted, &CipherOperation::Decrypt, &CipherMode::CBC); - assert_eq!(decrypted, source); - } - - #[test] - fn cipher_cfb_gost_r_34_13_2015() { - // Test vectors GOST R 34.13-2015 - // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf - // Page 39, Section A.2.5 - use crypto_vectors::gost::r3413_2015; - let mut source = Vec::::new(); - source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); - source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); - - // [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) - // CFB Mode: Page 39, Section A.2.5, uses MSB(128) part of IV - magma.set_iv(&Magma::IV_GOST_R3413_2015[..2]); - - let encrypted = magma.cipher(&source, &CipherOperation::Encrypt, &CipherMode::CFB); - assert!(!encrypted.is_empty()); - - let mut expected = Vec::::new(); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT1_CFB.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT2_CFB.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT3_CFB.to_be_bytes()); - expected.extend_from_slice(&r3413_2015::CIPHERTEXT4_CFB.to_be_bytes()); - assert_eq!(encrypted, expected); - - let decrypted = magma.cipher(&encrypted, &CipherOperation::Decrypt, &CipherMode::CFB); - assert_eq!(decrypted, source); - } - - #[test] - fn cipher_mac_gost_r_34_13_2015() { - // Test vectors GOST R 34.13-2015 - // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf - // Page 40, Section A.2.6 - - use crypto_vectors::gost::r3413_2015; - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); - - let mut src_buf = Vec::::new(); - src_buf.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); - src_buf.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); - src_buf.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); - src_buf.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - - let mac_vec = magma.cipher( - &src_buf, - &CipherOperation::MessageAuthentication, - &CipherMode::MAC, - ); - assert_eq!(mac_vec.len(), 4); - - let mut array_u8 = [0u8; 4]; - mac_vec - .iter() - .enumerate() - .for_each(|t| array_u8[t.0] = *t.1); - let mac = u32::from_be_bytes(array_u8); - assert_eq!(mac, r3413_2015::MAC); - } -} diff --git a/cipher_magma/src/magma/cipher_mode.rs b/cipher_magma/src/stream/cipher_mode.rs similarity index 75% rename from cipher_magma/src/magma/cipher_mode.rs rename to cipher_magma/src/stream/cipher_mode.rs index 7dcc5e3..bc63c2d 100644 --- a/cipher_magma/src/magma/cipher_mode.rs +++ b/cipher_magma/src/stream/cipher_mode.rs @@ -19,7 +19,7 @@ pub mod cfb; pub mod mac; /// Cipher Mode -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Copy)] pub enum CipherMode { /// Electronic Codebook (ECB) Mode ECB, @@ -60,3 +60,19 @@ impl CipherMode { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn has_padding() { + assert_eq!(CipherMode::ECB.has_padding(), true); + assert_eq!(CipherMode::CTR.has_padding(), false); + assert_eq!(CipherMode::CTR_ACPKM.has_padding(), false); + assert_eq!(CipherMode::OFB.has_padding(), false); + assert_eq!(CipherMode::CBC.has_padding(), true); + assert_eq!(CipherMode::CFB.has_padding(), false); + assert_eq!(CipherMode::MAC.has_padding(), true); + } +} \ No newline at end of file diff --git a/cipher_magma/src/magma/cipher_mode/cbc.rs b/cipher_magma/src/stream/cipher_mode/cbc.rs similarity index 81% rename from cipher_magma/src/magma/cipher_mode/cbc.rs rename to cipher_magma/src/stream/cipher_mode/cbc.rs index 12b5748..fda7e4d 100644 --- a/cipher_magma/src/magma/cipher_mode/cbc.rs +++ b/cipher_magma/src/stream/cipher_mode/cbc.rs @@ -1,8 +1,7 @@ //! Implements Cipher Block Chaining (CBC) mode use std::collections::VecDeque; - -use crate::{magma::Magma, CipherOperation, CipherMode}; +use crate::{MagmaStream, CipherOperation, CipherMode}; /// Returns encrypted result as `Vec` /// @@ -11,14 +10,14 @@ use crate::{magma::Magma, CipherOperation, CipherMode}; /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 20, Section 5.4.1 -pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { +pub fn encrypt(magma: &mut MagmaStream, buf: &[u8]) -> Vec { - core.ensure_iv_not_empty(); + magma.ensure_iv_not_empty(); - core.update_context(&CipherOperation::Encrypt, &CipherMode::CBC); - let mut register_r = match &core.context.feedback.vector { + magma.update_context(CipherOperation::Encrypt, CipherMode::CBC); + let mut register_r = match &magma.context.feedback.vector { Some(vector) => vector.clone(), - None => VecDeque::from(core.iv.clone()) + None => VecDeque::from(magma.context.iv.clone()) }; let mut result = Vec::::with_capacity(buf.len()); @@ -29,7 +28,7 @@ pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { let block = u64::from_be_bytes(array_u8); let register_n= register_r.pop_front().unwrap(); - let output = core.encrypt(block ^ register_n); + let output = magma.core.encrypt(block ^ register_n); register_r.push_back(output); @@ -37,7 +36,7 @@ pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { } // update the feedback state - core.context.feedback.vector = Some(register_r); + magma.context.feedback.vector = Some(register_r); result } @@ -49,14 +48,14 @@ pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 21, Section 5.4.2 -pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { +pub fn decrypt(magma: &mut MagmaStream, buf: &[u8]) -> Vec { - core.ensure_iv_not_empty(); + magma.ensure_iv_not_empty(); - core.update_context(&CipherOperation::Decrypt, &CipherMode::CBC); - let mut register_r = match &core.context.feedback.vector { + magma.update_context(CipherOperation::Decrypt, CipherMode::CBC); + let mut register_r = match &magma.context.feedback.vector { Some(vector) => vector.clone(), - None => VecDeque::from(core.iv.clone()) + None => VecDeque::from(magma.context.iv.clone()) }; let mut result = Vec::::with_capacity(buf.len()); @@ -67,7 +66,7 @@ pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { let block = u64::from_be_bytes(array_u8); let register_n= register_r.pop_front().unwrap(); - let output = core.decrypt(block) ^ register_n; + let output = magma.core.decrypt(block) ^ register_n; register_r.push_back(block); @@ -75,7 +74,7 @@ pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { } // update the feedback state - core.context.feedback.vector = Some(register_r); + magma.context.feedback.vector = Some(register_r); result } @@ -84,6 +83,8 @@ pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { mod tests { use super::*; + use crypto_vectors::gost::r3413_2015; + use crate::core::constants::*; #[test] fn cbc_steps_gost_r_34_13_2015() { @@ -96,9 +97,10 @@ mod tests { use crypto_vectors::gost::r3413_2015; - let magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + use crate::Magma; + let magma = Magma::with_key(r3413_2015::CIPHER_KEY.clone()); - let iv = Magma::IV_GOST_R3413_2015; + let iv = IV_GOST_R3413_2015; let mut r = [iv[0], iv[1], iv[2]]; let p1 = r3413_2015::PLAINTEXT1; @@ -149,15 +151,13 @@ mod tests { // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf // Page 38, Section A.2.4 - use crypto_vectors::gost::r3413_2015; - let mut source = Vec::::new(); source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::CBC); let encrypted = encrypt(&mut magma, &source); assert!(!encrypted.is_empty()); @@ -183,7 +183,7 @@ mod tests { source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::CBC); let mut encrypted = Vec::::new(); encrypted.extend_from_slice(&r3413_2015::CIPHERTEXT1_CBC.to_be_bytes()); diff --git a/cipher_magma/src/magma/cipher_mode/cfb.rs b/cipher_magma/src/stream/cipher_mode/cfb.rs similarity index 81% rename from cipher_magma/src/magma/cipher_mode/cfb.rs rename to cipher_magma/src/stream/cipher_mode/cfb.rs index a07b253..f3264a6 100644 --- a/cipher_magma/src/magma/cipher_mode/cfb.rs +++ b/cipher_magma/src/stream/cipher_mode/cfb.rs @@ -2,7 +2,7 @@ use std::collections::VecDeque; -use crate::{magma::Magma, CipherOperation, CipherMode}; +use crate::{MagmaStream, CipherOperation, CipherMode}; /// Returns encrypted result as `Vec` /// @@ -11,14 +11,14 @@ use crate::{magma::Magma, CipherOperation, CipherMode}; /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 23, Section 5.5.1 -pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { +pub fn encrypt(magma: &mut MagmaStream, buf: &[u8]) -> Vec { - core.ensure_iv_not_empty(); + magma.ensure_iv_not_empty(); - core.update_context(&CipherOperation::Encrypt, &CipherMode::CFB); - let mut register_r = match &core.context.feedback.vector { + magma.update_context(CipherOperation::Encrypt, CipherMode::CFB); + let mut register_r = match &magma.context.feedback.vector { Some(vector) => vector.clone(), - None => VecDeque::from(core.iv.clone()) + None => VecDeque::from(magma.context.iv.clone()) }; let mut result = Vec::::with_capacity(buf.len()); @@ -28,7 +28,7 @@ pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { let block = u64::from_be_bytes(array_u8); let register_n= register_r.pop_front().unwrap(); - let output = core.encrypt(register_n) ^ block; + let output = magma.core.encrypt(register_n) ^ block; register_r.push_back(output); @@ -36,7 +36,7 @@ pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { } // update the feedback state - core.context.feedback.vector = Some(register_r); + magma.context.feedback.vector = Some(register_r); result } @@ -48,14 +48,14 @@ pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 24, Section 5.5.2 -pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { +pub fn decrypt(magma: &mut MagmaStream, buf: &[u8]) -> Vec { - core.ensure_iv_not_empty(); + magma.ensure_iv_not_empty(); - core.update_context(&CipherOperation::Decrypt, &CipherMode::CFB); - let mut register_r = match &core.context.feedback.vector { + magma.update_context(CipherOperation::Decrypt, CipherMode::CFB); + let mut register_r = match &magma.context.feedback.vector { Some(vector) => vector.clone(), - None => VecDeque::from(core.iv.clone()) + None => VecDeque::from(magma.context.iv.clone()) }; let mut result = Vec::::with_capacity(buf.len()); @@ -65,7 +65,7 @@ pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { let block = u64::from_be_bytes(array_u8); let register_n= register_r.pop_front().unwrap(); - let output = core.encrypt(register_n) ^ block; + let output = magma.core.encrypt(register_n) ^ block; register_r.push_back(block); @@ -73,7 +73,7 @@ pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { } // update the feedback state - core.context.feedback.vector = Some(register_r); + magma.context.feedback.vector = Some(register_r); result } @@ -82,6 +82,8 @@ pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { mod tests { use super::*; + use crypto_vectors::gost::r3413_2015; + use crate::core::constants::*; #[test] fn cfb_steps_gost_r_34_13_2015() { @@ -89,20 +91,19 @@ mod tests { // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf // Page 39, Section A.2.5 - use crypto_vectors::gost::r3413_2015; - // s = n = 64, m = 2n = 128 // IV = 1234567890abcdef234567890abcdef1 let iv = 0x1234567890abcdef234567890abcdef1_u128; // [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) // CFB Mode: Page 39, Section A.2.5, uses MSB(128) part of IV - let mut r = [Magma::IV_GOST_R3413_2015[0], Magma::IV_GOST_R3413_2015[1]]; + let mut r = [IV_GOST_R3413_2015[0], IV_GOST_R3413_2015[1]]; let mut v1 = Vec::from(r[0].to_be_bytes()); v1.extend_from_slice(&r[1].to_be_bytes()); assert_eq!(iv.to_be_bytes(), v1.as_slice()); - let magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + use crate::Magma; + let magma = Magma::with_key(r3413_2015::CIPHER_KEY.clone()); let p1 = r3413_2015::PLAINTEXT1; let i1 = r[0]; @@ -160,11 +161,11 @@ mod tests { source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::CFB); // [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) // CFB Mode: Page 39, Section A.2.5, uses MSB(128) part of IV - magma.set_iv(&Magma::IV_GOST_R3413_2015[..2]); + magma.set_iv(&IV_GOST_R3413_2015[..2]); let encrypted = encrypt(&mut magma, &source); assert!(!encrypted.is_empty()); @@ -191,11 +192,11 @@ mod tests { source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::CFB); // [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) // CFB Mode: Page 39, Section A.2.5, uses MSB(128) part of IV - magma.set_iv(&Magma::IV_GOST_R3413_2015[..2]); + magma.set_iv(&IV_GOST_R3413_2015[..2]); let mut encrypted = Vec::::new(); encrypted.extend_from_slice(&r3413_2015::CIPHERTEXT1_CFB.to_be_bytes()); diff --git a/cipher_magma/src/magma/cipher_mode/ctr.rs b/cipher_magma/src/stream/cipher_mode/ctr.rs similarity index 83% rename from cipher_magma/src/magma/cipher_mode/ctr.rs rename to cipher_magma/src/stream/cipher_mode/ctr.rs index adf9d2e..eb6ae4e 100644 --- a/cipher_magma/src/magma/cipher_mode/ctr.rs +++ b/cipher_magma/src/stream/cipher_mode/ctr.rs @@ -1,6 +1,6 @@ //! Implements Counter Encryption (CTR) mode -use crate::{magma::Magma, CipherOperation, CipherMode}; +use crate::{MagmaStream, CipherOperation, CipherMode}; /// Returns encrypted result as `Vec` /// @@ -9,9 +9,9 @@ use crate::{magma::Magma, CipherOperation, CipherMode}; /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 15, Section 5.2.1 -pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { - core.update_context(&CipherOperation::Encrypt, &CipherMode::CTR); - cipher_ctr(core, buf) +pub fn encrypt(magma: &mut MagmaStream, buf: &[u8]) -> Vec { + magma.update_context(CipherOperation::Encrypt, CipherMode::CTR); + cipher_ctr(magma, buf) } /// Returns decrypted result as `Vec` @@ -21,9 +21,9 @@ pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 15, Section 5.2.2 -pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { - core.update_context(&CipherOperation::Decrypt, &CipherMode::CTR); - cipher_ctr(core, buf) +pub fn decrypt(magma: &mut MagmaStream, buf: &[u8]) -> Vec { + magma.update_context(CipherOperation::Decrypt, CipherMode::CTR); + cipher_ctr(magma, buf) } /// Returns encrypted/decrypted result as `Vec` @@ -33,10 +33,10 @@ pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 14, Section 5.2 -fn cipher_ctr(core: &mut Magma, buf: &[u8]) -> Vec { +fn cipher_ctr(magma: &mut MagmaStream, buf: &[u8]) -> Vec { - let iv_ctr = core.prepare_vector_ctr(); - let mut counter = match core.context.feedback.block { + let iv_ctr = magma.prepare_vector_ctr(); + let mut counter = match magma.context.feedback.block { Some(block) => block, None => 0 }; @@ -51,14 +51,14 @@ fn cipher_ctr(core: &mut Magma, buf: &[u8]) -> Vec { let ctr = iv_ctr.wrapping_add(counter); counter += 1; - let gamma = core.encrypt(ctr); + let gamma = magma.core.encrypt(ctr); let output = gamma ^ block; result.extend_from_slice(&output.to_be_bytes()[..chunk.len()]); } // update the feedback state - core.context.feedback.block = Some(counter); + magma.context.feedback.block = Some(counter); result } @@ -67,6 +67,7 @@ fn cipher_ctr(core: &mut Magma, buf: &[u8]) -> Vec { mod tests { use super::*; + use crypto_vectors::gost::r3413_2015; #[test] fn ctr_steps_gost_r_34_13_2015() { @@ -74,8 +75,8 @@ mod tests { // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf // Page 36, Section A.2.2 - use crypto_vectors::gost::r3413_2015; - let magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + use crate::Magma; + let magma = Magma::with_key(r3413_2015::CIPHER_KEY.clone()); let iv = 0x12345678_u32; @@ -120,15 +121,13 @@ mod tests { #[test] fn encrypt_ctr_gost_r_34_13_2015() { - use crypto_vectors::gost::r3413_2015; - let mut source = Vec::::new(); source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::CTR); let encrypted = encrypt(&mut magma, &source); assert!(!encrypted.is_empty()); @@ -142,15 +141,13 @@ mod tests { #[test] fn decrypt_ctr_gost_r_34_13_2015() { - use crypto_vectors::gost::r3413_2015; - let mut source = Vec::::new(); source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::CTR); let mut encrypted = Vec::::new(); encrypted.extend_from_slice(&r3413_2015::CIPHERTEXT1_CTR.to_be_bytes()); diff --git a/cipher_magma/src/magma/cipher_mode/ctr_acpkm.rs b/cipher_magma/src/stream/cipher_mode/ctr_acpkm.rs similarity index 66% rename from cipher_magma/src/magma/cipher_mode/ctr_acpkm.rs rename to cipher_magma/src/stream/cipher_mode/ctr_acpkm.rs index 43ec7ce..76864f5 100644 --- a/cipher_magma/src/magma/cipher_mode/ctr_acpkm.rs +++ b/cipher_magma/src/stream/cipher_mode/ctr_acpkm.rs @@ -1,7 +1,7 @@ //! Implements Counter Encryption (CTR_ACPKM) mode -use crate::magma::{Magma, CipherOperation}; -use crate::magma::cipher_mode::CipherMode; +use crate::{MagmaStream, CipherOperation, CipherMode}; +use crate::core::constants::*; /// Returns encrypted result as `Vec` /// @@ -10,10 +10,10 @@ use crate::magma::cipher_mode::CipherMode; /// [RFC8645](https://www.rfc-editor.org/rfc/rfc8645.html#section-6.2.2) /// /// [P 1323565.1.017— 2018](https://standartgost.ru/g/%D0%A0_1323565.1.017-2018) -pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { - core.update_context(&CipherOperation::Encrypt, &CipherMode::CTR_ACPKM); +pub fn encrypt(magma: &mut MagmaStream, buf: &[u8]) -> Vec { + magma.update_context(CipherOperation::Encrypt, CipherMode::CTR_ACPKM); - cipher_ctr_acpkm(core, buf) + cipher_ctr_acpkm(magma, buf) } /// Returns decrypted result as `Vec` @@ -23,10 +23,10 @@ pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { /// [RFC8645](https://www.rfc-editor.org/rfc/rfc8645.html#section-6.2.2) /// /// [P 1323565.1.017— 2018](https://standartgost.ru/g/%D0%A0_1323565.1.017-2018) -pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { - core.update_context(&CipherOperation::Decrypt, &CipherMode::CTR_ACPKM); +pub fn decrypt(magma_stream: &mut MagmaStream, buf: &[u8]) -> Vec { + magma_stream.update_context(CipherOperation::Decrypt, CipherMode::CTR_ACPKM); - cipher_ctr_acpkm(core, buf) + cipher_ctr_acpkm(magma_stream, buf) } /// Returns encrypted/decrypted as `Vec` @@ -36,15 +36,15 @@ pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { /// [RFC8645](https://www.rfc-editor.org/rfc/rfc8645.html#section-6.2.2) /// /// [P 1323565.1.017— 2018](https://standartgost.ru/g/%D0%A0_1323565.1.017-2018) -fn cipher_ctr_acpkm(core: &mut Magma, buf: &[u8]) -> Vec { +fn cipher_ctr_acpkm(magma: &mut MagmaStream, buf: &[u8]) -> Vec { - let iv_ctr = core.prepare_vector_ctr(); + let iv_ctr = magma.prepare_vector_ctr(); let mut result = Vec::::with_capacity(buf.len()); - let original_key = core.key; + let original_key = magma.core.key; let mut section_bits_processed = 0; - let mut counter = match core.context.feedback.block { + let mut counter = match magma.context.feedback.block { Some(block) => block, None => 0 }; @@ -57,26 +57,27 @@ fn cipher_ctr_acpkm(core: &mut Magma, buf: &[u8]) -> Vec { let ctr = iv_ctr.wrapping_add(counter); counter += 1; - let gamma = core.encrypt(ctr); + let gamma = magma.core.encrypt(ctr); let output = gamma ^ block; result.extend_from_slice(&output.to_be_bytes()[..chunk.len()]); section_bits_processed += 64; - if section_bits_processed >= Magma::CTR_ACPKM_SECTION_SIZE_N { - let state = core.context.clone(); - let section_key = core.cipher(&Magma::CTR_ACPKM_D, &CipherOperation::Encrypt, &CipherMode::ECB); - core.set_key_u8(§ion_key); - core.context = state; + if section_bits_processed >= CTR_ACPKM_SECTION_SIZE_N { + let context = magma.context.clone(); + magma.set_mode(CipherMode::ECB); + let section_key = magma.encrypt(&CTR_ACPKM_D); + magma.core.set_key_u8(§ion_key); + magma.context = context; section_bits_processed = 0; } } // update the feedback state - core.context.feedback.block = Some(counter); + magma.context.feedback.block = Some(counter); // restore the original cipher key - core.set_key_u32(&original_key); + magma.core.set_key_u32(&original_key); result } @@ -95,8 +96,7 @@ mod tests { use crypto_vectors::gost::r1323565_1_017_2018::ctr_acpkm; - let mut magma = Magma::new(); - magma.set_key_u8(&ctr_acpkm::CIPHER_KEY); + let mut magma = MagmaStream::new(ctr_acpkm::CIPHER_KEY.clone(), CipherMode::CTR_ACPKM); let encrypted = encrypt(&mut magma, &ctr_acpkm::PLAINTEXT); assert!(!encrypted.is_empty()); @@ -113,8 +113,8 @@ mod tests { use crypto_vectors::gost::r1323565_1_017_2018::ctr_acpkm; - let mut magma = Magma::new(); - magma.set_key_u8(&ctr_acpkm::CIPHER_KEY); + let mut magma = MagmaStream::new(ctr_acpkm::CIPHER_KEY.clone(), CipherMode::CTR_ACPKM); + let decrypted = decrypt(&mut magma, &ctr_acpkm::CIPHERTEXT); assert_eq!(decrypted, ctr_acpkm::PLAINTEXT); } diff --git a/cipher_magma/src/magma/cipher_mode/ecb.rs b/cipher_magma/src/stream/cipher_mode/ecb.rs similarity index 80% rename from cipher_magma/src/magma/cipher_mode/ecb.rs rename to cipher_magma/src/stream/cipher_mode/ecb.rs index e5fd475..f095b07 100644 --- a/cipher_magma/src/magma/cipher_mode/ecb.rs +++ b/cipher_magma/src/stream/cipher_mode/ecb.rs @@ -1,6 +1,6 @@ //! Implements Electronic Codebook (ECB) mode -use crate::{magma::Magma, CipherOperation, CipherMode}; +use crate::{Magma, MagmaStream, CipherOperation, CipherMode}; /// Returns encrypted result as `Vec` /// @@ -9,11 +9,11 @@ use crate::{magma::Magma, CipherOperation, CipherMode}; /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 13, Section 5.1.1 -pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { - core.update_context(&CipherOperation::Encrypt, &CipherMode::ECB); +pub fn encrypt(magma: &mut MagmaStream, buf: &[u8]) -> Vec { + magma.update_context(CipherOperation::Encrypt, CipherMode::ECB); let m_invoke = Magma::encrypt; - cipher_ecb(core, buf, m_invoke) + cipher_ecb(magma, buf, m_invoke) } /// Returns decrypted result as `Vec` @@ -23,11 +23,11 @@ pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 13, Section 5.1.2 -pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { - core.update_context(&CipherOperation::Decrypt, &CipherMode::ECB); +pub fn decrypt(magma: &mut MagmaStream, buf: &[u8]) -> Vec { + magma.update_context(CipherOperation::Decrypt, CipherMode::ECB); let m_invoke = Magma::decrypt; - cipher_ecb(core, buf, m_invoke) + cipher_ecb(magma, buf, m_invoke) } /// Returns encrypted/decrypted result as `Vec` @@ -37,13 +37,13 @@ pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 13, Section 5.1 -fn cipher_ecb(core: &Magma, buf: &[u8], m_invoke: fn(&Magma, u64) -> u64) -> Vec { +fn cipher_ecb(magma: &MagmaStream, buf: &[u8], m_invoke: fn(&Magma, u64) -> u64) -> Vec { let mut result = Vec::::with_capacity(buf.len()); for chunk in buf.chunks(8) { let mut array_u8 = [0u8;8]; chunk.iter().enumerate().for_each(|t| array_u8[t.0] = *t.1); let block = u64::from_be_bytes(array_u8); - let output = m_invoke(&core, block); + let output = m_invoke(&magma.core, block); result.extend_from_slice(&output.to_be_bytes()); } result @@ -65,7 +65,7 @@ mod tests { source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::ECB); let encrypted = encrypt(&mut magma, &source); assert!(!encrypted.is_empty()); @@ -87,7 +87,7 @@ mod tests { source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::ECB); let mut encrypted = Vec::::new(); encrypted.extend_from_slice(&r3413_2015::CIPHERTEXT1_ECB.to_be_bytes()); diff --git a/cipher_magma/src/magma/cipher_mode/mac.rs b/cipher_magma/src/stream/cipher_mode/mac.rs similarity index 74% rename from cipher_magma/src/magma/cipher_mode/mac.rs rename to cipher_magma/src/stream/cipher_mode/mac.rs index c38fd83..e3ab5ed 100644 --- a/cipher_magma/src/magma/cipher_mode/mac.rs +++ b/cipher_magma/src/stream/cipher_mode/mac.rs @@ -1,9 +1,7 @@ //! Implements Message Authentication Code (MAC) -use crate::{ - magma::{utils, Magma}, - CipherMode, CipherOperation, -}; +use crate::{MagmaStream, CipherOperation, CipherMode}; +use crate::core::utils; /// Returns the Message Authentication Code (MAC) /// @@ -14,12 +12,12 @@ use crate::{ /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 26, Section 5.6 -pub fn calculate(core: &mut Magma, msg_buf: &[u8]) -> u32 { - core.reset_feedback(); - core.update_context(&CipherOperation::MessageAuthentication, &CipherMode::MAC); +pub fn calculate(magma: &mut MagmaStream, msg_buf: &[u8]) -> u32 { + magma.reset_feedback(); + magma.update_context(CipherOperation::MessageAuthentication, CipherMode::MAC); - update(core, msg_buf); - finalize(core) + update(magma, msg_buf); + finalize(magma) } /// Updates the context of Message Authentication Code (MAC) @@ -31,12 +29,12 @@ pub fn calculate(core: &mut Magma, msg_buf: &[u8]) -> u32 { /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 26, Section 5.6 -pub fn update(core: &mut Magma, msg_buf: &[u8]) { - core.update_context(&CipherOperation::MessageAuthentication, &CipherMode::MAC); +pub fn update(magma: &mut MagmaStream, msg_buf: &[u8]) { + magma.update_context(CipherOperation::MessageAuthentication, CipherMode::MAC); - let mut feedback_chained = core.context.feedback.block.is_some(); + let mut feedback_chained = magma.context.feedback.block.is_some(); let mut feedback = if feedback_chained { - core.context.feedback.block.unwrap() + magma.context.feedback.block.unwrap() } else { 0 }; @@ -55,14 +53,14 @@ pub fn update(core: &mut Magma, msg_buf: &[u8]) { // 1. Mark the starting byte with 0x80 // 2. Other bytes already padded with 0x00 array_u8[chunk_len] = 0x80_u8; - core.context.padded = true; + magma.context.padded = true; } let block_in = u64::from_be_bytes(array_u8); feedback = block_in ^ if feedback_chained { - core.encrypt(feedback) + magma.core.encrypt(feedback) } else { feedback }; @@ -71,7 +69,7 @@ pub fn update(core: &mut Magma, msg_buf: &[u8]) { } // update the feedback state - core.context.feedback.block = Some(feedback); + magma.context.feedback.block = Some(feedback); } /// Finalizes the current context and returns the Message Authentication Code (MAC) @@ -82,21 +80,21 @@ pub fn update(core: &mut Magma, msg_buf: &[u8]) { /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 26, Section 5.6 -pub fn finalize(core: &mut Magma) -> u32 { - core.update_context(&CipherOperation::MessageAuthentication, &CipherMode::MAC); +pub fn finalize(magma: &mut MagmaStream) -> u32 { + magma.update_context(CipherOperation::MessageAuthentication, CipherMode::MAC); - let (k1, k2) = generate_cmac_subkeys(core); - let k_n = if core.context.padded { k2 } else { k1 }; + let (k1, k2) = generate_cmac_subkeys(magma); + let k_n = if magma.context.padded { k2 } else { k1 }; - let finalizer = match core.context.feedback.block { + let finalizer = match magma.context.feedback.block { Some(finalizer) => finalizer ^ k_n, None => panic!("Context not found, please use update() before finalizing."), }; - let final_block = core.encrypt(finalizer); + let final_block = magma.core.encrypt(finalizer); let (mac, _) = utils::u64_split(final_block); - core.reset_context(); + magma.reset_context(); mac } @@ -105,8 +103,8 @@ pub fn finalize(core: &mut Magma) -> u32 { /// Key generation algorithm is based on: /// /// [OMAC1 a.k.a CMAC](https://en.wikipedia.org/wiki/One-key_MAC) -fn generate_cmac_subkeys(core: &mut Magma) -> (u64, u64) { - let r = core.encrypt(0x0_u64); +fn generate_cmac_subkeys(magma: &mut MagmaStream) -> (u64, u64) { + let r = magma.core.encrypt(0x0_u64); let b64 = 0x1b_u64; let mcb_u64 = 0x80000000_00000000_u64; @@ -134,7 +132,7 @@ mod tests { #[test] fn cmac_subkeys_gost_r_34_13_2015() { use crypto_vectors::gost::r3413_2015; - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::MAC); let (k1, k2) = generate_cmac_subkeys(&mut magma); assert_eq!(k1, 0x5f459b3342521424_u64); assert_eq!(k2, 0xbe8b366684a42848_u64); @@ -147,7 +145,7 @@ mod tests { // Page 40, Section A.2.6 use crypto_vectors::gost::r3413_2015; - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::MAC); let (k1, k2) = generate_cmac_subkeys(&mut magma); assert_eq!(k1, 0x5f459b3342521424_u64); @@ -156,22 +154,22 @@ mod tests { let k_n = k1; let i1 = r3413_2015::PLAINTEXT1; - let o1 = magma.encrypt(i1); + let o1 = magma.core.encrypt(i1); assert_eq!(o1, 0x2b073f0494f372a0_u64); let i2 = o1 ^ r3413_2015::PLAINTEXT2; assert_eq!(i2, 0xf053f8006cebef80_u64); - let o2 = magma.encrypt(i2); + let o2 = magma.core.encrypt(i2); assert_eq!(o2, 0xc89ed814fd5e18e9_u64); let i3 = o2 ^ r3413_2015::PLAINTEXT3; assert_eq!(i3, 0x8206233a9af61aa5_u64); - let o3 = magma.encrypt(i3); + let o3 = magma.core.encrypt(i3); assert_eq!(o3, 0xf739b18d34289b00_u64); let i4 = o3 ^ r3413_2015::PLAINTEXT4 ^ k_n; assert_eq!(i4, 0x216e6a2561cff165_u64); - let o4 = magma.encrypt(i4); + let o4 = magma.core.encrypt(i4); assert_eq!(o4, 0x154e72102030c5bb_u64); let (mac, _) = utils::u64_split(o4); @@ -192,7 +190,7 @@ mod tests { source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::MAC); update(&mut magma, &source); let mac = finalize(&mut magma); @@ -207,7 +205,7 @@ mod tests { use crypto_vectors::gost::r3413_2015; - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::MAC); update(&mut magma, &r3413_2015::PLAINTEXT1.to_be_bytes()); update(&mut magma, &r3413_2015::PLAINTEXT2.to_be_bytes()); @@ -226,7 +224,7 @@ mod tests { use crypto_vectors::gost::r3413_2015; - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::MAC); let mut source = Vec::::new(); source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); @@ -241,7 +239,8 @@ mod tests { #[test] #[should_panic] fn mac_finilize_no_context() { - let mut magma = Magma::new(); - finalize(&mut magma); + use crypto_vectors::gost::r3413_2015; + let mut magma_stream = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::MAC); + finalize(&mut magma_stream); } } diff --git a/cipher_magma/src/magma/cipher_mode/ofb.rs b/cipher_magma/src/stream/cipher_mode/ofb.rs similarity index 82% rename from cipher_magma/src/magma/cipher_mode/ofb.rs rename to cipher_magma/src/stream/cipher_mode/ofb.rs index bcb9da7..764fb8e 100644 --- a/cipher_magma/src/magma/cipher_mode/ofb.rs +++ b/cipher_magma/src/stream/cipher_mode/ofb.rs @@ -1,8 +1,7 @@ //! Implements Output Feedback (OFB) mode use std::collections::VecDeque; - -use crate::{magma::Magma, CipherOperation, CipherMode}; +use crate::{MagmaStream, CipherOperation, CipherMode}; /// Returns encrypted result as `Vec` /// @@ -11,9 +10,9 @@ use crate::{magma::Magma, CipherOperation, CipherMode}; /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 16, Section 5.3 -pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { - core.update_context(&CipherOperation::Encrypt, &CipherMode::OFB); - cipher_ofb(core, buf) +pub fn encrypt(magma: &mut MagmaStream, buf: &[u8]) -> Vec { + magma.update_context(CipherOperation::Encrypt, CipherMode::OFB); + cipher_ofb(magma, buf) } /// Returns decrypted result as `Vec` @@ -23,9 +22,9 @@ pub fn encrypt(core: &mut Magma, buf: &[u8]) -> Vec { /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 16, Section 5.3 -pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { - core.update_context(&CipherOperation::Decrypt, &CipherMode::OFB); - cipher_ofb(core, buf) +pub fn decrypt(magma: &mut MagmaStream, buf: &[u8]) -> Vec { + magma.update_context(CipherOperation::Decrypt, CipherMode::OFB); + cipher_ofb(magma, buf) } /// Returns encrypted/decrypted result as `Vec` @@ -35,13 +34,13 @@ pub fn decrypt(core: &mut Magma, buf: &[u8]) -> Vec { /// [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) /// /// Page 16, Section 5.3 -fn cipher_ofb(core: &mut Magma, buf: &[u8]) -> Vec { +fn cipher_ofb(magma: &mut MagmaStream, buf: &[u8]) -> Vec { - core.ensure_iv_not_empty(); + magma.ensure_iv_not_empty(); - let mut register_r = match &core.context.feedback.vector { + let mut register_r = match &magma.context.feedback.vector { Some(vector) => vector.clone(), - None => VecDeque::from(core.iv.clone()) + None => VecDeque::from(magma.context.iv.clone()) }; let mut result = Vec::::with_capacity(buf.len()); @@ -52,7 +51,7 @@ fn cipher_ofb(core: &mut Magma, buf: &[u8]) -> Vec { let block = u64::from_be_bytes(array_u8); let register_n= register_r.pop_front().unwrap(); - let ofb = core.encrypt(register_n); + let ofb = magma.core.encrypt(register_n); let output = ofb ^ block; register_r.push_back(ofb); @@ -61,7 +60,7 @@ fn cipher_ofb(core: &mut Magma, buf: &[u8]) -> Vec { } // update the feedback state - core.context.feedback.vector = Some(register_r); + magma.context.feedback.vector = Some(register_r); result } @@ -70,6 +69,8 @@ fn cipher_ofb(core: &mut Magma, buf: &[u8]) -> Vec { mod tests { use super::*; + use crypto_vectors::gost::r3413_2015; + use crate::core::constants::*; #[test] fn ofb_steps_gost_r_34_13_2015() { @@ -83,14 +84,13 @@ mod tests { // [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) // OFB Mode: Page 37, Section A.2.3, uses MSB(128) part of IV - let mut r = [Magma::IV_GOST_R3413_2015[0], Magma::IV_GOST_R3413_2015[1]]; + let mut r = [IV_GOST_R3413_2015[0], IV_GOST_R3413_2015[1]]; let mut v1 = Vec::from(r[0].to_be_bytes()); v1.extend_from_slice(&r[1].to_be_bytes()); assert_eq!(iv.to_be_bytes(), v1.as_slice()); - use crypto_vectors::gost::r3413_2015; - - let magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + use crate::Magma; + let magma = Magma::with_key(r3413_2015::CIPHER_KEY.clone()); let p1 = r3413_2015::PLAINTEXT1; let i1 = r[0]; @@ -140,19 +140,17 @@ mod tests { // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf // Page 37, Section A.2.3 - use crypto_vectors::gost::r3413_2015; - let mut source = Vec::::new(); source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::OFB); // [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) // OFB Mode: Page 37, Section A.2.3, uses MSB(128) part of IV - magma.set_iv(&Magma::IV_GOST_R3413_2015[..2]); + magma.set_iv(&IV_GOST_R3413_2015[..2]); let encrypted = encrypt(&mut magma, &source); assert!(!encrypted.is_empty()); @@ -171,19 +169,17 @@ mod tests { // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf // Page 37, Section A.2.3 - use crypto_vectors::gost::r3413_2015; - let mut source = Vec::::new(); source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); - let mut magma = Magma::with_key_u32(&r3413_2015::CIPHER_KEY); + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::OFB); // [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) // OFB Mode: Page 37, Section A.2.3, uses MSB(128) part of IV - magma.set_iv(&Magma::IV_GOST_R3413_2015[..2]); + magma.set_iv(&IV_GOST_R3413_2015[..2]); let mut encrypted = Vec::::new(); encrypted.extend_from_slice(&r3413_2015::CIPHERTEXT1_OFB.to_be_bytes()); diff --git a/cipher_magma/src/magma/cipher_operation.rs b/cipher_magma/src/stream/cipher_operation.rs similarity index 86% rename from cipher_magma/src/magma/cipher_operation.rs rename to cipher_magma/src/stream/cipher_operation.rs index 015077e..34c3165 100644 --- a/cipher_magma/src/magma/cipher_operation.rs +++ b/cipher_magma/src/stream/cipher_operation.rs @@ -1,5 +1,5 @@ //! Cipher operation -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Copy)] pub enum CipherOperation { /// Encrypting operation Encrypt, diff --git a/cipher_magma/src/stream/magma_stream.rs b/cipher_magma/src/stream/magma_stream.rs new file mode 100644 index 0000000..d31dcb1 --- /dev/null +++ b/cipher_magma/src/stream/magma_stream.rs @@ -0,0 +1,432 @@ +//! Magma stream ciphering operations +//! +//! [Cipher Modes](https://tc26.ru/standard/gost/GOST_R_3413-2015.pdf) +//! * **ECB** - Electronic Codebook Mode +//! * **CTR** - Counter Encryption Mode +//! * **CTR-ACPKM** - Counter Encryption Mode as per [RFC8645](https://www.rfc-editor.org/rfc/rfc8645.html), [P 1323565.1.017— 2018](https://standartgost.ru/g/%D0%A0_1323565.1.017-2018) +//! * **OFB** - Output Feedback Mode +//! * **CBC** - Cipher Block Chaining Mode +//! * **CFB** - Cipher Feedback Mode +//! * **MAC** - Message Authentication Code Generation Mode + +use std::collections::VecDeque; + +use crate::*; +use crate::constants::*; +use crate::core::cipher_key::CipherKey; + +/// Magma stream ciphering operations +pub struct MagmaStream { + /// The core block-cipher + pub core: Magma, + + /// Stream ciphering context + pub(crate) context: StreamContext, +} + +#[derive(Clone)] +pub(crate) struct StreamContext { + pub(crate) mode: CipherMode, + pub(crate) operation: Option, + pub(crate) iv: Vec, + pub(crate) padded: bool, + pub(crate) feedback: Feedback, +} + +impl StreamContext { + fn new(cipher_mode: CipherMode) -> Self { + StreamContext { + mode: cipher_mode, + operation: None, + iv: Vec::from(IV_GOST_R3413_2015), + padded: false, + feedback: Feedback::new(), + } + } +} + +#[derive(Clone)] +pub(crate) struct Feedback { + pub(crate) block: Option, + pub(crate) vector: Option>, +} + +impl Feedback { + fn new() -> Self { + Feedback { + block: None, + vector: None, + } + } +} + +impl MagmaStream { + + /// Returns a new `MagmaStream` initialized with given cipher key and cipher mode + /// + /// Uses RFC7836 based substitution box + /// + /// # Arguments + /// + /// * `key` - array `[u32;8]` or `[u8;32]` + /// * `cipher_mode` - a `CipherMode' value + /// + /// # Example + /// + /// ``` + /// use cipher_magma::{MagmaStream, CipherMode}; + /// let key: [u32;8] = [ + /// 0xffeeddcc, 0xbbaa9988, 0x77665544, 0x33221100, 0xf0f1f2f3, 0xf4f5f6f7, 0xf8f9fafb, 0xfcfdfeff + /// ]; + /// let magma = MagmaStream::new(key, CipherMode::CBC); + /// ``` + /// Or + /// + /// ``` + /// use cipher_magma::{MagmaStream, CipherMode}; + /// let key: [u8;32] = [ + /// 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, + /// 0x00, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, + /// 0xfe, 0xff, + /// ]; + /// let magma = MagmaStream::new(key, CipherMode::CBC); + /// ``` + pub fn new(key: T, cipher_mode: CipherMode) -> Self + where + CipherKey: From, + { + MagmaStream { + core: Magma::with_key(key), + context: StreamContext::new(cipher_mode), + } + } + + /// Sets the cipher key from array + /// + /// # Arguments + /// + /// * `key` - a `[u8;32]' or `[u32;8]` array + pub fn set_key(&mut self, key: T) + where + CipherKey: From, + { + self.core.set_key(key); + self.reset_feedback(); + } + + /// Sets the cipher mode + /// + /// # Arguments + /// + /// * `cipher_mode` - a `CipherMode' value + pub fn set_mode(&mut self, cipher_mode: CipherMode) { + self.context.mode = cipher_mode; + self.reset_feedback(); + } + + /// Sets the substitution box + /// + /// # Arguments + /// + /// * `substitution_box` - A reference to `[u8;128]` array + pub fn set_substitution_box(&mut self, substitution_box: &[u8; 128]) { + self.core.set_substitution_box(substitution_box); + self.reset_feedback(); + } + + /// Sets the Initialization Vector (IV) + /// + /// # Arguments + /// + /// * `iv` - A slice to `&[u64]` array + /// + /// **Attention**: `CTR` Mode uses only the MSB(32) part of IV + pub fn set_iv(&mut self, iv: &[u64]) { + self.context.iv = Vec::from(iv); + self.reset_feedback(); + } + + #[inline] + pub(crate) fn prepare_vector_ctr(&self) -> u64 { + self.ensure_iv_not_empty(); + // [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) + // CTR Mode: Page 36, Section A.2.2, uses MSB(32) part of IV extended to 64bit with Initial Nonce + // Initial Nonce: 0x00000000 + self.context.iv[0] & 0xffffffff_00000000 + } + + #[inline] + pub(crate) fn ensure_iv_not_empty(&self) { + if self.context.iv.is_empty() { + panic!("Initialization vector is empty!"); + } + } + + // check and update cipher context + pub(crate) fn update_context( + &mut self, + cipher_operation: CipherOperation, + cipher_mode: CipherMode, + ) { + if self.context.operation.as_ref() != Some(&cipher_operation) + || self.context.mode != cipher_mode + { + self.context.operation = Some(cipher_operation); + self.context.mode = cipher_mode; + self.reset_feedback(); + } + } + + /// Resets the context of stream ciphering + pub fn reset_context(&mut self) { + let cipher_mode = self.context.mode.clone(); + self.context = StreamContext::new(cipher_mode); + } + + /// Resets the feedback state of stream ciphering + pub fn reset_feedback(&mut self) { + self.context.feedback = Feedback::new(); + } + + /// Returns current `CipherMode` + pub fn get_mode(&self) -> CipherMode { + return self.context.mode; + } + + /// Returns encrypted vector as `Vec` + /// + /// # Arguments + /// + /// * `buf` - a slice of `&[u8]` input data + /// * `cipher_mode` - reference to `CipherMode` + pub fn encrypt(&mut self, buf: &[u8]) -> Vec { + + let cipher_mode = self.context.mode; + + // check and update feedback state + self.update_context(CipherOperation::Encrypt, cipher_mode); + + match cipher_mode { + CipherMode::ECB => ecb::encrypt(self, buf), + CipherMode::CTR => ctr::encrypt(self, buf), + CipherMode::CTR_ACPKM => ctr_acpkm::encrypt(self, buf), + CipherMode::OFB => ofb::encrypt(self, buf), + CipherMode::CBC => cbc::encrypt(self, buf), + CipherMode::CFB => cfb::encrypt(self, buf), + CipherMode::MAC => { + panic!("CipherMode::MAC can not be used in encrypting operation!") + } + } + } + + /// Returns a decrypted vector as `Vec` + /// + /// # Arguments + /// + /// * `buf` - a slice of `&[u8]` input data + /// * `cipher_mode` - reference to `CipherMode` + pub fn decrypt(&mut self, buf: &[u8]) -> Vec { + + let cipher_mode = self.context.mode; + + // check and update feedback state + self.update_context(CipherOperation::Decrypt, cipher_mode); + + match cipher_mode { + CipherMode::ECB => ecb::decrypt(self, buf), + CipherMode::CTR => ctr::decrypt(self, buf), + CipherMode::CTR_ACPKM => ctr_acpkm::decrypt(self, buf), + CipherMode::OFB => ofb::decrypt(self, buf), + CipherMode::CBC => cbc::decrypt(self, buf), + CipherMode::CFB => cfb::decrypt(self, buf), + CipherMode::MAC => { + panic!("CipherMode::MAC can not be used in decrypting operation!") + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn default_initialization() { + let magma = MagmaStream::new([0;8],CipherMode::ECB); + assert_eq!(magma.context.iv, IV_GOST_R3413_2015); + } + + #[test] + fn set_initialization_vector() { + let mut magma = MagmaStream::new([0;8],CipherMode::ECB); + let initialization_vector = vec![0x11223344_u64]; + magma.set_iv(&initialization_vector); + assert_eq!(magma.context.iv, initialization_vector); + } + + #[test] + fn cipher_ecb_gost_r_34_13_2015() { + use crypto_vectors::gost::r3413_2015; + let mut source = Vec::::new(); + source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); + + let mut magma = + MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::ECB); + let encrypted = magma.encrypt(&source); + assert!(!encrypted.is_empty()); + + let mut expected = Vec::::new(); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT1_ECB.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT2_ECB.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT3_ECB.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT4_ECB.to_be_bytes()); + assert_eq!(encrypted, expected); + + let decrypted = magma.decrypt(&encrypted); + assert_eq!(decrypted, source); + } + + #[test] + fn cipher_ctr_gost_r_34_13_2015() { + // Test vectors GOST R 34.13-2015 + // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf + // Page 36, Section A.2.2 + + use crypto_vectors::gost::r3413_2015; + let mut source = Vec::::new(); + source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); + + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::CTR); + let encrypted = magma.encrypt(&source); + assert!(!encrypted.is_empty()); + + let mut expected = Vec::::new(); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT1_CTR.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT2_CTR.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT3_CTR.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT4_CTR.to_be_bytes()); + assert_eq!(encrypted, expected); + + let decrypted = magma.decrypt(&encrypted); + assert_eq!(decrypted, source); + } + + #[test] + fn cipher_ctr_acpkm_r_1323565_1_017_2018() { + // Test Vectors CTR-ACPKM + // Р 1323565.1.017—2018 + // https://standartgost.ru/g/%D0%A0_1323565.1.017-2018 + // Page 11 + + use crypto_vectors::gost::r1323565_1_017_2018::ctr_acpkm; + + let mut magma = MagmaStream::new(ctr_acpkm::CIPHER_KEY.clone(), CipherMode::CTR_ACPKM); + + let encrypted = magma.encrypt(&ctr_acpkm::PLAINTEXT); + assert!(!encrypted.is_empty()); + + assert_eq!(encrypted, ctr_acpkm::CIPHERTEXT); + + let decrypted = magma.decrypt(&encrypted); + assert_eq!(decrypted, ctr_acpkm::PLAINTEXT); + } + + #[test] + fn cipher_ofb_gost_r_34_13_2015() { + // Test vectors GOST R 34.13-2015 + // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf + // Page 37, Section A.2.3 + + use crypto_vectors::gost::r3413_2015; + let mut source = Vec::::new(); + source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); + + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::OFB); + + // [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) + // OFB Mode: Page 37, Section A.2.3, uses MSB(128) part of IV + magma.set_iv(&IV_GOST_R3413_2015[..2]); + + let encrypted = magma.encrypt(&source); + assert!(!encrypted.is_empty()); + + let mut expected = Vec::::new(); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT1_OFB.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT2_OFB.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT3_OFB.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT4_OFB.to_be_bytes()); + assert_eq!(encrypted, expected); + + let decrypted = magma.decrypt(&encrypted); + assert_eq!(decrypted, source); + } + + #[test] + fn cipher_cbc_gost_r_34_13_2015() { + // Test vectors GOST R 34.13-2015 + // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf + // Page 38, Section A.2.4 + use crypto_vectors::gost::r3413_2015; + let mut source = Vec::::new(); + source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); + + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::CBC); + + let encrypted = magma.encrypt(&source); + assert!(!encrypted.is_empty()); + + let mut expected = Vec::::new(); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT1_CBC.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT2_CBC.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT3_CBC.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT4_CBC.to_be_bytes()); + assert_eq!(encrypted, expected); + + let decrypted = magma.decrypt(&encrypted); + assert_eq!(decrypted, source); + } + + #[test] + fn cipher_cfb_gost_r_34_13_2015() { + // Test vectors GOST R 34.13-2015 + // https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf + // Page 39, Section A.2.5 + use crypto_vectors::gost::r3413_2015; + let mut source = Vec::::new(); + source.extend_from_slice(&r3413_2015::PLAINTEXT1.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT2.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT3.to_be_bytes()); + source.extend_from_slice(&r3413_2015::PLAINTEXT4.to_be_bytes()); + + let mut magma = MagmaStream::new(r3413_2015::CIPHER_KEY.clone(), CipherMode::CFB); + + // [GOST R 34.13-2015](https://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf) + // CFB Mode: Page 39, Section A.2.5, uses MSB(128) part of IV + magma.set_iv(&IV_GOST_R3413_2015[..2]); + + let encrypted = magma.encrypt(&source); + assert!(!encrypted.is_empty()); + + let mut expected = Vec::::new(); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT1_CFB.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT2_CFB.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT3_CFB.to_be_bytes()); + expected.extend_from_slice(&r3413_2015::CIPHERTEXT4_CFB.to_be_bytes()); + assert_eq!(encrypted, expected); + + let decrypted = magma.decrypt(&encrypted); + assert_eq!(decrypted, source); + } +} diff --git a/cipher_magma/src/stream/mod.rs b/cipher_magma/src/stream/mod.rs new file mode 100644 index 0000000..f4d021a --- /dev/null +++ b/cipher_magma/src/stream/mod.rs @@ -0,0 +1,3 @@ +pub mod cipher_mode; +pub mod cipher_operation; +pub mod magma_stream; diff --git a/magma_samples/README.md b/magma_samples/README.md index 897f5a6..a49590d 100644 --- a/magma_samples/README.md +++ b/magma_samples/README.md @@ -56,19 +56,16 @@ let decrypted = magma.decrypt(encrypted); println!("Decrypted:\n{:x}", decrypted); assert_eq!(decrypted, source); - ``` ### Text encryption sample: [encrypt_text.rs](https://github.com/sheroz/magma/tree/main/magma_samples/src/samples/encrypt_text.rs) ```rust -use cipher_magma::{CipherMode, CipherOperation, Magma}; - -let cipher_mode = CipherMode::CFB; +use cipher_magma::{CipherMode, MagmaStream}; let key = [0xab; 32]; println!("Key:\n{:x?}\n", key); -let mut magma = Magma::with_key(key); +let mut magma = MagmaStream::new(key, CipherMode::CFB); let source = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ Aenean ac sem leo. Morbi pretium neque eget felis finibus convallis. \ @@ -78,12 +75,12 @@ let source = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ println!("Source:\n{}\n", String::from_utf8(source.to_vec()).unwrap()); -let encrypted = magma.cipher(source, &CipherOperation::Encrypt, &cipher_mode); +let encrypted = magma.encrypt(source); println!("Encrypted:\n{:02x?}\n", encrypted); -let mut decrypted = magma.cipher(&encrypted, &CipherOperation::Decrypt, &cipher_mode); +let mut decrypted = magma.decrypt(&encrypted); -if cipher_mode.has_padding() { +if magma.get_mode().has_padding() { // remove padding bytes decrypted.truncate(source.len()); } @@ -95,7 +92,7 @@ println!("Decrypted:\n{}\n", String::from_utf8(decrypted).unwrap()); ### Message Authentication Code (MAC) sample: [calculate_mac.rs](https://github.com/sheroz/magma/tree/main/magma_samples/src/samples/calculate_mac.rs) ```rust -use cipher_magma::{mac, Magma}; +use cipher_magma::{mac, CipherMode, MagmaStream}; let key: [u8; 32] = [ 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, @@ -103,7 +100,6 @@ let key: [u8; 32] = [ 0xfe, 0xff, ]; println!("Key:\n{:x?}\n", key); -let mut magma = Magma::with_key(key); let message = [ 0x92, 0xde, 0xf0, 0x6b, 0x3c, 0x13, 0x0a, 0x59, 0xdb, 0x54, 0xc7, 0x04, 0xf8, 0x18, 0x9d, @@ -112,6 +108,8 @@ let message = [ ]; println!("Message:\n{:02x?}\n", message); +let mut magma = MagmaStream::new(key, CipherMode::MAC); + // update the context for chunk in message.chunks(8) { mac::update(&mut magma, &chunk); @@ -127,29 +125,29 @@ assert_eq!(mac, 0x154e7210); ### File encryption sample: [encrypt_file.rs](https://github.com/sheroz/magma/tree/main/magma_samples/src/samples/encrypt_file.rs) ```rust -use cipher_magma::{CipherMode, CipherOperation, Magma}; +use cipher_magma::{CipherMode, MagmaStream}; +use std::env; +use std::fs::File; use std::io::{Read, Seek, Write}; -let cipher_mode = CipherMode::CBC; - let key = [0xab; 32]; -let mut magma = Magma::with_key(key); +let mut magma = MagmaStream::new(key, CipherMode::CBC); -// opening file +// opening source file let source_filename = "README.md"; println!("Opening source file: {}", source_filename); -let mut source_file = std::fs::File::open(source_filename).expect("Could not open file."); +let mut source_file = File::open(source_filename).expect("Could not open file."); let source_len = source_file.metadata().unwrap().len(); -let temp_dir = std::env::temp_dir(); +let temp_dir = env::temp_dir(); // creating file for encrypted data let encrypted_filename = format!("{}.encrypted", source_filename); let encrypted_filepath = temp_dir.join(encrypted_filename); println!("Creating encrypted file: {:?}", encrypted_filepath); -let mut encrypted_file = std::fs::File::options() +let mut encrypted_file = File::options() .write(true) .read(true) .create(true) @@ -170,12 +168,13 @@ loop { break; } - let ciphertext = magma.cipher(&buf[0..read_count], &CipherOperation::Encrypt, &cipher_mode); + let ciphertext = magma.encrypt(&buf[0..read_count]); encrypted_file .write_all(&ciphertext) .expect("Could not write into encrypted file"); } + encrypted_file .flush() .expect("Could not flush the encrypted file"); @@ -185,10 +184,10 @@ println!("Encryption completed."); let decrypted_filename = format!("{}.decrypted", source_filename); let decrypted_filepath = temp_dir.join(decrypted_filename); -println!("Creating decrypted file: {:?}", decrypted_filepath); +println!("Creating file for decrypted data: {:?}", decrypted_filepath); let mut decrypted_file = - std::fs::File::create(decrypted_filepath).expect("Could not create decrypted file."); + File::create(decrypted_filepath).expect("Could not create decrypted file."); println!("Decrypting ..."); @@ -206,17 +205,18 @@ loop { break; } - let plaintext = magma.cipher(&buf[0..read_count], &CipherOperation::Decrypt, &cipher_mode); + let plaintext = magma.decrypt(&buf[0..read_count]); decrypted_file .write_all(&plaintext) .expect("Could not write into decrypted file"); } + decrypted_file .flush() .expect("Could not flush the decrypted file"); -if cipher_mode.has_padding() { +if magma.get_mode().has_padding() { // remove padding bytes decrypted_file .set_len(source_len) diff --git a/magma_samples/benches/magma_benchmark.rs b/magma_samples/benches/magma_benchmark.rs index ae819b6..86f1b68 100644 --- a/magma_samples/benches/magma_benchmark.rs +++ b/magma_samples/benches/magma_benchmark.rs @@ -1,5 +1,5 @@ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; -use cipher_magma::{Magma, CipherOperation, CipherMode}; +use cipher_magma::{Magma, MagmaStream, CipherMode}; fn magma_block_benchmark(c: &mut Criterion) { let magma = Magma::new(); @@ -13,12 +13,13 @@ fn magma_block_benchmark(c: &mut Criterion) { fn magma_buffer_benchmark(c: &mut Criterion) { let source_buffer = [0_u8; 4096]; - let mut magma = Magma::new(); + let mut magma = MagmaStream::new([0;8], CipherMode::CBC); + magma.set_mode(CipherMode::ECB); c.bench_function("encrypt", |bencher| { - bencher.iter(|| magma.cipher(&source_buffer, &CipherOperation::Encrypt, &CipherMode::ECB)) + bencher.iter(|| magma.encrypt(&source_buffer)) }); c.bench_function("decrypt", |bencher| { - bencher.iter(|| magma.cipher(&source_buffer, &CipherOperation::Decrypt, &CipherMode::ECB)) + bencher.iter(|| magma.decrypt(&source_buffer)) }); } diff --git a/magma_samples/src/samples/calculate_mac.rs b/magma_samples/src/samples/calculate_mac.rs index 7451345..19714fb 100644 --- a/magma_samples/src/samples/calculate_mac.rs +++ b/magma_samples/src/samples/calculate_mac.rs @@ -1,6 +1,6 @@ /// Message Authentication Code (MAC) calculation pub fn sample_calculate_mac() { - use cipher_magma::{mac, Magma}; + use cipher_magma::{mac, CipherMode, MagmaStream}; let key: [u32; 8] = [ 0xffeeddcc, 0xbbaa9988, 0x77665544, 0x33221100, 0xf0f1f2f3, 0xf4f5f6f7, 0xf8f9fafb, @@ -15,7 +15,7 @@ pub fn sample_calculate_mac() { ]; println!("Message:\n{:02x?}\n", message); - let mut magma = Magma::with_key(key); + let mut magma = MagmaStream::new(key, CipherMode::MAC); let mac = mac::calculate(&mut magma, &message); println!("Calculated MAC:\n{:x}\n", mac); assert_eq!(mac, 0x154e7210); @@ -24,7 +24,7 @@ pub fn sample_calculate_mac() { /// Message Authentication Code (MAC) /// Updating context with data chunks and finalizing result pub fn sample_calculate_mac_data_chunks() { - use cipher_magma::{mac, Magma}; + use cipher_magma::{mac, CipherMode, MagmaStream}; let key: [u8; 32] = [ 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, @@ -32,7 +32,6 @@ pub fn sample_calculate_mac_data_chunks() { 0xfe, 0xff, ]; println!("Key:\n{:x?}\n", key); - let mut magma = Magma::with_key(key); let message = [ 0x92, 0xde, 0xf0, 0x6b, 0x3c, 0x13, 0x0a, 0x59, 0xdb, 0x54, 0xc7, 0x04, 0xf8, 0x18, 0x9d, @@ -41,6 +40,8 @@ pub fn sample_calculate_mac_data_chunks() { ]; println!("Message:\n{:02x?}\n", message); + let mut magma = MagmaStream::new(key, CipherMode::MAC); + // update the context for chunk in message.chunks(8) { mac::update(&mut magma, &chunk); diff --git a/magma_samples/src/samples/encrypt_file.rs b/magma_samples/src/samples/encrypt_file.rs index 087ade5..5c689e3 100644 --- a/magma_samples/src/samples/encrypt_file.rs +++ b/magma_samples/src/samples/encrypt_file.rs @@ -1,28 +1,28 @@ /// File encryption sample pub fn sample_encrypt_file() { - use cipher_magma::{CipherMode, CipherOperation, Magma}; + use cipher_magma::{CipherMode, MagmaStream}; + use std::env; + use std::fs::File; use std::io::{Read, Seek, Write}; - let cipher_mode = CipherMode::CBC; - let key = [0xab; 32]; - let mut magma = Magma::with_key(key); + let mut magma = MagmaStream::new(key, CipherMode::CBC); - // opening file + // opening source file let source_filename = "README.md"; println!("Opening source file: {}", source_filename); - let mut source_file = std::fs::File::open(source_filename).expect("Could not open file."); + let mut source_file = File::open(source_filename).expect("Could not open file."); let source_len = source_file.metadata().unwrap().len(); - let temp_dir = std::env::temp_dir(); + let temp_dir = env::temp_dir(); // creating file for encrypted data let encrypted_filename = format!("{}.encrypted", source_filename); let encrypted_filepath = temp_dir.join(encrypted_filename); println!("Creating encrypted file: {:?}", encrypted_filepath); - let mut encrypted_file = std::fs::File::options() + let mut encrypted_file = File::options() .write(true) .read(true) .create(true) @@ -43,12 +43,13 @@ pub fn sample_encrypt_file() { break; } - let ciphertext = magma.cipher(&buf[0..read_count], &CipherOperation::Encrypt, &cipher_mode); + let ciphertext = magma.encrypt(&buf[0..read_count]); encrypted_file .write_all(&ciphertext) .expect("Could not write into encrypted file"); } + encrypted_file .flush() .expect("Could not flush the encrypted file"); @@ -58,10 +59,10 @@ pub fn sample_encrypt_file() { let decrypted_filename = format!("{}.decrypted", source_filename); let decrypted_filepath = temp_dir.join(decrypted_filename); - println!("Creating decrypted file: {:?}", decrypted_filepath); + println!("Creating file for decrypted data: {:?}", decrypted_filepath); let mut decrypted_file = - std::fs::File::create(decrypted_filepath).expect("Could not create decrypted file."); + File::create(decrypted_filepath).expect("Could not create decrypted file."); println!("Decrypting ..."); @@ -79,17 +80,18 @@ pub fn sample_encrypt_file() { break; } - let plaintext = magma.cipher(&buf[0..read_count], &CipherOperation::Decrypt, &cipher_mode); + let plaintext = magma.decrypt(&buf[0..read_count]); decrypted_file .write_all(&plaintext) .expect("Could not write into decrypted file"); } + decrypted_file .flush() .expect("Could not flush the decrypted file"); - if cipher_mode.has_padding() { + if magma.get_mode().has_padding() { // remove padding bytes decrypted_file .set_len(source_len) diff --git a/magma_samples/src/samples/encrypt_large_buffer.rs b/magma_samples/src/samples/encrypt_large_buffer.rs index 5618a51..1436cac 100644 --- a/magma_samples/src/samples/encrypt_large_buffer.rs +++ b/magma_samples/src/samples/encrypt_large_buffer.rs @@ -1,11 +1,11 @@ /// Sample of encryption of large data in chunks pub fn sample_encrypt_large_buffer() { - use cipher_magma::{CipherMode, CipherOperation, Magma}; + use cipher_magma::{CipherMode, MagmaStream}; - let cipher_mode = CipherMode::CFB; + const BUF_SIZE: usize = 128; - let key = [0xab;32]; - let mut magma = Magma::with_key(key); + let key = [0xab; 32]; + let mut magma = MagmaStream::new(key, CipherMode::CFB); let txt = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ Aenean ac sem leo. Morbi pretium neque eget felis finibus convallis. \ @@ -16,27 +16,27 @@ pub fn sample_encrypt_large_buffer() { // building data containing 5000x of txt let repeat_count = 5000; let mut source = Vec::::with_capacity(txt.len() * repeat_count); - (0..repeat_count).for_each(|_|source.extend_from_slice(txt)); + (0..repeat_count).for_each(|_| source.extend_from_slice(txt)); println!("Source len:{}", source.len()); let mut encrypted = Vec::::with_capacity(source.len()); - let source_chunks = source.chunks(4096); + let source_chunks = source.chunks(BUF_SIZE); for chunk in source_chunks { - let mut ciphertext = magma.cipher(&chunk, &CipherOperation::Encrypt, &cipher_mode); + let mut ciphertext = magma.encrypt(&chunk); encrypted.append(&mut ciphertext); } println!("Encrypted len:{}", encrypted.len()); let mut decrypted = Vec::::with_capacity(encrypted.len()); - let encrypted_chunks = encrypted.chunks(4096); + let encrypted_chunks = encrypted.chunks(BUF_SIZE); for chunk in encrypted_chunks { - let mut plaintext = magma.cipher(&chunk, &CipherOperation::Decrypt, &cipher_mode); + let mut plaintext = magma.decrypt(&chunk); decrypted.append(&mut plaintext); } println!("Decrypted len:{}", encrypted.len()); - if cipher_mode.has_padding() { + if magma.get_mode().has_padding() { // remove padding bytes decrypted.truncate(source.len()); } diff --git a/magma_samples/src/samples/encrypt_text.rs b/magma_samples/src/samples/encrypt_text.rs index ad4a1f3..7075917 100644 --- a/magma_samples/src/samples/encrypt_text.rs +++ b/magma_samples/src/samples/encrypt_text.rs @@ -1,12 +1,10 @@ /// Text encryption sample pub fn sample_encrypt_text() { - use cipher_magma::{CipherMode, CipherOperation, Magma}; - - let cipher_mode = CipherMode::CFB; + use cipher_magma::{CipherMode, MagmaStream}; let key = [0xab; 32]; println!("Key:\n{:x?}\n", key); - let mut magma = Magma::with_key(key); + let mut magma = MagmaStream::new(key, CipherMode::CFB); let source = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ Aenean ac sem leo. Morbi pretium neque eget felis finibus convallis. \ @@ -16,12 +14,12 @@ pub fn sample_encrypt_text() { println!("Source:\n{}\n", String::from_utf8(source.to_vec()).unwrap()); - let encrypted = magma.cipher(source, &CipherOperation::Encrypt, &cipher_mode); + let encrypted = magma.encrypt(source); println!("Encrypted:\n{:02x?}\n", encrypted); - let mut decrypted = magma.cipher(&encrypted, &CipherOperation::Decrypt, &cipher_mode); + let mut decrypted = magma.decrypt(&encrypted); - if cipher_mode.has_padding() { + if magma.get_mode().has_padding() { // remove padding bytes decrypted.truncate(source.len()); }