1
0
mirror of https://github.com/taigrr/arduinolibs synced 2025-01-18 04:33:12 -08:00

Test cases and bug fixes for Noise handshakes

This commit is contained in:
Rhys Weatherley 2018-06-17 14:48:35 +10:00
parent fa1400ea83
commit 8fd4a994dc
15 changed files with 1559 additions and 22 deletions

View File

@ -1,5 +1,5 @@
.PHONY: all clean check .PHONY: all clean check noise-check
TOPDIR = ../.. TOPDIR = ../..
SRCDIR = $(TOPDIR)/libraries/Crypto SRCDIR = $(TOPDIR)/libraries/Crypto
@ -29,10 +29,11 @@ CPPFLAGS = \
-I$(TOPDIR)/libraries/CryptoLW/src \ -I$(TOPDIR)/libraries/CryptoLW/src \
-I$(TOPDIR)/libraries/CryptoLegacy/src \ -I$(TOPDIR)/libraries/CryptoLegacy/src \
-I$(TOPDIR)/libraries/NewHope \ -I$(TOPDIR)/libraries/NewHope \
-I$(TOPDIR)/libraries/NoiseProtocol \ -I$(TOPDIR)/libraries/NoiseProtocol/src \
-DHOST_BUILD -DHOST_BUILD
CXXFLAGS = -g -Wall $(CPPFLAGS) CXXFLAGS = -g -Wall $(CPPFLAGS)
CCFLAGS = -g -Wall $(CPPFLAGS)
SOURCES = \ SOURCES = \
Acorn128.cpp \ Acorn128.cpp \
@ -149,12 +150,23 @@ SKETCHES = \
TestSpeck/TestSpeck.ino \ TestSpeck/TestSpeck.ino \
TestXTS/TestXTS.ino \ TestXTS/TestXTS.ino \
TSTPROG1 = noise/test-vector
TSTPROG1_SOURCES = \
noise/json-reader.cpp \
noise/test-vector.cpp
TSTPROG1_OBJECTS = $(patsubst %.cpp,%.o,$(TSTPROG1_SOURCES))
DEP_SOURCES = $(SOURCES) $(TSTPROG1_SOURCES)
OBJECTS = $(patsubst %.cpp,%.o,$(SOURCES)) OBJECTS = $(patsubst %.cpp,%.o,$(SOURCES))
DEPS = $(patsubst %.cpp,.depend/%.d,$(SOURCES)) DEPS = $(patsubst %.cpp,.depend/%.d,$(DEP_SOURCES))
SKETCH_OUTPUTS = $(patsubst %.ino,%.sketch,$(SKETCHES)) SKETCH_OUTPUTS = $(patsubst %.ino,%.sketch,$(SKETCHES))
all: $(LIBRARY) all: $(LIBRARY) $(TSTPROG1)
$(TSTPROG1): $(TSTPROG1_OBJECTS) $(LIBRARY)
$(CXX) $(CXXFLAGS) -o $@ $(TSTPROG1_OBJECTS) -L. -lCrypto
$(LIBRARY): $(OBJECTS) $(LIBRARY): $(OBJECTS)
$(RM) $(LIBRARY) $(RM) $(LIBRARY)
@ -163,14 +175,18 @@ $(LIBRARY): $(OBJECTS)
clean: clean:
$(RM) $(OBJECTS) $(LIBRARY) $(RM) $(OBJECTS) $(LIBRARY)
$(RM) $(SKETCH_OUTPUTS) $(RM) $(SKETCH_OUTPUTS)
$(RM) $(TSTPROG1) $(TSTPROG1_OBJECTS)
$(RM) -r .depend Test* $(RM) -r .depend Test*
check: all $(SKETCH_OUTPUTS) check: $(LIBRARY) $(SKETCH_OUTPUTS)
@for sketch in $(SKETCH_OUTPUTS); do \ @for sketch in $(SKETCH_OUTPUTS); do \
echo Running $$sketch; \ echo Running $$sketch; \
$$sketch | grep -i fail; \ $$sketch | grep -i fail; \
done; exit 0 done; exit 0
noise-check: $(TSTPROG1)
@$(TSTPROG1) noise/test-vectors.txt
%.o: %.cpp %.o: %.cpp
$(CXX) $(CXXFLAGS) -o $@ -c $< $(CXX) $(CXXFLAGS) -o $@ -c $<

1
host/Crypto/noise/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
test-vector

8
host/Crypto/noise/README Normal file
View File

@ -0,0 +1,8 @@
The test vectors in this directory come from the cacophony Noise
implementation:
https://github.com/centromere/cacophony
https://raw.githubusercontent.com/centromere/cacophony/master/vectors/cacophony.txt
Only those vectors that are relevant to the Arduino Noise library are included.

View File

