Last active
February 21, 2020 22:18
-
-
Save spllr/44d6b71901c1129422e0258b8d8c4af5 to your computer and use it in GitHub Desktop.
Feistel Cipher test implementation inspired by the "Feistel Cipher - Computerphile" Youtube video
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* build: | |
* gcc feistel_cipher_test.c -o feistel_cipher_test | |
* | |
* run: | |
* ./feistel_cipher_test | |
* | |
* Feistel Cipher test implementation inspired by the "Feistel Cipher - Computerphile" | |
* Youtube video: https://youtu.be/FGhj3CGxl8I | |
* | |
* This code was a created after watching Dr Mike Pound explain the principles | |
* underlying the Feistel Cipher. It follows the steps as demostrated in the | |
* video for the fun and education. | |
* | |
* The function used is a the HMAC-sha256 stream cypher which is explained by | |
* Dr Mike Pound here: https://youtu.be/wlSG3pEiQdc | |
* | |
* In this example we will use a fixed size for all buffers and keys so we can | |
* stay close the examples given in the video. | |
* | |
* None of this code is meant for real-world encryption or production and is | |
* only created to better understand the 'trick' Dr Mike demonstrates. | |
* | |
* - Using the sha256 implementation by Ilya O. Levin, see license below. | |
* - Using the hmac-sha256 implementation by Adrian Perez, see license below. | |
* | |
* Copyright (c) 2020, Klaas Speller, http://github.com/spllr | |
* | |
* Permission to use, copy, modify, and distribute this software for any | |
* purpose with or without fee is hereby granted, provided that the above | |
* copyright notice and this permission notice appear in all copies. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
* | |
*/ | |
#include <limits.h> | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <time.h> | |
#define CHUNK_SIZE 32 | |
typedef uint8_t key_t[CHUNK_SIZE]; | |
typedef uint8_t buf_t[CHUNK_SIZE + 1]; | |
// MARK: - The Feistel Cipher Stuff | |
void F(buf_t dest, buf_t value, key_t key); | |
void xor(buf_t dest, buf_t lh, buf_t rh); | |
void feistel_cipher_apply(buf_t left_out, buf_t right_out, buf_t left_in, buf_t right_in, key_t keys[], size_t num_keys); | |
// MARK: - Utils | |
void key_generator_init(); | |
void key_generate(uint8_t dest[]); | |
void print_hex(uint8_t buf[], size_t buf_size); | |
void print_pass_info(size_t pass_index, key_t key, buf_t left, buf_t right); | |
void print_begin_state(buf_t left, buf_t right); | |
void print_end_state(buf_t left, buf_t right); | |
void strip_newline(buf_t buf); | |
void hmac_sha256 (uint8_t out[CHUNK_SIZE], | |
const uint8_t *data, size_t data_len, | |
const uint8_t *key, size_t key_len); | |
// MARK: - Main | |
int main (int argc, char const *argv[]) | |
{ | |
key_generator_init(); | |
buf_t left_in = { 0 }, right_in = { 0 }, left_out, right_out; | |
key_t keys[2]; | |
// Generate the keys | |
key_generate(keys[0]); | |
key_generate(keys[1]); | |
// Get the left value | |
printf("left value (max 31 chars): "); | |
fgets((char *)left_in, CHUNK_SIZE, stdin); | |
strip_newline(left_in); | |
// Get the right value | |
printf("right value (max 31 chars): "); | |
fgets((char *)right_in, CHUNK_SIZE, stdin); | |
strip_newline(right_in); | |
// Encrypt | |
printf("\n- encrypt"); | |
feistel_cipher_apply(left_out, right_out, left_in, right_in, keys, 2); | |
// Decrypt | |
printf("\n\n- decrypt"); | |
key_t reversed_keys[2]; | |
memcpy(reversed_keys[0], keys[1], CHUNK_SIZE); | |
memcpy(reversed_keys[1], keys[0], CHUNK_SIZE); | |
feistel_cipher_apply(left_in, right_in, left_out, right_out, reversed_keys, 2); | |
return 0; | |
} | |
// MARK: - The Feistel Cipher Stuff Implementation | |
void feistel_cipher_apply(buf_t left_out, buf_t right_out, buf_t left_in, buf_t right_in, key_t keys[], size_t num_keys) | |
{ | |
buf_t out; | |
buf_t left; | |
buf_t right; | |
buf_t next_right; | |
memcpy(left, left_in, CHUNK_SIZE); | |
memcpy(right, right_in, CHUNK_SIZE); | |
print_begin_state(left, right); | |
for (size_t i = 0; i < num_keys; i++) | |
{ | |
F(out, right, keys[i]); | |
xor(next_right, left, out); | |
memcpy(left, right, CHUNK_SIZE); | |
memcpy(right, next_right, CHUNK_SIZE); | |
print_pass_info(i, keys[i], left, right); | |
} | |
memcpy(left_out, right, CHUNK_SIZE); | |
memcpy(right_out, left, CHUNK_SIZE); | |
print_end_state(left_out, right_out); | |
} | |
void F(buf_t dest, buf_t value, key_t key) | |
{ | |
hmac_sha256(dest, value, CHUNK_SIZE, key, CHUNK_SIZE); | |
} | |
void xor(buf_t dest, buf_t lh, buf_t rh) | |
{ | |
for (size_t i = 0; i < CHUNK_SIZE; i++) | |
{ | |
dest[i] = lh[i] ^ rh[i]; | |
} | |
} | |
// MARK: - Util Implementation | |
void key_generator_init() | |
{ | |
srand(time(NULL)); | |
} | |
void key_generate(uint8_t dest[]) | |
{ | |
for (size_t i = 0; i < CHUNK_SIZE; i++) | |
{ | |
dest[i] = rand() % UINT8_MAX; | |
} | |
} | |
void print_pass_info(size_t pass_index, key_t key, buf_t left, buf_t right) | |
{ | |
printf("\n\npass %zu: \n key=", pass_index + 1); | |
print_hex(key, CHUNK_SIZE); | |
printf("\n left="); | |
print_hex(left, CHUNK_SIZE); | |
printf("\n right="); | |
print_hex(right, CHUNK_SIZE); | |
} | |
void print_begin_state(buf_t left, buf_t right) | |
{ | |
printf("\n left="); | |
print_hex(left, CHUNK_SIZE); | |
printf("\n right="); | |
print_hex(right, CHUNK_SIZE); | |
} | |
void print_end_state(buf_t left, buf_t right) | |
{ | |
printf("\n\nresult:"); | |
printf("\n left="); | |
print_hex(left, CHUNK_SIZE); | |
printf("\n right="); | |
print_hex(right, CHUNK_SIZE); | |
printf("\n output: %s %s\n", left, right); | |
} | |
void print_hex(uint8_t buffer[], size_t buf_size) | |
{ | |
size_t str_size = buf_size * 2 + 1; | |
char hex_str[str_size]; | |
hex_str[str_size - 1] = '\0'; | |
for (size_t i = 0; i < buf_size; i++) | |
{ | |
sprintf((hex_str + i), "%X", buffer[i]); | |
} | |
printf("0x%s", hex_str); | |
} | |
void strip_newline(buf_t buf) | |
{ | |
buf[strnlen((char *)buf, CHUNK_SIZE) - 1] = '\0'; | |
} | |
/* | |
* SHA-256 implementation, Mark 2 | |
* | |
* Copyright (c) 2010,2014 Ilya O. Levin, http://www.literatecode.com | |
* | |
* Permission to use, copy, modify, and distribute this software for any | |
* purpose with or without fee is hereby granted, provided that the above | |
* copyright notice and this permission notice appear in all copies. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
* | |
*/ | |
// MARK:- HMAC and SHA256 interface | |
#define HMAC_SHA256_DIGEST_SIZE CHUNK_SIZE | |
#define SHA256_DIGEST_SIZE CHUNK_SIZE | |
typedef struct { | |
uint8_t buf[64]; | |
uint32_t hash[8]; | |
uint32_t bits[2]; | |
uint32_t len; | |
} sha256_context; | |
void sha256_init(sha256_context *ctx); | |
void sha256_hash(sha256_context *ctx, const void *data, size_t len); | |
void sha256_done(sha256_context *ctx, uint8_t *hash); | |
#define FN_ inline static | |
static const uint32_t K[64] = { | |
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, | |
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, | |
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, | |
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, | |
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, | |
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, | |
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, | |
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, | |
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, | |
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, | |
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, | |
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, | |
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, | |
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, | |
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, | |
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 | |
}; | |
#ifdef MINIMIZE_STACK_IMPACT | |
static uint32_t W[64]; | |
#endif | |
/* -------------------------------------------------------------------------- */ | |
FN_ uint8_t _shb(uint32_t x, uint32_t n) | |
{ | |
return ( (x >> (n & 31)) & 0xff ); | |
} /* _shb */ | |
/* -------------------------------------------------------------------------- */ | |
FN_ uint32_t _shw(uint32_t x, uint32_t n) | |
{ | |
return ( (x << (n & 31)) & 0xffffffff ); | |
} /* _shw */ | |
/* -------------------------------------------------------------------------- */ | |
FN_ uint32_t _r(uint32_t x, uint8_t n) | |
{ | |
return ( (x >> n) | _shw(x, 32 - n) ); | |
} /* _r */ | |
/* -------------------------------------------------------------------------- */ | |
FN_ uint32_t _Ch(uint32_t x, uint32_t y, uint32_t z) | |
{ | |
return ( (x & y) ^ ((~x) & z) ); | |
} /* _Ch */ | |
/* -------------------------------------------------------------------------- */ | |
FN_ uint32_t _Ma(uint32_t x, uint32_t y, uint32_t z) | |
{ | |
return ( (x & y) ^ (x & z) ^ (y & z) ); | |
} /* _Ma */ | |
/* -------------------------------------------------------------------------- */ | |
FN_ uint32_t _S0(uint32_t x) | |
{ | |
return ( _r(x, 2) ^ _r(x, 13) ^ _r(x, 22) ); | |
} /* _S0 */ | |
/* -------------------------------------------------------------------------- */ | |
FN_ uint32_t _S1(uint32_t x) | |
{ | |
return ( _r(x, 6) ^ _r(x, 11) ^ _r(x, 25) ); | |
} /* _S1 */ | |
/* -------------------------------------------------------------------------- */ | |
FN_ uint32_t _G0(uint32_t x) | |
{ | |
return ( _r(x, 7) ^ _r(x, 18) ^ (x >> 3) ); | |
} /* _G0 */ | |
/* -------------------------------------------------------------------------- */ | |
FN_ uint32_t _G1(uint32_t x) | |
{ | |
return ( _r(x, 17) ^ _r(x, 19) ^ (x >> 10) ); | |
} /* _G1 */ | |
/* -------------------------------------------------------------------------- */ | |
FN_ uint32_t _word(uint8_t *c) | |
{ | |
return ( _shw(c[0], 24) | _shw(c[1], 16) | _shw(c[2], 8) | (c[3]) ); | |
} /* _word */ | |
/* -------------------------------------------------------------------------- */ | |
FN_ void _addbits(sha256_context *ctx, uint32_t n) | |
{ | |
if ( ctx->bits[0] > (0xffffffff - n) ) | |
ctx->bits[1] = (ctx->bits[1] + 1) & 0xFFFFFFFF; | |
ctx->bits[0] = (ctx->bits[0] + n) & 0xFFFFFFFF; | |
} /* _addbits */ | |
/* -------------------------------------------------------------------------- */ | |
static void _hash(sha256_context *ctx) | |
{ | |
register uint32_t a, b, c, d, e, f, g, h, i; | |
uint32_t t[2]; | |
#ifndef MINIMIZE_STACK_IMPACT | |
uint32_t W[64]; | |
#endif | |
a = ctx->hash[0]; | |
b = ctx->hash[1]; | |
c = ctx->hash[2]; | |
d = ctx->hash[3]; | |
e = ctx->hash[4]; | |
f = ctx->hash[5]; | |
g = ctx->hash[6]; | |
h = ctx->hash[7]; | |
for (i = 0; i < 64; i++) { | |
if ( i < 16 ) | |
W[i] = _word(&ctx->buf[_shw(i, 2)]); | |
else | |
W[i] = _G1(W[i - 2]) + W[i - 7] + _G0(W[i - 15]) + W[i - 16]; | |
t[0] = h + _S1(e) + _Ch(e, f, g) + K[i] + W[i]; | |
t[1] = _S0(a) + _Ma(a, b, c); | |
h = g; | |
g = f; | |
f = e; | |
e = d + t[0]; | |
d = c; | |
c = b; | |
b = a; | |
a = t[0] + t[1]; | |
} | |
ctx->hash[0] += a; | |
ctx->hash[1] += b; | |
ctx->hash[2] += c; | |
ctx->hash[3] += d; | |
ctx->hash[4] += e; | |
ctx->hash[5] += f; | |
ctx->hash[6] += g; | |
ctx->hash[7] += h; | |
} /* _hash */ | |
/* -------------------------------------------------------------------------- */ | |
void sha256_init(sha256_context *ctx) | |
{ | |
if ( ctx != NULL ) { | |
ctx->bits[0] = ctx->bits[1] = 0; | |
ctx->len = 0; | |
ctx->hash[0] = 0x6a09e667; | |
ctx->hash[1] = 0xbb67ae85; | |
ctx->hash[2] = 0x3c6ef372; | |
ctx->hash[3] = 0xa54ff53a; | |
ctx->hash[4] = 0x510e527f; | |
ctx->hash[5] = 0x9b05688c; | |
ctx->hash[6] = 0x1f83d9ab; | |
ctx->hash[7] = 0x5be0cd19; | |
} | |
} /* sha256_init */ | |
/* -------------------------------------------------------------------------- */ | |
void sha256_hash(sha256_context *ctx, const void *data, size_t len) | |
{ | |
register size_t i; | |
const uint8_t *bytes = (const uint8_t *)data; | |
if ( (ctx != NULL) && (bytes != NULL) ) | |
for (i = 0; i < len; i++) { | |
ctx->buf[ctx->len] = bytes[i]; | |
ctx->len++; | |
if (ctx->len == sizeof(ctx->buf) ) { | |
_hash(ctx); | |
_addbits(ctx, sizeof(ctx->buf) * 8); | |
ctx->len = 0; | |
} | |
} | |
} /* sha256_hash */ | |
/* -------------------------------------------------------------------------- */ | |
void sha256_done(sha256_context *ctx, uint8_t *hash) | |
{ | |
register uint32_t i, j; | |
if ( ctx != NULL ) { | |
j = ctx->len % sizeof(ctx->buf); | |
ctx->buf[j] = 0x80; | |
for (i = j + 1; i < sizeof(ctx->buf); i++) | |
ctx->buf[i] = 0x00; | |
if ( ctx->len > 55 ) { | |
_hash(ctx); | |
for (j = 0; j < sizeof(ctx->buf); j++) | |
ctx->buf[j] = 0x00; | |
} | |
_addbits(ctx, ctx->len * 8); | |
ctx->buf[63] = _shb(ctx->bits[0], 0); | |
ctx->buf[62] = _shb(ctx->bits[0], 8); | |
ctx->buf[61] = _shb(ctx->bits[0], 16); | |
ctx->buf[60] = _shb(ctx->bits[0], 24); | |
ctx->buf[59] = _shb(ctx->bits[1], 0); | |
ctx->buf[58] = _shb(ctx->bits[1], 8); | |
ctx->buf[57] = _shb(ctx->bits[1], 16); | |
ctx->buf[56] = _shb(ctx->bits[1], 24); | |
_hash(ctx); | |
if ( hash != NULL ) | |
for (i = 0, j = 24; i < 4; i++, j -= 8) { | |
hash[i ] = _shb(ctx->hash[0], j); | |
hash[i + 4] = _shb(ctx->hash[1], j); | |
hash[i + 8] = _shb(ctx->hash[2], j); | |
hash[i + 12] = _shb(ctx->hash[3], j); | |
hash[i + 16] = _shb(ctx->hash[4], j); | |
hash[i + 20] = _shb(ctx->hash[5], j); | |
hash[i + 24] = _shb(ctx->hash[6], j); | |
hash[i + 28] = _shb(ctx->hash[7], j); | |
} | |
} | |
} /* sha256_done */ | |
/* -------------------------------------------------------------------------- */ | |
void sha256(const void *data, size_t len, uint8_t *hash) | |
{ | |
sha256_context ctx; | |
sha256_init(&ctx); | |
sha256_hash(&ctx, data, len); | |
sha256_done(&ctx, hash); | |
} /* sha256 */ | |
/* ========================================================================== */ | |
/* | |
* hmac-sha256.c | |
* Copyright (C) 2017 Adrian Perez <[email protected]> | |
* | |
* Distributed under terms of the MIT license. | |
*/ | |
typedef sha256_context sha256_t; | |
#define sha256_final(ctx, hash) sha256_done(ctx, hash) | |
#define sha256_update(ctx, data, len) sha256_hash(ctx, data, len) | |
#define B 64 | |
#define L (SHA256_DIGEST_SIZE) | |
#define K (SHA256_DIGEST_SIZE * 2) | |
#define I_PAD 0x36 | |
#define O_PAD 0x5C | |
void hmac_sha256 (uint8_t out[HMAC_SHA256_DIGEST_SIZE], | |
const uint8_t *data, size_t data_len, | |
const uint8_t *key, size_t key_len) | |
{ | |
// api_check_return (out); | |
// api_check_return (data); | |
// api_check_return (key); | |
// api_check_return (key_len <= B); | |
sha256_t ss; | |
uint8_t kh[SHA256_DIGEST_SIZE]; | |
/* | |
* If the key length is bigger than the buffer size B, apply the hash | |
* function to it first and use the result instead. | |
*/ | |
if (key_len > B) { | |
sha256_init (&ss); | |
sha256_update (&ss, key, key_len); | |
sha256_final (&ss, kh); | |
key_len = SHA256_DIGEST_SIZE; | |
key = kh; | |
} | |
/* | |
* (1) append zeros to the end of K to create a B byte string | |
* (e.g., if K is of length 20 bytes and B=64, then K will be | |
* appended with 44 zero bytes 0x00) | |
* (2) XOR (bitwise exclusive-OR) the B byte string computed in step | |
* (1) with ipad | |
*/ | |
uint8_t kx[B]; | |
for (size_t i = 0; i < key_len; i++) kx[i] = I_PAD ^ key[i]; | |
for (size_t i = key_len; i < B; i++) kx[i] = I_PAD ^ 0; | |
/* | |
* (3) append the stream of data 'text' to the B byte string resulting | |
* from step (2) | |
* (4) apply H to the stream generated in step (3) | |
*/ | |
sha256_init (&ss); | |
sha256_update (&ss, kx, B); | |
sha256_update (&ss, data, data_len); | |
sha256_final (&ss, out); | |
/* | |
* (5) XOR (bitwise exclusive-OR) the B byte string computed in | |
* step (1) with opad | |
* | |
* NOTE: The "kx" variable is reused. | |
*/ | |
for (size_t i = 0; i < key_len; i++) kx[i] = O_PAD ^ key[i]; | |
for (size_t i = key_len; i < B; i++) kx[i] = O_PAD ^ 0; | |
/* | |
* (6) append the H result from step (4) to the B byte string | |
* resulting from step (5) | |
* (7) apply H to the stream generated in step (6) and output | |
* the result | |
*/ | |
sha256_init (&ss); | |
sha256_update (&ss, kx, B); | |
sha256_update (&ss, out, SHA256_DIGEST_SIZE); | |
sha256_final (&ss, out); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment