Skip to content

Commit

Permalink
Belt-Wblock implementation (#362)
Browse files Browse the repository at this point in the history
  • Loading branch information
makavity committed Apr 15, 2023
1 parent 3a20cf5 commit b875dba
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 70 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions belt-block/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 0.1.2 (2023-04-15)
### Added
- `belt_wblock_enc`, `belt_wblock_dec`, and `to_u32` functions ([#362])

[#362]: https://github.com/RustCrypto/block-ciphers/pull/362

## 0.1.1 (2022-09-23)
### Added
- `belt_block_raw` function and `cipher` crate feature (enabled by default) ([#333])
Expand Down
2 changes: 1 addition & 1 deletion belt-block/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "belt-block"
version = "0.1.1"
version = "0.1.2"
description = "belt-block block cipher implementation"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
Expand Down
52 changes: 10 additions & 42 deletions belt-block/src/cipher_impl.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{belt_block_raw, g13, g21, g5, key_idx};
use crate::{belt_block_raw, from_u32, g13, g21, g5, key_idx, to_u32};
use cipher::consts::{U16, U32};
use cipher::{inout::InOut, AlgorithmName, Block, BlockCipher, Key, KeyInit, KeySizeUser};
use core::{fmt, mem::swap, num::Wrapping};
Expand All @@ -17,34 +17,25 @@ impl BeltBlock {
/// Encryption as described in section 6.1.3
#[inline]
fn encrypt(&self, mut block: InOut<'_, '_, Block<Self>>) {
let block_in = block.get_in();
// Steps 1 and 4
let x = [
get_u32(block_in, 0),
get_u32(block_in, 1),
get_u32(block_in, 2),
get_u32(block_in, 3),
];

let x = to_u32(block.get_in());
let y = belt_block_raw(x, &self.key);

let block_out = block.get_out();
// 6) Y ← b ‖ d ‖ a ‖ c
for i in 0..4 {
set_u32(block_out, &y, i);
}
*block_out = from_u32(&y).into();
}

/// Decryption as described in section 6.1.4
#[inline]
fn decrypt(&self, mut block: InOut<'_, '_, Block<Self>>) {
let key = &self.key;
let block_in = block.get_in();
let block_in: [u32; 4] = to_u32(block.get_in());
// Steps 1 and 4
let mut a = Wrapping(get_u32(block_in, 0));
let mut b = Wrapping(get_u32(block_in, 1));
let mut c = Wrapping(get_u32(block_in, 2));
let mut d = Wrapping(get_u32(block_in, 3));
let mut a = Wrapping(block_in[0]);
let mut b = Wrapping(block_in[1]);
let mut c = Wrapping(block_in[2]);
let mut d = Wrapping(block_in[3]);

// Step 5
for i in (1..9).rev() {
Expand Down Expand Up @@ -77,9 +68,7 @@ impl BeltBlock {
let block_out = block.get_out();
// 6) 𝑋 ← c ‖ a ‖ d ‖ b
let x = [c.0, a.0, d.0, b.0];
for i in 0..4 {
set_u32(block_out, &x, i);
}
*block_out = from_u32(&x).into();
}
}

Expand All @@ -91,18 +80,7 @@ impl KeySizeUser for BeltBlock {

impl KeyInit for BeltBlock {
fn new(key: &Key<Self>) -> Self {
Self {
key: [
get_u32(key, 0),
get_u32(key, 1),
get_u32(key, 2),
get_u32(key, 3),
get_u32(key, 4),
get_u32(key, 5),
get_u32(key, 6),
get_u32(key, 7),
],
}
Self { key: to_u32(key) }
}
}

Expand Down Expand Up @@ -133,13 +111,3 @@ cipher::impl_simple_block_encdec!(
cipher.decrypt(block);
}
);

#[inline(always)]
fn get_u32(block: &[u8], i: usize) -> u32 {
u32::from_le_bytes(block[4 * i..][..4].try_into().unwrap())
}

#[inline(always)]
fn set_u32(block: &mut [u8], val: &[u32; 4], i: usize) {
block[4 * i..][..4].copy_from_slice(&val[i].to_le_bytes());
}
100 changes: 100 additions & 0 deletions belt-block/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,103 @@ pub fn belt_block_raw(x: [u32; 4], key: &[u32; 8]) -> [u32; 4] {
// Step 6
[b.0, d.0, a.0, c.0]
}

const BLOCK_SIZE: usize = 16;
type Block = [u8; BLOCK_SIZE];

/// Wide block encryption as described in section 6.2.3 of the standard.
///
/// Returns [`InvalidLengthError`] if `data` is smaller than 32 bytes.
#[inline]
pub fn belt_wblock_enc(data: &mut [u8], key: &[u32; 8]) -> Result<(), InvalidLengthError> {
if data.len() < 2 * BLOCK_SIZE {
return Err(InvalidLengthError);
}

let len = data.len();
let n = (len + BLOCK_SIZE - 1) / BLOCK_SIZE;
for i in 1..(2 * n + 1) {
let s = data[..len - 1]
.chunks_exact(BLOCK_SIZE)
.fold(Block::default(), xor);

data.copy_within(BLOCK_SIZE.., 0);
let (tail1, tail2) = data[len - 2 * BLOCK_SIZE..].split_at_mut(BLOCK_SIZE);
tail2.copy_from_slice(&s);

let s = belt_block_raw(to_u32(&s), key);
xor_set(tail1, &from_u32::<16>(&s));
xor_set(tail1, &i.to_le_bytes());
}

Ok(())
}

/// Wide block decryption as described in section 6.2.4 of the standard.
///
/// Returns [`InvalidLengthError`] if `data` is smaller than 32 bytes.
#[inline]
pub fn belt_wblock_dec(data: &mut [u8], key: &[u32; 8]) -> Result<(), InvalidLengthError> {
if data.len() < 2 * BLOCK_SIZE {
return Err(InvalidLengthError);
}

let len = data.len();
let n = (len + BLOCK_SIZE - 1) / BLOCK_SIZE;
for i in (1..(2 * n + 1)).rev() {
let tail_pos = len - BLOCK_SIZE;
let s = Block::try_from(&data[tail_pos..]).unwrap();
data.copy_within(..tail_pos, BLOCK_SIZE);

let s_enc = belt_block_raw(to_u32(&s), key);
xor_set(&mut data[tail_pos..], &from_u32::<16>(&s_enc));
xor_set(&mut data[tail_pos..], &i.to_le_bytes());

let r1 = data[..len - 1]
.chunks_exact(BLOCK_SIZE)
.skip(1)
.fold(s, xor);
data[..BLOCK_SIZE].copy_from_slice(&r1);
}
Ok(())
}

/// Error used when data smaller than 32 bytes is passed to the `belt-wblock` functions.
#[derive(Debug, Copy, Clone)]
pub struct InvalidLengthError;

/// Helper function for transforming BelT keys and blocks from a byte array
/// to an array of `u32`s.
///
/// # Panics
/// If length of `src` is not equal to `4 * N`.
#[inline(always)]
pub fn to_u32<const N: usize>(src: &[u8]) -> [u32; N] {
assert_eq!(src.len(), 4 * N);
let mut res = [0u32; N];
res.iter_mut()
.zip(src.chunks_exact(4))
.for_each(|(dst, src)| *dst = u32::from_le_bytes(src.try_into().unwrap()));
res
}

#[inline(always)]
fn from_u32<const N: usize>(src: &[u32]) -> [u8; N] {
assert_eq!(N, 4 * src.len());
let mut res = [0u8; N];
res.chunks_exact_mut(4)
.zip(src.iter())
.for_each(|(dst, src)| dst.copy_from_slice(&src.to_le_bytes()));
res
}

#[inline(always)]
fn xor_set(block: &mut [u8], val: &[u8]) {
block.iter_mut().zip(val.iter()).for_each(|(a, b)| *a ^= b);
}

#[inline(always)]
fn xor(mut block: Block, val: &[u8]) -> Block {
block.iter_mut().zip(val.iter()).for_each(|(a, b)| *a ^= b);
block
}
127 changes: 101 additions & 26 deletions belt-block/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,33 @@
//! Example vectors from STB 34.101.31 (2020):
//! http://apmi.bsu.by/assets/files/std/belt-spec371.pdf
use belt_block::{belt_block_raw, belt_wblock_dec, belt_wblock_enc, to_u32};
#[cfg(feature = "cipher")]
use belt_block::BeltBlock;
#[cfg(feature = "cipher")]
use cipher::{BlockDecrypt, BlockEncrypt, KeyInit};
use belt_block::{
cipher::{BlockDecrypt, BlockEncrypt, KeyInit},
BeltBlock,
};
use hex_literal::hex;

fn get_u32(block: &[u8], i: usize) -> u32 {
u32::from_le_bytes(block[4 * i..][..4].try_into().unwrap())
}

/// Example vectors from STB 34.101.31 (2020):
/// http://apmi.bsu.by/assets/files/std/belt-spec371.pdf
#[test]
fn belt_block() {
// Table A.1
let key1 = hex!("E9DEE72C 8F0C0FA6 2DDB49F4 6F739647 06075316 ED247A37 39CBA383 03A98BF6");
let key1 = hex!(
"E9DEE72C 8F0C0FA6 2DDB49F4 6F739647"
"06075316 ED247A37 39CBA383 03A98BF6"
);
let pt1 = hex!("B194BAC8 0A08F53B 366D008E 584A5DE4");
let ct1 = hex!("69CCA1C9 3557C9E3 D66BC3E0 FA88FA6E");
// Table A.2
let key2 = hex!("92BD9B1C E5D14101 5445FBC9 5E4D0EF2 682080AA 227D642F 2687F934 90405511");
let key2 = hex!(
"92BD9B1C E5D14101 5445FBC9 5E4D0EF2"
"682080AA 227D642F 2687F934 90405511"
);
let pt2 = hex!("0DC53006 00CAB840 B38448E5 E993F421");
let ct2 = hex!("E12BDC1A E28257EC 703FCCF0 95EE8DF1");

for (key, pt, ct) in [(key1, pt1, ct1), (key2, pt2, ct2)] {
let mut k = [0u32; 8];
for i in 0..8 {
k[i] = get_u32(&key, i);
}
let mut x = [0u32; 4];
for i in 0..4 {
x[i] = get_u32(&pt, i);
}
let mut y = [0u32; 4];
for i in 0..4 {
y[i] = get_u32(&ct, i);
}

let res = belt_block::belt_block_raw(x, &k);
assert_eq!(res, y);
let res = belt_block_raw(to_u32(&pt), &to_u32(&key));
assert_eq!(res, to_u32(&ct));

#[cfg(feature = "cipher")]
{
Expand All @@ -49,3 +40,87 @@ fn belt_block() {
}
}
}

#[test]
fn belt_wblock() {
// Table A.6
let k1 = hex!(
"E9DEE72C 8F0C0FA6 2DDB49F4 6F739647"
"06075316 ED247A37 39CBA383 03A98BF6"
);
let x1 = hex!(
"B194BAC8 0A08F53B 366D008E 584A5DE4"
"8504FA9D 1BB6C7AC 252E72C2 02FDCE0D"
"5BE3D612 17B96181 FE6786AD 716B890B"
);
let y1 = hex!(
"49A38EE1 08D6C742 E52B774F 00A6EF98"
"B106CBD1 3EA4FB06 80323051 BC04DF76"
"E487B055 C69BCF54 1176169F 1DC9F6C8"
);
let x2 = hex!(
"B194BAC8 0A08F53B 366D008E 584A5DE4"
"8504FA9D 1BB6C7AC 252E72C2 02FDCE0D"
"5BE3D612 17B96181 FE6786AD 716B89"
);
let y2 = hex!(
"F08EF22D CAA06C81 FB127219 74221CA7"
"AB82C628 56FCF2F9 FCA006E0 19A28F16"
"E5821A51 F5735946 25DBAB8F 6A5C94"
);

// Table A.7
let k2 = hex!(
"92BD9B1C E5D14101 5445FBC9 5E4D0EF2"
"682080AA 227D642F 2687F934 90405511"
);
let y3 = hex!(
"E12BDC1A E28257EC 703FCCF0 95EE8DF1"
"C1AB7638 9FE678CA F7C6F860 D5BB9C4F"
"F33C657B 637C306A DD4EA779 9EB23D31"
);
let x3 = hex!(
"92632EE0 C21AD9E0 9A39343E 5C07DAA4"
"889B03F2 E6847EB1 52EC99F7 A4D9F154"
"B5EF68D8 E4A39E56 7153DE13 D72254EE"
);
let x4 = hex!(
"DF3F8822 30BAAFFC 92F05660 32117231"
"0E3CB218 2681EF43 102E6717 5E177BD7"
"5E93E4E8"
);
let y4 = hex!(
"E12BDC1A E28257EC 703FCCF0 95EE8DF1"
"C1AB7638 9FE678CA F7C6F860 D5BB9C4F"
"F33C657B"
);

let tests = [
(k1, &x1[..], &y1[..]),
(k1, &x2[..], &y2[..]),
(k2, &x3[..], &y3[..]),
(k2, &x4[..], &y4[..]),
];
for (key, x, y) in tests {
let k = to_u32(&key);
let mut t = x.to_vec();
belt_wblock_enc(&mut t, &k).unwrap();
assert_eq!(t, y);
belt_wblock_dec(&mut t, &k).unwrap();
assert_eq!(t, x)
}

// synthetic round-trip tests
let k = to_u32(&k1);
let x: Vec<u8> = (0u8..255).collect();
for i in 32..x.len() {
let mut t = x[..i].to_vec();
for _ in 0..16 {
belt_wblock_enc(&mut t, &k).unwrap();
}
for _ in 0..16 {
belt_wblock_dec(&mut t, &k).unwrap();
}
assert_eq!(t, x[..i]);
}
}

0 comments on commit b875dba

Please sign in to comment.