@ -0,0 +1,253 @@
/*
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "json-reader.h"
#include <stdarg.h>
/* Note: This is not a complete JSON parser. It has enough support
to process the Noise test vector format only */
#define JSON_STR_MAX 8192
/**
* \brief Initializes a JSON reader object.
*
* \param reader The reader to initialize.
* \param stream The stream to read from.
*/
void json_init(JSONReader *reader, const char *filename, FILE *stream)
{
reader->stream = stream;
reader->token = JSON_TOKEN_START;
reader->str_value = 0;
reader->filename = filename;
reader->line_number = 1;
reader->saw_eof = 0;
reader->errors = 0;
}
/**
* \brief Frees a JSON reader object.
*
* \param reader The reader to free.
*/
void json_free(JSONReader *reader)
{
reader->stream = 0;
reader->token = JSON_TOKEN_END;
reader->filename = 0;
reader->line_number = 1;
reader->saw_eof = 1;
reader->errors = 0;
if (reader->str_value) {
free(reader->str_value);
reader->str_value = 0;
}
}
/**
* \brief Recognizes a named token.
*
* \param reader The reader.
* \param token The token code that we expect to recognize.
* \param name The name of the token. The first character is assumed
* to have already been recognized.
*/
static void json_named_token
(JSONReader *reader, JSONToken token, const char *name)
{
const char *n = name + 1;
int ch;
for (;;) {
ch = getc(reader->stream);
if (*n == '\0') {
if (ch == ',' || ch == ' ' || ch == '\t' ||
ch == '\r' || ch == '\n') {
ungetc(ch, reader->stream);
reader->token = token;
return;
} else if (ch == EOF) {
reader->token = token;
reader->saw_eof = 1;
return;
} else {
break;
}
}
if (ch == EOF || ch != *n)
break;
++n;
}
json_error(reader, "Could not recognize '%s' token", name);
reader->token = JSON_TOKEN_END;
}
/**
* \brief Reads the next token from the input stream.
*
* \param reader The reader.
*
* \return The token code.
*/
JSONToken json_next_token(JSONReader *reader)
{
int ch;
/* Bail out if we already reached the end of the stream */
if (reader->token == JSON_TOKEN_END)
return JSON_TOKEN_END;
if (reader->saw_eof) {
reader->token = JSON_TOKEN_END;
return JSON_TOKEN_END;
}
/* Free the previous token's string value */
if (reader->str_value) {
free(reader->str_value);
reader->str_value = 0;
}
/* Skip whitespace */
for (;;) {
ch = getc(reader->stream);
if (ch == EOF) {
reader->token = JSON_TOKEN_END;
reader->saw_eof = 1;
return reader->token;
} else if (ch == '\n') {
++(reader->line_number);
} else if (ch != ' ' && ch != '\t' && ch != '\r') {
break;
}
}
/* Parse the next token */
if (ch == '{') {
reader->token = JSON_TOKEN_LBRACE;
} else if (ch == '}') {
reader->token = JSON_TOKEN_RBRACE;
} else if (ch == '[') {
reader->token = JSON_TOKEN_LSQUARE;
} else if (ch == ']') {
reader->token = JSON_TOKEN_RSQUARE;
} else if (ch == ',') {
reader->token = JSON_TOKEN_COMMA;
} else if (ch == ':') {
reader->token = JSON_TOKEN_COLON;
} else if (ch == 't') {
json_named_token(reader, JSON_TOKEN_TRUE, "true");
} else if (ch == 'f') {
json_named_token(reader, JSON_TOKEN_FALSE, "false");
} else if (ch == 'n') {
json_named_token(reader, JSON_TOKEN_FALSE, "null");
} else if (ch == '"') {
/* Recognize very simple strings with no escaping */
char buffer[JSON_STR_MAX];
size_t posn = 0;
for (;;) {
ch = getc(reader->stream);
if (ch == '"') {
break;
} else if (ch == '\r' || ch == '\n' || ch == EOF) {
json_error(reader, "Unterminated string");
reader->token = JSON_TOKEN_END;
return reader->token;
} else if (ch == '\\') {
json_error(reader, "String escapes are not supported");
reader->token = JSON_TOKEN_END;
return reader->token;
} else {
if (posn >= (sizeof(buffer) - 1)) {
json_error(reader, "String is too long");
reader->token = JSON_TOKEN_END;
return reader->token;
}
buffer[posn++] = (char)ch;
}
}
buffer[posn] = '\0';
reader->str_value = (char *)malloc(posn + 1);
if (!(reader->str_value)) {
json_error(reader, "Out of memory");
reader->token = JSON_TOKEN_END;
return reader->token;
}
strcpy(reader->str_value, buffer);
reader->token = JSON_TOKEN_STRING;
} else {
/* Unknown character. Note: numbers are not yet supported. */
json_error(reader, "Unknown character 0x%02x", ch);
reader->token = JSON_TOKEN_END;
}
return reader->token;
}
/**
* \brief Matches the current token against a specific field name.
*
* \param reader The reader.
* \param name The name of the field.
*
* \return Returns 1 if the name matches, 0 if not.
*/
int json_is_name(JSONReader *reader, const char *name)
{
if (reader->token != JSON_TOKEN_STRING || !reader->str_value)
return 0;
return !strcmp(reader->str_value, name);
}
/**
* \brief Reports an error on the current line of the input.
*
* \param reader The reader.
* \param format The printf-style format to use.
*/
void json_error(JSONReader *reader, const char *format, ...)
{
va_list va;
va_start(va, format);
fprintf(stderr, "%s:%ld: ", reader->filename, reader->line_number);
vfprintf(stderr, format, va);
putc('\n', stderr);
va_end(va);
++(reader->errors);
}
/**
* \brief Reports an error on a specific line of the input.
*
* \param reader The reader.
* \param line The line number to report in the error message.
* \param format The printf-style format to use.
*/
void json_error_on_line(JSONReader *reader, long line, const char *format, ...)
{
va_list va;
va_start(va, format);
fprintf(stderr, "%s:%ld: ", reader->filename, line);
vfprintf(stderr, format, va);
putc('\n', stderr);
va_end(va);
++(reader->errors);
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef JSON_READER_H
#define JSON_READER_H
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/**
* \brief Token codes.
*/
typedef enum
{
JSON_TOKEN_START, /**< Still at the start, need to read first token */
JSON_TOKEN_STRING, /**< Quoted string */
JSON_TOKEN_NUMBER, /**< Numeric value */
JSON_TOKEN_NULL, /**< "null" */
JSON_TOKEN_TRUE, /**< "true" */
JSON_TOKEN_FALSE, /**< "false" */
JSON_TOKEN_LBRACE, /**< "{" */
JSON_TOKEN_RBRACE, /**< "}" */
JSON_TOKEN_LSQUARE, /**< "[" */
JSON_TOKEN_RSQUARE, /**< "]" */
JSON_TOKEN_COMMA, /**< "," */
JSON_TOKEN_COLON, /**< ":" */
JSON_TOKEN_END /**< End of stream or error */
} JSONToken;
/**
* \brief State information for JSON readers.
*/
typedef struct
{
FILE *stream; /**< Input stream to read from */
JSONToken token; /**< Current token type */
char *str_value; /**< String value for the current token */
const char *filename; /**< Name of the file being read from */
long line_number; /**< Current line number in the file */
int saw_eof; /**< Non-zero if already seen EOF */
int errors; /**< Non-zero if errors seen during parsing */
} JSONReader;
void json_init(JSONReader *reader, const char *filename, FILE *stream);
void json_free(JSONReader *reader);
JSONToken json_next_token(JSONReader *reader);
int json_is_name(JSONReader *reader, const char *name);
void json_error(JSONReader *reader, const char *format, ...);
void json_error_on_line(JSONReader *reader, long line, const char *format, ...);
#endif

View File

@ -0,0 +1,815 @@
/*
* Copyright (C) 2016,2018 Southern Storm Software, Pty Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <NoiseProtocol.h>
#include "json-reader.h"
#include <setjmp.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_MESSAGES 32
#define MAX_MESSAGE_SIZE 4096
#define MAX_PSKS 8
/**
* \brief Information about a single test vector.
*/
typedef struct
{
long line_number; /**< Line number for the "name" */
char *name; /**< Full name of the test case */
char *protocol_name; /**< Full name of the protocol */
uint8_t *init_static; /**< Initiator's static private key */
size_t init_static_len; /**< Length of init_static in bytes */
uint8_t *init_public_static; /**< Initiator's public key known to responder */
size_t init_public_static_len; /**< Length of init_public_static in bytes */
uint8_t *resp_static; /**< Responder's static private key */
size_t resp_static_len; /**< Length of resp_static in bytes */
uint8_t *resp_public_static; /**< Responder's public key known to initiator */
size_t resp_public_static_len; /**< Length of resp_public_static in bytes */
uint8_t *init_ephemeral; /**< Initiator's ephemeral key */
size_t init_ephemeral_len; /**< Length of init_ephemeral in bytes */
uint8_t *resp_ephemeral; /**< Responder's ephemeral key */
size_t resp_ephemeral_len; /**< Length of resp_ephemeral in bytes */
uint8_t *init_hybrid; /**< Initiator's hybrid ephemeral key */
size_t init_hybrid_len; /**< Length of init_hybrid in bytes */
uint8_t *resp_hybrid; /**< Responder's hybrid ephemeral key */
size_t resp_hybrid_len; /**< Length of resp_hybrid in bytes */
uint8_t *init_prologue; /**< Initiator's prologue data */
size_t init_prologue_len; /**< Length of init_prologue in bytes */
uint8_t *resp_prologue; /**< Responder's prologue data */
size_t resp_prologue_len; /**< Length of resp_prologue in bytes */
uint8_t init_psks[MAX_PSKS][32];/**< Initiator pre-shared keys */
size_t num_init_psks; /**< Number of initiator PSK's */
uint8_t resp_psks[MAX_PSKS][32];/**< Responder pre-shared keys */
size_t num_resp_psks; /**< Number of responder PSK's */
uint8_t *handshake_hash; /**< Hash at the end of the handshake */
size_t handshake_hash_len; /**< Length of handshake_hash in bytes */
int fail; /**< Failure expected on last message */
int fallback; /**< Handshake involves IK to XXfallback */
char *fallback_pattern; /**< Name of the pattern to fall back to */
int is_one_way; /**< True if the base pattern is one-way */
struct {
uint8_t *payload; /**< Payload for this message */
size_t payload_len; /**< Length of payload in bytes */
uint8_t *ciphertext; /**< Ciphertext for this message */
size_t ciphertext_len; /**< Length of ciphertext in bytes */
} messages[MAX_MESSAGES]; /**< All test messages */
size_t num_messages; /**< Number of test messages */
} TestVector;
/**
* \brief Frees the memory for a test vector.
*
* \param vec The test vector.
*/
static void test_vector_free(TestVector *vec)
{
size_t index;
#define free_field(name) do { if (vec->name) free(vec->name); } while (0)
free_field(name);
free_field(protocol_name);
free_field(init_static);
free_field(init_public_static);
free_field(resp_static);
free_field(resp_public_static);
free_field(init_ephemeral);
free_field(resp_ephemeral);
free_field(init_hybrid);
free_field(resp_hybrid);
free_field(init_prologue);
free_field(resp_prologue);
free_field(handshake_hash);
free_field(fallback_pattern);
for (index = 0; index < vec->num_messages; ++index) {
if (vec->messages[index].payload)
free(vec->messages[index].payload);
if (vec->messages[index].ciphertext)
free(vec->messages[index].ciphertext);
}
memset(vec, 0, sizeof(TestVector));
}
static jmp_buf test_jump_back;
/**
* \brief Immediate fail of the test.
*
* \param message The failure message to print.
*/
#define _fail(message) \
do { \
printf("%s, failed at " __FILE__ ":%d\n", (message), __LINE__); \
longjmp(test_jump_back, 1); \
} while (0)
#define fail(message) _fail((message))
/**
* \brief Skips the current test.
*/
#define skip() longjmp(test_jump_back, 2)
/**
* \brief Verifies that a condition is true, failing the test if not.
*
* \param condition The boolean condition to test.
*/
#define _verify(condition) \
do { \
if (!(condition)) { \
printf(#condition " failed at " __FILE__ ":%d\n", __LINE__); \
longjmp(test_jump_back, 1); \
} \
} while (0)
#define verify(condition) _verify((condition))
/**
* \brief Compares two integer values for equality, failing the test if not.
*
* \param actual The actual value that was computed by the code under test.
* \param expected The value that is expected.
*/
#define compare(actual, expected) \
do { \
long long _actual = (long long)(actual); \
long long _expected = (long long)(expected); \
if (_actual != _expected) { \
printf(#actual " != " #expected " at " __FILE__ ":%d\n", __LINE__); \
printf(" actual : %lld (0x%llx)\n", _actual, _actual); \
printf(" expected: %lld (0x%llx)\n", _expected, _expected); \
longjmp(test_jump_back, 1); \
} \
} while (0)
static void dump_block(uint8_t *block, size_t len)
{
size_t index;
if (len > 16)
printf("\n ");
for (index = 0; index < len; ++index) {
printf(" %02x", block[index]);
if ((index % 16) == 15 && len > 16)
printf("\n ");
}
printf("\n");
}
#define compare_blocks(name, actual, actual_len, expected, expected_len) \
do { \
if ((actual_len) != (expected_len) || \
memcmp((actual), (expected), (actual_len)) != 0) { \
printf("%s wrong at " __FILE__ ":%d\n", (name), __LINE__); \
printf(" actual :"); \
dump_block((actual), (actual_len)); \
printf(" expected:"); \
dump_block((expected), (expected_len)); \
longjmp(test_jump_back, 1); \
} \
} while (0)
/**
* \brief Creates a handshake object for a specific protocol.
*
* \pararm protocol The name of the protocol.
*/
static NoiseHandshakeState *create_handshake(const char *protocol)
{
if (!strcmp(protocol, "Noise_IK_25519_AESGCM_SHA256"))
return new NoiseHandshakeState_IK_25519_AESGCM_SHA256();
if (!strcmp(protocol, "Noise_IK_25519_ChaChaPoly_BLAKE2s"))
return new NoiseHandshakeState_IK_25519_ChaChaPoly_BLAKE2s();
if (!strcmp(protocol, "Noise_IK_25519_ChaChaPoly_SHA256"))
return new NoiseHandshakeState_IK_25519_ChaChaPoly_SHA256();
if (!strcmp(protocol, "Noise_NNpsk0_25519_AESGCM_SHA256"))
return new NoiseHandshakeState_NNpsk0_25519_AESGCM_SHA256();
if (!strcmp(protocol, "Noise_NNpsk0_25519_ChaChaPoly_BLAKE2s"))
return new NoiseHandshakeState_NNpsk0_25519_ChaChaPoly_BLAKE2s();
if (!strcmp(protocol, "Noise_NNpsk0_25519_ChaChaPoly_SHA256"))
return new NoiseHandshakeState_NNpsk0_25519_ChaChaPoly_SHA256();
if (!strcmp(protocol, "Noise_XX_25519_AESGCM_SHA256"))
return new NoiseHandshakeState_XX_25519_AESGCM_SHA256();
if (!strcmp(protocol, "Noise_XX_25519_ChaChaPoly_BLAKE2s"))
return new NoiseHandshakeState_XX_25519_ChaChaPoly_BLAKE2s();
if (!strcmp(protocol, "Noise_XX_25519_ChaChaPoly_SHA256"))
return new NoiseHandshakeState_XX_25519_ChaChaPoly_SHA256();
if (!strcmp(protocol, "Noise_XXfallback_25519_AESGCM_SHA256"))
return new NoiseHandshakeState_XXfallback_25519_AESGCM_SHA256();
if (!strcmp(protocol, "Noise_XXfallback_25519_ChaChaPoly_BLAKE2s"))
return new NoiseHandshakeState_XXfallback_25519_ChaChaPoly_BLAKE2s();
if (!strcmp(protocol, "Noise_XXfallback_25519_ChaChaPoly_SHA256"))
return new NoiseHandshakeState_XXfallback_25519_ChaChaPoly_SHA256();
fail(protocol);
return 0;
}
/**
* \brief Test a connection between an initiator and a responder.
*
* \param vec The test vector.
*/
static void test_connection(const TestVector *vec)
{
NoiseHandshakeState *initiator = 0;
NoiseHandshakeState *responder = 0;
NoiseHandshakeState *send;
NoiseHandshakeState *recv;
#if 0
NoiseCipherState *c1init;
NoiseCipherState *c2init;
NoiseCipherState *c1resp;
NoiseCipherState *c2resp;
NoiseCipherState *csend;
NoiseCipherState *crecv;
#endif
uint8_t message[MAX_MESSAGE_SIZE];
uint8_t payload[MAX_MESSAGE_SIZE];
int result;
size_t index;
//size_t mac_len;
Noise::Party role;
/* Create the two ends of the connection */
initiator = create_handshake(vec->protocol_name);
responder = create_handshake(vec->protocol_name);
/* Should be able to start the handshake now on both sides */
initiator->start
(Noise::Initiator, vec->init_prologue, vec->init_prologue_len);
responder->start
(Noise::Responder, vec->resp_prologue, vec->resp_prologue_len);
compare(initiator->state(), Noise::Write);
compare(responder->state(), Noise::Read);
/* Set all keys that we need to use. We do this after start()
because otherwise it will clear the test ephemeral keys we're
about to set on the objects */
if (vec->init_static) {
verify(initiator->setParameter
(Noise::LocalStaticPrivateKey,
vec->init_static, vec->init_static_len));
}
if (vec->init_public_static) {
verify(responder->setParameter
(Noise::RemoteStaticPublicKey,
vec->init_public_static, vec->init_public_static_len));
}
if (vec->resp_static) {
verify(responder->setParameter
(Noise::LocalStaticPrivateKey,
vec->resp_static, vec->resp_static_len));
}
if (vec->resp_public_static) {
verify(initiator->setParameter
(Noise::RemoteStaticPublicKey,
vec->resp_public_static, vec->resp_public_static_len));
}
if (vec->init_ephemeral) {
verify(initiator->setParameter
(Noise::LocalEphemPrivateKey,
vec->init_ephemeral, vec->init_ephemeral_len));
}
if (vec->resp_ephemeral) {
verify(responder->setParameter
(Noise::LocalEphemPrivateKey,
vec->resp_ephemeral, vec->resp_ephemeral_len));
}
if (vec->num_init_psks) {
/* For now we only support one PSK per handshake */
verify(initiator->setParameter
(Noise::PreSharedKey, vec->init_psks[0], 32));
}
if (vec->num_resp_psks) {
verify(responder->setParameter
(Noise::PreSharedKey, vec->resp_psks[0], 32));
}
/* Work through the messages one by one until both sides "split" */
role = Noise::Initiator;
for (index = 0; index < vec->num_messages; ++index) {
if (initiator->state() == Noise::Split &&
responder->state() == Noise::Split) {
break;
}
if (role == Noise::Initiator) {
/* Send on the initiator, receive on the responder */
send = initiator;
recv = responder;
role = Noise::Responder;
} else {
/* Send on the responder, receive on the initiator */
send = responder;
recv = initiator;
role = Noise::Initiator;
}
compare(send->state(), Noise::Write);
compare(recv->state(), Noise::Read);
memset(message, 0xAA, sizeof(message));
result = send->write(message, sizeof(message),
vec->messages[index].payload,
vec->messages[index].payload_len);
verify(result >= 0);
compare_blocks("ciphertext", message, (size_t)result,
vec->messages[index].ciphertext,
vec->messages[index].ciphertext_len);
memset(payload, 0xBB, sizeof(payload));
result = recv->read(payload, sizeof(payload), message, result);
verify(result >= 0);
compare_blocks("plaintext", payload, (size_t)result,
vec->messages[index].payload,
vec->messages[index].payload_len);
}
#if 0
/* Handshake finished. Check the handshake hash values */
#if 0
if (vec->handshake_hash_len) {
memset(payload, 0xAA, sizeof(payload));
compare(noise_handshakestate_get_handshake_hash
(initiator, payload, vec->handshake_hash_len),
NOISE_ERROR_NONE);
compare_blocks("handshake_hash", payload, vec->handshake_hash_len,
vec->handshake_hash, vec->handshake_hash_len);
memset(payload, 0xAA, sizeof(payload));
compare(noise_handshakestate_get_handshake_hash
(responder, payload, vec->handshake_hash_len),
NOISE_ERROR_NONE);
compare_blocks("handshake_hash", payload, vec->handshake_hash_len,
vec->handshake_hash, vec->handshake_hash_len);
}
#endif
/* Now handle the data transport */
compare(noise_handshakestate_split(initiator, &c1init, &c2init),
NOISE_ERROR_NONE);
compare(noise_handshakestate_split(responder, &c2resp, &c1resp),
NOISE_ERROR_NONE);
mac_len = noise_cipherstate_get_mac_length(c1init);
for (; index < vec->num_messages; ++index) {
if (role == NOISE_ROLE_INITIATOR) {
/* Send on the initiator, receive on the responder */
csend = c1init;
crecv = c1resp;
if (!is_one_way)
role = NOISE_ROLE_RESPONDER;
} else {
/* Send on the responder, receive on the initiator */
csend = c2resp;
crecv = c2init;
role = NOISE_ROLE_INITIATOR;
}
verify(sizeof(message) >= (vec->messages[index].payload_len + mac_len));
memcpy(message, vec->messages[index].payload,
vec->messages[index].payload_len);
noise_buffer_set_inout(mbuf, message, vec->messages[index].payload_len,
sizeof(message));
compare(noise_cipherstate_encrypt(csend, &mbuf),
NOISE_ERROR_NONE);
compare_blocks("ciphertext", mbuf.data, mbuf.size,
vec->messages[index].ciphertext,
vec->messages[index].ciphertext_len);
compare(noise_cipherstate_decrypt(crecv, &mbuf),
NOISE_ERROR_NONE);
compare_blocks("plaintext", mbuf.data, mbuf.size,
vec->messages[index].payload,
vec->messages[index].payload_len);
}
#endif
/* Clean up */
delete initiator;
delete responder;
#if 0
compare(noise_cipherstate_free(c1init), NOISE_ERROR_NONE);
compare(noise_cipherstate_free(c2init), NOISE_ERROR_NONE);
compare(noise_cipherstate_free(c1resp), NOISE_ERROR_NONE);
compare(noise_cipherstate_free(c2resp), NOISE_ERROR_NONE);
#endif
}
/**
* \brief Runs a fully parsed test vector.
*
* \param reader The input stream, for error reporting.
* \param vec The test vector.
*
* \return Non-zero if the test succeeded, zero if it failed.
*/
static int test_vector_run(JSONReader *reader, const TestVector *vec)
{
int value;
printf("%s ... ", vec->name);
fflush(stdout);
if ((value = setjmp(test_jump_back)) == 0) {
test_connection(vec);
printf("ok\n");
return 1;
} else if (value == 2) {
printf("skipped\n");
return 1;
} else {
printf("-> test data at %s:%ld\n", reader->filename, vec->line_number);
return 0;
}
}
/**
* \brief Look for a specific token next in the input stream.
*
* \param reader The input stream.
* \param token The token code.
* \param name The token name for error reporting.
*/
static void expect_token(JSONReader *reader, JSONToken token, const char *name)
{
if (reader->errors)
return;
if (reader->token == token)
json_next_token(reader);
else
json_error(reader, "Expecting '%s'", name);
}
/**
* \brief Look for a specific field name next in the input stream,
* followed by a colon.
*
* \param reader The input stream.
* \param name The field name.
*/
static void expect_name(JSONReader *reader, const char *name)
{
if (reader->errors)
return;
if (json_is_name(reader, name)) {
json_next_token(reader);
expect_token(reader, JSON_TOKEN_COLON, ":");
} else {
json_error(reader, "Expecting \"%s\"", name);
}
}
/**
* \brief Look for a field with a string value.
*
* \param reader The input stream.
* \param value The location where to place the string value.
*/
static void expect_string_field(JSONReader *reader, char **value)
{
json_next_token(reader);
expect_token(reader, JSON_TOKEN_COLON, ":");
if (!reader->errors && reader->token == JSON_TOKEN_STRING) {
*value = reader->str_value;
reader->str_value = 0;
json_next_token(reader);
if (!reader->errors && reader->token == JSON_TOKEN_COMMA)
json_next_token(reader);
}
}
/**
* \brief Converts an ASCII character into a hexadecimal digit.
*
* \param ch The ASCII character.
*
* \return The digit between 0 and 15, or -1 if \a ch is not hexadecimal.
*/
static int from_hex_digit(int ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
else if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
else if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
else
return -1;
}
/**
* \brief Look for a field with a binary value.
*
* \param reader The input stream.
* \param value The location where to place the binary value.
*
* \return The size of the binary value in bytes.
*/
static size_t expect_binary_field(JSONReader *reader, uint8_t **value)
{
size_t size = 0;
size_t posn;
const char *hex;
int digit1, digit2;
json_next_token(reader);
expect_token(reader, JSON_TOKEN_COLON, ":");
if (!reader->errors && reader->token == JSON_TOKEN_STRING) {
size = strlen(reader->str_value) / 2;
*value = (uint8_t *)calloc(1, size + 1);
if (!(*value)) {
json_error(reader, "Out of memory");
return 0;
}
hex = reader->str_value;
for (posn = 0; posn < size; ++posn) {
digit1 = from_hex_digit(hex[posn * 2]);
digit2 = from_hex_digit(hex[posn * 2 + 1]);
if (digit1 < 0 || digit2 < 0) {
json_error(reader, "Invalid hexadecimal data");
return 0;
}
(*value)[posn] = digit1 * 16 + digit2;
}
json_next_token(reader);
if (!reader->errors && reader->token == JSON_TOKEN_COMMA)
json_next_token(reader);
}
return size;
}
/**
* \brief Look for a field with a boolean value.
*
* \param reader The input stream.
* \return The boolean value.
*/
static int expect_boolean_field(JSONReader *reader)
{
int result = 0;
json_next_token(reader);
expect_token(reader, JSON_TOKEN_COLON, ":");
if (!reader->errors && (reader->token == JSON_TOKEN_TRUE ||
reader->token == JSON_TOKEN_FALSE)) {
result = (reader->token == JSON_TOKEN_TRUE);
json_next_token(reader);
if (!reader->errors && reader->token == JSON_TOKEN_COMMA)
json_next_token(reader);
}
return result;
}
/**
* \brief Parse a list of PSK's from a JSON input stream.
*
* \param reader The input stream.
* \param Array to receive the PSK's.
* \return The number of PSK's that were parsed.
*/
static size_t parse_psk_list(JSONReader *reader, uint8_t psks[MAX_PSKS][32])
{
size_t count = 0;
json_next_token(reader);
expect_token(reader, JSON_TOKEN_COLON, ":");
expect_token(reader, JSON_TOKEN_LSQUARE, "[");
while (!reader->errors && reader->token == JSON_TOKEN_STRING) {
const char *hex = reader->str_value;
size_t size = strlen(hex) / 2;
size_t posn;
if (size != 32) {
json_error(reader, "PSK is not 32 bytes in size");
return 0;
}
if (count >= MAX_PSKS) {
json_error(reader, "Too many PSK's");
return 0;
}
for (posn = 0; posn < size; ++posn) {
int digit1 = from_hex_digit(hex[posn * 2]);
int digit2 = from_hex_digit(hex[posn * 2 + 1]);
if (digit1 < 0 || digit2 < 0) {
json_error(reader, "Invalid hexadecimal data");
return 0;
}
psks[count][posn] = digit1 * 16 + digit2;
}
++count;
json_next_token(reader);
if (!reader->errors && reader->token == JSON_TOKEN_COMMA)
json_next_token(reader);
}
expect_token(reader, JSON_TOKEN_RSQUARE, "]");
if (!reader->errors && reader->token == JSON_TOKEN_COMMA)
json_next_token(reader);
return reader->errors ? 0 : count;
}
/**
* \brief Processes a single test vector from an input stream.
*
* \param reader The reader representing the input stream.
*
* \return Non-zero if the test succeeded, zero if it failed.
*/
static int process_test_vector(JSONReader *reader)
{
TestVector vec;
int retval = 1;
memset(&vec, 0, sizeof(TestVector));
while (!reader->errors && reader->token == JSON_TOKEN_STRING) {
if (json_is_name(reader, "name")) {
vec.line_number = reader->line_number;
expect_string_field(reader, &(vec.name));
} else if (json_is_name(reader, "protocol_name")) {
vec.line_number = reader->line_number;
expect_string_field(reader, &(vec.protocol_name));
} else if (json_is_name(reader, "init_static")) {
vec.init_static_len =
expect_binary_field(reader, &(vec.init_static));
} else if (json_is_name(reader, "init_remote_static")) {
/* Refers to the initiator have pre-knowledge of the responder's
public key, which is "resp_public_static" in TestVector */
vec.resp_public_static_len =
expect_binary_field(reader, &(vec.resp_public_static));
} else if (json_is_name(reader, "resp_static")) {
vec.resp_static_len =
expect_binary_field(reader, &(vec.resp_static));
} else if (json_is_name(reader, "resp_remote_static")) {
/* Refers to the responder have pre-knowledge of the initiator's
public key, which is "init_public_static" in TestVector */
vec.init_public_static_len =
expect_binary_field(reader, &(vec.init_public_static));
} else if (json_is_name(reader, "init_ephemeral")) {
vec.init_ephemeral_len =
expect_binary_field(reader, &(vec.init_ephemeral));
} else if (json_is_name(reader, "resp_ephemeral")) {
vec.resp_ephemeral_len =
expect_binary_field(reader, &(vec.resp_ephemeral));
} else if (json_is_name(reader, "init_hybrid_ephemeral")) {
vec.init_hybrid_len =
expect_binary_field(reader, &(vec.init_hybrid));
} else if (json_is_name(reader, "resp_hybrid_ephemeral")) {
vec.resp_hybrid_len =
expect_binary_field(reader, &(vec.resp_hybrid));
} else if (json_is_name(reader, "init_prologue")) {
vec.init_prologue_len =
expect_binary_field(reader, &(vec.init_prologue));
} else if (json_is_name(reader, "resp_prologue")) {
vec.resp_prologue_len =
expect_binary_field(reader, &(vec.resp_prologue));
} else if (json_is_name(reader, "init_psks")) {
vec.num_init_psks = parse_psk_list(reader, vec.init_psks);
} else if (json_is_name(reader, "resp_psks")) {
vec.num_resp_psks = parse_psk_list(reader, vec.resp_psks);
} else if (json_is_name(reader, "handshake_hash")) {
vec.handshake_hash_len =
expect_binary_field(reader, &(vec.handshake_hash));
} else if (json_is_name(reader, "fail")) {
vec.fail = expect_boolean_field(reader);
} else if (json_is_name(reader, "fallback")) {
vec.fallback = expect_boolean_field(reader);
} else if (json_is_name(reader, "fallback_pattern")) {
expect_string_field(reader, &(vec.fallback_pattern));
} else if (json_is_name(reader, "messages")) {
json_next_token(reader);
expect_token(reader, JSON_TOKEN_COLON, ":");
expect_token(reader, JSON_TOKEN_LSQUARE, "[");
while (!reader->errors && reader->token == JSON_TOKEN_LBRACE) {
if (vec.num_messages >= MAX_MESSAGES) {
json_error(reader, "Too many messages for test vector");
break;
}
expect_token(reader, JSON_TOKEN_LBRACE, "{");
while (!reader->errors && reader->token == JSON_TOKEN_STRING) {
if (json_is_name(reader, "payload")) {
vec.messages[vec.num_messages].payload_len =
expect_binary_field
(reader, &(vec.messages[vec.num_messages].payload));
} else if (json_is_name(reader, "ciphertext")) {
vec.messages[vec.num_messages].ciphertext_len =
expect_binary_field
(reader, &(vec.messages[vec.num_messages].ciphertext));
} else {
json_error(reader, "Unknown message field '%s'",
reader->str_value);
}
}
if (!vec.messages[vec.num_messages].payload)
json_error(reader, "Missing payload for message");
if (!vec.messages[vec.num_messages].ciphertext)
json_error(reader, "Missing ciphertext for message");
++(vec.num_messages);
expect_token(reader, JSON_TOKEN_RBRACE, "}");
if (!reader->errors && reader->token == JSON_TOKEN_COMMA)
json_next_token(reader);
}
expect_token(reader, JSON_TOKEN_RSQUARE, "]");
if (!reader->errors && reader->token == JSON_TOKEN_COMMA)
json_next_token(reader);
} else {
json_error(reader, "Unknown field '%s'", reader->str_value);
}
}
if (!vec.protocol_name) {
json_error(reader, "Missing 'protocol_name' field");
} else if (!vec.name) {
vec.name = strdup(vec.protocol_name);
}
if (!reader->errors) {
retval = test_vector_run(reader, &vec);
}
test_vector_free(&vec);
return retval;
}
/**
* \brief Processes the test vectors from an input stream.
*
* \param reader The reader representing the input stream.
*/
static void process_test_vectors(JSONReader *reader)
{
int ok = 1;
printf("--------------------------------------------------------------\n");
printf("Processing vectors from %s\n", reader->filename);
json_next_token(reader);
expect_token(reader, JSON_TOKEN_LBRACE, "{");
expect_name(reader, "vectors");
expect_token(reader, JSON_TOKEN_LSQUARE, "[");
while (!reader->errors && reader->token != JSON_TOKEN_RSQUARE) {
expect_token(reader, JSON_TOKEN_LBRACE, "{");
if (!process_test_vector(reader))
ok = 0;
expect_token(reader, JSON_TOKEN_RBRACE, "}");
if (!reader->errors && reader->token == JSON_TOKEN_COMMA)
expect_token(reader, JSON_TOKEN_COMMA, ",");
}
expect_token(reader, JSON_TOKEN_RSQUARE, "]");
expect_token(reader, JSON_TOKEN_RBRACE, "}");
expect_token(reader, JSON_TOKEN_END, "EOF");
printf("--------------------------------------------------------------\n");
if (!ok) {
/* Some of the test vectors failed, so report a global failure */
++(reader->errors);
}
}
static int process_file(const char *filename)
{
int retval = 0;
FILE *file = fopen(filename, "r");
if (file) {
JSONReader reader;
json_init(&reader, filename, file);
process_test_vectors(&reader);
if (reader.errors > 0)
retval = 1;
json_free(&reader);
fclose(file);
} else {
perror(filename);
retval = 1;
}
return retval;
}
int main(int argc, char *argv[])
{
int retval = 0;
char *srcdir = getenv("srcdir");
if (argc <= 1 && !srcdir) {
fprintf(stderr, "Usage: %s vectors1.txt vectors2.txt ...\n", argv[0]);
return 1;
} else if (argc > 1) {
while (argc > 1) {
retval |= process_file(argv[1]);
--argc;
++argv;
}
} else {
if (chdir(srcdir) < 0) {
perror(srcdir);
return 1;
}
retval |= process_file("cacophony.txt");
retval |= process_file("noise-c-basic.txt");
retval |= process_file("noise-c-fallback.txt");
retval |= process_file("noise-c-hybrid.txt");
}
return retval;
}

View File

@ -0,0 +1,331 @@
{
"vectors": [
{
"protocol_name": "Noise_IK_25519_AESGCM_SHA256",
"init_prologue": "4a6f686e2047616c74",
"init_static": "e61ef9919cde45dd5f82166404bd08e38bceb5dfdfded0a34c8df7ed542214d1",
"init_ephemeral": "893e28b9dc6ca8d611ab664754b8ceb7bac5117349a4439a6b0569da977c464a",
"init_remote_static": "31e0303fd6418d2f8c0e78b91f22e8caed0fbe48656dcf4767e4834f701b8f62",
"resp_prologue": "4a6f686e2047616c74",
"resp_static": "4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893",
"resp_ephemeral": "bbdb4cdbd309f1a1f2e1456967fe288cadd6f712d65dc7b7793d5e63da6b375b",
"handshake_hash": "669c8640d9e42a3cda2f232f78597ceefb01daa6e3df81181ccce6fc6b5026bf",
"messages": [
{
"payload": "4c756477696720766f6e204d69736573",
"ciphertext": "ca35def5ae56cec33dc2036731ab14896bc4c75dbb07a61f879f8e3afa4c79444e417bc55c7a8166c993356c1be41ef67818a292426f301556c7f26b21d25ddb097153891a9a956cff47b83e63ad8d701c1342c209cff1ca5ecd43402762ac249e3bd3a4c0a145fe07cb5dae28ea13a3"
},
{
"payload": "4d757272617920526f746862617264",
"ciphertext": "95ebc60d2b1fa672c1f46a8aa265ef51bfe38e7ccb39ec5be34069f144808843af2ccf9972e22afc67aeafcd25162f7f98c363b7762e3e4cb7d272e39f27a5"
},
{
"payload": "462e20412e20486179656b",
"ciphertext": "66acfc92e3197de166809e6d4d5d003dcc819a84bc3522ca53c9d9"
},
{
"payload": "4361726c204d656e676572",
"ciphertext": "71f89aa6533a6de70b0826864dd75f60806ee40170c16290189eb3"
},
{
"payload": "4a65616e2d426170746973746520536179",
"ciphertext": "4795a3423550c8bf00386bd496a3e2c76c10669d2a75ab8f79b5094c5412a25705"
},
{
"payload": "457567656e2042f6686d20766f6e2042617765726b",
"ciphertext": "aa0bb39097555c918e40be82abc2b909eb79d9eb87adb07e268fc37323a6cf904fd01fb391"
}
]
},
{
"protocol_name": "Noise_IK_25519_ChaChaPoly_BLAKE2s",
"init_prologue": "4a6f686e2047616c74",
"init_static": "e61ef9919cde45dd5f82166404bd08e38bceb5dfdfded0a34c8df7ed542214d1",
"init_ephemeral": "893e28b9dc6ca8d611ab664754b8ceb7bac5117349a4439a6b0569da977c464a",
"init_remote_static": "31e0303fd6418d2f8c0e78b91f22e8caed0fbe48656dcf4767e4834f701b8f62",
"resp_prologue": "4a6f686e2047616c74",
"resp_static": "4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893",
"resp_ephemeral": "bbdb4cdbd309f1a1f2e1456967fe288cadd6f712d65dc7b7793d5e63da6b375b",
"handshake_hash": "48f3cb8bc9319da4ba1e9933991b1c4ed4034f1f126a76d3a1fbcfd7f94248d4",
"messages": [
{
"payload": "4c756477696720766f6e204d69736573",
"ciphertext": "ca35def5ae56cec33dc2036731ab14896bc4c75dbb07a61f879f8e3afa4c79440b03ddc7aac5123d06a1b23b71670e32e76c28239a7ca4ac8f784de7e44c1adbfc6e83fef7352a58d9d56157400c0a737b1d171ce368229c7b752ac25b8faf4eca690f6d896f543be02c996ab2b86b76"
},
{
"payload": "4d757272617920526f746862617264",
"ciphertext": "95ebc60d2b1fa672c1f46a8aa265ef51bfe38e7ccb39ec5be34069f144808843d9b5a8927f0ac9655ef76833bc7e5561f42e691ac8404efd6fbd6308b6a27c"
},
{
"payload": "462e20412e20486179656b",
"ciphertext": "2c256ed08fcd08c2980f954ee4beaccb61c9581340f5dd2fd1cf3b"
},
{
"payload": "4361726c204d656e676572",
"ciphertext": "d6033f70eee20945c7c9dba304e397ee3b284ff5e00fd9efb095d3"
},
{
"payload": "4a65616e2d426170746973746520536179",
"ciphertext": "a9c068ca5d8babf72560652d8e851adbfac35c8a66e810d560863173e96adf4cfe"
},
{
"payload": "457567656e2042f6686d20766f6e2042617765726b",
"ciphertext": "2a09d8f459e5927e40fdd2eddc99bdafb04e13a26f145cb5cfe9e6ba34c94331ebc17d5156"
}
]
},
{
"protocol_name": "Noise_IK_25519_ChaChaPoly_SHA256",
"init_prologue": "4a6f686e2047616c74",
"init_static": "e61ef9919cde45dd5f82166404bd08e38bceb5dfdfded0a34c8df7ed542214d1",
"init_ephemeral": "893e28b9dc6ca8d611ab664754b8ceb7bac5117349a4439a6b0569da977c464a",
"init_remote_static": "31e0303fd6418d2f8c0e78b91f22e8caed0fbe48656dcf4767e4834f701b8f62",
"resp_prologue": "4a6f686e2047616c74",
"resp_static": "4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893",
"resp_ephemeral": "bbdb4cdbd309f1a1f2e1456967fe288cadd6f712d65dc7b7793d5e63da6b375b",
"handshake_hash": "0b0f68fb0c27e03ce9b97565995ed4838cc0581b762ef72b062f6a546419fad7",
"messages": [
{
"payload": "4c756477696720766f6e204d69736573",
"ciphertext": "ca35def5ae56cec33dc2036731ab14896bc4c75dbb07a61f879f8e3afa4c7944718da798efbcd91528520204f904b9bd6c7413dccdc214d951e15253e39987f18146e8cd0873654207148333479d4d16c289f0294b29960a72f48e0b7bba2e89083169825e59642148d492020664ccf7"
},
{
"payload": "4d757272617920526f746862617264",
"ciphertext": "95ebc60d2b1fa672c1f46a8aa265ef51bfe38e7ccb39ec5be34069f1448088435361e70b2ed446e6c9ec387d1d6b3b840f194e373979d241b203c4acafccf5"
},
{
"payload": "462e20412e20486179656b",
"ciphertext": "050e9f3c8fac16b68dbce8f8c4bfbf6617c897f9ada4aa29aa19c8"
},
{
"payload": "4361726c204d656e676572",
"ciphertext": "344233a6cabb7141d80f3da2fedc311d9646bbb0f505afe403a667"
},
{
"payload": "4a65616e2d426170746973746520536179",
"ciphertext": "62cdeeb172ad7ade7aa7d9e069da5790f12331bfa00177787a1d0810c67dc3b2b4"
},
{
"payload": "457567656e2042f6686d20766f6e2042617765726b",
"ciphertext": "029bead1b40992327044d409d9a1f3ad8f36c3c452775d557e18bbeb2e8dfcead32d514024"
}
]
},
{
"protocol_name": "Noise_NNpsk0_25519_AESGCM_SHA256",
"init_prologue": "4a6f686e2047616c74",
"init_psks": ["54686973206973206d7920417573747269616e20706572737065637469766521"],
"init_ephemeral": "893e28b9dc6ca8d611ab664754b8ceb7bac5117349a4439a6b0569da977c464a",
"resp_prologue": "4a6f686e2047616c74",
"resp_psks": ["54686973206973206d7920417573747269616e20706572737065637469766521"],
"resp_ephemeral": "bbdb4cdbd309f1a1f2e1456967fe288cadd6f712d65dc7b7793d5e63da6b375b",
"handshake_hash": "9f3b1f9afd7767767b5b9d1069844a6154fdbdcc36dfb3e31c0fffc5c973d530",
"messages": [
{
"payload": "4c756477696720766f6e204d69736573",
"ciphertext": "ca35def5ae56cec33dc2036731ab14896bc4c75dbb07a61f879f8e3afa4c79447c37a89fc17813788d30df2d59501d6066f5f8aecc3406bbd9829f2d24a531b1"
},
{
"payload": "4d757272617920526f746862617264",
"ciphertext": "95ebc60d2b1fa672c1f46a8aa265ef51bfe38e7ccb39ec5be34069f1448088432f29d79368ef9de4e1ae7a362fb05bf0ad668e026bf5714554497b3a720461"
},
{
"payload": "462e20412e20486179656b",
"ciphertext": "7c01e898a6201f36aecee532e29cd0b7f8ffa29d3366ad0dce39a6"
},
{
"payload": "4361726c204d656e676572",
"ciphertext": "a9ff535aace3db6a1da1a7fb00fe2de2622bb373abad9fb42c00e0"
},
{
"payload": "4a65616e2d426170746973746520536179",
"ciphertext": "6cfc49a3e65472edc2d0152abe5ce950170a8a47c959f30bd48b5f0aacf29adbfd"
},
{
"payload": "457567656e2042f6686d20766f6e2042617765726b",
"ciphertext": "9bc86e6d974ed21ba04604a94aa68de21677eb409ead070c0cd569b069ee19ba142aa4c1a9"
}
]
},
{
"protocol_name": "Noise_NNpsk0_25519_ChaChaPoly_BLAKE2s",
"init_prologue": "4a6f686e2047616c74",
"init_psks": ["54686973206973206d7920417573747269616e20706572737065637469766521"],
"init_ephemeral": "893e28b9dc6ca8d611ab664754b8ceb7bac5117349a4439a6b0569da977c464a",
"resp_prologue": "4a6f686e2047616c74",
"resp_psks": ["54686973206973206d7920417573747269616e20706572737065637469766521"],
"resp_ephemeral": "bbdb4cdbd309f1a1f2e1456967fe288cadd6f712d65dc7b7793d5e63da6b375b",
"handshake_hash": "b3e9c846d264120a4211e18307da91157a21e92e69b639c50f027f101db3e1a6",
"messages": [
{
"payload": "4c756477696720766f6e204d69736573",
"ciphertext": "ca35def5ae56cec33dc2036731ab14896bc4c75dbb07a61f879f8e3afa4c7944fda936bec35a8adfdff198386f7d5475880897edaaf7495314c99095a2e4d66a"
},
{
"payload": "4d757272617920526f746862617264",
"ciphertext": "95ebc60d2b1fa672c1f46a8aa265ef51bfe38e7ccb39ec5be34069f1448088434cd2a371993ba41ea11448024fca32766b169183c9e691a7a433279da7e729"
},
{
"payload": "462e20412e20486179656b",
"ciphertext": "bc44da303ae0beb08075fc4eb4e58235c67c2d1f53a4f2fff0bca7"
},
{
"payload": "4361726c204d656e676572",
"ciphertext": "416d1af83e9fa6966ce4e871156b131aa9bd7e9a1d6f8794f4872a"
},
{
"payload": "4a65616e2d426170746973746520536179",
"ciphertext": "8a7d81b77bcc6c072f2b807da066efba6b5fab9edf71a7faceb2c8454b0cfef608"
},
{
"payload": "457567656e2042f6686d20766f6e2042617765726b",
"ciphertext": "1e2ee010f72894824a25a867664ff298f2548a145dc4e9d27b1cad83f32fa7c54d69dc3279"
}
]
},
{
"protocol_name": "Noise_NNpsk0_25519_ChaChaPoly_SHA256",
"init_prologue": "4a6f686e2047616c74",
"init_psks": ["54686973206973206d7920417573747269616e20706572737065637469766521"],
"init_ephemeral": "893e28b9dc6ca8d611ab664754b8ceb7bac5117349a4439a6b0569da977c464a",
"resp_prologue": "4a6f686e2047616c74",
"resp_psks": ["54686973206973206d7920417573747269616e20706572737065637469766521"],
"resp_ephemeral": "bbdb4cdbd309f1a1f2e1456967fe288cadd6f712d65dc7b7793d5e63da6b375b",
"handshake_hash": "f4d03dc34495c95729ea6de9e1b59004b59733102488b3e24bc441e0be208eaf",
"messages": [
{
"payload": "4c756477696720766f6e204d69736573",
"ciphertext": "ca35def5ae56cec33dc2036731ab14896bc4c75dbb07a61f879f8e3afa4c794479b962b8aff8485742ac32f905ba45369e2465fb59e138a93d67a0d1266b6a54"
},
{
"payload": "4d757272617920526f746862617264",
"ciphertext": "95ebc60d2b1fa672c1f46a8aa265ef51bfe38e7ccb39ec5be34069f144808843d6062704d5a9c422a8e834423f8c1feada7e8d0d910a1a2cd030fb584221e3"
},
{
"payload": "462e20412e20486179656b",
"ciphertext": "e632c3763d7669067383433197a3baddf146e9e70ad4b4e9e59e0f"
},
{
"payload": "4361726c204d656e676572",
"ciphertext": "64c6bee32ea91c8474bb4c21d7a700109ad45af77b29764ba5eb1e"
},
{
"payload": "4a65616e2d426170746973746520536179",
"ciphertext": "e2fa0bed0603b62d3ccac2ecabbf3fe33f3e86514909b323361626266cb2471cc8"
},
{
"payload": "457567656e2042f6686d20766f6e2042617765726b",
"ciphertext": "0c01dc9cec1fe4ddd692e8dd32188aa351088dc91183639a53b57aa4692b5ebdef8b8ca111"
}
]
},
{
"protocol_name": "Noise_XX_25519_AESGCM_SHA256",
"init_prologue": "4a6f686e2047616c74",
"init_static": "e61ef9919cde45dd5f82166404bd08e38bceb5dfdfded0a34c8df7ed542214d1",
"init_ephemeral": "893e28b9dc6ca8d611ab664754b8ceb7bac5117349a4439a6b0569da977c464a",
"resp_prologue": "4a6f686e2047616c74",
"resp_static": "4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893",
"resp_ephemeral": "bbdb4cdbd309f1a1f2e1456967fe288cadd6f712d65dc7b7793d5e63da6b375b",
"handshake_hash": "1b7aefb1125762aa21a252890d00af54519638b76437444538f9a52f21e2e0dc",
"messages": [
{
"payload": "4c756477696720766f6e204d69736573",
"ciphertext": "ca35def5ae56cec33dc2036731ab14896bc4c75dbb07a61f879f8e3afa4c79444c756477696720766f6e204d69736573"
},
{
"payload": "4d757272617920526f746862617264",
"ciphertext": "95ebc60d2b1fa672c1f46a8aa265ef51bfe38e7ccb39ec5be34069f144808843757117acceb05bd7a45733bc22015c97a9d0cbaf41b80446d5988ff5127235d76b79eade70f473d6a4ef521fdcbeda5340d01e028ba793fc059f2724a83af05f12dda0448a7621a926b379a92477fd"
},
{
"payload": "462e20412e20486179656b",
"ciphertext": "c90f1cf77eba4e50edb038991565e36c9758943a989229b6051244dc4fbecb6946744b401af2ee1a5881b65fbb87fd07cb6a328ececc9ce6ce84c399dc332d4fd521fa4bb7f467ce909395"
},
{
"payload": "4361726c204d656e676572",
"ciphertext": "bc3fa77f6aca3e8466d7dc6bea10013e88a6a29add5132b461806c"
},
{
"payload": "4a65616e2d426170746973746520536179",
"ciphertext": "250b01074cdfe0df2ecf8ccbf1737b15a2ddb5b52fd9a396604e9c793cee3b3bb9"
},
{
"payload": "457567656e2042f6686d20766f6e2042617765726b",
"ciphertext": "449d4d433b3cdc3d02bf6fc881774b9df54366ebcffb9689bb13f14709822cd7ef42bcdb4d"
}
]
},
{
"protocol_name": "Noise_XX_25519_ChaChaPoly_BLAKE2s",
"init_prologue": "4a6f686e2047616c74",
"init_static": "e61ef9919cde45dd5f82166404bd08e38bceb5dfdfded0a34c8df7ed542214d1",
"init_ephemeral": "893e28b9dc6ca8d611ab664754b8ceb7bac5117349a4439a6b0569da977c464a",
"resp_prologue": "4a6f686e2047616c74",
"resp_static": "4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893",
"resp_ephemeral": "bbdb4cdbd309f1a1f2e1456967fe288cadd6f712d65dc7b7793d5e63da6b375b",
"handshake_hash": "6c4c56cf71612f72d05ceb96c0155e6f4ea54a26b504c93de632a2db4a49d200",
"messages": [
{
"payload": "4c756477696720766f6e204d69736573",
"ciphertext": "ca35def5ae56cec33dc2036731ab14896bc4c75dbb07a61f879f8e3afa4c79444c756477696720766f6e204d69736573"
},
{
"payload": "4d757272617920526f746862617264",
"ciphertext": "95ebc60d2b1fa672c1f46a8aa265ef51bfe38e7ccb39ec5be34069f1448088437c365eb362a1c991b0557fe8a7fb187d99346765d93ec63db6c1b01504ebeec55a2298d2dbff80eff034d20595153f63a196a6cead1e11b2bb13e336fa13616dd3e8b0a070c882ed3f1a78c7c06c93"
},
{
"payload": "462e20412e20486179656b",
"ciphertext": "46c3307de83b014258717d97781c1f50936d8b7d50c0722a1739654d10392d415b670c114f79b9a4f80541570f77ce88802efa4220cff733e7b5668ba38059ec904b4b8eef9448085faf51"
},
{
"payload": "4361726c204d656e676572",
"ciphertext": "d5e83adfaac5dc324a68f1862df54549e56d209fba707205f328b2"
},
{
"payload": "4a65616e2d426170746973746520536179",
"ciphertext": "d102c9029b1f55c788f561ba7737afbccef9c9f1bf2f238167fd40ba9c1c134867"
},
{
"payload": "457567656e2042f6686d20766f6e2042617765726b",
"ciphertext": "cb1ce80960382c6d5d5e740ffb724d1432f0310b200fb6f8424120f506092744baa415e155"
}
]
},
{
"protocol_name": "Noise_XX_25519_ChaChaPoly_SHA256",
"init_prologue": "4a6f686e2047616c74",
"init_static": "e61ef9919cde45dd5f82166404bd08e38bceb5dfdfded0a34c8df7ed542214d1",
"init_ephemeral": "893e28b9dc6ca8d611ab664754b8ceb7bac5117349a4439a6b0569da977c464a",
"resp_prologue": "4a6f686e2047616c74",
"resp_static": "4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893",
"resp_ephemeral": "bbdb4cdbd309f1a1f2e1456967fe288cadd6f712d65dc7b7793d5e63da6b375b",
"handshake_hash": "c8e5f64e846193be2a834104c2a009868d6c9f3bd3c186299888b488b2f1f58e",
"messages": [
{
"payload": "4c756477696720766f6e204d69736573",
"ciphertext": "ca35def5ae56cec33dc2036731ab14896bc4c75dbb07a61f879f8e3afa4c79444c756477696720766f6e204d69736573"
},
{
"payload": "4d757272617920526f746862617264",
"ciphertext": "95ebc60d2b1fa672c1f46a8aa265ef51bfe38e7ccb39ec5be34069f14480884381cbad1f276e038c48378ffce2b65285e08d6b68aaa3629a5a8639392490e5b9bd5269c2f1e4f488ed8831161f19b7815528f8982ffe09be9b5c412f8a0db50f8814c7194e83f23dbd8d162c9326ad"
},
{
"payload": "462e20412e20486179656b",
"ciphertext": "c7195ffacac1307ff99046f219750fc47693e23c3cb08b89c2af808b444850a80ae475b9df0f169ae80a89be0865b57f58c9fea0d4ec82a286427402f113e4b6ae769a1d95941d49b25030"
},
{
"payload": "4361726c204d656e676572",
"ciphertext": "96763ed773f8e47bb3712f0e29b3060ffc956ffc146cee53d5e1df"
},
{
"payload": "4a65616e2d426170746973746520536179",
"ciphertext": "3e40f15f6f3a46ae446b253bf8b1d9ffb6ed9b174d272328ff91a7e2e5c79c07f5"
},
{
"payload": "457567656e2042f6686d20766f6e2042617765726b",
"ciphertext": "eb3f3515110702e047a6c9da4478b6ead94873c11c0f2d710ddb3f09fce024b3a58502ae3f"
}
]
},
]
}

View File

@ -458,7 +458,7 @@ bool NoiseDHState_Curve25519::hashPublicKey
case Noise::RemoteStatic25519PublicKey: case Noise::RemoteStatic25519PublicKey:
if (!(st.flags & HAVE_25519_REMOTE_STATIC_PUBLIC)) if (!(st.flags & HAVE_25519_REMOTE_STATIC_PUBLIC))
break; break;
sym->mixHash(st.re, sizeof(st.re)); sym->mixHash(st2.rs, sizeof(st2.rs));
return true; return true;
default: default:
return NoiseDHState_Curve25519_EphemOnly::hashPublicKey(sym, id); return NoiseDHState_Curve25519_EphemOnly::hashPublicKey(sym, id);

View File

@ -632,8 +632,10 @@ void NoiseHandshakeState::write_ee(NoiseHandshakeState::Packet &packet)
*/ */
void NoiseHandshakeState::write_es(NoiseHandshakeState::Packet &packet) void NoiseHandshakeState::write_es(NoiseHandshakeState::Packet &packet)
{ {
if (!dhState()->hasParameter(Noise::RemoteStaticPublicKey)) { if (!dhState()->hasParameter
// We don't have a remote static key. It probably should have (pty == Noise::Initiator ? Noise::RemoteStaticPublicKey
: Noise::LocalStaticPublicKey)) {
// We don't have the relevent static key. It probably should have
// been provided ahead of time when the handshake started. // been provided ahead of time when the handshake started.
packet.error = true; packet.error = true;
return; return;
@ -657,8 +659,10 @@ void NoiseHandshakeState::write_es(NoiseHandshakeState::Packet &packet)
*/ */
void NoiseHandshakeState::write_se(NoiseHandshakeState::Packet &packet) void NoiseHandshakeState::write_se(NoiseHandshakeState::Packet &packet)
{ {
if (!dhState()->hasParameter(Noise::LocalStaticPrivateKey)) { if (!dhState()->hasParameter
// We don't have a local static key. It probably should have (pty == Noise::Initiator ? Noise::LocalStaticPublicKey
: Noise::RemoteStaticPublicKey)) {
// We don't have a relevant static key. It probably should have
// been provided ahead of time when the handshake started. // been provided ahead of time when the handshake started.
packet.error = true; packet.error = true;
return; return;
@ -744,7 +748,7 @@ void NoiseHandshakeState::read_s(NoiseHandshakeState::Packet &packet)
int size = symmetricState()->decryptAndHash int size = symmetricState()->decryptAndHash
(s, len, packet.data + packet.posn, fullLen); (s, len, packet.data + packet.posn, fullLen);
if (size > 0) { if (size > 0) {
packet.posn += size; packet.posn += fullLen;
if (!dhState()->setParameter(Noise::RemoteStaticPublicKey, s, len)) if (!dhState()->setParameter(Noise::RemoteStaticPublicKey, s, len))
packet.error = true; packet.error = true;
} else { } else {
@ -804,6 +808,28 @@ void NoiseHandshakeState::read_s(NoiseHandshakeState::Packet &packet)
* \sa write_ss() * \sa write_ss()
*/ */
/**
* \brief Hashes a public key from the pre-message.
*
* \param packet The handshake packet that is being processed.
* \param initiator The public key to hash if party() is Noise::Initiator.
* \param responder The public key to hash if party() is Noise::Responder.
*
* The handshake will fail if the required key is not present.
*
* This function should be called within the subclass's writeTokens() and
* readTokens() implementations when message number 0 is processed.
*/
void NoiseHandshakeState::premessage
(NoiseHandshakeState::Packet &packet,
Noise::Parameter initiator, Noise::Parameter responder)
{
if (pty == Noise::Responder)
initiator = responder;
if (!dhState()->hashPublicKey(symmetricState(), initiator))
packet.error = true;
}
/** /**
* \fn void NoiseHandshakeState::setState(Noise::HandshakeState state) * \fn void NoiseHandshakeState::setState(Noise::HandshakeState state)
* \brief Sets the state of this handshake. * \brief Sets the state of this handshake.

View File

@ -107,6 +107,10 @@ protected:
void read_se(NoiseHandshakeState::Packet &packet) { write_se(packet); } void read_se(NoiseHandshakeState::Packet &packet) { write_se(packet); }
void read_ss(NoiseHandshakeState::Packet &packet) { write_ss(packet); } void read_ss(NoiseHandshakeState::Packet &packet) { write_ss(packet); }
void premessage
(NoiseHandshakeState::Packet &packet,
Noise::Parameter initiator, Noise::Parameter responder);
void setState(Noise::HandshakeState state) { st = state; } void setState(Noise::HandshakeState state) { st = state; }
private: private:

View File

@ -149,9 +149,10 @@ int NoiseSymmetricState_AESGCM_SHA256::encryptAndHash
if (st.hasKey) { if (st.hasKey) {
if (outputSize < 16 || (outputSize - 16) < inputSize) if (outputSize < 16 || (outputSize - 16) < inputSize)
return -1; return -1;
uint8_t iv[24]; uint8_t iv[12];
noiseAESGCMFormatIV(iv, st.n); noiseAESGCMFormatIV(iv, st.n);
cipher.setIV(iv, sizeof(iv)); cipher.setIV(iv, sizeof(iv));
cipher.addAuthData(st.h, sizeof(st.h));
cipher.encrypt(output, input, inputSize); cipher.encrypt(output, input, inputSize);
cipher.computeTag(output + inputSize, 16); cipher.computeTag(output + inputSize, 16);
mixHash(output, inputSize + 16); mixHash(output, inputSize + 16);
@ -174,10 +175,11 @@ int NoiseSymmetricState_AESGCM_SHA256::decryptAndHash
if (inputSize < 16 || outputSize < (inputSize - 16)) if (inputSize < 16 || outputSize < (inputSize - 16))
return -1; return -1;
outputSize = inputSize - 16; outputSize = inputSize - 16;
mixHash(input, inputSize); uint8_t iv[12];
uint8_t iv[24];
noiseAESGCMFormatIV(iv, st.n); noiseAESGCMFormatIV(iv, st.n);
cipher.setIV(iv, sizeof(iv)); cipher.setIV(iv, sizeof(iv));
cipher.addAuthData(st.h, sizeof(st.h));
mixHash(input, inputSize);
cipher.decrypt(output, input, outputSize); cipher.decrypt(output, input, outputSize);
if (cipher.checkTag(input + outputSize, 16)) { if (cipher.checkTag(input + outputSize, 16)) {
++st.n; ++st.n;

View File

@ -126,6 +126,7 @@ int NoiseSymmetricState_ChaChaPoly_BLAKE2s::encryptAndHash
uint64_t iv = htole64(st.n); uint64_t iv = htole64(st.n);
cipher.setKey(st.key, 32); cipher.setKey(st.key, 32);
cipher.setIV((const uint8_t *)&iv, sizeof(iv)); cipher.setIV((const uint8_t *)&iv, sizeof(iv));
cipher.addAuthData(st.h, sizeof(st.h));
cipher.encrypt(output, input, inputSize); cipher.encrypt(output, input, inputSize);
cipher.computeTag(output + inputSize, 16); cipher.computeTag(output + inputSize, 16);
mixHash(output, inputSize + 16); mixHash(output, inputSize + 16);
@ -148,11 +149,12 @@ int NoiseSymmetricState_ChaChaPoly_BLAKE2s::decryptAndHash
if (inputSize < 16 || outputSize < (inputSize - 16)) if (inputSize < 16 || outputSize < (inputSize - 16))
return -1; return -1;
outputSize = inputSize - 16; outputSize = inputSize - 16;
mixHash(input, inputSize);
ChaChaPoly cipher; ChaChaPoly cipher;
uint64_t iv = htole64(st.n); uint64_t iv = htole64(st.n);
cipher.setKey(st.key, 32); cipher.setKey(st.key, 32);
cipher.setIV((const uint8_t *)&iv, sizeof(iv)); cipher.setIV((const uint8_t *)&iv, sizeof(iv));
cipher.addAuthData(st.h, sizeof(st.h));
mixHash(input, inputSize);
cipher.decrypt(output, input, outputSize); cipher.decrypt(output, input, outputSize);
if (cipher.checkTag(input + outputSize, 16)) { if (cipher.checkTag(input + outputSize, 16)) {
++st.n; ++st.n;

View File

@ -126,6 +126,7 @@ int NoiseSymmetricState_ChaChaPoly_SHA256::encryptAndHash
uint64_t iv = htole64(st.n); uint64_t iv = htole64(st.n);
cipher.setKey(st.key, 32); cipher.setKey(st.key, 32);
cipher.setIV((const uint8_t *)&iv, sizeof(iv)); cipher.setIV((const uint8_t *)&iv, sizeof(iv));
cipher.addAuthData(st.h, sizeof(st.h));
cipher.encrypt(output, input, inputSize); cipher.encrypt(output, input, inputSize);
cipher.computeTag(output + inputSize, 16); cipher.computeTag(output + inputSize, 16);
mixHash(output, inputSize + 16); mixHash(output, inputSize + 16);
@ -148,11 +149,12 @@ int NoiseSymmetricState_ChaChaPoly_SHA256::decryptAndHash
if (inputSize < 16 || outputSize < (inputSize - 16)) if (inputSize < 16 || outputSize < (inputSize - 16))
return -1; return -1;
outputSize = inputSize - 16; outputSize = inputSize - 16;
mixHash(input, inputSize);
ChaChaPoly cipher; ChaChaPoly cipher;
uint64_t iv = htole64(st.n); uint64_t iv = htole64(st.n);
cipher.setKey(st.key, 32); cipher.setKey(st.key, 32);
cipher.setIV((const uint8_t *)&iv, sizeof(iv)); cipher.setIV((const uint8_t *)&iv, sizeof(iv));
cipher.addAuthData(st.h, sizeof(st.h));
mixHash(input, inputSize);
cipher.decrypt(output, input, outputSize); cipher.decrypt(output, input, outputSize);
if (cipher.checkTag(input + outputSize, 16)) { if (cipher.checkTag(input + outputSize, 16)) {
++st.n; ++st.n;

View File

@ -54,6 +54,8 @@ void NoiseHandshakeState_IK::writeTokens
(NoiseHandshakeState::Packet &packet, uint8_t msgnum) (NoiseHandshakeState::Packet &packet, uint8_t msgnum)
{ {
if (msgnum == 0) { if (msgnum == 0) {
premessage(packet, Noise::RemoteStaticPublicKey,
Noise::LocalStaticPublicKey);
write_e(packet); write_e(packet);
write_es(packet); write_es(packet);
write_s(packet); write_s(packet);
@ -70,6 +72,8 @@ void NoiseHandshakeState_IK::readTokens
(NoiseHandshakeState::Packet &packet, uint8_t msgnum) (NoiseHandshakeState::Packet &packet, uint8_t msgnum)
{ {
if (msgnum == 0) { if (msgnum == 0) {
premessage(packet, Noise::RemoteStaticPublicKey,
Noise::LocalStaticPublicKey);
read_e(packet); read_e(packet);
read_es(packet); read_es(packet);
read_s(packet); read_s(packet);

View File

@ -69,12 +69,6 @@ bool NoiseHandshakeState_XXfallback::startFallback
// Start the new handshake. // Start the new handshake.
start(party, prologue, prologueLen); start(party, prologue, prologueLen);
// Hash the initiator's ephemeral public key from the pre-message.
if (party == Noise::Initiator)
thisDH->hashPublicKey(symmetricState(), Noise::LocalEphemPublicKey);
else
thisDH->hashPublicKey(symmetricState(), Noise::RemoteEphemPublicKey);
// The responder writes first in a XXfallback handshake. // The responder writes first in a XXfallback handshake.
setState(party == Noise::Initiator ? Noise::Read : Noise::Write); setState(party == Noise::Initiator ? Noise::Read : Noise::Write);
return true; return true;
@ -91,6 +85,8 @@ void NoiseHandshakeState_XXfallback::writeTokens
(NoiseHandshakeState::Packet &packet, uint8_t msgnum) (NoiseHandshakeState::Packet &packet, uint8_t msgnum)
{ {
if (msgnum == 0) { if (msgnum == 0) {
premessage(packet, Noise::LocalEphemPublicKey,
Noise::RemoteEphemPublicKey);
write_e(packet); write_e(packet);
write_ee(packet); write_ee(packet);
write_s(packet); write_s(packet);
@ -106,6 +102,8 @@ void NoiseHandshakeState_XXfallback::readTokens
(NoiseHandshakeState::Packet &packet, uint8_t msgnum) (NoiseHandshakeState::Packet &packet, uint8_t msgnum)
{ {
if (msgnum == 0) { if (msgnum == 0) {
premessage(packet, Noise::LocalEphemPublicKey,
Noise::RemoteEphemPublicKey);
read_e(packet); read_e(packet);
read_ee(packet); read_ee(packet);
read_s(packet); read_s(packet);