Compare commits

...

3 Commits

Author SHA1 Message Date
Juhani Krekelä 900c7b4c93 Implement the KDF 2021-04-08 22:45:08 +03:00
Juhani Krekelä c9defbaafe Fix the MDC-2 implementation, again 2021-04-08 21:41:35 +03:00
Juhani Krekelä 9a0b35609b Implement (modified) HMAC 2021-04-08 21:30:09 +03:00
2 changed files with 100 additions and 5 deletions

View File

@ -8,7 +8,7 @@ CFLAGS ?= -std=gnu11 -Os -g -Wall -Wextra -Werror -pedantic
.SUFFIXES:
.SUFFIXES: .c .o
all: puer.o
all: puer
puer: puer.o
$(CC) $(LDFLAGS) -o $@ $<

103
puer.c
View File

@ -1,5 +1,6 @@
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
void xxtea128(uint32_t const key[4], uint32_t block[4]) {
@ -138,10 +139,10 @@ void compress_hash(struct hashstate *state) {
state->a[3] = w[3];
// B_{i+1} = W_i^L || V_i^R
state->b[0] = v[0];
state->b[1] = v[1];
state->b[2] = w[2];
state->b[3] = w[3];
state->b[0] = w[0];
state->b[1] = w[1];
state->b[2] = v[2];
state->b[3] = v[3];
// Mark that we have consumed the buffer
state->length = 0;
@ -205,3 +206,97 @@ void finalize_hash(struct hashstate *state, unsigned char hash[32]) {
// there
explicit_bzero(state, sizeof(struct hashstate));
}
void hmac(unsigned char output[32], unsigned char key[], size_t keylen, unsigned char message[], size_t messagelen) {
// The blocksize of the underlying has function is 128 bits (16B)
// but HMAC is specified assuming that the hash function output (in
// our case 256 bits or 32B) fits in one block. As far as I can
// tell extending the key to be two blocks long is not a problem.
unsigned char padded_key[32];
if (keylen > 16) {
// We hash it even if it is shorter than our extended key
// length to avoid giving attacker any funny surfaces to
// play with at the interface of two blocks
struct hashstate state;
initialize_hash(&state);
feed_hash(&state, key, keylen);
finalize_hash(&state, padded_key);
} else {
// Copy the key and zero-pad if necessary
memset(padded_key, 0, 32);
memcpy(padded_key, key, keylen);
}
// Outer and inner key derivation
unsigned char outer_key[32], inner_key[32];
for (size_t i = 0; i < 32; i++) {
outer_key[i] = padded_key[i] ^ 0x5c;
inner_key[i] = padded_key[i] ^ 0x36;
}
// Inner hash
unsigned char inner_hash[32];
struct hashstate state;
initialize_hash(&state);
feed_hash(&state, inner_key, 32);
feed_hash(&state, message, messagelen);
finalize_hash(&state, inner_hash);
// Outer hash
initialize_hash(&state);
feed_hash(&state, outer_key, 32);
feed_hash(&state, inner_hash, 32);
finalize_hash(&state, output);
}
// KDF_ROUNDS must be at least 2
#define KDF_ROUNDS 100000
unsigned char kdf_buf[KDF_ROUNDS * 32];
void kdf(unsigned char key[16], unsigned char salt[32], unsigned char passphrase[], size_t passphraselen) {
// This is based on the design of PBKDF2 but aims to be memory hard
// This is achieved by storing all the hashes in a buffer and the
// in the end hashing them together in reverse order, instead of
// just xoring together.
//
// The memory-hardness of this scheme rests of the assumption that
// it is not feasible to compute the final hash backwards, that is,
// starting with the first hash and working towards the final hash.
// While I cannot prove this to be the case, the fact that our hash
// is made out of a one-way compression function makes me
// relatively confident in it.
// Place the hash of the salt at the top of the buffer. We do not
// include the counter i from PBKDF2 since we will ever only
// produce one block of output
size_t index = KDF_ROUNDS*32 - 32;
hmac(&kdf_buf[index], passphrase, passphraselen, salt, 32);
index -= 32;
// Walk back along the buffer, at each step hashing the previous
// hashes
while (index > 0) {
hmac(&kdf_buf[index], passphrase, passphraselen, &kdf_buf[index+32], 32);
index -= 32;
}
hmac(kdf_buf, passphrase, passphraselen, &kdf_buf[32], 32);
// Perform the final hash
unsigned char final_hash[32];
hmac(final_hash, passphrase, passphraselen, kdf_buf, KDF_ROUNDS * 32);
// Use first 128 bits of final hash as the key
memcpy(key, final_hash, 16);
}
int main(void) {
unsigned char key[16] = {0};
unsigned char salt[32] = "seasaltrocksalt seasaltrocksalt";
unsigned char passphrase[] = "a quick brown fox jumps over the lazy dog";
kdf(key, salt, passphrase, sizeof(passphrase) - 1);
for (size_t i = 0; i < 16; i++) {
printf("%02hhx ", key[i]);
}
printf("\n");
return 0;
}