disco: Adds disco sources

This commit is contained in:
Henry Case 2018-12-08 20:38:10 +00:00
parent e6a210e0c4
commit f3154fd505
12 changed files with 1978 additions and 0 deletions

12
disco/Makefile Normal file
View 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
View 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 */
}

View 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;
}

View 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
View 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(&copy, 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);
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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();
}