Skip to content

Commit

Permalink
add IGE block cipher mode
Browse files Browse the repository at this point in the history
  • Loading branch information
bstnbuck committed Apr 22, 2024
1 parent 871c444 commit 34f238d
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ The V wrapper libsodium [[Git](https://github.com/vlang/libsodium)] has some of
| --- | --- | --- | --- |
| **argon2** | hash-algorithm / key derivation function | high | :x: |
| Ascon | lightweight AEAD | moderate | :x: |
| *blockcipher modes* → ECB, EAX, IGE, OCB | Electronic-Codebook, encrypt-then-authenticate-then-translate, Infinite Garble Extension, Offset codebook mode (AEAD) | moderate | experimental (only ECB) :yellow_circle: [[Git](https://github.com/bstnbuck/V-crypto/tree/main/_cipher)] |
| *blockcipher modes* → ECB, EAX, IGE, OCB | Electronic-Codebook, encrypt-then-authenticate-then-translate, Infinite Garble Extension, Offset codebook mode (AEAD) | moderate | experimental (only ECB, IGE) :yellow_circle: [[Git](https://github.com/bstnbuck/V-crypto/tree/main/_cipher)] |
| **brainpoolP(256,384,521)r1** | elliptic curve | high | :x: |
| Camellia | symmetric block cipher | low | :x: |
| CAST | symmetric block cipher | moderate | :x: |
Expand Down
47 changes: 47 additions & 0 deletions _cipher/aes_ige_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module cipher_

import crypto.aes

fn test_aes_ige() {
key1 := [u8(0x00), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
0x0D, 0x0E, 0x0F]
key2 := [u8(0x54), 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6E, 0x20, 0x69, 0x6D,
0x70, 0x6C, 0x65]

iv1 := [u8(0x00), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
0x1D, 0x1E, 0x1F]
iv2 := [u8(0x6D), 0x65, 0x6E, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x49,
0x47, 0x45, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x4F, 0x70, 0x65,
0x6E, 0x53, 0x53]

plain1 := [u8(0x00), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00]
plain2 := [u8(0x99), 0x70, 0x64, 0x87, 0xA1, 0xCD, 0xE6, 0x13, 0xBC, 0x6D, 0xE0, 0xB6, 0xF2,
0x4B, 0x1C, 0x7A, 0xA4, 0x48, 0xC8, 0xB9, 0xC3, 0x40, 0x3E, 0x34, 0x67, 0xA8, 0xCA, 0xD8,
0x93, 0x40, 0xF5, 0x3B]

out1 := [u8(0x1A), 0x85, 0x19, 0xA6, 0x55, 0x7B, 0xE6, 0x52, 0xE9, 0xDA, 0x8E, 0x43, 0xDA,
0x4E, 0xF4, 0x45, 0x3C, 0xF4, 0x56, 0xB4, 0xCA, 0x48, 0x8A, 0xA3, 0x83, 0xC7, 0x9C, 0x98,
0xB3, 0x47, 0x97, 0xCB]
out2 := [u8(0x4C), 0x2E, 0x20, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x68, 0x6F, 0x70, 0x65,
0x20, 0x42, 0x65, 0x6E, 0x20, 0x67, 0x6F, 0x74, 0x20, 0x69, 0x74, 0x20, 0x72, 0x69, 0x67,
0x68, 0x74, 0x21, 0x0A]

block1 := aes.new_cipher(key1)
mut mode1 := new_ige(block1, iv1)!
mut dst1 := []u8{len: plain1.len}
mode1.encrypt_blocks(mut dst1, plain1)
assert dst1 == out1
mode1.decrypt_blocks(mut dst1, dst1.clone())
assert dst1 == plain1

block2 := aes.new_cipher(key2)
mut mode2 := new_ige(block2, iv2)!
mut dst2 := []u8{len: out2.len}
mode2.encrypt_blocks(mut dst2, plain2)
assert dst2 == out2
mode2.decrypt_blocks(mut dst2, dst2.clone())
assert dst2 == plain2
}
99 changes: 99 additions & 0 deletions _cipher/ige.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Based on: https://github.com/karlmcguire/ige

// Infinite Garble Extension mode.
//
// See: www.links.org/files/openssl-ige.pdf
module cipher_

import crypto.cipher
import crypto.internal.subtle

struct Ige {
mut:
b cipher.Block
out []u8
iv []u8
block_size int
}

// free the resources taken by the Ige `x`
@[unsafe]
pub fn (mut x Ige) free() {
$if prealloc {
return
}
unsafe {
x.iv.free()
x.out.free()
}
}

// new_ige returns a Ige which encrypts/decrypts using the given Block in Infinite Garble Extension.
// iv must contain two IV's which will be split internal.
pub fn new_ige(b cipher.Block, iv []u8) !Ige {
if iv.len != b.block_size * 2 {
return error('v_crypto/_cipher: IV must be: (block size * 2) ${b.block_size * 2} != ${iv.len}')
}

return Ige{
b: b
out: []u8{len: b.block_size}
iv: iv.clone()
block_size: b.block_size
}
}

pub fn (mut c Ige) block_size() int {
return c.b.block_size
}

pub fn (mut c Ige) encrypt_blocks(mut dst []u8, src []u8) {
if src.len % c.b.block_size != 0 {
panic('v_crypto/_cipher: src not full blocks')
}
if dst.len < src.len {
panic('v_crypto/_cipher: dst.len < src.len')
}
if subtle.inexact_overlap(dst[..src.len], src) {
panic('crypto.cipher: invalid buffer overlap')
}

b := c.b.block_size
mut l := c.iv[..b].clone()
mut r := c.iv[b..].clone()

for i := 0; i < src.len; i += b {
cipher.xor_bytes(mut dst[i..i + b], src[i..i + b], l)
c.b.encrypt(mut dst[i..i + b], dst[i..i + b])
cipher.xor_bytes(mut dst[i..i + b], dst[i..i + b], r)

l = dst[i..i + b].clone()
r = src[i..i + b].clone()
}
}

pub fn (mut c Ige) decrypt_blocks(mut dst []u8, src []u8) {
if src.len % c.b.block_size != 0 {
panic('v_crypto/_cipher: src not full blocks')
}
if dst.len < src.len {
panic('v_crypto/_cipher: dst.len < src.len')
}
if subtle.inexact_overlap(dst[..src.len], src) {
panic('crypto.cipher: invalid buffer overlap')
}

b := c.b.block_size
mut l := c.iv[..b].clone()
mut r := c.iv[b..].clone()

for i := 0; i < src.len; i += b {
//t := src[i..i + b]
cipher.xor_bytes(mut dst[i..i + b], src[i..i + b], r)
c.b.decrypt(mut dst[i..i + b], dst[i..i + b])
cipher.xor_bytes(mut dst[i..i + b], dst[i..i + b], l)

r = dst[i..i + b].clone()
l = src[i..i + b].clone()
}
}

0 comments on commit 34f238d

Please sign in to comment.