Skip to content

Commit b875dba

Browse files
authored
Belt-Wblock implementation (#362)
1 parent 3a20cf5 commit b875dba

File tree

6 files changed

+219
-70
lines changed

6 files changed

+219
-70
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

belt-block/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## 0.1.2 (2023-04-15)
9+
### Added
10+
- `belt_wblock_enc`, `belt_wblock_dec`, and `to_u32` functions ([#362])
11+
12+
[#362]: https://github.com/RustCrypto/block-ciphers/pull/362
13+
814
## 0.1.1 (2022-09-23)
915
### Added
1016
- `belt_block_raw` function and `cipher` crate feature (enabled by default) ([#333])

belt-block/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "belt-block"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
description = "belt-block block cipher implementation"
55
authors = ["RustCrypto Developers"]
66
license = "MIT OR Apache-2.0"

belt-block/src/cipher_impl.rs

Lines changed: 10 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{belt_block_raw, g13, g21, g5, key_idx};
1+
use crate::{belt_block_raw, from_u32, g13, g21, g5, key_idx, to_u32};
22
use cipher::consts::{U16, U32};
33
use cipher::{inout::InOut, AlgorithmName, Block, BlockCipher, Key, KeyInit, KeySizeUser};
44
use core::{fmt, mem::swap, num::Wrapping};
@@ -17,34 +17,25 @@ impl BeltBlock {
1717
/// Encryption as described in section 6.1.3
1818
#[inline]
1919
fn encrypt(&self, mut block: InOut<'_, '_, Block<Self>>) {
20-
let block_in = block.get_in();
2120
// Steps 1 and 4
22-
let x = [
23-
get_u32(block_in, 0),
24-
get_u32(block_in, 1),
25-
get_u32(block_in, 2),
26-
get_u32(block_in, 3),
27-
];
28-
21+
let x = to_u32(block.get_in());
2922
let y = belt_block_raw(x, &self.key);
3023

3124
let block_out = block.get_out();
3225
// 6) Y ← b ‖ d ‖ a ‖ c
33-
for i in 0..4 {
34-
set_u32(block_out, &y, i);
35-
}
26+
*block_out = from_u32(&y).into();
3627
}
3728

3829
/// Decryption as described in section 6.1.4
3930
#[inline]
4031
fn decrypt(&self, mut block: InOut<'_, '_, Block<Self>>) {
4132
let key = &self.key;
42-
let block_in = block.get_in();
33+
let block_in: [u32; 4] = to_u32(block.get_in());
4334
// Steps 1 and 4
44-
let mut a = Wrapping(get_u32(block_in, 0));
45-
let mut b = Wrapping(get_u32(block_in, 1));
46-
let mut c = Wrapping(get_u32(block_in, 2));
47-
let mut d = Wrapping(get_u32(block_in, 3));
35+
let mut a = Wrapping(block_in[0]);
36+
let mut b = Wrapping(block_in[1]);
37+
let mut c = Wrapping(block_in[2]);
38+
let mut d = Wrapping(block_in[3]);
4839

4940
// Step 5
5041
for i in (1..9).rev() {
@@ -77,9 +68,7 @@ impl BeltBlock {
7768
let block_out = block.get_out();
7869
// 6) 𝑋 ← c ‖ a ‖ d ‖ b
7970
let x = [c.0, a.0, d.0, b.0];
80-
for i in 0..4 {
81-
set_u32(block_out, &x, i);
82-
}
71+
*block_out = from_u32(&x).into();
8372
}
8473
}
8574

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

9281
impl KeyInit for BeltBlock {
9382
fn new(key: &Key<Self>) -> Self {
94-
Self {
95-
key: [
96-
get_u32(key, 0),
97-
get_u32(key, 1),
98-
get_u32(key, 2),
99-
get_u32(key, 3),
100-
get_u32(key, 4),
101-
get_u32(key, 5),
102-
get_u32(key, 6),
103-
get_u32(key, 7),
104-
],
105-
}
83+
Self { key: to_u32(key) }
10684
}
10785
}
10886

@@ -133,13 +111,3 @@ cipher::impl_simple_block_encdec!(
133111
cipher.decrypt(block);
134112
}
135113
);
136-
137-
#[inline(always)]
138-
fn get_u32(block: &[u8], i: usize) -> u32 {
139-
u32::from_le_bytes(block[4 * i..][..4].try_into().unwrap())
140-
}
141-
142-
#[inline(always)]
143-
fn set_u32(block: &mut [u8], val: &[u32; 4], i: usize) {
144-
block[4 * i..][..4].copy_from_slice(&val[i].to_le_bytes());
145-
}

belt-block/src/lib.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,103 @@ pub fn belt_block_raw(x: [u32; 4], key: &[u32; 8]) -> [u32; 4] {
9999
// Step 6
100100
[b.0, d.0, a.0, c.0]
101101
}
102+
103+
const BLOCK_SIZE: usize = 16;
104+
type Block = [u8; BLOCK_SIZE];
105+
106+
/// Wide block encryption as described in section 6.2.3 of the standard.
107+
///
108+
/// Returns [`InvalidLengthError`] if `data` is smaller than 32 bytes.
109+
#[inline]
110+
pub fn belt_wblock_enc(data: &mut [u8], key: &[u32; 8]) -> Result<(), InvalidLengthError> {
111+
if data.len() < 2 * BLOCK_SIZE {
112+
return Err(InvalidLengthError);
113+
}
114+
115+
let len = data.len();
116+
let n = (len + BLOCK_SIZE - 1) / BLOCK_SIZE;
117+
for i in 1..(2 * n + 1) {
118+
let s = data[..len - 1]
119+
.chunks_exact(BLOCK_SIZE)
120+
.fold(Block::default(), xor);
121+
122+
data.copy_within(BLOCK_SIZE.., 0);
123+
let (tail1, tail2) = data[len - 2 * BLOCK_SIZE..].split_at_mut(BLOCK_SIZE);
124+
tail2.copy_from_slice(&s);
125+
126+
let s = belt_block_raw(to_u32(&s), key);
127+
xor_set(tail1, &from_u32::<16>(&s));
128+
xor_set(tail1, &i.to_le_bytes());
129+
}
130+
131+
Ok(())
132+
}
133+
134+
/// Wide block decryption as described in section 6.2.4 of the standard.
135+
///
136+
/// Returns [`InvalidLengthError`] if `data` is smaller than 32 bytes.
137+
#[inline]
138+
pub fn belt_wblock_dec(data: &mut [u8], key: &[u32; 8]) -> Result<(), InvalidLengthError> {
139+
if data.len() < 2 * BLOCK_SIZE {
140+
return Err(InvalidLengthError);
141+
}
142+
143+
let len = data.len();
144+
let n = (len + BLOCK_SIZE - 1) / BLOCK_SIZE;
145+
for i in (1..(2 * n + 1)).rev() {
146+
let tail_pos = len - BLOCK_SIZE;
147+
let s = Block::try_from(&data[tail_pos..]).unwrap();
148+
data.copy_within(..tail_pos, BLOCK_SIZE);
149+
150+
let s_enc = belt_block_raw(to_u32(&s), key);
151+
xor_set(&mut data[tail_pos..], &from_u32::<16>(&s_enc));
152+
xor_set(&mut data[tail_pos..], &i.to_le_bytes());
153+
154+
let r1 = data[..len - 1]
155+
.chunks_exact(BLOCK_SIZE)
156+
.skip(1)
157+
.fold(s, xor);
158+
data[..BLOCK_SIZE].copy_from_slice(&r1);
159+
}
160+
Ok(())
161+
}
162+
163+
/// Error used when data smaller than 32 bytes is passed to the `belt-wblock` functions.
164+
#[derive(Debug, Copy, Clone)]
165+
pub struct InvalidLengthError;
166+
167+
/// Helper function for transforming BelT keys and blocks from a byte array
168+
/// to an array of `u32`s.
169+
///
170+
/// # Panics
171+
/// If length of `src` is not equal to `4 * N`.
172+
#[inline(always)]
173+
pub fn to_u32<const N: usize>(src: &[u8]) -> [u32; N] {
174+
assert_eq!(src.len(), 4 * N);
175+
let mut res = [0u32; N];
176+
res.iter_mut()
177+
.zip(src.chunks_exact(4))
178+
.for_each(|(dst, src)| *dst = u32::from_le_bytes(src.try_into().unwrap()));
179+
res
180+
}
181+
182+
#[inline(always)]
183+
fn from_u32<const N: usize>(src: &[u32]) -> [u8; N] {
184+
assert_eq!(N, 4 * src.len());
185+
let mut res = [0u8; N];
186+
res.chunks_exact_mut(4)
187+
.zip(src.iter())
188+
.for_each(|(dst, src)| dst.copy_from_slice(&src.to_le_bytes()));
189+
res
190+
}
191+
192+
#[inline(always)]
193+
fn xor_set(block: &mut [u8], val: &[u8]) {
194+
block.iter_mut().zip(val.iter()).for_each(|(a, b)| *a ^= b);
195+
}
196+
197+
#[inline(always)]
198+
fn xor(mut block: Block, val: &[u8]) -> Block {
199+
block.iter_mut().zip(val.iter()).for_each(|(a, b)| *a ^= b);
200+
block
201+
}

belt-block/tests/mod.rs

Lines changed: 101 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,33 @@
1+
//! Example vectors from STB 34.101.31 (2020):
2+
//! http://apmi.bsu.by/assets/files/std/belt-spec371.pdf
3+
use belt_block::{belt_block_raw, belt_wblock_dec, belt_wblock_enc, to_u32};
14
#[cfg(feature = "cipher")]
2-
use belt_block::BeltBlock;
3-
#[cfg(feature = "cipher")]
4-
use cipher::{BlockDecrypt, BlockEncrypt, KeyInit};
5+
use belt_block::{
6+
cipher::{BlockDecrypt, BlockEncrypt, KeyInit},
7+
BeltBlock,
8+
};
59
use hex_literal::hex;
610

7-
fn get_u32(block: &[u8], i: usize) -> u32 {
8-
u32::from_le_bytes(block[4 * i..][..4].try_into().unwrap())
9-
}
10-
11-
/// Example vectors from STB 34.101.31 (2020):
12-
/// http://apmi.bsu.by/assets/files/std/belt-spec371.pdf
1311
#[test]
1412
fn belt_block() {
1513
// Table A.1
16-
let key1 = hex!("E9DEE72C 8F0C0FA6 2DDB49F4 6F739647 06075316 ED247A37 39CBA383 03A98BF6");
14+
let key1 = hex!(
15+
"E9DEE72C 8F0C0FA6 2DDB49F4 6F739647"
16+
"06075316 ED247A37 39CBA383 03A98BF6"
17+
);
1718
let pt1 = hex!("B194BAC8 0A08F53B 366D008E 584A5DE4");
1819
let ct1 = hex!("69CCA1C9 3557C9E3 D66BC3E0 FA88FA6E");
1920
// Table A.2
20-
let key2 = hex!("92BD9B1C E5D14101 5445FBC9 5E4D0EF2 682080AA 227D642F 2687F934 90405511");
21+
let key2 = hex!(
22+
"92BD9B1C E5D14101 5445FBC9 5E4D0EF2"
23+
"682080AA 227D642F 2687F934 90405511"
24+
);
2125
let pt2 = hex!("0DC53006 00CAB840 B38448E5 E993F421");
2226
let ct2 = hex!("E12BDC1A E28257EC 703FCCF0 95EE8DF1");
2327

2428
for (key, pt, ct) in [(key1, pt1, ct1), (key2, pt2, ct2)] {
25-
let mut k = [0u32; 8];
26-
for i in 0..8 {
27-
k[i] = get_u32(&key, i);
28-
}
29-
let mut x = [0u32; 4];
30-
for i in 0..4 {
31-
x[i] = get_u32(&pt, i);
32-
}
33-
let mut y = [0u32; 4];
34-
for i in 0..4 {
35-
y[i] = get_u32(&ct, i);
36-
}
37-
38-
let res = belt_block::belt_block_raw(x, &k);
39-
assert_eq!(res, y);
29+
let res = belt_block_raw(to_u32(&pt), &to_u32(&key));
30+
assert_eq!(res, to_u32(&ct));
4031

4132
#[cfg(feature = "cipher")]
4233
{
@@ -49,3 +40,87 @@ fn belt_block() {
4940
}
5041
}
5142
}
43+
44+
#[test]
45+
fn belt_wblock() {
46+
// Table A.6
47+
let k1 = hex!(
48+
"E9DEE72C 8F0C0FA6 2DDB49F4 6F739647"
49+
"06075316 ED247A37 39CBA383 03A98BF6"
50+
);
51+
let x1 = hex!(
52+
"B194BAC8 0A08F53B 366D008E 584A5DE4"
53+
"8504FA9D 1BB6C7AC 252E72C2 02FDCE0D"
54+
"5BE3D612 17B96181 FE6786AD 716B890B"
55+
);
56+
let y1 = hex!(
57+
"49A38EE1 08D6C742 E52B774F 00A6EF98"
58+
"B106CBD1 3EA4FB06 80323051 BC04DF76"
59+
"E487B055 C69BCF54 1176169F 1DC9F6C8"
60+
);
61+
let x2 = hex!(
62+
"B194BAC8 0A08F53B 366D008E 584A5DE4"
63+
"8504FA9D 1BB6C7AC 252E72C2 02FDCE0D"
64+
"5BE3D612 17B96181 FE6786AD 716B89"
65+
);
66+
let y2 = hex!(
67+
"F08EF22D CAA06C81 FB127219 74221CA7"
68+
"AB82C628 56FCF2F9 FCA006E0 19A28F16"
69+
"E5821A51 F5735946 25DBAB8F 6A5C94"
70+
);
71+
72+
// Table A.7
73+
let k2 = hex!(
74+
"92BD9B1C E5D14101 5445FBC9 5E4D0EF2"
75+
"682080AA 227D642F 2687F934 90405511"
76+
);
77+
let y3 = hex!(
78+
"E12BDC1A E28257EC 703FCCF0 95EE8DF1"
79+
"C1AB7638 9FE678CA F7C6F860 D5BB9C4F"
80+
"F33C657B 637C306A DD4EA779 9EB23D31"
81+
);
82+
let x3 = hex!(
83+
"92632EE0 C21AD9E0 9A39343E 5C07DAA4"
84+
"889B03F2 E6847EB1 52EC99F7 A4D9F154"
85+
"B5EF68D8 E4A39E56 7153DE13 D72254EE"
86+
);
87+
let x4 = hex!(
88+
"DF3F8822 30BAAFFC 92F05660 32117231"
89+
"0E3CB218 2681EF43 102E6717 5E177BD7"
90+
"5E93E4E8"
91+
);
92+
let y4 = hex!(
93+
"E12BDC1A E28257EC 703FCCF0 95EE8DF1"
94+
"C1AB7638 9FE678CA F7C6F860 D5BB9C4F"
95+
"F33C657B"
96+
);
97+
98+
let tests = [
99+
(k1, &x1[..], &y1[..]),
100+
(k1, &x2[..], &y2[..]),
101+
(k2, &x3[..], &y3[..]),
102+
(k2, &x4[..], &y4[..]),
103+
];
104+
for (key, x, y) in tests {
105+
let k = to_u32(&key);
106+
let mut t = x.to_vec();
107+
belt_wblock_enc(&mut t, &k).unwrap();
108+
assert_eq!(t, y);
109+
belt_wblock_dec(&mut t, &k).unwrap();
110+
assert_eq!(t, x)
111+
}
112+
113+
// synthetic round-trip tests
114+
let k = to_u32(&k1);
115+
let x: Vec<u8> = (0u8..255).collect();
116+
for i in 32..x.len() {
117+
let mut t = x[..i].to_vec();
118+
for _ in 0..16 {
119+
belt_wblock_enc(&mut t, &k).unwrap();
120+
}
121+
for _ in 0..16 {
122+
belt_wblock_dec(&mut t, &k).unwrap();
123+
}
124+
assert_eq!(t, x[..i]);
125+
}
126+
}

0 commit comments

Comments
 (0)