Which Encryption Method Does An Encrypts On Either Side?

Introduction

Which Encryption Method Does An Encrypts On Either Side? And When Is a Block Cipher A Stream Cipher?

Introduction

In the encryption process for symmetric key, we normally have one method to encrypt, and then we do the reverse to decrypt. In AES, for example, we perform 10 rounds of scrambling with the encryption key and some salt, and then do the reverse of the either side. The methods are then different, as we unwind with the decryption method. It is a bit like doing on a random walk from an original starting place and using a secret value to talk you on the walk, and when you get there, you trace your steps back with the secret key. But there’s a method where you implement the same method on both sides, and that method is defined a CFB (Cipher Feedback) mode.

Normally, too, we think of AES as a block cipher, and which makes it slower and more complex in the ciphering process than stream ciphers. But, AES can be converted into a stream cipher (that processes one bit at a time) using CFB, CTR and OFB modes. This stream cipher can then be massively scaled up with the use of parallel processing, and where a stream cipher can be at least 10 times faster than its equivalent block cipher mode.

Cipher Feedback (CFB)

Normally we think of the encryption process as taking plain text and using an encryption key to create the cipher text, and then applying the same key with a decryption process. The problem with this type of process is that an error in the cipher text will cause problems in decrypting.

So we apply Cipher Feedback (CFB), which basically converts our cipher block into a bit stream, and we can this encrypt and decrypt each bit at a time. This method is defined as self-synchronising. For this we do not encrypt and decrypt, but we encrypt the IV (Initialisation Vector) or a previous cipher block with the key, and EX-OR that with the data (on the sending side) or with the cipher stream. After the first block of data, we use encrypt the previous cipher block (Figure 2), and on the receiver we do the same. So the process on either side is almost identical, apart from the data which is used, where the sender uses data blocks (such as 128-bit blocks) and the receiver uses the cipher stream to build the data blocks.

If you are interested, here is the process for CFB (where P are the data blocks and C are the re-built cipher blocks):

Figure 1: Cipher feedback (CFB)
Figure 2: CFB process

Implementation

So, let’s implement CFB within the AES method, and use the most secure programming langauge: Rust. First we create a project with:

cargo new cfb

We then go into the cfb folder, and add the following to the cargo.toml file:

[package]
name = "my-project"
version = "0.1.0"
authors = ["runner"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
aes="0.7.5"
hex="0.4.3"
block-modes="0.8.1"
hex-literal="0.3.3"

The toml file defines the crates we will using in the project. In this case we will use AES CFB with a 128-bit key and a static IV 128-bit value (“f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff”) [here]:

use aes::Aes128;
use block_modes::{BlockMode, Cfb};
use block_modes::block_padding::Pkcs7;
use hex_literal::hex;
use std::str;
use std::env;
type Aes128ECfb = Cfb<Aes128, Pkcs7>;
fn main() {
let iv = hex!("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
let mut message = String::from("Hello world!");
let mut mykey =String::from("000102030405060708090A0B0C0D0E0F");
let args: Vec<String> = env::args().collect();
	if args.len() >1 {
		message = args[1].clone();
}
  if args.len() >2 {
		mykey = args[2].clone();
}
  println!("==128-bit AES CFB Mode==");
println!("Message: {}",message);
println!("Key: {}",mykey);

let plaintext=message.as_bytes();
let key = hex::decode(mykey).expect("Decoding failed");

let cipher = Aes128ECfb::new_from_slices(&key, &iv).unwrap();

let pos = plaintext.len();
let mut buffer = [0u8; 128];
buffer[..pos].copy_from_slice(plaintext);
let ciphertext = cipher.encrypt(&mut buffer, pos).unwrap();
println!("\nCiphertext: {:?}",hex::encode(ciphertext));

let cipher = Aes128ECfb::new_from_slices(&key,&iv).unwrap();
let mut buf = ciphertext.to_vec();
let decrypted_ciphertext = cipher.decrypt(&mut buf).unwrap();
println!("\nCiphertext: {:?}",str::from_utf8(decrypted_ciphertext).unwrap());
}

Finally we simply build with [here]:

cargo build

A sample run is [here]:

==128-bit AES CFB Mode==
Message: Hello World!
Key: 000102030405060708090A0B0C0D0E0F
Ciphertext: "2ec2ab845b726627e53dba263712a9a9"
Ciphertext: "Hello World!"

The running code is here:

If you want to see OFB, which is also a stream cipher, try here:

https://asecuritysite.com/rust/rust_ofb

Conclusions

And, there you go. The same decryption process as the encryption process. Magic! If you want to see CBC and ECB in action, try these links:

https://asecuritysite.com/rust/rust_aes

https://asecuritysite.com/rust/rust_aes2

and for Rust:

https://asecuritysite.com/rust/