Skip to content

Instantly share code, notes, and snippets.

@Silur
Last active November 4, 2018 10:21
Show Gist options
  • Save Silur/40ecd1de7cc1a7f1d2596cf9af6d3d9a to your computer and use it in GitHub Desktop.
Save Silur/40ecd1de7cc1a7f1d2596cf9af6d3d9a to your computer and use it in GitHub Desktop.
Even-Mansour block cipher using GIMLI in CTR mode
#include <stdint.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#define MIN(x,y) y ^ ((x ^ y) & -(x < y))
#define rateInBytes 16
uint32_t
rotate(uint32_t x, int bits)
{
if (bits == 0) return x;
return (x << bits) | (x >> (32 - bits));
}
void
gimli(uint32_t *state)
{
int round;
int column;
uint32_t x;
uint32_t y;
uint32_t z;
for (round = 24; round > 0; --round)
{
for (column = 0; column < 4; ++column)
{
x = rotate(state[ column], 24);
y = rotate(state[4 + column], 9);
z = state[8 + column];
state[8 + column] = x ^ (z << 1) ^ ((y&z) << 2);
state[4 + column] = y ^ x ^ ((x|z) << 1);
state[column] = z ^ y ^ ((x&y) << 3);
}
if ((round & 3) == 0) {
x = state[0];
state[0] = state[1];
state[1] = x;
x = state[2];
state[2] = state[3];
state[3] = x;
}
if ((round & 3) == 2) {
x = state[0];
state[0] = state[2];
state[2] = x;
x = state[1];
state[1] = state[3];
state[3] = x;
}
if ((round & 3) == 0) {
state[0] ^= (0x9e377900 | round);
}
}
}
void
gimli_hash(const uint8_t *input,
uint64_t inputByteLen,
uint8_t *output,
uint64_t outputByteLen)
{
uint32_t state[12];
uint8_t* state_8 = (uint8_t*)state;
uint64_t blockSize = 0;
uint64_t i;
memset(state, 0, sizeof(state));
while(inputByteLen > 0) {
blockSize = MIN(inputByteLen, rateInBytes);
for(i=0; i<blockSize; i++)
state_8[i] ^= input[i];
input += blockSize;
inputByteLen -= blockSize;
if (blockSize == rateInBytes) {
gimli(state);
blockSize = 0;
}
}
state_8[blockSize] ^= 0x1F;
state_8[rateInBytes-1] ^= 0x80;
gimli(state);
while(outputByteLen > 0) {
blockSize = MIN(outputByteLen, rateInBytes);
memcpy(output, state, blockSize);
output += blockSize;
outputByteLen -= blockSize;
if (outputByteLen > 0)
gimli(state);
}
}
size_t
gimli_parse(
const unsigned char *input,
uint64_t inlen,
const unsigned char *key,
const unsigned char *iv,
unsigned char *output)
{
unsigned char nonce[48];
uint64_t ctr = 0;
unsigned char ctr_bytes[8] = {0};
int i;
unsigned char block[48] = {0};
memcpy(nonce, iv, 40);
while((ctr+1)*48 < inlen)
{
memcpy(nonce+40, ctr_bytes, 8);
for(i=0; i<48; i++)
block[i] = nonce[i] ^ key[i];
gimli((uint32_t*)block);
for(i=0; i<48; i++)
output[(ctr*48)+i] = input[(ctr*48)+i] ^ (block[i] ^ key[i]);
ctr++;
memcpy(&ctr_bytes, &ctr, 8);
}
unsigned char padval = (ctr+1)*48 - inlen;
assert(padval < 48);
unsigned char padded[48] = {0};
for(i=48-padval; i<padval; i++)
padded[i] = padval;
memcpy(padded, input+(ctr*48), 48-padval);
for(i=0; i<48; i++)
block[i] = nonce[i] ^ key[i];
gimli((uint32_t*)block);
for(i=0; i<48; i++)
output[ctr*48+i] = padded[i] ^ (block[i] ^ key[i]);
return ctr*48+i;
}
int
main(int argc, char **argv)
{
if(argc<3) return 1;
int plain_len = atoi(argv[2]);
unsigned char derived_key[48];
gimli_hash((unsigned char*)argv[1], strlen(argv[1])+1, derived_key, 48);
size_t blen = (size_t)ceil(plain_len/48.0)*48;
unsigned char *ciphertext = malloc(blen);
unsigned char iv[40];
FILE *fd = fopen("/dev/urandom", "rb");
fread(iv, 1, 40, fd);
unsigned char *plain = malloc(plain_len);
fread(plain, 1, plain_len, fd);
fclose(fd);
gimli_parse(plain, plain_len, derived_key, iv, ciphertext);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment