diff --git a/Cargo.lock b/Cargo.lock index b3d5fe3681baa74fc71ae3ce5dca9dd37e98f015..5b071e7547513ba25f339c3c80c3ea962264e89e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,37 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "aes" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" +dependencies = [ + "aes-soft", + "aesni", + "block-cipher-trait", +] + +[[package]] +name = "aes-soft" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" +dependencies = [ + "block-cipher-trait", + "byteorder", + "opaque-debug", +] + +[[package]] +name = "aesni" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" +dependencies = [ + "block-cipher-trait", + "opaque-debug", +] + [[package]] name = "anyhow" version = "1.0.26" @@ -34,6 +66,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-cipher-trait" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" +dependencies = [ + "generic-array", +] + [[package]] name = "block-padding" version = "0.1.5" @@ -102,6 +143,7 @@ dependencies = [ name = "dup-crypto" version = "0.8.0" dependencies = [ + "aes", "base64", "bincode", "bs58", diff --git a/Cargo.toml b/Cargo.toml index 7d67f1192edbdb92ea2ea3395755771e5099c68f..c79644f8f5a46ec91a0c43d567ddd86b9af64094 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ edition = "2018" path = "src/lib.rs" [dependencies] +aes = { version = "0.3.2", optional = true } base64 = "0.11.0" bs58 = "0.3.0" byteorder = "1.3.2" @@ -28,4 +29,5 @@ bincode = "1.2.0" [features] default = ["ser"] +aes256 = ["aes"] ser = ["serde"] diff --git a/src/aes256.rs b/src/aes256.rs new file mode 100644 index 0000000000000000000000000000000000000000..d3b5c0bd794f06cdadca5e7f3a4e53d516f4923a --- /dev/null +++ b/src/aes256.rs @@ -0,0 +1,57 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Aes256 encryption/decryption + +pub(crate) mod decrypt; +pub(crate) mod encrypt; + +pub use aes::Aes256; +pub use decrypt::decrypt_bytes; +pub use encrypt::encrypt_bytes; + +use crate::seeds::Seed32; +use aes::block_cipher_trait::generic_array::GenericArray; +use aes::block_cipher_trait::BlockCipher; + +type Block = GenericArray<u8, <Aes256 as BlockCipher>::BlockSize>; +type ParBlocks = <Aes256 as BlockCipher>::ParBlocks; + +/// Create cipher from seed of 32 bytes +pub fn new_cipher(seed: Seed32) -> Aes256 { + Aes256::new(GenericArray::from_slice(seed.as_ref())) +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn encrypt_and_decrypt_128_bytes() { + let cipher = new_cipher(Seed32::default()); + + let bytes = [3u8; 128]; + let mut encrypted_bytes = bytes; + + encrypt_bytes(&cipher, &mut encrypted_bytes); + + decrypt_bytes(&cipher, &mut encrypted_bytes); + + for i in 0..128 { + assert_eq!(bytes[i], encrypted_bytes[i]); + } + } +} diff --git a/src/aes256/decrypt.rs b/src/aes256/decrypt.rs new file mode 100644 index 0000000000000000000000000000000000000000..90bab47c54c1ce0c8c37902ef38b6e44e548b835 --- /dev/null +++ b/src/aes256/decrypt.rs @@ -0,0 +1,70 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Aes256 decryption + +use super::{Aes256, Block, ParBlocks}; +use aes::block_cipher_trait::generic_array::GenericArray; +use aes::block_cipher_trait::BlockCipher; + +/// Decrypt bytes. +/// The length of the bytes slice must be a multiple of 16 ! +/// Panics if the length of the bytes slice is not a multiple of 16. +pub fn decrypt_bytes(cipher: &Aes256, bytes: &mut [u8]) { + assert!(bytes.len() % 16 == 0); + + let mut remaining_len = bytes.len(); + let par_len = bytes.len() / 128; + if par_len > 0 { + decrypt_par_n_blocks(cipher, &mut bytes[..par_len], par_len / 8); + remaining_len -= par_len; + } + if remaining_len > 0 { + decrypt_n_blocks(cipher, &mut bytes[par_len..], remaining_len / 16); + } +} + +fn decrypt_par_n_blocks(cipher: &Aes256, bytes: &mut [u8], n: usize) { + for i in (0..n).step_by(8) { + decrypt_8_blocks(cipher, &mut bytes[i..i + 128]); + } +} + +pub(crate) fn decrypt_8_blocks(cipher: &Aes256, bytes: &mut [u8]) { + let mut blocks: GenericArray<Block, ParBlocks> = (0..8) + .map(|i| { + let begin = i * 16; + let end = begin + 16; + GenericArray::clone_from_slice(&bytes[begin..end]) + }) + .collect(); + + cipher.decrypt_blocks(&mut blocks); + + for (i, block) in blocks.into_iter().enumerate() { + let begin = i * 16; + let end = (i + 1) * 16; + bytes[begin..end].copy_from_slice(block.as_slice()); + } +} + +pub(crate) fn decrypt_n_blocks(cipher: &Aes256, bytes: &mut [u8], n: usize) { + for i in 0..n { + let begin = i * 16; + let end = (i + 1) * 16; + let mut block = GenericArray::from_mut_slice(&mut bytes[begin..end]); + cipher.decrypt_block(&mut block); + } +} diff --git a/src/aes256/encrypt.rs b/src/aes256/encrypt.rs new file mode 100644 index 0000000000000000000000000000000000000000..b3995f6d102ce95b082d4cfcbf2936181f3f83f5 --- /dev/null +++ b/src/aes256/encrypt.rs @@ -0,0 +1,70 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Aes256 encryption + +use super::{Aes256, Block, ParBlocks}; +use aes::block_cipher_trait::generic_array::GenericArray; +use aes::block_cipher_trait::BlockCipher; + +/// Encrypt bytes. +/// The length of the bytes slice must be a multiple of 16 ! +/// Panics if the length of the bytes slice is not a multiple of 16. +pub fn encrypt_bytes(cipher: &Aes256, bytes: &mut [u8]) { + assert!(bytes.len() % 16 == 0); + + let mut remaining_len = bytes.len(); + let par_len = bytes.len() / 128; + if par_len > 0 { + encrypt_par_n_blocks(cipher, &mut bytes[..par_len], par_len / 8); + remaining_len -= par_len; + } + if remaining_len > 0 { + encrypt_n_blocks(cipher, &mut bytes[par_len..], remaining_len / 16); + } +} + +fn encrypt_par_n_blocks(cipher: &Aes256, bytes: &mut [u8], n: usize) { + for i in (0..n).step_by(8) { + encrypt_8_blocks(cipher, &mut bytes[i..i + 128]); + } +} + +pub(crate) fn encrypt_8_blocks(cipher: &Aes256, bytes: &mut [u8]) { + let mut blocks: GenericArray<Block, ParBlocks> = (0..8) + .map(|i| { + let begin = i * 16; + let end = begin + 16; + GenericArray::clone_from_slice(&bytes[begin..end]) + }) + .collect(); + + cipher.encrypt_blocks(&mut blocks); + + for (i, block) in blocks.into_iter().enumerate() { + let begin = i * 16; + let end = (i + 1) * 16; + bytes[begin..end].copy_from_slice(block.as_slice()); + } +} + +pub(crate) fn encrypt_n_blocks(cipher: &Aes256, bytes: &mut [u8], n: usize) { + for i in 0..n { + let begin = i * 16; + let end = (i + 1) * 16; + let mut block = GenericArray::from_mut_slice(&mut bytes[begin..end]); + cipher.encrypt_block(&mut block); + } +} diff --git a/src/lib.rs b/src/lib.rs index 4c36431b691ce4533866f4d9716c21a975d0adc1..e8a5ed6390c88c01280b2a63eee5f022202b85a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,8 @@ )] #![allow(non_camel_case_types)] +#[cfg(feature = "aes256")] +pub mod aes256; pub mod bases; pub mod hashs; pub mod keys;