disco: Adds disco sources
This commit is contained in:
parent
e6a210e0c4
commit
f3154fd505
12
disco/Makefile
Normal file
12
disco/Makefile
Normal file
@ -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
|
8
disco/src/devurandom.c
Normal file
8
disco/src/devurandom.c
Normal file
@ -0,0 +1,8 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int fd = -1;
|
||||
|
||||
void randombytes(unsigned char *x, unsigned long long xlen) {
|
||||
/* TODO */
|
||||
}
|
577
disco/src/disco_asymmetric.c
Normal file
577
disco/src/disco_asymmetric.c
Normal file
@ -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;
|
||||
}
|
114
disco/src/disco_asymmetric.h
Normal file
114
disco/src/disco_asymmetric.h
Normal file
@ -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__ */
|
160
disco/src/disco_symmetric.c
Normal file
160
disco/src/disco_symmetric.c
Normal file
@ -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);
|
||||
}
|
59
disco/src/disco_symmetric.h
Normal file
59
disco/src/disco_symmetric.h
Normal file
@ -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_
|
127
disco/src/keccak_f.c.inc
Normal file
127
disco/src/keccak_f.c.inc
Normal file
@ -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]);
|
||||
}
|
154
disco/src/tweetX25519.c
Normal file
154
disco/src/tweetX25519.c
Normal file
@ -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);
|
||||
}
|
10
disco/src/tweetX25519.h
Normal file
10
disco/src/tweetX25519.h
Normal file
@ -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__ */
|
222
disco/src/tweetstrobe.c
Normal file
222
disco/src/tweetstrobe.c
Normal file
@ -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");
|
||||
}
|
140
disco/src/tweetstrobe.h
Normal file
140
disco/src/tweetstrobe.h
Normal file
@ -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__ */
|
395
disco/tests/test_disco.c
Normal file
395
disco/tests/test_disco.c
Normal file
@ -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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user