mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Add AES algorithm (#2496)
* crypto: add AES algorithm * Some updates to the API * Silence test. * Fixed stdlib tests * Some cleanup. Comments. Make internal methods functions. --------- Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
This commit is contained in:
31
benchmarks/stdlib/crypto/aes_bench.c3
Normal file
31
benchmarks/stdlib/crypto/aes_bench.c3
Normal file
@@ -0,0 +1,31 @@
|
||||
module std::crypto::aes_bench;
|
||||
|
||||
import std::crypto::aes;
|
||||
|
||||
fn void init() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(5);
|
||||
set_benchmark_max_iterations(10_000);
|
||||
}
|
||||
|
||||
|
||||
AesType aes = AES256;
|
||||
char[] key = x"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4";
|
||||
char[] text = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710";
|
||||
char[] cipher = x"601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c52b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6";
|
||||
char[16] iv = x"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
|
||||
|
||||
fn void bench_ctr_xcrypt() @benchmark
|
||||
{
|
||||
char[64] out;
|
||||
Aes ctx;
|
||||
|
||||
// encrypt
|
||||
ctx.init_with_iv(aes, CTR, key, iv);
|
||||
ctx.encrypt_buffer(text, &out);
|
||||
|
||||
// decrypt
|
||||
ctx.init_with_iv(aes, CTR, key, iv);
|
||||
ctx.decrypt_buffer(cipher, &out);
|
||||
}
|
||||
|
||||
650
lib/std/crypto/aes.c3
Normal file
650
lib/std/crypto/aes.c3
Normal file
@@ -0,0 +1,650 @@
|
||||
<*
|
||||
This is an implementation of the AES algorithm with the ECB, CTR and CBC
|
||||
modes. The key size can be chosen among AES128, AES192, AES256.
|
||||
|
||||
Ported from github.com/kokke/tiny-aes-c by Koni Marti.
|
||||
|
||||
The implementation is verified against the test vectors from the National
|
||||
Institute of Standards and Technology Special Publication 800-38A 2001 ED.
|
||||
|
||||
Data length must be evenly divisible by 16 bytes (len % 16 == 0) unless CTR is
|
||||
used. You should pad the end of the string with zeros or use PKCS7 if this is not the case.
|
||||
For AES192/256 the key size is proportionally larger.
|
||||
|
||||
The following example demonstrates the AES encryption of a plaintext string
|
||||
with an AES 128-bit key:
|
||||
|
||||
```
|
||||
module app;
|
||||
import std::crypto::aes, std::io;
|
||||
fn void main()
|
||||
{
|
||||
char[] key = x"2b7e151628aed2a6abf7158809cf4f3c";
|
||||
char[] text = x"6bc1bee22e409f96e93d7e117393172a";
|
||||
char[16] iv = x"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
|
||||
Aes aes;
|
||||
aes.init(AES128, key, iv);
|
||||
defer aes.destroy();
|
||||
char[] cipher = aes.encrypt(mem, text);
|
||||
defer free(cipher);
|
||||
assert(cipher == x"874d6191b620e3261bef6864990db6ce");
|
||||
}
|
||||
```
|
||||
|
||||
*>
|
||||
module std::crypto::aes;
|
||||
|
||||
<* Block length in bytes. AES is 128-bit blocks only. *>
|
||||
const BLOCKLEN = 16;
|
||||
|
||||
<* Number of columns of a AES state. *>
|
||||
const COLNUM = 4;
|
||||
|
||||
<*
|
||||
Block modes:
|
||||
ECB - Electronic Code Book (Not recommended, indata be 16 byte multiple)
|
||||
CBC - Cipher Block Chaining (Indata be 16 byte multiple)
|
||||
CTR - Counter Mode (Recommended, data may be any size)
|
||||
*>
|
||||
enum BlockMode
|
||||
{
|
||||
ECB,
|
||||
CBC,
|
||||
CTR,
|
||||
}
|
||||
|
||||
<* AES type: 128, 192 or 256 bits *>
|
||||
enum AesType : (AesKey key)
|
||||
{
|
||||
AES128 = { 128, 16, 176, 4, 10 },
|
||||
AES192 = { 192, 24, 208, 6, 12 },
|
||||
AES256 = { 256, 32, 240, 8, 14 }
|
||||
}
|
||||
|
||||
struct AesKey
|
||||
{
|
||||
<* Size of key in bits *>
|
||||
usz key_size;
|
||||
<* Size of key in bytes *>
|
||||
int key_len;
|
||||
<* Size of the expanded round_key *>
|
||||
int key_exp_size; // expected size of round_key
|
||||
<* Number of 32 bit words in key *>
|
||||
usz nk;
|
||||
<* Number of rounds in the cipher *>
|
||||
usz nr;
|
||||
}
|
||||
|
||||
struct Aes
|
||||
{
|
||||
<* The type, AES128, AES192 or AES256 *>
|
||||
AesKey type;
|
||||
<* Block mode: ECB, CBC or CTR *>
|
||||
BlockMode mode;
|
||||
<* Initialization Vector *>
|
||||
char[BLOCKLEN] iv;
|
||||
<* Internal key state *>
|
||||
char[256] round_key;
|
||||
<* Internal state *>
|
||||
AesState state;
|
||||
}
|
||||
alias AesState = char[COLNUM][COLNUM];
|
||||
|
||||
<*
|
||||
Initializes the AES crypto. The initialization vector should be securely random for each encryption
|
||||
to mitigate things like replay attacks.
|
||||
|
||||
@param type : "The type or AES: 128, 192 or 256 bits"
|
||||
@param [in] key : "The key to use, should be the same bit size as the type, so 16, 24 or 32 bytes"
|
||||
@param iv : "The initialization vector"
|
||||
@param mode : "The block mode: EBC, CBC, CTR. Defaults to CTR"
|
||||
|
||||
@require key.len == type.key.key_len : "Key does not match expected length."
|
||||
*>
|
||||
fn Aes* Aes.init(&self, AesType type, char[] key, char[BLOCKLEN] iv, BlockMode mode = CTR)
|
||||
{
|
||||
*self = { .type = type.key, .mode = mode, .iv = iv };
|
||||
key_expansion(type, key, &self.round_key);
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
Completely erases data stored in the context.
|
||||
*>
|
||||
fn void Aes.destroy(&self)
|
||||
{
|
||||
*self = {};
|
||||
}
|
||||
|
||||
<*
|
||||
Check if the length is valid using the given block mode. It has to be a multiple of 16 bytes unless CTR is used.
|
||||
*>
|
||||
macro bool is_valid_encryption_len(BlockMode mode, usz len)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case CTR:
|
||||
return true;
|
||||
case ECB:
|
||||
case CBC:
|
||||
return len % BLOCKLEN == 0;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] in : "Plaintext input."
|
||||
@param [out] out : "Cipher output."
|
||||
@require is_valid_encryption_len(self.mode, in.len) : "The input must be a multiple of 16 unless CTR is used"
|
||||
@require out.len >= in.len : "Out buffer must be sufficiently large to hold the data"
|
||||
*>
|
||||
fn void Aes.encrypt_buffer(&self, char[] in, char[] out)
|
||||
{
|
||||
switch (self.mode)
|
||||
{
|
||||
case CTR: ctr_xcrypt_buffer(self, in, out);
|
||||
case ECB: ecb_encrypt_buffer(self, in, out);
|
||||
case CBC: cbc_encrypt_buffer(self, in, out);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] in : "Cipher input."
|
||||
@param [out] out : "Plaintext output."
|
||||
@require is_valid_encryption_len(self.mode, in.len) : "The encrypted data must be a multiple of 16 unless CTR is used"
|
||||
@require out.len >= in.len : "Out buffer must be sufficiently large to hold the data"
|
||||
*>
|
||||
fn void Aes.decrypt_buffer(&self, char[] in, char[] out)
|
||||
{
|
||||
switch (self.mode)
|
||||
{
|
||||
case ECB: ecb_decrypt_buffer(self, in, out);
|
||||
case CBC: cbc_decrypt_buffer(self, in, out);
|
||||
case CTR: ctr_xcrypt_buffer(self, in, out);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Encrypt the data, allocating memory for the encrypted data.
|
||||
|
||||
@param [in] in : "Plaintext input."
|
||||
@param [&inout] allocator : "The allocator to use for the output"
|
||||
@require is_valid_encryption_len(self.mode, in.len) : "The in-data needs to be a multiple of 16 unless CTR is used"
|
||||
*>
|
||||
fn char[] Aes.encrypt(&self, Allocator allocator, char[] in)
|
||||
{
|
||||
char[] out = allocator::alloc_array(allocator, char, in.len);
|
||||
self.encrypt_buffer(in, out) @inline;
|
||||
return out;
|
||||
}
|
||||
|
||||
<*
|
||||
Encrypt the data, allocating temp memory for the encrypted data.
|
||||
|
||||
@param [in] in : "Plaintext input."
|
||||
@require is_valid_encryption_len(self.mode, in.len) : "The in-data needs to be a multiple of 16 unless CTR is used"
|
||||
*>
|
||||
fn char[] Aes.tencrypt(&self, char[] in)
|
||||
{
|
||||
return self.encrypt(tmem, in);
|
||||
}
|
||||
|
||||
<*
|
||||
Decrypt the data, allocating memory for the decrypted data.
|
||||
|
||||
@param [in] in : "Encrypted input."
|
||||
@param [&inout] allocator : "The allocator to use for the output"
|
||||
@require is_valid_encryption_len(self.mode, in.len) : "The in-data needs to be a multiple of 16 unless CTR is used"
|
||||
*>
|
||||
fn char[] Aes.decrypt(&self, Allocator allocator, char[] in)
|
||||
{
|
||||
char[] out = allocator::alloc_array(allocator, char, in.len);
|
||||
self.decrypt_buffer(in, out) @inline;
|
||||
return out;
|
||||
}
|
||||
|
||||
<*
|
||||
Decrypt the data, allocating temp memory for the decrypted data.
|
||||
|
||||
@param [in] in : "Encrypted input."
|
||||
@require is_valid_encryption_len(self.mode, in.len) : "The in-data needs to be a multiple of 16 unless CTR is used"
|
||||
|
||||
*>
|
||||
fn char[] Aes.tdecrypt(&self, char[] in)
|
||||
{
|
||||
return self.decrypt(tmem, in);
|
||||
}
|
||||
|
||||
module std::crypto::aes @private;
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Plaintext input."
|
||||
@param [out] out : "Cipher output."
|
||||
*>
|
||||
fn void ecb_encrypt_block(Aes *aes, char[BLOCKLEN]* in, char[BLOCKLEN]* out)
|
||||
{
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
for (usz j = 0; j < 4; j++)
|
||||
{
|
||||
aes.state[i][j] = (*in)[i * 4 + j];
|
||||
}
|
||||
}
|
||||
aes_cipher(aes, &aes.round_key);
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
for (usz j = 0; j < 4; j++)
|
||||
{
|
||||
(*out)[i * 4 + j] = aes.state[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Cipher input."
|
||||
@param [out] out : "Plaintext output."
|
||||
*>
|
||||
fn void ecb_decrypt_block(Aes *aes, char[BLOCKLEN]* in, char[BLOCKLEN]* out)
|
||||
{
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
for (usz j = 0; j < 4; j++)
|
||||
{
|
||||
aes.state[i][j] = (*in)[i * 4 + j];
|
||||
}
|
||||
}
|
||||
inv_cipher(aes, &aes.round_key);
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
for (usz j = 0; j < 4; j++)
|
||||
{
|
||||
(*out)[i * 4 + j] = aes.state[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Cipher input."
|
||||
@param [out] out : "Plaintext output."
|
||||
@require out.len >= in.len : "out must be at least as large as buf"
|
||||
*>
|
||||
fn void ecb_decrypt_buffer(Aes *aes, char[] in, char[] out)
|
||||
{
|
||||
usz len = in.len;
|
||||
for (usz i = 0; i < len; i += 4)
|
||||
{
|
||||
ecb_decrypt_block(aes, in[:BLOCKLEN], out[:BLOCKLEN]) @inline;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Plaintext input."
|
||||
@param [out] out : "Cipher output."
|
||||
*>
|
||||
fn void ecb_encrypt_buffer(Aes *aes, char[] in, char[] out)
|
||||
{
|
||||
usz len = in.len;
|
||||
for (usz i = 0; i < len; i += BLOCKLEN)
|
||||
{
|
||||
ecb_encrypt_block(aes, in[i:BLOCKLEN], out[i:BLOCKLEN]) @inline;
|
||||
}
|
||||
}
|
||||
|
||||
fn void xor_with_iv(char[] buf, char[BLOCKLEN]* iv) @local
|
||||
{
|
||||
foreach (i, b : *iv)
|
||||
{
|
||||
buf[i] ^= b;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Plaintext input."
|
||||
@param [out] out : "Cipher output."
|
||||
*>
|
||||
fn void cbc_encrypt_buffer(Aes *aes, char[] in, char[] out)
|
||||
{
|
||||
char[] iv = aes.iv[..];
|
||||
usz len = in.len;
|
||||
char[BLOCKLEN] tmp;
|
||||
char[BLOCKLEN] tmp2;
|
||||
for (usz i = 0; i < len; i += BLOCKLEN)
|
||||
{
|
||||
tmp[:BLOCKLEN] = in[i:BLOCKLEN];
|
||||
xor_with_iv(&tmp, iv);
|
||||
ecb_encrypt_block(aes, &tmp, &tmp2);
|
||||
out[i:BLOCKLEN] = tmp2[..];
|
||||
iv = tmp2[..];
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Cipher input."
|
||||
@param [out] out : "Plaintext output."
|
||||
*>
|
||||
fn void cbc_decrypt_buffer(Aes *aes, char[] in, char[] out)
|
||||
{
|
||||
char[BLOCKLEN] tmp;
|
||||
usz len = in.len;
|
||||
for (usz i = 0; i < len; i += BLOCKLEN)
|
||||
{
|
||||
ecb_decrypt_block(aes, in[i:BLOCKLEN], &tmp);
|
||||
xor_with_iv(&tmp, aes.iv[..]);
|
||||
aes.iv[:BLOCKLEN] = in[i:BLOCKLEN];
|
||||
out[i:BLOCKLEN] = tmp[..];
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Plaintext/cipher input."
|
||||
@param [out] out : "Cipher/plaintext output."
|
||||
*>
|
||||
fn void ctr_xcrypt_buffer(Aes *aes, char[] in, char[] out)
|
||||
{
|
||||
char[BLOCKLEN] buffer @noinit;
|
||||
usz len = in.len;
|
||||
for (int bi = BLOCKLEN, usz i = 0; i < len; i++)
|
||||
{
|
||||
if (bi == BLOCKLEN)
|
||||
{
|
||||
buffer = aes.iv;
|
||||
ecb_encrypt_block(aes, &buffer, &buffer);
|
||||
|
||||
for LOOP: (bi = (BLOCKLEN - 1); bi >= 0; bi--)
|
||||
{
|
||||
if (aes.iv[bi] == 255)
|
||||
{
|
||||
aes.iv[bi] = 0;
|
||||
continue;
|
||||
}
|
||||
aes.iv[bi]++;
|
||||
break LOOP;
|
||||
}
|
||||
bi = 0;
|
||||
}
|
||||
out[i] = in[i] ^ buffer[bi];
|
||||
bi++;
|
||||
}
|
||||
}
|
||||
|
||||
macro char get_sbox_value(num) => SBOX[num];
|
||||
macro char get_sbox_invert(num) => RSBOX[num];
|
||||
|
||||
const char[256] SBOX =
|
||||
x`637c777bf26b6fc53001672bfed7ab76
|
||||
ca82c97dfa5947f0add4a2af9ca472c0
|
||||
b7fd9326363ff7cc34a5e5f171d83115
|
||||
04c723c31896059a071280e2eb27b275
|
||||
09832c1a1b6e5aa0523bd6b329e32f84
|
||||
53d100ed20fcb15b6acbbe394a4c58cf
|
||||
d0efaafb434d338545f9027f503c9fa8
|
||||
51a3408f929d38f5bcb6da2110fff3d2
|
||||
cd0c13ec5f974417c4a77e3d645d1973
|
||||
60814fdc222a908846eeb814de5e0bdb
|
||||
e0323a0a4906245cc2d3ac629195e479
|
||||
e7c8376d8dd54ea96c56f4ea657aae08
|
||||
ba78252e1ca6b4c6e8dd741f4bbd8b8a
|
||||
703eb5664803f60e613557b986c11d9e
|
||||
e1f8981169d98e949b1e87e9ce5528df
|
||||
8ca1890dbfe6426841992d0fb054bb16`;
|
||||
|
||||
const char[256] RSBOX =
|
||||
x`52096ad53036a538bf40a39e81f3d7fb
|
||||
7ce339829b2fff87348e4344c4dee9cb
|
||||
547b9432a6c2233dee4c950b42fac34e
|
||||
082ea16628d924b2765ba2496d8bd125
|
||||
72f8f66486689816d4a45ccc5d65b692
|
||||
6c704850fdedb9da5e154657a78d9d84
|
||||
90d8ab008cbcd30af7e45805b8b34506
|
||||
d02c1e8fca3f0f02c1afbd0301138a6b
|
||||
3a9111414f67dcea97f2cfcef0b4e673
|
||||
96ac7422e7ad3585e2f937e81c75df6e
|
||||
47f11a711d29c5896fb7620eaa18be1b
|
||||
fc563e4bc6d279209adbc0fe78cd5af4
|
||||
1fdda8338807c731b11210592780ec5f
|
||||
60517fa919b54a0d2de57a9f93c99cef
|
||||
a0e03b4dae2af5b0c8ebbb3c83539961
|
||||
172b047eba77d626e169146355210c7d`;
|
||||
|
||||
const char[11] RCON = x`8d01020408102040801b36`;
|
||||
|
||||
fn void add_round_key(Aes* aes, usz round, char[] round_key)
|
||||
{
|
||||
usz i, j;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
aes.state[i][j] ^= round_key[(round * COLNUM * 4) + (i * COLNUM) + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void sub_bytes(Aes* aes)
|
||||
{
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
for (usz j = 0; j < 4; j++)
|
||||
{
|
||||
aes.state[j][i] = get_sbox_value(aes.state[j][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void shift_rows(Aes* aes)
|
||||
{
|
||||
char temp;
|
||||
|
||||
temp = aes.state[0][1];
|
||||
aes.state[0][1] = aes.state[1][1];
|
||||
aes.state[1][1] = aes.state[2][1];
|
||||
aes.state[2][1] = aes.state[3][1];
|
||||
aes.state[3][1] = temp;
|
||||
|
||||
temp = aes.state[0][2];
|
||||
aes.state[0][2] = aes.state[2][2];
|
||||
aes.state[2][2] = temp;
|
||||
|
||||
temp = aes.state[1][2];
|
||||
aes.state[1][2] = aes.state[3][2];
|
||||
aes.state[3][2] = temp;
|
||||
|
||||
temp = aes.state[0][3];
|
||||
aes.state[0][3] = aes.state[3][3];
|
||||
aes.state[3][3] = aes.state[2][3];
|
||||
aes.state[2][3] = aes.state[1][3];
|
||||
aes.state[1][3] = temp;
|
||||
}
|
||||
|
||||
fn char xtime(char x) @local
|
||||
{
|
||||
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
|
||||
}
|
||||
|
||||
fn void mix_columns(Aes* aes)
|
||||
{
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
char t = aes.state[i][0];
|
||||
char tmp = aes.state[i][0] ^ aes.state[i][1] ^ aes.state[i][2] ^ aes.state[i][3];
|
||||
|
||||
char tm = aes.state[i][0] ^ aes.state[i][1];
|
||||
tm = xtime(tm);
|
||||
aes.state[i][0] ^= tm ^ tmp;
|
||||
|
||||
tm = aes.state[i][1] ^ aes.state[i][2];
|
||||
tm = xtime(tm);
|
||||
aes.state[i][1] ^= tm ^ tmp;
|
||||
|
||||
tm = aes.state[i][2] ^ aes.state[i][3];
|
||||
tm = xtime(tm);
|
||||
aes.state[i][2] ^= tm ^ tmp;
|
||||
|
||||
tm = aes.state[i][3] ^ t;
|
||||
tm = xtime(tm);
|
||||
aes.state[i][3] ^= tm ^ tmp;
|
||||
}
|
||||
}
|
||||
|
||||
fn char multiply(char x, char y) @local
|
||||
{
|
||||
return (((y & 1) * x) ^
|
||||
(((y>>1) & 1) * xtime(x)) ^
|
||||
(((y>>2) & 1) * xtime(xtime(x))) ^
|
||||
(((y>>3) & 1) * xtime(xtime(xtime(x)))) ^
|
||||
(((y>>4) & 1) * xtime(xtime(xtime(xtime(x))))));
|
||||
}
|
||||
|
||||
fn void inv_mix_columns(Aes* aes)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
char a = aes.state[i][0];
|
||||
char b = aes.state[i][1];
|
||||
char c = aes.state[i][2];
|
||||
char d = aes.state[i][3];
|
||||
|
||||
aes.state[i][0] = multiply(a, 0x0e) ^ multiply(b, 0x0b) ^ multiply(c, 0x0d) ^ multiply(d, 0x09);
|
||||
aes.state[i][1] = multiply(a, 0x09) ^ multiply(b, 0x0e) ^ multiply(c, 0x0b) ^ multiply(d, 0x0d);
|
||||
aes.state[i][2] = multiply(a, 0x0d) ^ multiply(b, 0x09) ^ multiply(c, 0x0e) ^ multiply(d, 0x0b);
|
||||
aes.state[i][3] = multiply(a, 0x0b) ^ multiply(b, 0x0d) ^ multiply(c, 0x09) ^ multiply(d, 0x0e);
|
||||
}
|
||||
}
|
||||
|
||||
fn void inv_sub_bytes(Aes* aes)
|
||||
{
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
for (usz j = 0; j < 4; j++)
|
||||
{
|
||||
aes.state[j][i] = get_sbox_invert(aes.state[j][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void inv_shift_rows(Aes* aes)
|
||||
{
|
||||
char temp;
|
||||
|
||||
temp = aes.state[3][1];
|
||||
aes.state[3][1] = aes.state[2][1];
|
||||
aes.state[2][1] = aes.state[1][1];
|
||||
aes.state[1][1] = aes.state[0][1];
|
||||
aes.state[0][1] = temp;
|
||||
|
||||
temp = aes.state[0][2];
|
||||
aes.state[0][2] = aes.state[2][2];
|
||||
aes.state[2][2] = temp;
|
||||
|
||||
temp = aes.state[1][2];
|
||||
aes.state[1][2] = aes.state[3][2];
|
||||
aes.state[3][2] = temp;
|
||||
|
||||
temp = aes.state[0][3];
|
||||
aes.state[0][3] = aes.state[1][3];
|
||||
aes.state[1][3] = aes.state[2][3];
|
||||
aes.state[2][3] = aes.state[3][3];
|
||||
aes.state[3][3] = temp;
|
||||
}
|
||||
|
||||
fn void aes_cipher(Aes* aes, char[] round_key)
|
||||
{
|
||||
usz round = 0;
|
||||
add_round_key(aes, 0, round_key);
|
||||
|
||||
for LOOP: (round = 1;; round++)
|
||||
{
|
||||
sub_bytes(aes);
|
||||
shift_rows(aes);
|
||||
if (round == aes.type.nr) break LOOP;
|
||||
mix_columns(aes);
|
||||
add_round_key(aes, round, round_key);
|
||||
}
|
||||
add_round_key(aes, aes.type.nr, round_key);
|
||||
}
|
||||
|
||||
fn void inv_cipher(Aes* aes, char[] round_key)
|
||||
{
|
||||
add_round_key(aes, aes.type.nr, round_key);
|
||||
for (usz round = aes.type.nr - 1; ; round--)
|
||||
{
|
||||
inv_shift_rows(aes);
|
||||
inv_sub_bytes(aes);
|
||||
add_round_key(aes, round, round_key);
|
||||
if (!round) return;
|
||||
inv_mix_columns(aes);
|
||||
}
|
||||
}
|
||||
|
||||
<*¨
|
||||
@param type : "The AES variant to expant the key for"
|
||||
@param [out] round_key : "Key to expand into"
|
||||
@param [in] key : "The key to expand"
|
||||
@require key.len == type.key.key_len : "Key does not match expected length."
|
||||
*>
|
||||
fn void key_expansion(AesType type, char[] key, char[] round_key) @private
|
||||
{
|
||||
usz nk = type.key.nk;
|
||||
for (usz i = 0; i < nk; i++)
|
||||
{
|
||||
round_key[(i * 4) + 0] = key[(i * 4) + 0];
|
||||
round_key[(i * 4) + 1] = key[(i * 4) + 1];
|
||||
round_key[(i * 4) + 2] = key[(i * 4) + 2];
|
||||
round_key[(i * 4) + 3] = key[(i * 4) + 3];
|
||||
}
|
||||
|
||||
for (usz i = nk; i < COLNUM * (type.key.nr + 1); i++)
|
||||
{
|
||||
usz k = (i - 1) * 4;
|
||||
|
||||
char[4] tempa @noinit;
|
||||
|
||||
tempa[0] = round_key[k + 0];
|
||||
tempa[1] = round_key[k + 1];
|
||||
tempa[2] = round_key[k + 2];
|
||||
tempa[3] = round_key[k + 3];
|
||||
|
||||
if (i % nk == 0)
|
||||
{
|
||||
// rotword
|
||||
char tmp = tempa[0];
|
||||
tempa[0] = tempa[1];
|
||||
tempa[1] = tempa[2];
|
||||
tempa[2] = tempa[3];
|
||||
tempa[3] = tmp;
|
||||
|
||||
// subword
|
||||
tempa[0] = get_sbox_value(tempa[0]);
|
||||
tempa[1] = get_sbox_value(tempa[1]);
|
||||
tempa[2] = get_sbox_value(tempa[2]);
|
||||
tempa[3] = get_sbox_value(tempa[3]);
|
||||
|
||||
tempa[0] = tempa[0] ^ RCON[i / nk];
|
||||
}
|
||||
|
||||
if (type.key.key_size == 256)
|
||||
{
|
||||
if (i % nk == 4)
|
||||
{
|
||||
// subword
|
||||
tempa[0] = get_sbox_value(tempa[0]);
|
||||
tempa[1] = get_sbox_value(tempa[1]);
|
||||
tempa[2] = get_sbox_value(tempa[2]);
|
||||
tempa[3] = get_sbox_value(tempa[3]);
|
||||
}
|
||||
}
|
||||
usz j = i * 4;
|
||||
k = (i - nk) * 4;
|
||||
round_key[j + 0] = round_key[k + 0] ^ tempa[0];
|
||||
round_key[j + 1] = round_key[k + 1] ^ tempa[1];
|
||||
round_key[j + 2] = round_key[k + 2] ^ tempa[2];
|
||||
round_key[j + 3] = round_key[k + 3] ^ tempa[3];
|
||||
}
|
||||
}
|
||||
|
||||
87
lib/std/crypto/aes_128_192_256.c3
Normal file
87
lib/std/crypto/aes_128_192_256.c3
Normal file
@@ -0,0 +1,87 @@
|
||||
// Experimental implementation
|
||||
module std::crypto::aes128;
|
||||
import std::crypto::aes;
|
||||
|
||||
fn char[] encrypt(Allocator allocator, char[16]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
Aes aes @noinit;
|
||||
aes.init(AES128, key, iv, CTR);
|
||||
defer aes.destroy();
|
||||
return aes.encrypt(allocator, data);
|
||||
}
|
||||
|
||||
fn char[] tencrypt(char[16]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
return encrypt(tmem, key, iv, data);
|
||||
}
|
||||
|
||||
fn char[] decrypt(Allocator allocator, char[16]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
Aes aes @noinit;
|
||||
aes.init(AES128, key, iv, CTR);
|
||||
defer aes.destroy();
|
||||
return aes.decrypt(allocator, data);
|
||||
}
|
||||
|
||||
fn char[] tdecrypt(char[16]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
return decrypt(tmem, key, iv, data);
|
||||
}
|
||||
|
||||
module std::crypto::aes192;
|
||||
import std::crypto::aes;
|
||||
|
||||
fn char[] encrypt(Allocator allocator, char[24]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
Aes aes @noinit;
|
||||
aes.init(AES192, key, iv, CTR);
|
||||
defer aes.destroy();
|
||||
return aes.encrypt(allocator, data);
|
||||
}
|
||||
|
||||
fn char[] tencrypt(char[24]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
return encrypt(tmem, key, iv, data);
|
||||
}
|
||||
|
||||
fn char[] decrypt(Allocator allocator, char[24]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
Aes aes @noinit;
|
||||
aes.init(AES192, key, iv, CTR);
|
||||
defer aes.destroy();
|
||||
return aes.decrypt(allocator, data);
|
||||
}
|
||||
|
||||
fn char[] tdecrypt(char[24]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
return decrypt(tmem, key, iv, data);
|
||||
}
|
||||
|
||||
module std::crypto::aes256;
|
||||
import std::crypto::aes;
|
||||
|
||||
fn char[] encrypt(Allocator allocator, char[32]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
Aes aes @noinit;
|
||||
aes.init(AES256, key, iv, CTR);
|
||||
defer aes.destroy();
|
||||
return aes.encrypt(allocator, data);
|
||||
}
|
||||
|
||||
fn char[] tencrypt(char[32]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
return encrypt(tmem, key, iv, data);
|
||||
}
|
||||
|
||||
fn char[] decrypt(Allocator allocator, char[32]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
Aes aes @noinit;
|
||||
aes.init(AES256, key, iv, CTR);
|
||||
defer aes.destroy();
|
||||
return aes.decrypt(allocator, data);
|
||||
}
|
||||
|
||||
fn char[] tdecrypt(char[32]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
return decrypt(tmem, key, iv, data);
|
||||
}
|
||||
@@ -9,4 +9,3 @@ fn bool safe_compare(void* data1, void* data2, usz len)
|
||||
}
|
||||
return match == 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -164,6 +164,7 @@
|
||||
- Added array `@reduce`, `@filter`, `@any`, `@all`, `@sum`, `@product`, and `@indices_of` macros.
|
||||
- `String.bformat` has reduced overhead.
|
||||
- Supplemental `roundeven` has a normal implementation.
|
||||
- Added Advanced Encryption Standard (AES) algorithm (ECB, CTR, CBC modes) in `std::crypto::aes`.
|
||||
|
||||
## 0.7.4 Change list
|
||||
|
||||
|
||||
@@ -802,7 +802,8 @@ typedef struct
|
||||
{
|
||||
Expr* expr;
|
||||
UnaryOp operator : 8;
|
||||
bool no_wrap : 1;
|
||||
bool no_wrap;
|
||||
bool no_read;
|
||||
} ExprUnary;
|
||||
|
||||
|
||||
|
||||
@@ -2357,6 +2357,7 @@ static inline bool sema_call_check_contract_param_match(SemaContext *context, De
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (expr->expr_kind != EXPR_IDENTIFIER) return true;
|
||||
Decl *ident = expr->ident_expr;
|
||||
if (ident->decl_kind != DECL_VAR) return true;
|
||||
@@ -3927,6 +3928,9 @@ static inline bool sema_expr_analyse_subscript_lvalue(SemaContext *context, Expr
|
||||
case EXPR_CT_IDENT:
|
||||
if (!sema_analyse_expr_lvalue(context, subscripted, NULL)) return false;
|
||||
break;
|
||||
case EXPR_UNARY:
|
||||
subscripted->unary_expr.no_read = true;
|
||||
goto DEFAULT;
|
||||
case EXPR_SUBSCRIPT:
|
||||
{
|
||||
Expr *inner = expr_copy(subscripted);
|
||||
@@ -3938,9 +3942,11 @@ static inline bool sema_expr_analyse_subscript_lvalue(SemaContext *context, Expr
|
||||
subscripted->expr_kind = EXPR_UNARY;
|
||||
subscripted->unary_expr.operator = UNARYOP_DEREF;
|
||||
subscripted->unary_expr.expr = inner;
|
||||
subscripted->unary_expr.no_read = true;
|
||||
FALLTHROUGH;
|
||||
}
|
||||
default:
|
||||
DEFAULT:
|
||||
if (!sema_analyse_expr(context, subscripted)) return false;
|
||||
break;
|
||||
}
|
||||
@@ -11657,6 +11663,7 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr, bool mutat
|
||||
break;
|
||||
case EXPR_SUBSCRIPT:
|
||||
case EXPR_SLICE:
|
||||
/*
|
||||
{
|
||||
Expr *inner = exprptr(expr->expr_kind == EXPR_SUBSCRIPT ? expr->subscript_expr.expr : expr->slice_expr.expr);
|
||||
if (inner->expr_kind != EXPR_IDENTIFIER) break;
|
||||
@@ -11665,7 +11672,8 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr, bool mutat
|
||||
if (!decl->var.out_param || decl->var.in_param) break;
|
||||
if (context->active_scope.flags & (SCOPE_ENSURE | SCOPE_ENSURE_MACRO)) break;
|
||||
RETURN_SEMA_ERROR(expr, "'out' parameters may not be read.");
|
||||
}
|
||||
}*/
|
||||
break;
|
||||
case EXPR_UNARY:
|
||||
{
|
||||
if (expr->unary_expr.operator != UNARYOP_DEREF) break;
|
||||
@@ -11673,6 +11681,7 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr, bool mutat
|
||||
if (inner->expr_kind != EXPR_IDENTIFIER) break;
|
||||
Decl *decl = inner->ident_expr;
|
||||
if (decl->decl_kind != DECL_VAR) break;
|
||||
if (expr->unary_expr.no_read) break;
|
||||
if (!decl->var.out_param || decl->var.in_param) break;
|
||||
if (context->active_scope.flags & (SCOPE_ENSURE | SCOPE_ENSURE_MACRO)) return true;
|
||||
RETURN_SEMA_ERROR(expr, "'out' parameters may not be read.");
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
|
||||
<*
|
||||
@param [out] z
|
||||
@param [out] out_data
|
||||
*>
|
||||
fn void tes2t(char* z, char[] out_data, char[] in_data) {
|
||||
z[0] = 2;
|
||||
z[0] += 1; // #error: 'out' parameters may not be read
|
||||
z[0] += 1; // error: 'out' parameters may not be read
|
||||
out_data[0] = 3;
|
||||
out_data[0] *= 1; // #error: 'out' parameters may not be read
|
||||
out_data[0..3]; // #error: 'out' parameters may not be read
|
||||
out_data[0] *= 1; // error: 'out' parameters may not be read
|
||||
out_data[0..3]; // error: 'out' parameters may not be read
|
||||
out_data[0..3] = 23;
|
||||
}
|
||||
326
test/unit/stdlib/crypto/aes_test.c3
Normal file
326
test/unit/stdlib/crypto/aes_test.c3
Normal file
@@ -0,0 +1,326 @@
|
||||
module std::crypto::aes_test;
|
||||
|
||||
import std::crypto, std::io;
|
||||
|
||||
struct TestCase
|
||||
{
|
||||
AesType aes;
|
||||
char[] key;
|
||||
char[] plaintext;
|
||||
char[] cipher;
|
||||
}
|
||||
|
||||
fn void test_ecb_encrypt() @test
|
||||
{
|
||||
char[16] out;
|
||||
TestCase[] tests = {
|
||||
{
|
||||
.aes = AES128,
|
||||
.key = x"2b7e151628aed2a6abf7158809cf4f3c",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172a",
|
||||
.cipher = x"3ad77bb40d7a3660a89ecaf32466ef97",
|
||||
},
|
||||
{
|
||||
.aes = AES128,
|
||||
.key = x"2b7e151628aed2a6abf7158809cf4f3c",
|
||||
.plaintext = x"ae2d8a571e03ac9c9eb76fac45af8e51",
|
||||
.cipher = x"f5d3d58503b9699de785895a96fdbaaf",
|
||||
},
|
||||
{
|
||||
.aes = AES128,
|
||||
.key = x"2b7e151628aed2a6abf7158809cf4f3c",
|
||||
.plaintext = x"30c81c46a35ce411e5fbc1191a0a52ef",
|
||||
.cipher = x"43b1cd7f598ece23881b00e3ed030688",
|
||||
},
|
||||
{
|
||||
.aes = AES128,
|
||||
.key = x"2b7e151628aed2a6abf7158809cf4f3c",
|
||||
.plaintext = x"f69f2445df4f9b17ad2b417be66c3710",
|
||||
.cipher = x"7b0c785e27e8ad3f8223207104725dd4",
|
||||
},
|
||||
{
|
||||
.aes = AES192,
|
||||
.key = x"8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172a",
|
||||
.cipher = x"bd334f1d6e45f25ff712a214571fa5cc",
|
||||
},
|
||||
{
|
||||
.aes = AES256,
|
||||
.key = x"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172a",
|
||||
.cipher = x"f3eed1bdb5d2a03c064b5a7e3db181f8",
|
||||
},
|
||||
};
|
||||
|
||||
Aes ctx;
|
||||
foreach (i, t : tests)
|
||||
{
|
||||
ctx.init(t.aes, t.key, {}, ECB);
|
||||
ctx.encrypt_buffer(t.plaintext, &out);
|
||||
assert(out[:16] == t.cipher[:16],
|
||||
"Test %d failed; invalid cipher; got: %s, want: %s", i+1, out, t.cipher);
|
||||
|
||||
}
|
||||
|
||||
foreach (i, t : tests)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
ctx.init(t.aes, t.key, {}, ECB);
|
||||
char[] tmp_out = ctx.tencrypt(t.plaintext);
|
||||
assert(tmp_out[:16] == t.cipher[:16],
|
||||
"Test %d failed; invalid cipher; got: %s, want: %s", i+1, tmp_out, t.cipher);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn void test_ecb_decrypt() @test
|
||||
{
|
||||
char[16] out;
|
||||
TestCase[] tests = {
|
||||
{
|
||||
.aes = AES128,
|
||||
.key = x"2b7e151628aed2a6abf7158809cf4f3c",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172a",
|
||||
.cipher = x"3ad77bb40d7a3660a89ecaf32466ef97",
|
||||
},
|
||||
{
|
||||
.aes = AES192,
|
||||
.key = x"8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172a",
|
||||
.cipher = x"bd334f1d6e45f25ff712a214571fa5cc",
|
||||
},
|
||||
{
|
||||
.aes = AES256,
|
||||
.key = x"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172a",
|
||||
.cipher = x"f3eed1bdb5d2a03c064b5a7e3db181f8",
|
||||
},
|
||||
};
|
||||
|
||||
Aes ctx;
|
||||
foreach (i, t : tests)
|
||||
{
|
||||
ctx.init(t.aes, t.key, {}, ECB);
|
||||
ctx.decrypt_buffer(t.cipher, &out);
|
||||
assert(out[:16] == t.plaintext[:16],
|
||||
"Test %d failed; invalid plaintext; got: %s, want: %s", i+1, out, t.plaintext);
|
||||
}
|
||||
|
||||
foreach (i, t : tests)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
ctx.init(t.aes, t.key, {}, ECB);
|
||||
char[] tmp_out = ctx.tdecrypt(t.cipher);
|
||||
assert(tmp_out[:16] == t.plaintext[:16],
|
||||
"Test %d failed; invalid plaintext; got: %s, want: %s", i+1, tmp_out, t.plaintext);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn void test_cbc_encrypt() @test
|
||||
{
|
||||
char[64] out;
|
||||
TestCase[] tests = {
|
||||
{
|
||||
.aes = AES128,
|
||||
.key = x"2b7e151628aed2a6abf7158809cf4f3c",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
||||
.cipher = x"7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b273bed6b8e3c1743b7116e69e222295163ff1caa1681fac09120eca307586e1a7",
|
||||
},
|
||||
{
|
||||
.aes = AES192,
|
||||
.key = x"8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
||||
.cipher = x"4f021db243bc633d7178183a9fa071e8b4d9ada9ad7dedf4e5e738763f69145a571b242012fb7ae07fa9baac3df102e008b0e27988598881d920a9e64f5615cd",
|
||||
},
|
||||
{
|
||||
.aes = AES256,
|
||||
.key = x"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
||||
.cipher = x"f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d39f23369a9d9bacfa530e26304231461b2eb05e2c39be9fcda6c19078c6a9d1b",
|
||||
},
|
||||
};
|
||||
char[16] iv = x"000102030405060708090a0b0c0d0e0f";
|
||||
|
||||
Aes ctx;
|
||||
foreach (i, t : tests)
|
||||
{
|
||||
ctx.init(t.aes, t.key, iv, CBC);
|
||||
ctx.encrypt_buffer(t.plaintext, &out);
|
||||
assert(out[:64] == t.cipher[:64],
|
||||
"Test %d failed; invalid cipher; got: %s, want: %s", i+1, out, t.cipher);
|
||||
}
|
||||
|
||||
foreach (i, t : tests)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
ctx.init(t.aes, t.key, iv, CBC);
|
||||
char[] tmp_out = ctx.tencrypt(t.plaintext);
|
||||
assert(tmp_out[:64] == t.cipher[:64],
|
||||
"Test %d failed; invalid cipher; got: %s, want: %s", i+1, tmp_out, t.cipher);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn void test_cbc_decrypt() @test
|
||||
{
|
||||
char[64] out;
|
||||
TestCase[] tests = {
|
||||
{
|
||||
.aes = AES128,
|
||||
.key = x"2b7e151628aed2a6abf7158809cf4f3c",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
||||
.cipher = x"7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b273bed6b8e3c1743b7116e69e222295163ff1caa1681fac09120eca307586e1a7",
|
||||
},
|
||||
{
|
||||
.aes = AES192,
|
||||
.key = x"8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
||||
.cipher = x"4f021db243bc633d7178183a9fa071e8b4d9ada9ad7dedf4e5e738763f69145a571b242012fb7ae07fa9baac3df102e008b0e27988598881d920a9e64f5615cd",
|
||||
},
|
||||
{
|
||||
.aes = AES256,
|
||||
.key = x"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
||||
.cipher = x"f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d39f23369a9d9bacfa530e26304231461b2eb05e2c39be9fcda6c19078c6a9d1b",
|
||||
},
|
||||
};
|
||||
char[16] iv = x"000102030405060708090a0b0c0d0e0f";
|
||||
|
||||
Aes ctx;
|
||||
foreach (i, t : tests)
|
||||
{
|
||||
ctx.init(t.aes, t.key, iv, CBC);
|
||||
ctx.decrypt_buffer(t.cipher, &out);
|
||||
assert(out[:64] == t.plaintext[:64],
|
||||
"Test %d failed; invalid plaintext; got: %s, want: %s", i+1, out, t.plaintext);
|
||||
}
|
||||
|
||||
foreach (i, t : tests)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
ctx.init(t.aes, t.key, iv, CBC);
|
||||
char[] tmp_out = ctx.tdecrypt(t.cipher);
|
||||
assert(tmp_out[:64] == t.plaintext[:64],
|
||||
"Test %d failed; invalid plaintext; got: %s, want: %s", i+1, tmp_out, t.plaintext);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn void test_ctr_xcrypt() @test
|
||||
{
|
||||
char[64] out;
|
||||
TestCase[] tests = {
|
||||
{
|
||||
.aes = AES128,
|
||||
.key = x"2b7e151628aed2a6abf7158809cf4f3c",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
||||
.cipher = x"874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee",
|
||||
},
|
||||
{
|
||||
.aes = AES192,
|
||||
.key = x"8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
||||
.cipher = x"1abc932417521ca24f2b0459fe7e6e0b090339ec0aa6faefd5ccc2c6f4ce8e941e36b26bd1ebc670d1bd1d665620abf74f78a7f6d29809585a97daec58c6b050",
|
||||
},
|
||||
{
|
||||
.aes = AES256,
|
||||
.key = x"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
||||
.cipher = x"601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c52b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6",
|
||||
},
|
||||
};
|
||||
char[16] iv = x"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
|
||||
|
||||
Aes ctx;
|
||||
foreach (i, t : tests)
|
||||
{
|
||||
// encrypt
|
||||
ctx.init(t.aes, t.key, iv, CTR);
|
||||
ctx.encrypt_buffer(t.plaintext, &out);
|
||||
assert(out[:64] == t.cipher[:64],
|
||||
"Test %d failed; invalid cipher; got: %s, want: %s", i+1, out, t.cipher);
|
||||
|
||||
// decrypt
|
||||
ctx.init(t.aes, t.key, iv, CTR);
|
||||
ctx.decrypt_buffer(t.cipher, &out);
|
||||
assert(out[:64] == t.plaintext[:64],
|
||||
"Test %d failed; invalid plaintext; got: %s, want: %s", i+1, out, t.plaintext);
|
||||
}
|
||||
foreach (i, t : tests)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
// encrypt
|
||||
ctx.init(t.aes, t.key, iv, CTR);
|
||||
char[] cipher = ctx.tencrypt(t.plaintext);
|
||||
assert(cipher[:64] == t.cipher[:64],
|
||||
"Test %d failed; invalid cipher; got: %s, want: %s", i+1, cipher, t.cipher);
|
||||
|
||||
// decrypt
|
||||
ctx.init(t.aes, t.key, iv, CTR);
|
||||
char[] text = ctx.tdecrypt(t.cipher);
|
||||
assert(text[:64] == t.plaintext[:64],
|
||||
"Test %d failed; invalid plaintext; got: %s, want: %s", i+1, text, t.plaintext);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn void test_aes_xcrypt() @test
|
||||
{
|
||||
char[64] out;
|
||||
TestCase[] tests = {
|
||||
{
|
||||
.aes = AES128,
|
||||
.key = x"2b7e151628aed2a6abf7158809cf4f3c",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
||||
.cipher = x"874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee",
|
||||
},
|
||||
{
|
||||
.aes = AES192,
|
||||
.key = x"8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
||||
.cipher = x"1abc932417521ca24f2b0459fe7e6e0b090339ec0aa6faefd5ccc2c6f4ce8e941e36b26bd1ebc670d1bd1d665620abf74f78a7f6d29809585a97daec58c6b050",
|
||||
},
|
||||
{
|
||||
.aes = AES256,
|
||||
.key = x"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
|
||||
.plaintext = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
||||
.cipher = x"601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c52b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6",
|
||||
},
|
||||
};
|
||||
char[16] iv = x"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
|
||||
|
||||
foreach (i, t : tests)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
// encrypt
|
||||
char[] cipher;
|
||||
switch (t.aes)
|
||||
{
|
||||
case AES128: cipher = aes128::tencrypt(t.key, iv, t.plaintext);
|
||||
case AES192: cipher = aes192::tencrypt(t.key, iv, t.plaintext);
|
||||
case AES256: cipher = aes256::tencrypt(t.key, iv, t.plaintext);
|
||||
}
|
||||
assert(cipher == t.cipher[:64],
|
||||
"Test %d failed; invalid cipher; got: %s, want: %s", i + 1, cipher, t.cipher);
|
||||
|
||||
// decrypt
|
||||
char[] text;
|
||||
switch (t.aes)
|
||||
{
|
||||
case AES128: text = aes128::tdecrypt(t.key, iv, t.cipher);
|
||||
case AES192: text = aes192::tdecrypt(t.key, iv, t.cipher);
|
||||
case AES256: text = aes256::tdecrypt(t.key, iv, t.cipher);
|
||||
}
|
||||
assert(text[:64] == t.plaintext[:64],
|
||||
"Test %d failed; invalid plaintext; got: %h, want: %h", i + 1, text, t.plaintext);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user