Implement XXTEA128-CCM
This commit is contained in:
parent
900c7b4c93
commit
7bf44017a4
173
puer.c
173
puer.c
|
@ -2,6 +2,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
void xxtea128(uint32_t const key[4], uint32_t block[4]) {
|
void xxtea128(uint32_t const key[4], uint32_t block[4]) {
|
||||||
// Encryption half of the XXTEA algorithm, with block size limited
|
// Encryption half of the XXTEA algorithm, with block size limited
|
||||||
|
@ -65,6 +66,20 @@ void word2bytes(unsigned char *bytes, uint32_t word) {
|
||||||
bytes[3] = word>>24;
|
bytes[3] = word>>24;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void block2words(uint32_t words[4], unsigned char const bytes[16]) {
|
||||||
|
words[0] = bytes2word(&bytes[0]);
|
||||||
|
words[1] = bytes2word(&bytes[4]);
|
||||||
|
words[2] = bytes2word(&bytes[8]);
|
||||||
|
words[3] = bytes2word(&bytes[12]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void words2block(unsigned char bytes[16], uint32_t const words[4]) {
|
||||||
|
word2bytes(&bytes[0], words[0]);
|
||||||
|
word2bytes(&bytes[4], words[1]);
|
||||||
|
word2bytes(&bytes[8], words[2]);
|
||||||
|
word2bytes(&bytes[12], words[3]);
|
||||||
|
}
|
||||||
|
|
||||||
struct hashstate {
|
struct hashstate {
|
||||||
// A_n and B_n of the MDC-2 algorithm
|
// A_n and B_n of the MDC-2 algorithm
|
||||||
uint32_t a[4];
|
uint32_t a[4];
|
||||||
|
@ -108,10 +123,7 @@ void compress_hash(struct hashstate *state) {
|
||||||
|
|
||||||
// M_i
|
// M_i
|
||||||
uint32_t message[4];
|
uint32_t message[4];
|
||||||
message[0] = bytes2word(&state->buffer[0]);
|
block2words(message, state->buffer);
|
||||||
message[1] = bytes2word(&state->buffer[4]);
|
|
||||||
message[2] = bytes2word(&state->buffer[8]);
|
|
||||||
message[3] = bytes2word(&state->buffer[12]);
|
|
||||||
|
|
||||||
// V_i = M_i ^ E(M_i, A_i)
|
// V_i = M_i ^ E(M_i, A_i)
|
||||||
// Note: In this description A_i is the *key*, not the plaintext
|
// Note: In this description A_i is the *key*, not the plaintext
|
||||||
|
@ -289,6 +301,132 @@ void kdf(unsigned char key[16], unsigned char salt[32], unsigned char passphrase
|
||||||
memcpy(key, final_hash, 16);
|
memcpy(key, final_hash, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 16 bit authentication tag
|
||||||
|
const int mprime = (16-2)/2;
|
||||||
|
// 32 bit = 4 byte length field
|
||||||
|
const int lprime = 4-1;
|
||||||
|
|
||||||
|
void ccm_mac(unsigned char mac[16], uint32_t key[4], uint32_t messageindex, unsigned char message[], uint32_t length) {
|
||||||
|
// CCM specifies that the length field is big endian while we are
|
||||||
|
// natively little endian. Flip it.
|
||||||
|
unsigned char length_bytes[4];
|
||||||
|
length_bytes[0] = length >> 24;
|
||||||
|
length_bytes[1] = length >> 16;
|
||||||
|
length_bytes[2] = length >> 8;
|
||||||
|
length_bytes[3] = length;
|
||||||
|
uint32_t be_length = bytes2word(length_bytes);
|
||||||
|
|
||||||
|
// First block is special
|
||||||
|
uint32_t mac_words[4] = {mprime<<3 | lprime, 0, messageindex, be_length};
|
||||||
|
xxtea128(key, mac_words);
|
||||||
|
|
||||||
|
// Process all full blocks
|
||||||
|
size_t index = 0;
|
||||||
|
for (; index + 16 <= length; index += 16) {
|
||||||
|
// Xor the plaintext block and previous encrypted block
|
||||||
|
uint32_t block[4];
|
||||||
|
block2words(block, &message[index]);
|
||||||
|
mac_words[0] ^= block[0];
|
||||||
|
mac_words[1] ^= block[1];
|
||||||
|
mac_words[2] ^= block[2];
|
||||||
|
mac_words[3] ^= block[3];
|
||||||
|
// Encrypt
|
||||||
|
xxtea128(key, mac_words);
|
||||||
|
}
|
||||||
|
if (index < length) {
|
||||||
|
// Pad with zeros to block width
|
||||||
|
unsigned char fullblock[16];
|
||||||
|
memset(fullblock, 0, 16);
|
||||||
|
memcpy(fullblock, &message[index], length - index);
|
||||||
|
// Xor the plaintext block and previous encrypted block
|
||||||
|
uint32_t block[4];
|
||||||
|
block2words(block, fullblock);
|
||||||
|
mac_words[0] ^= block[0];
|
||||||
|
mac_words[1] ^= block[1];
|
||||||
|
mac_words[2] ^= block[2];
|
||||||
|
mac_words[3] ^= block[3];
|
||||||
|
// Encrypt
|
||||||
|
xxtea128(key, mac_words);
|
||||||
|
}
|
||||||
|
|
||||||
|
words2block(mac, mac_words);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ccm_xor_block(unsigned char block[16], uint32_t key[4], uint32_t messageindex, uint32_t counter) {
|
||||||
|
// CCM specifies that the counter field is big endian while we are
|
||||||
|
// natively little endian. Flip it.
|
||||||
|
unsigned char counter_bytes[4];
|
||||||
|
counter_bytes[0] = counter >> 24;
|
||||||
|
counter_bytes[1] = counter >> 16;
|
||||||
|
counter_bytes[2] = counter >> 8;
|
||||||
|
counter_bytes[3] = counter;
|
||||||
|
uint32_t be_counter = bytes2word(counter_bytes);
|
||||||
|
|
||||||
|
uint32_t words[4] = {lprime, 0, messageindex, be_counter};
|
||||||
|
xxtea128(key, words);
|
||||||
|
unsigned char keystream[16];
|
||||||
|
words2block(keystream, words);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 16; i++) {
|
||||||
|
block[i] ^= keystream[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IMPORTANT: The underlying message[] must be addressable until the index
|
||||||
|
// ceil(length / 16)*16, but the length only reflects the actual message
|
||||||
|
// length
|
||||||
|
void ccm_encrypt(unsigned char key[16], uint32_t messageindex, unsigned char message[], uint32_t length, unsigned char mac[16]) {
|
||||||
|
uint32_t key_words[4];
|
||||||
|
block2words(key_words, key);
|
||||||
|
|
||||||
|
// Authenticate
|
||||||
|
ccm_mac(mac, key_words, messageindex, message, length);
|
||||||
|
|
||||||
|
// Encrypt
|
||||||
|
// MAC is xored with first block of keystream
|
||||||
|
ccm_xor_block(mac, key_words, messageindex, 0);
|
||||||
|
// Xor the message
|
||||||
|
for (uint32_t index = 0; index < length; index += 16) {
|
||||||
|
// Message blocks are numbered from index 1 onwards
|
||||||
|
ccm_xor_block(&message[index], key_words, messageindex, index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same requirements for message[] hold as with ccm_encrypt
|
||||||
|
bool ccm_decrypt(unsigned char key[16], uint32_t messageindex, unsigned char message[], uint32_t length, unsigned char mac[16]) {
|
||||||
|
uint32_t key_words[4];
|
||||||
|
block2words(key_words, key);
|
||||||
|
|
||||||
|
// Decrypt
|
||||||
|
// MAC is xored with first block of keystream
|
||||||
|
ccm_xor_block(mac, key_words, messageindex, 0);
|
||||||
|
// Xor the message
|
||||||
|
for (uint32_t index = 0; index < length; index += 16) {
|
||||||
|
// Message blocks are numbered from index 1 onwards
|
||||||
|
ccm_xor_block(&message[index], key_words, messageindex, index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the expected authentication tag
|
||||||
|
unsigned char computed_mac[16];
|
||||||
|
ccm_mac(computed_mac, key_words, messageindex, message, length);
|
||||||
|
|
||||||
|
// Compare the expected and actual tag in constant time
|
||||||
|
unsigned char different = 0;
|
||||||
|
for (size_t i = 0; i < 16; i++) {
|
||||||
|
different |= computed_mac[i] ^ mac[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the tags match?
|
||||||
|
if (different) {
|
||||||
|
// Nope. Wipe what we decrypted and return false
|
||||||
|
explicit_bzero(message, length);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// They do, return true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
unsigned char key[16] = {0};
|
unsigned char key[16] = {0};
|
||||||
unsigned char salt[32] = "seasaltrocksalt seasaltrocksalt";
|
unsigned char salt[32] = "seasaltrocksalt seasaltrocksalt";
|
||||||
|
@ -297,6 +435,31 @@ int main(void) {
|
||||||
for (size_t i = 0; i < 16; i++) {
|
for (size_t i = 0; i < 16; i++) {
|
||||||
printf("%02hhx ", key[i]);
|
printf("%02hhx ", key[i]);
|
||||||
}
|
}
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
|
char *message = "Syökää parsaa ja palvokaa saatanaa";
|
||||||
|
unsigned char buf[64] = {69};
|
||||||
|
strcpy((char*)buf, message);
|
||||||
|
|
||||||
|
ccm_encrypt(key, 25, buf, strlen(message), &buf[48]);
|
||||||
|
for (size_t i = 0; i < sizeof(buf); i++) {
|
||||||
|
printf("%02hhx ", buf[i]);
|
||||||
|
if (i % 16 == 15) printf("\n");
|
||||||
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
return 0;
|
|
||||||
|
memset(&buf[strlen(message)], 0x0f, 48-strlen(message));
|
||||||
|
for (size_t i = 0; i < sizeof(buf); i++) {
|
||||||
|
printf("%02hhx ", buf[i]);
|
||||||
|
if (i % 16 == 15) printf("\n");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
|
||||||
|
bool auth = ccm_decrypt(key, 25, buf, strlen(message), &buf[48]);
|
||||||
|
printf("auth %s\n", auth ? "succeeded" : "failed");
|
||||||
|
for (size_t i = 0; i < sizeof(buf); i++) {
|
||||||
|
printf("%02hhx ", buf[i]);
|
||||||
|
if (i % 16 == 15) printf("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue