@@ -0,0 +1,12 @@ | |||
TARGET := disco.elf | |||
LOCAL_CFLAGS := -Os -fno-builtin-printf -I. | |||
LOCAL_SRC_C := \ | |||
tests/test_disco.c \ | |||
disco_asymmetric.c \ | |||
disco_symmetric.c \ | |||
tweetstrobe.c \ | |||
tweetX25519.c \ | |||
devurandom.c | |||
include ../conf/toolchain.mk | |||
include ../conf/sdk.mk |
@@ -0,0 +1,8 @@ | |||
#include <fcntl.h> | |||
#include <unistd.h> | |||
static int fd = -1; | |||
void randombytes(unsigned char *x, unsigned long long xlen) { | |||
/* TODO */ | |||
} |
@@ -0,0 +1,577 @@ | |||
#include "disco_asymmetric.h" | |||
#include "tweetX25519.h" | |||
#include "tweetstrobe.h" | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
// | |||
// Handshake Patterns | |||
// ================== | |||
// Handshake patterns are written as string literals, where every character | |||
// represent a token. This is implementation-specific and you can safely ignore | |||
// this if you are not auditing the code. | |||
// clang-format off | |||
#define token_e 'e' | |||
#define token_s 's' | |||
#define token_ee 'E' | |||
#define token_es 'R' | |||
#define token_se 'D' | |||
#define token_ss 'S' | |||
#define token_psk 'p' | |||
#define token_end_turn '|' | |||
#define token_end_handshake '\0' | |||
// clang-format on | |||
// | |||
// Asymmetric Cryptography | |||
// ====== | |||
// Used for key exchanges. | |||
/** | |||
* disco_generateKeyPair can be used to generate a X25519 keypair. This is | |||
* useful for creating long-term keypairs for a peer. | |||
* @kp an initialized keyPair struct. It is over-written by the function. | |||
*/ | |||
void disco_generateKeyPair(keyPair *kp) { | |||
crypto_box_keypair(kp->pub, kp->priv); | |||
kp->isSet = true; // TODO: is this useful? If it is, should we use a magic | |||
// number here in case it's not initialized to false? | |||
} | |||
void DH(keyPair mine, keyPair theirs, uint8_t *output) { | |||
crypto_scalarmult(output, mine.priv, theirs.pub); | |||
} | |||
// | |||
// SymmetricState | |||
// ============== | |||
// Refer to the Disco specification to understand the meaning of these | |||
// functions. | |||
void initializeSymmetric(symmetricState *ss, const char *protocol_name, | |||
size_t protocol_name_len) { | |||
strobe_init(&(ss->strobe), protocol_name, protocol_name_len); | |||
} | |||
void mixKey(symmetricState *ss, uint8_t *input_key_material) { | |||
strobe_operate(&(ss->strobe), TYPE_AD, input_key_material, 32, false); | |||
ss->isKeyed = true; | |||
} | |||
void mixHash(symmetricState *ss, uint8_t *data, size_t data_len) { | |||
strobe_operate(&(ss->strobe), TYPE_AD, data, data_len, false); | |||
} | |||
void mixKeyAndHash(symmetricState *ss, uint8_t *input_key_material) { | |||
strobe_operate(&(ss->strobe), TYPE_AD, input_key_material, 32, false); | |||
} | |||
void getHandshakeHash(symmetricState *ss, uint8_t *result) { | |||
strobe_operate(&(ss->strobe), TYPE_PRF, result, 32, false); | |||
} | |||
// note that this function modifies the plaintext in place, and requires the | |||
// plaintext buffer to have 16 more bytes of capacity for the authentication tag | |||
// if the symmetric state is keyed | |||
void encryptAndHash(symmetricState *ss, uint8_t *plaintext, | |||
size_t plaintext_len) { | |||
if (!ss->isKeyed) { | |||
strobe_operate(&(ss->strobe), TYPE_CLR, plaintext, plaintext_len, false); | |||
} else { | |||
strobe_operate(&(ss->strobe), TYPE_ENC, plaintext, plaintext_len, false); | |||
strobe_operate(&(ss->strobe), TYPE_MAC, plaintext + plaintext_len, 16, | |||
false); | |||
} | |||
} | |||
// note that the decryption occurs in place, and the the result is | |||
// `ciphertext_len-16` in case the symmetric state is keyed. | |||
bool decryptAndHash(symmetricState *ss, uint8_t *ciphertext, | |||
size_t ciphertext_len) { | |||
if (!ss->isKeyed) { | |||
return strobe_operate(&(ss->strobe), TYPE_CLR | FLAG_I, ciphertext, | |||
ciphertext_len, false); | |||
} | |||
if (ciphertext_len < 16) { | |||
return false; | |||
} | |||
strobe_operate(&(ss->strobe), TYPE_ENC | FLAG_I, ciphertext, | |||
ciphertext_len - 16, false); | |||
return strobe_operate(&(ss->strobe), TYPE_MAC | FLAG_I, | |||
ciphertext + ciphertext_len - 16, 16, false); | |||
} | |||
// split takes a symmetric state ss, a strobe state s1 and an empty | |||
// but allocated strobe state s2 | |||
// TODO: perhaps return only s1 if this is a one-way handshake pattern? | |||
// TODO: how do I ensure that a server don't send msg on a one-way hp? | |||
void split(symmetricState *ss, strobe_s *s1, strobe_s *s2, bool half_duplex) { | |||
assert(s1 != NULL); | |||
if (!half_duplex) { | |||
assert(s2 != NULL); | |||
} | |||
// s1 = our current strobe state | |||
*s1 = ss->strobe; | |||
if (!half_duplex) { | |||
// s2 = s1 | |||
*s2 = ss->strobe; | |||
} | |||
// differentiate by aborbing different domain strings | |||
strobe_operate(s1, TYPE_AD | FLAG_M, (uint8_t *)"initiator", 9, false); | |||
if (!half_duplex) { | |||
strobe_operate(s2, TYPE_AD | FLAG_M, (uint8_t *)"responder", 9, false); | |||
} | |||
// forward-secrecy | |||
unsigned char ratchet_buffer[32]; | |||
strobe_operate(s1, TYPE_RATCHET, ratchet_buffer, 32, false); | |||
if (!half_duplex) { | |||
strobe_operate(s2, TYPE_RATCHET, ratchet_buffer, 32, false); | |||
} | |||
} | |||
// | |||
// HandshakeState | |||
// ============== | |||
// Refer to the Disco specification to understand the meaning of these | |||
// functions. | |||
// destroy removes any secret information contained in the handshakeState passed | |||
// as argument | |||
void destroy(handshakeState *hs) { | |||
uint8_t size_to_remove; | |||
// remove keys | |||
volatile uint8_t *p; | |||
if (hs->s.isSet) { | |||
p = hs->s.priv; | |||
size_to_remove = 32; | |||
while (size_to_remove--) { | |||
*p++ = 0; | |||
} | |||
} | |||
if (hs->e.isSet) { | |||
p = hs->e.priv; | |||
size_to_remove = 32; | |||
while (size_to_remove--) { | |||
*p++ = 0; | |||
} | |||
} | |||
// remove symmetric state / strobe | |||
strobe_destroy(&(hs->symmetric_state.strobe)); | |||
} | |||
/** | |||
* @brief to initialize a handshakeState | |||
* disco_Initialize is used to initialize a non-NULL handshakeState with a | |||
* protocol name, a Noise handshake pattern, a boolean indicating if the | |||
* handshakeState represents a client or a server, an optional prologue, a set | |||
* of optional key pairs (depending on the handshake pattern chosen). | |||
* @hs a non-NULL handshakeState to be initialized. | |||
* @hp the chosen handshake pattern. see tweetdisco.h for the | |||
* handshake patterns define (HANDSHAKE_NX, HANDSHAKE_NK, etc.) | |||
* @initiator true if the peer is the client (sending the first message). | |||
* False if the peer is the server. | |||
* @prologue any data that was exchanged between the two peers prior to the | |||
* handshake. See Noise's specification for more information on this field. | |||
* @prologue_len The length of the `prologue` buffer. | |||
* @s NULL or a keypair containing the peer's long-term static key. | |||
* @e NULL or a keypair containing the peer's ephemeral key (see | |||
* fallback patterns in the Noise specification). | |||
* @rs NULL or a keypair containing the remote peer's long-term static | |||
* key. | |||
* @re NULL or a keypair containing the remote peer's ephemeral key | |||
* (see fallback patterns in the Noise specification). | |||
*/ | |||
void disco_Initialize(handshakeState *hs, const char *handshake_pattern, | |||
bool initiator, uint8_t *prologue, size_t prologue_len, | |||
keyPair *s, keyPair *e, keyPair *rs, keyPair *re) { | |||
assert(handshake_pattern != NULL); | |||
assert((prologue_len > 0 && prologue != NULL) || | |||
(prologue_len == 0 && prologue == NULL)); | |||
// handshake_pattern is of the following form: | |||
// "protocol_name \x00 pre-message patterns \x00 message patterns" | |||
printf("debug1:%s\n", handshake_pattern); | |||
initializeSymmetric(&(hs->symmetric_state), handshake_pattern, | |||
strlen(handshake_pattern)); | |||
handshake_pattern = handshake_pattern + strlen(handshake_pattern) + 1; | |||
printf("debug2:%s\n", handshake_pattern); | |||
hs->symmetric_state.isKeyed = false; | |||
// prologue | |||
mixHash(&(hs->symmetric_state), prologue, prologue_len); | |||
// set variables | |||
if (s != NULL) { | |||
// TODO: should we do assert(hs->s.isSet) ? | |||
hs->s = *s; | |||
hs->s.isSet = true; | |||
} else { | |||
hs->s.isSet = false; | |||
} | |||
if (e != NULL) { | |||
hs->e = *e; | |||
hs->e.isSet = true; | |||
} else { | |||
hs->e.isSet = false; | |||
} | |||
if (rs != NULL) { | |||
hs->rs = *rs; | |||
hs->rs.isSet = true; | |||
} else { | |||
hs->rs.isSet = false; | |||
} | |||
if (re != NULL) { | |||
hs->re = *re; | |||
hs->re.isSet = true; | |||
} else { | |||
hs->re.isSet = false; | |||
} | |||
hs->initiator = initiator; | |||
hs->sending = initiator; | |||
hs->handshake_done = false; | |||
// pre-messages | |||
bool direction = true; | |||
// handshake_pattern is of the following form: | |||
// pre_message_patterns | token_end_handshake | message_patterns" | |||
while (*handshake_pattern != token_end_handshake) { | |||
switch (*handshake_pattern) { | |||
case token_s: | |||
if ((initiator && direction) || (!initiator && !direction)) { | |||
mixHash(&(hs->symmetric_state), hs->s.pub, 32); | |||
} else { | |||
mixHash(&(hs->symmetric_state), hs->rs.pub, 32); | |||
} | |||
break; | |||
case token_e: | |||
if ((initiator && direction) || (!initiator && !direction)) { | |||
mixHash(&(hs->symmetric_state), hs->e.pub, 32); | |||
} else { | |||
mixHash(&(hs->symmetric_state), hs->re.pub, 32); | |||
} | |||
break; | |||
case token_end_turn: | |||
direction = !direction; | |||
break; | |||
case token_end_handshake: | |||
break; | |||
default: | |||
assert(false); | |||
} | |||
// next token | |||
handshake_pattern++; | |||
} | |||
// point to message patterns | |||
hs->message_patterns = handshake_pattern + 1; | |||
// half duplex is disabled by default | |||
hs->half_duplex = false; | |||
} | |||
/** | |||
* disco_WriteMessage takes | |||
* @hs an initialized `handshakeState`. | |||
* @payload an optional (can be NULL) payload to send at the end of the | |||
* handshake message. Depending on the handshake the security properties | |||
* associated to that payload can be different (even non-existent). | |||
* @payload_len the length of the optional payload (can be 0). | |||
* @message_buffer the buffer that will contain the final handshake | |||
* message to send to the other peer. It must be allocated with enough room for | |||
* the | |||
* relevant handshake message's content, the additional payload (of size | |||
* `payload_len`) and each authentication tag (16 bytes). | |||
* @message_len will be overwritten with the length of the message written in | |||
* message_buffer | |||
* @client_s the Strobe state that will be used by the client to encrypt | |||
* data | |||
* post-handshake. It can be set to `NULL` if this is not processing the end of | |||
* the handshake. | |||
* @server_s the Strobe state that will be used by the server to encrypt | |||
* data | |||
* post-handshake. It can be set to `NULL` if this is not processing the end of | |||
* the handshake. | |||
* @return the length of the content written in `message_buffer`. | |||
*/ | |||
bool disco_WriteMessage(handshakeState *hs, uint8_t *payload, | |||
size_t payload_len, uint8_t *message_buffer, | |||
size_t *message_len, strobe_s *client_s, | |||
strobe_s *server_s) { | |||
assert(hs != NULL && message_buffer != NULL); | |||
assert( | |||
(payload == NULL && payload_len == 0) || | |||
(payload != NULL && payload_len > 0 && payload_len < MAX_SIZE_MESSAGE)); | |||
// TODO: should the payload_len be a return -1 ? | |||
assert(hs->handshake_done == false && hs->sending == true); | |||
// Fetches and deletes the next message pattern from message_patterns | |||
assert(hs->message_patterns != NULL); | |||
uint8_t *p = message_buffer; | |||
uint8_t DH_result[32]; | |||
// state machine | |||
const char *current_token = hs->message_patterns; | |||
while (true) { | |||
switch (*current_token) { | |||
case token_e: | |||
assert(!hs->e.isSet); | |||
disco_generateKeyPair(&(hs->e)); | |||
memcpy(p, hs->e.pub, 32); | |||
p += 32; | |||
mixHash(&(hs->symmetric_state), hs->e.pub, 32); | |||
break; | |||
case token_s: | |||
assert(hs->s.isSet); | |||
memcpy(p, hs->s.pub, 32); | |||
encryptAndHash(&(hs->symmetric_state), p, 32); | |||
p += 32; | |||
if (hs->symmetric_state.isKeyed) { | |||
p += 16; | |||
} | |||
break; | |||
case token_ee: | |||
// TODO: does this really replaces everything in DH_Result? | |||
DH(hs->e, hs->re, DH_result); | |||
mixKey(&(hs->symmetric_state), DH_result); | |||
// TODO: reset DH_result? | |||
// | |||
break; | |||
case token_es: | |||
if (hs->initiator) { | |||
DH(hs->e, hs->rs, DH_result); | |||
} else { | |||
DH(hs->s, hs->re, DH_result); | |||
} | |||
mixKey(&(hs->symmetric_state), DH_result); | |||
break; | |||
case token_se: | |||
if (hs->initiator) { | |||
DH(hs->s, hs->re, DH_result); | |||
} else { | |||
DH(hs->e, hs->rs, DH_result); | |||
} | |||
mixKey(&(hs->symmetric_state), DH_result); | |||
break; | |||
case token_ss: | |||
DH(hs->s, hs->rs, DH_result); | |||
mixKey(&(hs->symmetric_state), DH_result); | |||
break; | |||
case token_end_turn: | |||
hs->sending = !hs->sending; | |||
hs->message_patterns = current_token + 1; | |||
goto payload; | |||
case token_end_handshake: | |||
hs->handshake_done = true; | |||
goto payload; | |||
default: | |||
assert(false); | |||
} | |||
current_token++; | |||
} | |||
payload: | |||
// Payload | |||
if (payload != NULL) { | |||
memcpy(p, payload, payload_len); | |||
} | |||
encryptAndHash(&(hs->symmetric_state), p, payload_len); | |||
p += payload_len; | |||
if (hs->symmetric_state.isKeyed) { | |||
p += 16; | |||
} | |||
// Split? | |||
if (hs->handshake_done == true) { | |||
split(&(hs->symmetric_state), client_s, server_s, hs->half_duplex); | |||
hs->message_patterns = NULL; | |||
destroy(hs); | |||
} | |||
// set length of what was written into buffer | |||
*message_len = p - message_buffer; | |||
// | |||
return true; | |||
} | |||
/** | |||
* @brief used to read and process the next handshake message. | |||
* disco_ReadMessage reads and process the next handshake message. | |||
* @hs the initialized `handshakeState`. | |||
* @message the received message buffer. | |||
* @message_len the length of the received message. | |||
* @payload_buffer this buffer will be over-written with the received | |||
* payload. Its capacity should contain enough room for it. You can calculate | |||
* this required size by substracting other content from the message's length. | |||
* @payload_len will be the length of the produced payload | |||
* @client_s the Strobe state that will be used by the client to | |||
* encrypt data post-handshake. It can be set to `NULL` if this is not | |||
* processing the end of the handshake. | |||
* @server_s the Strobe state that will be used by the server to | |||
* encrypt data post-handshake. It can be set to `NULL` if this is not | |||
* processing the end of the handshake. | |||
* @return the length of the content written in `payload_buffer`. | |||
*/ | |||
bool disco_ReadMessage(handshakeState *hs, uint8_t *message, size_t message_len, | |||
uint8_t *payload_buffer, size_t *payload_len, | |||
strobe_s *client_s, strobe_s *server_s) { | |||
assert(hs != NULL && message != NULL && payload_buffer != NULL); | |||
assert(hs->handshake_done == false && hs->sending == false); | |||
assert(hs->message_patterns != NULL); | |||
if (message_len >= 65535) { | |||
return false; | |||
} | |||
uint8_t DH_result[32]; | |||
// state machine | |||
const char *current_token = hs->message_patterns; | |||
while (true) { | |||
switch (*current_token) { | |||
case token_e: | |||
if (message_len < 32) { | |||
return false; | |||
} | |||
assert(!hs->re.isSet); | |||
memcpy(hs->re.pub, message, 32); | |||
message_len -= 32; | |||
message += 32; | |||
hs->re.isSet = true; | |||
mixHash(&(hs->symmetric_state), hs->re.pub, 32); | |||
break; | |||
case token_s: | |||
assert(!hs->rs.isSet); | |||
size_t ciphertext_len = 32; | |||
if (hs->symmetric_state.isKeyed) { | |||
ciphertext_len += 16; | |||
} | |||
if (message_len < ciphertext_len) { | |||
return false; | |||
} | |||
bool res = | |||
decryptAndHash(&(hs->symmetric_state), message, ciphertext_len); | |||
if (!res) { | |||
return false; | |||
} | |||
memcpy(hs->rs.pub, message, 32); | |||
message_len -= ciphertext_len; | |||
message += ciphertext_len; | |||
hs->rs.isSet = true; | |||
break; | |||
case token_ee: | |||
// TODO: does this really replaces everything in DH_Result? | |||
DH(hs->e, hs->re, DH_result); | |||
mixKey(&(hs->symmetric_state), DH_result); | |||
// TODO: reset DH_result? | |||
break; | |||
case token_es: | |||
if (hs->initiator) { | |||
DH(hs->e, hs->rs, DH_result); | |||
} else { | |||
DH(hs->s, hs->re, DH_result); | |||
} | |||
mixKey(&(hs->symmetric_state), DH_result); | |||
break; | |||
case token_se: | |||
if (hs->initiator) { | |||
DH(hs->s, hs->re, DH_result); | |||
} else { | |||
DH(hs->e, hs->rs, DH_result); | |||
} | |||
mixKey(&(hs->symmetric_state), DH_result); | |||
break; | |||
case token_ss: | |||
DH(hs->s, hs->rs, DH_result); | |||
mixKey(&(hs->symmetric_state), DH_result); | |||
break; | |||
case token_end_turn: | |||
hs->sending = !hs->sending; | |||
hs->message_patterns = current_token + 1; | |||
goto payload; | |||
case token_end_handshake: | |||
hs->handshake_done = true; | |||
goto payload; | |||
default: | |||
assert(false); | |||
} | |||
current_token++; | |||
} | |||
payload: | |||
// Decrypt payload | |||
if (hs->symmetric_state.isKeyed && message_len < 16) { // a tag must be here | |||
return false; | |||
} | |||
bool res = decryptAndHash(&(hs->symmetric_state), message, message_len); | |||
if (!res) { | |||
return false; // TODO: should we return different errors? | |||
} | |||
if (hs->symmetric_state.isKeyed) { | |||
message_len -= 16; // remove the authentication tag if there is one | |||
} | |||
memcpy(payload_buffer, message, message_len); | |||
// Split? | |||
if (hs->handshake_done == true) { | |||
split(&(hs->symmetric_state), client_s, server_s, hs->half_duplex); | |||
hs->message_patterns = NULL; | |||
destroy(hs); | |||
} | |||
// set the decrypted payload length | |||
*payload_len = message_len; | |||
// return length of what was read into buffer | |||
return true; | |||
} | |||
// disco_EncryptInPlace takes a plaintext and replaces it with the encrypted | |||
// plaintext and 16 bytes of authentication tag. | |||
// For this reason, the buffer must have 16 additional bytes than plaintext_len | |||
// Note that the strobe state is also mutated to reflect the send_ENC and | |||
// send_MAC operations | |||
void disco_EncryptInPlace(strobe_s *strobe, uint8_t *plaintext, | |||
size_t plaintext_len, size_t plaintext_capacity) { | |||
assert(plaintext_capacity >= plaintext_len + 16); | |||
assert(plaintext != NULL); | |||
strobe_operate(strobe, TYPE_ENC, plaintext, plaintext_len, false); | |||
strobe_operate(strobe, TYPE_MAC, plaintext + plaintext_len, 16, false); | |||
} | |||
// disco_DecryptInPlace decrypts the ciphertext and replaces the buffer's | |||
// content with the obtained plaintext. the new length will be 16 bytes less | |||
// Note that the strobe state is also mutated to reflect the recv_ENC and | |||
// recv_MAC operations | |||
bool disco_DecryptInPlace(strobe_s *strobe, uint8_t *ciphertext, | |||
size_t ciphertext_len) { | |||
assert(ciphertext != NULL); | |||
// can't contain authentication tag | |||
if (ciphertext_len < 16) { | |||
return false; | |||
} | |||
// decrypt in place | |||
strobe_operate(strobe, TYPE_ENC | FLAG_I, ciphertext, ciphertext_len - 16, | |||
false); | |||
// verify authentication tag | |||
int res = strobe_operate(strobe, TYPE_MAC | FLAG_I, | |||
ciphertext + ciphertext_len - 16, 16, false); | |||
// bad authentication tag | |||
if (res < 0) { | |||
return false; | |||
// TODO: should we destroy the strobe object at this point? | |||
} | |||
// all good | |||
return true; | |||
} |
@@ -0,0 +1,114 @@ | |||
/** | |||
* The EmbeddedDisco Library | |||
* ========================= | |||
* | |||
* This protocol was designed and implemented by David Wong. | |||
* - contact: david.wong@nccgroup.trust | |||
* - more info: www.embeddeddisco.com | |||
* | |||
*/ | |||
#ifndef __DISCO_H__ | |||
#define __DISCO_H__ | |||
#include "tweetstrobe.h" | |||
#include <stdlib.h> | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
// the maximum size of a Disco (encrypted or not) message | |||
#define MAX_SIZE_MESSAGE 65000 | |||
// asymmetric | |||
typedef struct keyPair_ { | |||
uint8_t priv[32]; | |||
uint8_t pub[32]; | |||
bool isSet; | |||
} keyPair; | |||
// | |||
// Handshake Patterns | |||
// ================= | |||
// The following defines were generated using python | |||
// You can manually audit the handshake_patterns.py file | |||
#define HANDSHAKE_N "Noise_N_25519_STROBEv1.0.2\0|s\0eR\0" | |||
#define HANDSHAKE_K "Noise_K_25519_STROBEv1.0.2\0s|s\0eRS\0" | |||
#define HANDSHAKE_X "Noise_X_25519_STROBEv1.0.2\0|s\0eRsS\0" | |||
#define HANDSHAKE_NN "Noise_NN_25519_STROBEv1.0.2\0\0e|eE\0" | |||
#define HANDSHAKE_KN "Noise_KN_25519_STROBEv1.0.2\0s\0e|eED\0" | |||
#define HANDSHAKE_NK "Noise_NK_25519_STROBEv1.0.2\0|s\0eR|eE\0" | |||
#define HANDSHAKE_KK "Noise_KK_25519_STROBEv1.0.2\0s|s\0eRS|eED\0" | |||
#define HANDSHAKE_NX "Noise_NX_25519_STROBEv1.0.2\0\0e|eEsR\0" | |||
#define HANDSHAKE_KX "Noise_KX_25519_STROBEv1.0.2\0s\0e|eEDsR\0" | |||
#define HANDSHAKE_XN "Noise_XN_25519_STROBEv1.0.2\0\0e|eE|sD\0" | |||
#define HANDSHAKE_IN "Noise_IN_25519_STROBEv1.0.2\0\0es|eED\0" | |||
#define HANDSHAKE_XK "Noise_XK_25519_STROBEv1.0.2\0|s\0eR|eE|sD\0" | |||
#define HANDSHAKE_IK "Noise_IK_25519_STROBEv1.0.2\0|s\0eRsS|eED\0" | |||
#define HANDSHAKE_XX "Noise_XX_25519_STROBEv1.0.2\0\0e|eEsR|sD\0" | |||
#define HANDSHAKE_IX "Noise_IX_25519_STROBEv1.0.2\0\0es|eEDsR\0" | |||
// | |||
// States | |||
// ====== | |||
// See the Disco specification to understand the meaning of these states. | |||
typedef struct symmetricState_ { | |||
strobe_s strobe; | |||
bool isKeyed; | |||
} symmetricState; | |||
typedef struct handshakeState_ { | |||
symmetricState symmetric_state; | |||
keyPair s; | |||
keyPair e; | |||
// TODO: no need for the privatekey part of these keyPairs, takes space | |||
keyPair rs; | |||
keyPair re; | |||
bool initiator; | |||
const char *message_patterns; | |||
bool sending; | |||
bool handshake_done; | |||
bool half_duplex; | |||
} handshakeState; | |||
// | |||
// Public API | |||
// ========== | |||
// used to generate long-term key pairs | |||
void disco_generateKeyPair(keyPair *kp); | |||
// used to initialized your handshakeState with a handshake pattern | |||
void disco_Initialize(handshakeState *hs, const char *handshake_pattern, | |||
bool initiator, uint8_t *prologue, size_t prologue_len, | |||
keyPair *s, keyPair *e, keyPair *rs, keyPair *re); | |||
// used to generate the next handshake message to send | |||
bool disco_WriteMessage(handshakeState *hs, uint8_t *payload, | |||
size_t payload_len, uint8_t *message_buffer, | |||
size_t *message_len, strobe_s *client_s, | |||
strobe_s *server_s); | |||
// used to parse a the last handshake message received | |||
bool disco_ReadMessage(handshakeState *hs, uint8_t *message, size_t message_len, | |||
uint8_t *payload_buffer, size_t *payload_len, | |||
strobe_s *client_s, strobe_s *server_s); | |||
// post-handshake encryption | |||
void disco_EncryptInPlace(strobe_s *strobe, uint8_t *plaintext, | |||
size_t plaintext_len, size_t plaintext_capacity); | |||
// post-handshake decryption | |||
bool disco_DecryptInPlace(strobe_s *strobe, uint8_t *ciphertext, | |||
size_t ciphertext_len); | |||
// | |||
// | |||
// | |||
#endif /* __DISCO_H__ */ |
@@ -0,0 +1,160 @@ | |||
#include <assert.h> | |||
#include <stdio.h> | |||
#include "tweetstrobe.h" | |||
#include "disco_symmetric.h" | |||
// | |||
// Hash Functions | |||
// | |||
// disco_Hash is a simple hash function that can produce a digest of any length | |||
// from an `input`. The minimum output length `out_len` accepted is 32 bytes. | |||
// The `out` buffer must have at least `out_len` bytes of capacity to receive | |||
// the digest. | |||
void disco_Hash(uint8_t* input, size_t input_len, uint8_t* out, | |||
size_t out_len) { | |||
assert(out_len >= 32); | |||
strobe_s strobe; | |||
strobe_init(&strobe, "DiscoHash", 9); | |||
strobe_operate(&strobe, TYPE_AD, input, input_len, false); | |||
strobe_operate(&strobe, TYPE_PRF, out, out_len, false); | |||
} | |||
// disco_HashNew is a way to hold on to a hash context in order to continuously | |||
// write to it. This is usefull when we don't know in advance what we're going | |||
// to hash, or if we going to have to produce a digest at several point in time. | |||
// It is to be used once to initialize a discoHashCtx context. | |||
// Then the context can be used with disco_HashWrite or disco_HashWriteTuple to | |||
// absorb a message to hash. | |||
// Then the context can be used with disco_HashSum to produce a digest of any | |||
// length. | |||
void disco_HashNew(discoHashCtx* ctx) { | |||
assert(ctx != NULL); | |||
// can't check if it's already initialized coz memory is uninitialized in C | |||
strobe_init(&(ctx->strobe), "DiscoHash", 9); | |||
strobe_operate(&(ctx->strobe), TYPE_AD, NULL, 0, | |||
false); // to start streaming | |||
ctx->initialized = INITIALIZED; | |||
} | |||
// disco_HashWrite absorbs data to hash. Several call to this function on | |||
// fragmented data are equivalent to a single call to this function on the full | |||
// data (or a single call to disco_Hash on the full data). | |||
void disco_HashWrite(discoHashCtx* ctx, uint8_t* input, size_t input_len) { | |||
assert(ctx != NULL && ctx->initialized == INITIALIZED); | |||
assert((input != NULL && input_len > 0) || input_len == 0); | |||
strobe_operate(&(ctx->strobe), TYPE_AD, input, input_len, true); | |||
} | |||
// disco_HashWriteTuple absorbs data to hash, and place delimiters around it. | |||
// Several calls to this function on fragmented data are not equivalent to a | |||
// single call to that function on the full data. To reproduce the same digest | |||
// you must call disco_HashWriteTuple in-order with the same fragments of data. | |||
void disco_HashWriteTuple(discoHashCtx* ctx, uint8_t* input, size_t input_len) { | |||
assert(ctx != NULL && ctx->initialized == INITIALIZED); | |||
assert((input != NULL && input_len > 0) || input_len == 0); | |||
strobe_operate(&(ctx->strobe), TYPE_AD, input, input_len, false); | |||
} | |||
// disco_HashSum produces a digest. It does not mutate the context, and thus can | |||
// be re-used many times to produce the same digest. The context can also be | |||
// used to absorb more data to hash after. | |||
void disco_HashSum(discoHashCtx* ctx, uint8_t* out, size_t out_len) { | |||
assert(ctx != NULL && ctx->initialized == INITIALIZED); | |||
assert(out != NULL && out_len > 0); | |||
strobe_s copy = ctx->strobe; | |||
strobe_operate(©, TYPE_PRF, out, out_len, false); | |||
} | |||
// reset the context for re-use. Must be re-initialized after that. | |||
void disco_HashResetCtx(discoHashCtx* ctx) { | |||
assert(ctx != NULL); | |||
ctx->initialized = 0; | |||
strobe_destroy(&(ctx->strobe)); | |||
} | |||
// | |||
// Key Derivation Function | |||
// | |||
// disco_DeriveKeys can be used to write random data of size `out_len` to an | |||
// `out` buffer. It requires an `inputKey` of size `key_len` at least 16 bytes. | |||
// This key must be of high entropy: the output of a key exchange, randomness | |||
// generated by /dev/urandom, etc... | |||
void disco_DeriveKeys(uint8_t* inputKey, size_t key_len, uint8_t* out, | |||
size_t out_len) { | |||
assert(inputKey != NULL && out != NULL); | |||
assert(key_len >= 16 && out_len > 0); | |||
strobe_s strobe; | |||
strobe_init(&strobe, "DiscoKDF", 8); | |||
strobe_operate(&strobe, TYPE_AD, inputKey, key_len, false); | |||
strobe_operate(&strobe, TYPE_PRF, out, out_len, false); | |||
} | |||
// | |||
// Message Authentication Code | |||
// | |||
// disco_ProtectIntegrity takes a key of at least 16 bytes, and creates an | |||
// authentication tag of 16 bytes (or higher) in the `out` buffer (so the `out` | |||
// buffer must have a capacity of at least 16 bytes and `out_len` must be set to | |||
// 16 or higher) | |||
void disco_ProtectIntegrity(uint8_t* key, size_t key_len, uint8_t* data, | |||
size_t data_len, uint8_t* out, size_t out_len) { | |||
assert(key != NULL && key_len >= 16); | |||
assert(data != NULL && data_len > 0); | |||
assert(out != NULL && out_len >= 16); | |||
strobe_s strobe; | |||
strobe_init(&strobe, "DiscoMAC", 8); | |||
strobe_operate(&strobe, TYPE_AD, key, key_len, false); | |||
strobe_operate(&strobe, TYPE_AD, data, data_len, false); | |||
strobe_operate(&strobe, TYPE_MAC, out, out_len, false); | |||
} | |||
// disco_VerifyIntegrity can be used to verify a `tag` of size `tag_len` | |||
// produced by disco_ProtectIntegrity with a key of size `key_len` over the | |||
// `data` of size `data_len`. It returns true if the data has not been modified. | |||
// False otherwise. | |||
bool disco_VerifyIntegrity(uint8_t* key, size_t key_len, uint8_t* data, | |||
size_t data_len, uint8_t* tag, size_t tag_len) { | |||
assert(key != NULL && key_len >= 16); | |||
assert(data != NULL && data_len > 0); | |||
assert(tag != NULL && tag_len >= 16); | |||
strobe_s strobe; | |||
strobe_init(&strobe, "DiscoMAC", 8); | |||
strobe_operate(&strobe, TYPE_AD, key, key_len, false); | |||
strobe_operate(&strobe, TYPE_AD, data, data_len, false); | |||
return strobe_operate(&strobe, TYPE_MAC | FLAG_I, tag, tag_len, false); | |||
} | |||
// | |||
// Pseudo-Random Number Generator | |||
// | |||
// This is a sensible PRNG: if your process forks, the two threads will produce | |||
// the same random numbers. If you're inside a VM that gets cloned, the VMs will | |||
// then produce the same random numbers. | |||
void disco_RandomSeed(discoRandomCtx* ctx, uint8_t* seed, size_t seed_len) { | |||
assert(ctx != NULL); | |||
assert(seed != NULL && seed_len > 16); | |||
strobe_init(&(ctx->strobe), "DiscoPRNG", 9); | |||
strobe_operate(&(ctx->strobe), TYPE_AD, seed, seed_len, false); | |||
ctx->initialized = INITIALIZED; | |||
} | |||
// You can re-inject entropy into your PRNG. | |||
void disco_InjectEntropy(discoRandomCtx* ctx, uint8_t* entropy, | |||
size_t entropy_len) { | |||
assert(ctx != NULL && ctx->initialized == INITIALIZED); | |||
strobe_operate(&(ctx->strobe), TYPE_KEY, entropy, entropy_len, false); | |||
} | |||
// to obtain a random number of size `out_len`. | |||
void disco_RandomGet(discoRandomCtx* ctx, uint8_t* out, size_t out_len) { | |||
assert(ctx != NULL && ctx->initialized == INITIALIZED); | |||
assert(out != NULL && out_len > 0); | |||
strobe_operate(&(ctx->strobe), TYPE_PRF, out, out_len, false); | |||
} |
@@ -0,0 +1,59 @@ | |||
#ifndef DISCO_SYMMETRIC_H_ | |||
#define DISCO_SYMMETRIC_H_ | |||
#include "tweetstrobe.h" | |||
// hashing | |||
typedef struct discoHashCtx_ { | |||
strobe_s strobe; | |||
uint8_t initialized; | |||
} discoHashCtx; | |||
#define INITIALIZED 111 | |||
// Hashing | |||
void disco_Hash(uint8_t* input, size_t input_len, uint8_t* out, size_t out_len); | |||
void disco_HashNew(discoHashCtx* ctx); | |||
void disco_HashWrite(discoHashCtx* ctx, uint8_t* input, size_t input_len); | |||
void disco_HashWriteTuple(discoHashCtx* ctx, uint8_t* input, size_t input_len); | |||
void disco_HashSum(discoHashCtx* ctx, uint8_t* out, size_t out_len); | |||
void disco_HashResetCtx(discoHashCtx* ctx); | |||
// Key Derivation | |||
void disco_DeriveKeys(uint8_t* inputKey, size_t key_len, uint8_t* out, | |||
size_t out_len); | |||
// Integrity Protection | |||
void disco_ProtectIntegrity(uint8_t* key, size_t key_len, uint8_t* data, | |||
size_t data_len, uint8_t* out, size_t out_len); | |||
bool disco_VerifyIntegrity(uint8_t* key, size_t key_len, uint8_t* data, | |||
size_t data_len, uint8_t* tag, size_t tag_len); | |||
/* | |||
// Authenticated Encryption | |||
ssize_t disco_Encrypt(uint8_t* key, size_t key_len, uint8_t* plaintext, | |||
size_t plaintext_len, size_t plaintext_capacity); | |||
ssize_t disco_Decrypt(uint8_t* key, size_t key_len, uint8_t* ciphertext, | |||
size_t ciphertext_len); | |||
// Authenticated Encryption with Additional Data | |||
ssize_t disco_EncryptAndAuthenticate(uint8_t* key, size_t key_len, | |||
uint8_t* plaintext, size_t plaintext_len, | |||
size_t plaintext_capacity); | |||
ssize_t disco_DecryptAndAuthenticate(uint8_t* key, size_t key_len, | |||
uint8_t* ciphertext, | |||
size_t ciphertext_len); | |||
*/ | |||
typedef struct discoRandomCtx_ { | |||
strobe_s strobe; | |||
uint8_t initialized; | |||
} discoRandomCtx; | |||
// Pseudo-Random Number Generator | |||
void disco_RandomSeed(discoRandomCtx* ctx, uint8_t* seed, size_t seed_len); | |||
void disco_InjectEntropy(discoRandomCtx* ctx, uint8_t* entropy, | |||
size_t entropy_len); | |||
void disco_RandomGet(discoRandomCtx* ctx, uint8_t* out, size_t out_len); | |||
#endif // DISCO_SYMMETRIC_H_ |
@@ -0,0 +1,127 @@ | |||
/** | |||
* @cond internal | |||
* @file keccak_f.c.inc | |||
* @copyright | |||
* Copyright (c) 2015-2016 Cryptography Research, Inc. \n | |||
* Released under the MIT License. See LICENSE.txt for license information. | |||
* | |||
* Loosely based on CC0 implementations of Keccak-F: | |||
* Keccak-Tiny: | |||
* David Leon Gil | |||
* TweetFIPS202: | |||
* Dan J Bernstein | |||
* Peter Schwabe | |||
* Gilles van Assche | |||
* | |||
* @author Mike Hamburg | |||
* | |||
* @brief Keccak-f[n] implementation. Designed to be included in another C | |||
* file, so no headers. | |||
*/ | |||
/* Endian swaps used by keccak implementation */ | |||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ | |||
static inline kword_t eswap_letoh(kword_t w) { return w; } | |||
static inline kword_t eswap_htole(kword_t w) { return w; } | |||
#else | |||
#error "Fix eswap() on non-little-endian machine" | |||
#endif | |||
/* Could lose this to save size, maybe, depends on arch */ | |||
#ifndef KECCAK_OPT_RC_TABLE | |||
#define KECCAK_OPT_RC_TABLE 1 | |||
#endif | |||
/* Helper macros to unroll the permutation. */ | |||
#define REPEAT5(e) e e e e e | |||
#if KECCAK_OPT_FOR_SIZE // Size + 0 bytes, speed x 1/2 | |||
# define FOR51(v, e) v = 0; REPEAT5(e; v += 1;) | |||
# define FOR55(v, e) for (v=0; v<25; v+= 5) { e; } | |||
# define REPEAT24(e) {int _j=0; for (_j=0; _j<24; _j++) { e }} | |||
#elif KECCAK_OPT_FOR_SPEED // Size + 600 bytes, speed x1 | |||
# define FOR51(v, e) v = 0; REPEAT5(e; v += 1;) | |||
# define FOR55(v, e) v = 0; REPEAT5(e; v += 5;) | |||
# define REPEAT24(e) e e e e e e e e e e e e e e e e e e e e e e e e | |||
#elif KECCAK_OPT_FOR_SIZE_AGGRESSIVE // Terrible. Actually makes things bigger | |||
# define FOR51(v, e) for (v=0; v<5; v++) { e; } | |||
# define FOR55(v, e) for (v=0; v<25; v+= 5) { e; } | |||
# define REPEAT24(e) {int _j=0; for (_j=0; _j<24; _j++) { e }} | |||
#else // Size + 100 bytes, speed x 3/4 | |||
# define FOR51(v, e) v = 0; REPEAT5(e; v += 1;) | |||
# define FOR55(v, e) for (v=0; v<25; v+= 5) { e; } | |||
# define REPEAT24(e) e e e e e e e e e e e e e e e e e e e e e e e e | |||
#endif | |||
#if KECCAK_INTEROP_F_BITS == 1600 | |||
#define NROUNDS 24 | |||
#elif KECCAK_INTEROP_F_BITS == 800 | |||
#define NROUNDS 22 | |||
#elif KECCAK_INTEROP_F_BITS == 400 | |||
#define NROUNDS 20 | |||
#elif sKECCAK_INTEROP_F_BITS == 200 | |||
#define NROUNDS 18 | |||
#else | |||
#error "Only implementing KeccakF[200,400,800,1600]'" | |||
#endif | |||
/** Rotate left */ | |||
static inline kword_t rol(kword_t x, int s) { | |||
static const int WBITS = 8*sizeof(kword_t); | |||
s %= WBITS; | |||
return (x << s) | (x >> (WBITS - s)); | |||
} | |||
/*** The keccak-f[] permutation ***/ | |||
static void keccak_f(kdomain_s *state) { | |||
const uint8_t pi[24] = { | |||
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, | |||
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 | |||
}; | |||
#define RC_B(x,n) ((((x##ull)>>n)&1)<<((1<<n)-1)) | |||
#define RC_X(x) ((kword_t)(RC_B(x,0)|RC_B(x,1)|RC_B(x,2)|RC_B(x,3)|RC_B(x,4)|RC_B(x,5)|RC_B(x,6))) | |||
const kword_t RC[NROUNDS] = { | |||
#if NROUNDS >= 24 | |||
RC_X(0x74), RC_X(0x21), | |||
#endif | |||
#if NROUNDS >= 22 | |||
RC_X(0x58), RC_X(0x79), | |||
#endif | |||
#if NROUNDS >= 20 | |||
RC_X(0x66), RC_X(0x16), | |||
#endif | |||
RC_X(0x48), RC_X(0x52), RC_X(0x53), RC_X(0x5d), RC_X(0x4f), RC_X(0x3f), | |||
RC_X(0x26), RC_X(0x35), RC_X(0x0c), RC_X(0x0e), RC_X(0x55), RC_X(0x79), | |||
RC_X(0x21), RC_X(0x1f), RC_X(0x70), RC_X(0x5e), RC_X(0x1a), RC_X(0x01) | |||
}; | |||
kword_t* a = state->w; | |||
kword_t b[5] = {0}, t, u; | |||
unsigned int x, y; | |||
int i; | |||
for (i=0; i<25; i++) a[i] = eswap_letoh(a[i]); | |||
for (i = NROUNDS-1; i >=0; i--) { | |||
// Theta | |||
FOR51(x, b[x] = 0;) | |||
FOR55(y, FOR51(x, b[x] ^= a[x + y];)) | |||
FOR55(y, FOR51(x, | |||
a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); | |||
)) | |||
// Rho and pi | |||
t = a[1]; | |||
x = y = 0; | |||
REPEAT24(u = a[pi[x]]; y += x+1; a[pi[x]] = rol(t, y); t = u; x++; ) | |||
// Chi | |||
FOR55(y, | |||
FOR51(x, b[x] = a[y + x];) | |||
FOR51(x, a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]);) | |||
) | |||
// Iota | |||
a[0] ^= RC[i]; | |||
} | |||
for (i=0; i<25; i++) a[i] = eswap_htole(a[i]); | |||
} |
@@ -0,0 +1,154 @@ | |||
// Original code is tweetnacl | |||
// modified to remove what I don't need | |||
// should I add a license here? | |||
#include "tweetX25519.h" | |||
#define FOR(i, n) for (i = 0; i < n; ++i) | |||
#define sv static void | |||
typedef long long i64; | |||
typedef i64 gf[16]; | |||
extern void randombytes(u8 *, u64); | |||
static const u8 _9[32] = {9}; | |||
static const gf _121665 = {0xDB41, 1}; | |||
sv car25519(gf o) { | |||
int i; | |||
i64 c; | |||
FOR(i, 16) { | |||
o[i] += (1LL << 16); | |||
c = o[i] >> 16; | |||
o[(i + 1) * (i < 15)] += c - 1 + 37 * (c - 1) * (i == 15); | |||
o[i] -= c << 16; | |||
} | |||
} | |||
sv sel25519(gf p, gf q, int b) { | |||
i64 t, i, c = ~(b - 1); | |||
FOR(i, 16) { | |||
t = c & (p[i] ^ q[i]); | |||
p[i] ^= t; | |||
q[i] ^= t; | |||
} | |||
} | |||
sv pack25519(u8 *o, const gf n) { | |||
int i, j, b; | |||
gf m, t; | |||
FOR(i, 16) t[i] = n[i]; | |||
car25519(t); | |||
car25519(t); | |||
car25519(t); | |||
FOR(j, 2) { | |||
m[0] = t[0] - 0xffed; | |||
for (i = 1; i < 15; i++) { | |||
m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); | |||
m[i - 1] &= 0xffff; | |||
} | |||
m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); | |||
b = (m[15] >> 16) & 1; | |||
m[14] &= 0xffff; | |||
sel25519(t, m, 1 - b); | |||
} | |||
FOR(i, 16) { | |||
o[2 * i] = t[i] & 0xff; | |||
o[2 * i + 1] = t[i] >> 8; | |||
} | |||
} | |||
sv unpack25519(gf o, const u8 *n) { | |||
int i; | |||
FOR(i, 16) o[i] = n[2 * i] + ((i64)n[2 * i + 1] << 8); | |||
o[15] &= 0x7fff; | |||
} | |||
sv A(gf o, const gf a, const gf b) { | |||
int i; | |||
FOR(i, 16) o[i] = a[i] + b[i]; | |||
} | |||
sv Z(gf o, const gf a, const gf b) { | |||
int i; | |||
FOR(i, 16) o[i] = a[i] - b[i]; | |||
} | |||
sv M(gf o, const gf a, const gf b) { | |||
i64 i, j, t[31]; | |||
FOR(i, 31) t[i] = 0; | |||
FOR(i, 16) FOR(j, 16) t[i + j] += a[i] * b[j]; | |||
FOR(i, 15) t[i] += 38 * t[i + 16]; | |||
FOR(i, 16) o[i] = t[i]; | |||
car25519(o); | |||
car25519(o); | |||
} | |||
sv S(gf o, const gf a) { M(o, a, a); } | |||
sv inv25519(gf o, const gf i) { | |||
gf c; | |||
int a; | |||
FOR(a, 16) c[a] = i[a]; | |||
for (a = 253; a >= 0; a--) { | |||
S(c, c); | |||
if (a != 2 && a != 4) M(c, c, i); | |||
} | |||
FOR(a, 16) o[a] = c[a]; | |||
} | |||
int crypto_scalarmult(u8 *q, const u8 *n, const u8 *p) { | |||
u8 z[32]; | |||
i64 x[80], r, i; | |||
gf a, b, c, d, e, f; | |||
FOR(i, 31) z[i] = n[i]; | |||
z[31] = (n[31] & 127) | 64; | |||
z[0] &= 248; | |||
unpack25519(x, p); | |||
FOR(i, 16) { | |||
b[i] = x[i]; | |||
d[i] = a[i] = c[i] = 0; | |||
} | |||
a[0] = d[0] = 1; | |||
for (i = 254; i >= 0; --i) { | |||
r = (z[i >> 3] >> (i & 7)) & 1; | |||
sel25519(a, b, r); | |||
sel25519(c, d, r); | |||
A(e, a, c); | |||
Z(a, a, c); | |||
A(c, b, d); | |||
Z(b, b, d); | |||
S(d, e); | |||
S(f, a); | |||
M(a, c, a); | |||
M(c, b, e); | |||
A(e, a, c); | |||
Z(a, a, c); | |||
S(b, a); | |||
Z(c, d, f); | |||
M(a, c, _121665); | |||
A(a, a, d); | |||
M(c, c, a); | |||
M(a, d, f); | |||
M(d, b, x); | |||
S(b, e); | |||
sel25519(a, b, r); | |||
sel25519(c, d, r); | |||
} | |||
FOR(i, 16) { | |||
x[i + 16] = a[i]; | |||
x[i + 32] = c[i]; | |||
x[i + 48] = b[i]; | |||
x[i + 64] = d[i]; | |||
} | |||
inv25519(x + 32, x + 32); | |||
M(x + 16, x + 16, x + 32); | |||
pack25519(q, x + 16); | |||
return 0; | |||
} | |||
int crypto_scalarmult_base(u8 *q, const u8 *n) { | |||
return crypto_scalarmult(q, n, _9); | |||
} | |||
int crypto_box_keypair(u8 *y, u8 *x) { | |||
randombytes(x, 32); | |||
return crypto_scalarmult_base(y, x); | |||
} |
@@ -0,0 +1,10 @@ | |||
#ifndef __CURVE25519_H__ | |||
#define __CURVE25519_H__ | |||
typedef unsigned char u8; | |||
typedef unsigned long long u64; | |||
int crypto_scalarmult(u8 *q, const u8 *n, const u8 *p); | |||
int crypto_box_keypair(u8 *y, u8 *x); | |||
#endif /* __CURVE25519_H__ */ |
@@ -0,0 +1,222 @@ | |||
/** | |||
* @cond internal | |||
* @file strobe.c | |||
* @copyright | |||
* Copyright (c) 2015-2016 Cryptography Research, Inc. \n | |||
* Released under the MIT License. See LICENSE.txt for license information. | |||
* @author Mike Hamburg | |||
* @brief Strobe protocol code. | |||
*/ | |||
#include <assert.h> | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include <limits.h> /* for INT_MAX */ | |||
#include <stdbool.h> | |||
#include <stdio.h> // to delete | |||
#include "tweetstrobe.h" | |||
/* Sets the security level at 128 bits (but this holds even | |||
* when the attacker has lots of data). | |||
*/ | |||
#define CAPACITY_BITS (2 * STROBE_INTEROP_SECURITY_BITS) | |||
/* Internal rate is 2 bytes less than sponge's "rate" */ | |||
#define PAD_BYTES 2 | |||
#define RATE_INNER ((25 * sizeof(kword_t) - CAPACITY_BITS / 8)) | |||
#define RATE (RATE_INNER - PAD_BYTES) | |||
/* Pull in a Keccak-F implementation. Use the target-specific | |||
* asm one if available. | |||
*/ | |||
#include "keccak_f.c.inc" | |||
static void _run_f(strobe_s *strobe, unsigned int pos) { | |||
strobe->state.b[pos] ^= strobe->pos_begin; | |||
strobe->pos_begin = 0; | |||
strobe->state.b[pos + 1] ^= 0x04; | |||
strobe->state.b[RATE + 1] ^= 0x80; | |||
keccak_f(&strobe->state); | |||
} | |||
static inline void _begin_op(strobe_s *strobe, uint8_t flags) { | |||
unsigned int pos = strobe->position; | |||
// Adjust the direction based on transport | |||
if (flags & FLAG_T) { | |||
if (strobe->initiator == 2) { // None | |||
// Set who is initiator and who is responder | |||
strobe->initiator = flags & FLAG_I; | |||
} | |||
flags ^= strobe->initiator; | |||
} | |||
// Mark the state | |||
strobe->state.b[pos] ^= strobe->pos_begin; | |||
pos++; | |||
strobe->pos_begin = pos; | |||
if (pos >= RATE) { | |||
_run_f(strobe, pos); | |||
pos = 0; | |||
} | |||
// Absorb the rest of the mode marker | |||
strobe->state.b[pos] ^= flags; | |||
pos++; | |||
if (pos >= RATE || (flags & FLAG_C) != 0) { | |||
_run_f(strobe, pos); | |||
pos = 0; | |||
} | |||
// Save the state position | |||
strobe->position = pos; | |||
} | |||
/* The core duplex mode */ | |||
static bool _strobe_duplex(strobe_s *strobe, uint8_t *buffer, size_t buffer_len, | |||
bool cbefore, bool cafter, bool recv_MAC) { | |||
// get our position in state | |||
unsigned int pos = strobe->position; | |||
// for recv_MAC | |||
uint8_t MAC_verif = 0; | |||
// consume the buffer | |||
size_t left = buffer_len; | |||
while (left > 0) { | |||
// duplex magic | |||
if (cbefore) { | |||
*buffer ^= strobe->state.b[pos]; | |||
} | |||
strobe->state.b[pos] ^= *buffer; | |||
if (cafter) { | |||
*buffer = strobe->state.b[pos]; | |||
} | |||
// recv_MAC | |||
if (recv_MAC) { | |||
MAC_verif |= *buffer; | |||
} | |||
// advance | |||
pos++; | |||
buffer++; | |||
left--; | |||
// runF | |||
if (pos >= RATE) { | |||
_run_f(strobe, pos); | |||
pos = 0; | |||
} | |||
} | |||
// save state position | |||
strobe->position = pos; | |||
// recv_MAC | |||
if (recv_MAC) { | |||
if (MAC_verif == 0) { | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
// | |||
return true; | |||
} | |||
// strobe_operate | |||
// Note: if you're using PRF, RATCHET or send_MAC. Your buffer needs to be | |||
// initialized with 0s. | |||
bool strobe_operate(strobe_s *strobe, uint8_t flags, uint8_t *buffer, | |||
size_t buffer_len, bool more) { | |||
assert(strobe->position < RATE); | |||
// set buffer to 0 if RATCHET, send_MAC or PRF | |||
// RATCHET = C, send_MAC = C | T, PRF = I | A | C | |||
if ((flags & FLAG_C) == FLAG_C) { | |||
if (flags == (FLAG_I | FLAG_A | FLAG_C) || flags == (FLAG_C) || | |||
flags == (FLAG_C | FLAG_T)) { | |||
for (size_t i = 0; i < buffer_len; i++) { | |||
buffer[i] = 0; | |||
} | |||
} | |||
} | |||
if (more) { | |||
assert(flags == strobe->flags); | |||
} else { | |||
_begin_op(strobe, flags); | |||
strobe->flags = flags; | |||
} | |||
bool cafter = (flags & (FLAG_C | FLAG_I | FLAG_T)) == (FLAG_C | FLAG_T); | |||
bool cbefore = (flags & FLAG_C) && (!cafter); | |||
bool recv_MAC = (flags & (0xF | FLAG_I)) == (TYPE_MAC | FLAG_I); | |||
return _strobe_duplex(strobe, buffer, buffer_len, cbefore, cafter, recv_MAC); | |||
} | |||
void strobe_init(strobe_s *strobe, const char *protocol_name, | |||
size_t protocol_name_len) { | |||
const uint8_t proto[18] = { | |||
1, RATE + PAD_BYTES, 1, 0, /* Empty NIST perso string */ | |||
1, 12 * 8, /* 12 = strlen("STROBEvX.Y.Z") */ | |||
'S', 'T', 'R', 'O', 'B', 'E', 'v', '0' + STROBE_INTEROP_V_MAJOR, '.', | |||
'0' + STROBE_INTEROP_V_MINOR, '.', '0' + STROBE_INTEROP_V_PATCH, | |||
/* Rest is 0s, which is already there because we memset it */ | |||
}; | |||
memset(strobe, 0, sizeof(*strobe)); | |||
memcpy(strobe, proto, sizeof(proto)); | |||
keccak_f(&strobe->state); | |||
strobe->initiator = 2; // None | |||
strobe_operate(strobe, FLAG_A | FLAG_M, (uint8_t *)protocol_name, | |||
protocol_name_len, false); | |||
strobe->initialized = 111; | |||
} | |||
bool strobe_isInitialized(strobe_s *strobe) { | |||
if (strobe->initialized == 111) { | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
/** Destroy a STROBE object by writing zeros over it. */ | |||
inline void strobe_destroy(strobe_s *strobe) { | |||
volatile uint8_t *p = strobe->state.b; | |||
int size_to_remove = 25 * sizeof(kword_t) / sizeof(uint8_t); | |||
while (size_to_remove--) { | |||
*p++ = 0; | |||
} | |||
strobe->position = 0; | |||
strobe->pos_begin = 0; | |||
strobe->flags = 0; | |||
strobe->initiator = 0; | |||
strobe->initialized = 0; | |||
} | |||
/** clone a STROBE object */ | |||
/* I think this is unecessary, we can just do dst = src; we don't have pointers | |||
inline void strobe_clone(const strobe_s *src, strobe_s *dst) { | |||
assert(src != NULL && dst != NULL); | |||
memcpy(dst->state.b, src->state.b, 25 * sizeof(kword_t) / sizeof(uint8_t)); | |||
dst->position = src->position; | |||
dst->pos_begin = src->pos_begin; | |||
dst->flags = src->flags; | |||
dst->initiator = src->initiator; | |||
dst->initialized = src->initialized; | |||
} | |||
*/ | |||
void strobe_print(const strobe_s *strobe) { | |||
for (int i = 0; i < sizeof(strobe->state); i++) { | |||
printf("%02x", strobe->state.b[i]); | |||
} | |||
printf("\n"); | |||
} |
@@ -0,0 +1,140 @@ | |||
/* | |||
* This is a re-write of strobe from David Wong | |||
* no idea how the MIT license or licenses work so... | |||
* here is teh original copyright | |||
*/ | |||
/** | |||
* @file strobe.h | |||
* @copyright | |||
* Copyright (c) 2015-2016 Cryptography Research, Inc. \n | |||
* Released under the MIT License. See LICENSE.txt for license information. | |||
* @author Mike Hamburg | |||
* @brief Strobe lite protocol instances. | |||
*/ | |||
#ifndef __STROBE_H__ | |||
#define __STROBE_H__ | |||
// remember: | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <assert.h> | |||
#include <string.h> | |||
#include <sys/types.h> | |||
#include <stdbool.h> | |||
/****************************************************************/ | |||
/* STROBE */ | |||
/****************************************************************/ | |||
#ifndef STROBE_INTEROP_V_MAJOR | |||
/** INTEROP: STROBE major version number. */ | |||
#define STROBE_INTEROP_V_MAJOR 1 | |||
#endif | |||
#ifndef STROBE_INTEROP_V_MINOR | |||
/** INTEROP: STROBE minor version number. */ | |||
#define STROBE_INTEROP_V_MINOR 0 | |||
#endif | |||
#ifndef STROBE_INTEROP_V_PATCH | |||
/** INTEROP: STROBE patch version number. */ | |||
#define STROBE_INTEROP_V_PATCH 2 | |||
#endif | |||
/****************************************************************/ | |||
/* KECCAK */ | |||
/****************************************************************/ | |||
#ifndef STROBE_INTEROP_SECURITY_BITS | |||
/** | |||
* INTEROP: STROBE nominal security strength, in bits. | |||
* | |||
* The capacity of the sponge will be 2*STROBE_INTEROP_SECURITY_BITS | |||
* long. Therefore certain security properties will scale better | |||
* than the 128-bit security level implies. In particular, if you | |||
* use 256-bit keys at a 128-bit security level, the 128-bit security | |||
* holds even if the attacker acquires enormous amounts of data. | |||
*/ | |||
#define STROBE_INTEROP_SECURITY_BITS 128 | |||
#endif | |||
#ifndef KECCAK_INTEROP_F_BITS | |||
/** INTEROP: STROBE bit size. Default is Keccak-F800. */ | |||
//#define KECCAK_INTEROP_F_BITS 800 | |||
#define KECCAK_INTEROP_F_BITS 1600 | |||
#endif | |||
#ifndef KECCAK_OPT_FOR_SIZE | |||
/** Global: optimize STROBE code for size at the expense of speed. */ | |||
#define KECCAK_OPT_FOR_SIZE 0 | |||
#endif | |||
#ifndef KECCAK_OPT_FOR_SPEED | |||
/** Global: optimize STROBE code for speed at the expense of size. */ | |||
#define KECCAK_OPT_FOR_SPEED 0 | |||
#endif | |||
// Size of the Keccak permutation | |||
#if KECCAK_INTEROP_F_BITS == 1600 | |||
#define kword_t uint64_t | |||
#elif KECCAK_INTEROP_F_BITS == 800 | |||
#define kword_t uint32_t | |||
#elif KECCAK_INTEROP_F_BITS == 400 | |||
#define kword_t uint16_t | |||
#else | |||
#error "Strobe supports only Keccak-F{400,800,1600}" | |||
#endif | |||
/** Keccak's domain: 25 words of size b/25, or b/8 bytes. */ | |||
typedef union { | |||
kword_t w[25]; | |||
uint8_t b[25 * sizeof(kword_t) / sizeof(uint8_t)]; | |||
} kdomain_s; | |||
/** The main strobe state object. */ | |||
typedef struct strobe_s_ { | |||
kdomain_s state; | |||
uint8_t position; | |||
uint8_t pos_begin; | |||
uint8_t flags; | |||
uint8_t initiator; | |||
uint8_t initialized; // strobe is initialized if this value is set to 111. | |||
// This is because we cannot assume that a boolean would | |||
// be set to false initially (C stuff). A uint8_t is a | |||
// short value but here we do not care about security | |||
// much, rather catching bugs early in a development | |||
// environement. | |||
} strobe_s; | |||
/* Initialize a Strobe object with a protocol name */ | |||
void strobe_init(strobe_s *strobe, const char *protocol_name, size_t desclen); | |||
/* Operate on the Strobe object */ | |||
bool strobe_operate(strobe_s *strobe, uint8_t control_flags, uint8_t *buffer, | |||
size_t buffer_len, bool more); | |||
/* Flags as defined in the paper */ | |||
#define FLAG_I (1 << 0) /**< Inbound */ | |||
#define FLAG_A (1 << 1) /**< Has application-side data (eg, not a MAC) */ | |||
#define FLAG_C (1 << 2) /**< Uses encryption or rekeying. */ | |||
#define FLAG_T (1 << 3) /**< Send or receive data via transport */ | |||
#define FLAG_M (1 << 4) /**< Operation carries metadata. */ | |||
/* Operation types as defined in the paper */ | |||
#define TYPE_AD FLAG_A /**< Context data, not sent to trensport */ | |||
#define TYPE_KEY \ | |||
(FLAG_A | FLAG_C) /**< Symmetric key, not sent to transport \ | |||
*/ | |||
#define TYPE_CLR (FLAG_T | FLAG_A) /**< Data to be sent in the clear */ | |||
#define TYPE_ENC (FLAG_T | FLAG_A | FLAG_C) /**< Data sent encrypted */ | |||
#define TYPE_MAC (FLAG_T | FLAG_C) /**< Message authentication code */ | |||
#define TYPE_RATCHET FLAG_C /**< Erase data to prevent rollback */ | |||
#define TYPE_PRF (FLAG_I | FLAG_A | FLAG_C) /**< Return pseudorandom hash */ | |||
bool strobe_isInitialized(strobe_s *strobe); | |||
void strobe_destroy(strobe_s *strobe); | |||
void strobe_print(const strobe_s *strobe); | |||
#endif /* __STROBE_H__ */ |
@@ -0,0 +1,395 @@ | |||
#include "disco_asymmetric.h" | |||
#include "disco_symmetric.h" | |||
#include <stdio.h> | |||
void test_N() { | |||
// generate server keypair | |||
keyPair server_keypair; | |||
disco_generateKeyPair(&server_keypair); | |||
// initialize client | |||
handshakeState hs_client; | |||
disco_Initialize(&hs_client, HANDSHAKE_N, true, NULL, 0, NULL, NULL, | |||
&server_keypair, NULL); | |||
// initialize server | |||
handshakeState hs_server; | |||
disco_Initialize(&hs_server, HANDSHAKE_N, false, NULL, 0, &server_keypair, | |||
NULL, NULL, NULL); | |||
// write the first handshake message → e, es | |||
uint8_t out[500]; | |||
strobe_s c_write; | |||
strobe_s c_read; | |||
size_t out_len; | |||
int ret = | |||
disco_WriteMessage(&hs_client, NULL, 0, out, &out_len, &c_write, &c_read); | |||
if (ret < 0) { | |||
printf("can't write handshake message\n"); | |||
abort(); | |||
} | |||
// handshake should be done for the client | |||
assert(strobe_isInitialized(&c_write) && strobe_isInitialized(&c_read)); | |||
// debug | |||
printf("sent %zu bytes\n", out_len); | |||
// process the first handshake message → e, es | |||
uint8_t in[500]; | |||
strobe_s s_read; | |||
strobe_s s_write; | |||
size_t in_len; | |||
ret = disco_ReadMessage(&hs_server, out, out_len, in, &in_len, &s_read, | |||
&s_write); | |||
if (ret < 0) { | |||
printf("can't read handshake message\n"); | |||
abort(); | |||
} | |||
assert(strobe_isInitialized(&s_write) && strobe_isInitialized(&s_read)); | |||
// debug | |||
printf("received %zu bytes:%s\n", in_len, in); | |||
// trying to send a post-handshake message | |||
uint8_t pt_in_place[] = | |||
"Us little brogammers we like to brogamme on our motorbikes yeah "; | |||
uint8_t *ct_and_mac = (uint8_t *)malloc(sizeof(pt_in_place) + 16); | |||
memcpy(ct_and_mac, pt_in_place, sizeof(pt_in_place)); | |||
disco_EncryptInPlace(&c_write, ct_and_mac, sizeof(pt_in_place), | |||
sizeof(pt_in_place) + 16); | |||
// decrypt | |||
if (disco_DecryptInPlace(&s_read, ct_and_mac, sizeof(pt_in_place) + 16) == | |||
false) { | |||
printf("cannot decrypt in place"); | |||
abort(); | |||
} | |||
printf("final decrypt in place: %s\n", ct_and_mac); | |||
free(ct_and_mac); | |||
} | |||
void test_NX() { | |||
// generate server keypair | |||
keyPair server_keypair; | |||
disco_generateKeyPair(&server_keypair); | |||
// initialize client | |||
handshakeState hs_client; | |||
disco_Initialize(&hs_client, HANDSHAKE_NX, true, NULL, 0, NULL, NULL, NULL, | |||
NULL); | |||
// initialize server | |||
handshakeState hs_server; | |||
disco_Initialize(&hs_server, HANDSHAKE_NX, false, NULL, 0, &server_keypair, | |||
NULL, NULL, NULL); | |||
// write the first handshake message → e, es | |||
uint8_t out[500]; | |||
uint8_t text[] = "hey!"; | |||
size_t out_len; | |||
int ret = disco_WriteMessage(&hs_client, text, 5, out, &out_len, NULL, NULL); | |||
if (ret < 0) { | |||
printf("can't write handshake message\n"); | |||
abort(); | |||
} | |||
// debug | |||
printf("sent %zu bytes\n", out_len); | |||
for (int i = 0; i < out_len; i++) { | |||
printf("%02x", out[i]); | |||
} | |||
printf("\n"); | |||
// process the first handshake message → e, es | |||
uint8_t in[500]; | |||
size_t in_len; | |||
ret = disco_ReadMessage(&hs_server, out, out_len, in, &in_len, NULL, NULL); | |||
if (ret < 0) { | |||
printf("can't read handshake message\n"); | |||
abort(); | |||
} | |||
// debug | |||
printf("received %zu bytes:%s\n", in_len, in); | |||
printf("hs_client and hs_server state after first trip\n"); | |||
strobe_print(&(hs_client.symmetric_state.strobe)); | |||
strobe_print(&(hs_server.symmetric_state.strobe)); | |||
// create second handshake message | |||
printf("\n\npreparing second handshake message ->\n\n"); | |||
strobe_s s_write; | |||
strobe_s s_read; | |||
ret = disco_WriteMessage(&hs_server, (uint8_t *)"hello hello", 12, out, | |||
&out_len, &s_read, &s_write); | |||
if (ret < 0) { | |||
printf("can't write handshake message\n"); | |||
abort(); | |||
} | |||
// should be initialized | |||
assert(s_write.initialized && s_read.initialized); | |||
// debug | |||
printf("sent %zu bytes:\n", out_len); | |||
for (int i = 0; i < out_len; i++) { | |||
printf("%02x", out[i]); | |||
} | |||
printf("\n"); | |||
// process second handshake message | |||
printf("\n\nparsing second handshake message <-\n\n"); | |||
strobe_s c_write; | |||
strobe_s c_read; | |||
ret = disco_ReadMessage(&hs_client, out, out_len, in, &in_len, &c_write, | |||
&c_read); | |||
if (ret < 0) { | |||
printf("can't read handshake message\n"); | |||
abort(); | |||
} | |||
// debug | |||
printf("received %zu bytes:%s\n", in_len, in); | |||
// should be initialized | |||
assert(c_write.initialized && c_read.initialized); | |||
// trying to send a post-handshake message | |||
uint8_t pt_in_place[] = | |||
"Us little brogammers we like to brogamme on our motorbikes yeah "; | |||
uint8_t *ct_and_mac = (uint8_t *)malloc(sizeof(pt_in_place) + 16); | |||
memcpy(ct_and_mac, pt_in_place, sizeof(pt_in_place)); | |||
disco_EncryptInPlace(&c_write, ct_and_mac, sizeof(pt_in_place), | |||
sizeof(pt_in_place) + 16); | |||
// decrypt | |||
if (disco_DecryptInPlace(&s_read, ct_and_mac, sizeof(pt_in_place) + 16) == | |||
false) { | |||
printf("cannot decrypt in place"); | |||
abort(); | |||
} | |||
printf("final decrypt in place: %s\n", ct_and_mac); | |||
// another one | |||
memcpy(ct_and_mac, pt_in_place, sizeof(pt_in_place)); | |||
disco_EncryptInPlace(&c_write, ct_and_mac, sizeof(pt_in_place), | |||
sizeof(pt_in_place) + 16); | |||
if (disco_DecryptInPlace(&s_read, ct_and_mac, sizeof(pt_in_place) + 16) == | |||
false) { | |||
printf("cannot decrypt in place"); | |||
abort(); | |||
} | |||
printf("final decrypt in place: %s\n", ct_and_mac); | |||
free(ct_and_mac); | |||
} | |||
void test_IK() { | |||
// generate server keypair | |||
keyPair client_keypair; | |||
keyPair server_keypair; | |||
disco_generateKeyPair(&client_keypair); | |||
disco_generateKeyPair(&server_keypair); | |||
// initialize client | |||
handshakeState hs_client; | |||
disco_Initialize(&hs_client, HANDSHAKE_IK, true, NULL, 0, &client_keypair, | |||
NULL, &server_keypair, NULL); | |||
// initialize server | |||
handshakeState hs_server; | |||
disco_Initialize(&hs_server, HANDSHAKE_IK, false, NULL, 0, &server_keypair, | |||
NULL, NULL, NULL); | |||
// write the first handshake message → e, es, s, ss | |||
uint8_t out[500]; | |||
uint8_t text[] = "hey!"; | |||
size_t out_len; | |||
int ret = disco_WriteMessage(&hs_client, text, 5, out, &out_len, NULL, NULL); | |||
if (ret < 0) { | |||
printf("can't write handshake message\n"); | |||
abort(); | |||
} | |||
// debug | |||
printf("sent %zu bytes\n", out_len); | |||
for (int i = 0; i < out_len; i++) { | |||
printf("%02x", out[i]); | |||
} | |||
printf("\n"); | |||
// process the first handshake message | |||
uint8_t in[500]; | |||
size_t in_len; | |||
ret = disco_ReadMessage(&hs_server, out, out_len, in, &in_len, NULL, NULL); | |||
if (ret < 0) { | |||
printf("can't read handshake message\n"); | |||
abort(); | |||
} | |||
// debug | |||
printf("received %zu bytes:%s\n", in_len, in); | |||
printf("hs_client and hs_server state after first trip\n"); | |||
strobe_print(&(hs_client.symmetric_state.strobe)); | |||
strobe_print(&(hs_server.symmetric_state.strobe)); | |||
// create second handshake message <- e, ee, se | |||
printf("\n\npreparing second handshake message ->\n\n"); | |||
strobe_s s_write; | |||
strobe_s s_read; | |||
ret = disco_WriteMessage(&hs_server, (uint8_t *)"hello hello", 12, out, | |||
&out_len, &s_read, &s_write); | |||
if (ret < 0) { | |||
printf("can't write handshake message\n"); | |||
abort(); | |||
} | |||
// should be initialized | |||
assert(s_write.initialized && s_read.initialized); | |||
// debug | |||
printf("sent %zu bytes:\n", out_len); | |||
for (int i = 0; i < out_len; i++) { | |||
printf("%02x", out[i]); | |||
} | |||
printf("\n"); | |||
// process second handshake message | |||
printf("\n\nparsing second handshake message <-\n\n"); | |||
strobe_s c_write; | |||
strobe_s c_read; | |||
ret = disco_ReadMessage(&hs_client, out, out_len, in, &in_len, &c_write, | |||
&c_read); | |||
if (ret < 0) { | |||
printf("can't read handshake message\n"); | |||
abort(); | |||
} | |||
// debug | |||
printf("received %zu bytes:%s\n", in_len, in); | |||
// should be initialized | |||
assert(c_write.initialized && c_read.initialized); | |||
// trying to send a post-handshake message | |||
uint8_t pt_in_place[] = | |||
"Us little brogammers we like to brogamme on our motorbikes yeah "; | |||
uint8_t *ct_and_mac = (uint8_t *)malloc(sizeof(pt_in_place) + 16); | |||
memcpy(ct_and_mac, pt_in_place, sizeof(pt_in_place)); | |||
disco_EncryptInPlace(&c_write, ct_and_mac, sizeof(pt_in_place), | |||
sizeof(pt_in_place) + 16); | |||
// decrypt | |||
if (disco_DecryptInPlace(&s_read, ct_and_mac, sizeof(pt_in_place) + 16) == | |||
false) { | |||
printf("cannot decrypt in place"); | |||
abort(); | |||
} | |||
printf("final decrypt in place: %s\n", ct_and_mac); | |||
// other direction | |||
uint8_t pt_in_place2[] = "this is hopefully the last test for today"; | |||
uint8_t *ct_and_mac2 = (uint8_t *)malloc(sizeof(pt_in_place2) + 16); | |||
memcpy(ct_and_mac2, pt_in_place2, sizeof(pt_in_place2)); | |||
disco_EncryptInPlace(&s_write, ct_and_mac2, sizeof(pt_in_place2), | |||
sizeof(pt_in_place2) + 16); | |||
// decrypt | |||
if (disco_DecryptInPlace(&c_read, ct_and_mac2, sizeof(pt_in_place2) + 16) == | |||
false) { | |||
printf("cannot decrypt in place"); | |||
abort(); | |||
} | |||
printf("final decrypt in place: %s\n", ct_and_mac2); | |||
free(ct_and_mac); | |||
free(ct_and_mac2); | |||
} | |||
void test_HashInteropGolang() { | |||
uint8_t input[] = "hi, how are you?"; | |||
uint8_t out[32]; | |||
disco_Hash(input, 16, out, 32); | |||
uint8_t res[] = | |||
"\xed\xa8\x50\x6c\x1f\xb0\xbb\xcc\x3f\x62\x62\x6f\xef\x07\x4b\xbf\x2d\x09" | |||
"\xa8\xc7\xc6\x08\xf3\xfa\x14\x82\xc9\xa6\x25\xd0\x0f\x75"; | |||
for (int i = 0; i < 32; i++) { | |||
assert(out[i] == res[i]); | |||
} | |||
printf("\n"); | |||
} | |||
void test_Hash() { | |||
// hashing with simple disco_Hash function | |||
uint8_t input[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; | |||
uint8_t out[32]; | |||
disco_Hash(input, 10, out, 32); | |||
/* | |||
printf("result:\n"); | |||
for (int i = 0; i < 32; i++) { | |||
printf("%02x", out[i]); | |||
} | |||
printf("\n"); | |||
*/ | |||
// hashing with New-Write-Sum | |||
discoHashCtx ctx; | |||
disco_HashNew(&ctx); | |||
disco_HashWrite(&ctx, input, 10); | |||
uint8_t out2[32]; | |||
// compare | |||
disco_HashSum(&ctx, out2, 32); | |||
for (int i = 0; i < 32; i++) { | |||
assert(out[i] == out2[i]); | |||
} | |||
// hash again and compare | |||
disco_HashSum(&ctx, out2, 32); | |||
for (int i = 0; i < 32; i++) { | |||
assert(out[i] == out2[i]); | |||
} | |||
// this time hash in two times | |||
disco_HashResetCtx(&ctx); | |||
disco_HashNew(&ctx); | |||
disco_HashWrite(&ctx, input, 5); | |||
disco_HashWrite(&ctx, input + 5, 5); | |||
disco_HashSum(&ctx, out2, 32); | |||
for (int i = 0; i < 32; i++) { | |||
assert(out[i] == out2[i]); | |||
} | |||
// hash with Tuple | |||
disco_HashResetCtx(&ctx); | |||
disco_HashNew(&ctx); | |||
disco_HashWriteTuple(&ctx, input, 5); | |||
disco_HashWriteTuple(&ctx, input + 5, 5); | |||
disco_HashSum(&ctx, out2, 32); | |||
bool different = false; | |||
for (int i = 0; i < 32; i++) { | |||
if (out[i] != out2[i]) { | |||
different = true; | |||
break; | |||
} | |||
} | |||
assert(different); | |||
} | |||
int main() { | |||
// doing a loop coz I have a bug SOMETIMES | |||
for (int j = 0; j < 10; j++) { | |||
printf("iteration #%d\n", j); | |||
printf("\n\ntesting N\n\n"); | |||
test_N(); | |||
printf("\n\ntesting NX\n\n"); | |||
test_NX(); | |||
printf("\n\ntesting IK\n\n"); | |||
test_IK(); | |||
} | |||
test_Hash(); | |||
test_HashInteropGolang(); | |||
} |