diff --git a/host/Crypto/Makefile b/host/Crypto/Makefile index 34f5aa11..843a2df9 100644 --- a/host/Crypto/Makefile +++ b/host/Crypto/Makefile @@ -1,5 +1,5 @@ -.PHONY: all clean check +.PHONY: all clean check noise-check TOPDIR = ../.. SRCDIR = $(TOPDIR)/libraries/Crypto @@ -29,10 +29,11 @@ CPPFLAGS = \ -I$(TOPDIR)/libraries/CryptoLW/src \ -I$(TOPDIR)/libraries/CryptoLegacy/src \ -I$(TOPDIR)/libraries/NewHope \ - -I$(TOPDIR)/libraries/NoiseProtocol \ + -I$(TOPDIR)/libraries/NoiseProtocol/src \ -DHOST_BUILD CXXFLAGS = -g -Wall $(CPPFLAGS) +CCFLAGS = -g -Wall $(CPPFLAGS) SOURCES = \ Acorn128.cpp \ @@ -149,12 +150,23 @@ SKETCHES = \ TestSpeck/TestSpeck.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)) -DEPS = $(patsubst %.cpp,.depend/%.d,$(SOURCES)) +DEPS = $(patsubst %.cpp,.depend/%.d,$(DEP_SOURCES)) SKETCH_OUTPUTS = $(patsubst %.ino,%.sketch,$(SKETCHES)) -all: $(LIBRARY) +all: $(LIBRARY) $(TSTPROG1) + +$(TSTPROG1): $(TSTPROG1_OBJECTS) $(LIBRARY) + $(CXX) $(CXXFLAGS) -o $@ $(TSTPROG1_OBJECTS) -L. -lCrypto $(LIBRARY): $(OBJECTS) $(RM) $(LIBRARY) @@ -163,14 +175,18 @@ $(LIBRARY): $(OBJECTS) clean: $(RM) $(OBJECTS) $(LIBRARY) $(RM) $(SKETCH_OUTPUTS) + $(RM) $(TSTPROG1) $(TSTPROG1_OBJECTS) $(RM) -r .depend Test* -check: all $(SKETCH_OUTPUTS) +check: $(LIBRARY) $(SKETCH_OUTPUTS) @for sketch in $(SKETCH_OUTPUTS); do \ echo Running $$sketch; \ $$sketch | grep -i fail; \ done; exit 0 +noise-check: $(TSTPROG1) + @$(TSTPROG1) noise/test-vectors.txt + %.o: %.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/host/Crypto/noise/.gitignore b/host/Crypto/noise/.gitignore new file mode 100644 index 00000000..80435eb1 --- /dev/null +++ b/host/Crypto/noise/.gitignore @@ -0,0 +1 @@ +test-vector diff --git a/host/Crypto/noise/README b/host/Crypto/noise/README new file mode 100644 index 00000000..abebb0e9 --- /dev/null +++ b/host/Crypto/noise/README @@ -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. diff --git a/host/Crypto/noise/json-reader.cpp b/host/Crypto/noise/json-reader.cpp new file mode 100644 index 00000000..a2610bfd --- /dev/null +++ b/host/Crypto/noise/json-reader.cpp @@ -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 + +/* 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); +} diff --git a/host/Crypto/noise/json-reader.h b/host/Crypto/noise/json-reader.h new file mode 100644 index 00000000..5dc95df9 --- /dev/null +++ b/host/Crypto/noise/json-reader.h @@ -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 +#include +#include +#include +#include + +/** + * \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 diff --git a/host/Crypto/noise/test-vector.cpp b/host/Crypto/noise/test-vector.cpp new file mode 100644 index 00000000..ee49ff26 --- /dev/null +++ b/host/Crypto/noise/test-vector.cpp @@ -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 +#include "json-reader.h" +#include +#include +#include + +#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; +} diff --git a/host/Crypto/noise/test-vectors.txt b/host/Crypto/noise/test-vectors.txt new file mode 100644 index 00000000..ff9060b0 --- /dev/null +++ b/host/Crypto/noise/test-vectors.txt @@ -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" +} +] +}, +] +} diff --git a/libraries/NoiseProtocol/src/NoiseDHState_Curve25519.cpp b/libraries/NoiseProtocol/src/NoiseDHState_Curve25519.cpp index 360f1485..65b4e44d 100644 --- a/libraries/NoiseProtocol/src/NoiseDHState_Curve25519.cpp +++ b/libraries/NoiseProtocol/src/NoiseDHState_Curve25519.cpp @@ -458,7 +458,7 @@ bool NoiseDHState_Curve25519::hashPublicKey case Noise::RemoteStatic25519PublicKey: if (!(st.flags & HAVE_25519_REMOTE_STATIC_PUBLIC)) break; - sym->mixHash(st.re, sizeof(st.re)); + sym->mixHash(st2.rs, sizeof(st2.rs)); return true; default: return NoiseDHState_Curve25519_EphemOnly::hashPublicKey(sym, id); diff --git a/libraries/NoiseProtocol/src/NoiseHandshakeState.cpp b/libraries/NoiseProtocol/src/NoiseHandshakeState.cpp index 7901fb7a..4daad8d5 100644 --- a/libraries/NoiseProtocol/src/NoiseHandshakeState.cpp +++ b/libraries/NoiseProtocol/src/NoiseHandshakeState.cpp @@ -632,8 +632,10 @@ void NoiseHandshakeState::write_ee(NoiseHandshakeState::Packet &packet) */ void NoiseHandshakeState::write_es(NoiseHandshakeState::Packet &packet) { - if (!dhState()->hasParameter(Noise::RemoteStaticPublicKey)) { - // We don't have a remote static key. It probably should have + if (!dhState()->hasParameter + (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. packet.error = true; return; @@ -657,8 +659,10 @@ void NoiseHandshakeState::write_es(NoiseHandshakeState::Packet &packet) */ void NoiseHandshakeState::write_se(NoiseHandshakeState::Packet &packet) { - if (!dhState()->hasParameter(Noise::LocalStaticPrivateKey)) { - // We don't have a local static key. It probably should have + if (!dhState()->hasParameter + (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. packet.error = true; return; @@ -744,7 +748,7 @@ void NoiseHandshakeState::read_s(NoiseHandshakeState::Packet &packet) int size = symmetricState()->decryptAndHash (s, len, packet.data + packet.posn, fullLen); if (size > 0) { - packet.posn += size; + packet.posn += fullLen; if (!dhState()->setParameter(Noise::RemoteStaticPublicKey, s, len)) packet.error = true; } else { @@ -804,6 +808,28 @@ void NoiseHandshakeState::read_s(NoiseHandshakeState::Packet &packet) * \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) * \brief Sets the state of this handshake. diff --git a/libraries/NoiseProtocol/src/NoiseHandshakeState.h b/libraries/NoiseProtocol/src/NoiseHandshakeState.h index 0efdbaff..a6bd809c 100644 --- a/libraries/NoiseProtocol/src/NoiseHandshakeState.h +++ b/libraries/NoiseProtocol/src/NoiseHandshakeState.h @@ -107,6 +107,10 @@ protected: void read_se(NoiseHandshakeState::Packet &packet) { write_se(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; } private: diff --git a/libraries/NoiseProtocol/src/NoiseSymmetricState_AESGCM_SHA256.cpp b/libraries/NoiseProtocol/src/NoiseSymmetricState_AESGCM_SHA256.cpp index 9774b07d..bc03b421 100644 --- a/libraries/NoiseProtocol/src/NoiseSymmetricState_AESGCM_SHA256.cpp +++ b/libraries/NoiseProtocol/src/NoiseSymmetricState_AESGCM_SHA256.cpp @@ -149,9 +149,10 @@ int NoiseSymmetricState_AESGCM_SHA256::encryptAndHash if (st.hasKey) { if (outputSize < 16 || (outputSize - 16) < inputSize) return -1; - uint8_t iv[24]; + uint8_t iv[12]; noiseAESGCMFormatIV(iv, st.n); cipher.setIV(iv, sizeof(iv)); + cipher.addAuthData(st.h, sizeof(st.h)); cipher.encrypt(output, input, inputSize); cipher.computeTag(output + inputSize, 16); mixHash(output, inputSize + 16); @@ -174,10 +175,11 @@ int NoiseSymmetricState_AESGCM_SHA256::decryptAndHash if (inputSize < 16 || outputSize < (inputSize - 16)) return -1; outputSize = inputSize - 16; - mixHash(input, inputSize); - uint8_t iv[24]; + uint8_t iv[12]; noiseAESGCMFormatIV(iv, st.n); cipher.setIV(iv, sizeof(iv)); + cipher.addAuthData(st.h, sizeof(st.h)); + mixHash(input, inputSize); cipher.decrypt(output, input, outputSize); if (cipher.checkTag(input + outputSize, 16)) { ++st.n; diff --git a/libraries/NoiseProtocol/src/NoiseSymmetricState_ChaChaPoly_BLAKE2s.cpp b/libraries/NoiseProtocol/src/NoiseSymmetricState_ChaChaPoly_BLAKE2s.cpp index 90059aa1..f3231a09 100644 --- a/libraries/NoiseProtocol/src/NoiseSymmetricState_ChaChaPoly_BLAKE2s.cpp +++ b/libraries/NoiseProtocol/src/NoiseSymmetricState_ChaChaPoly_BLAKE2s.cpp @@ -126,6 +126,7 @@ int NoiseSymmetricState_ChaChaPoly_BLAKE2s::encryptAndHash uint64_t iv = htole64(st.n); cipher.setKey(st.key, 32); cipher.setIV((const uint8_t *)&iv, sizeof(iv)); + cipher.addAuthData(st.h, sizeof(st.h)); cipher.encrypt(output, input, inputSize); cipher.computeTag(output + inputSize, 16); mixHash(output, inputSize + 16); @@ -148,11 +149,12 @@ int NoiseSymmetricState_ChaChaPoly_BLAKE2s::decryptAndHash if (inputSize < 16 || outputSize < (inputSize - 16)) return -1; outputSize = inputSize - 16; - mixHash(input, inputSize); ChaChaPoly cipher; uint64_t iv = htole64(st.n); cipher.setKey(st.key, 32); cipher.setIV((const uint8_t *)&iv, sizeof(iv)); + cipher.addAuthData(st.h, sizeof(st.h)); + mixHash(input, inputSize); cipher.decrypt(output, input, outputSize); if (cipher.checkTag(input + outputSize, 16)) { ++st.n; diff --git a/libraries/NoiseProtocol/src/NoiseSymmetricState_ChaChaPoly_SHA256.cpp b/libraries/NoiseProtocol/src/NoiseSymmetricState_ChaChaPoly_SHA256.cpp index 0d5343a6..1c926ad9 100644 --- a/libraries/NoiseProtocol/src/NoiseSymmetricState_ChaChaPoly_SHA256.cpp +++ b/libraries/NoiseProtocol/src/NoiseSymmetricState_ChaChaPoly_SHA256.cpp @@ -126,6 +126,7 @@ int NoiseSymmetricState_ChaChaPoly_SHA256::encryptAndHash uint64_t iv = htole64(st.n); cipher.setKey(st.key, 32); cipher.setIV((const uint8_t *)&iv, sizeof(iv)); + cipher.addAuthData(st.h, sizeof(st.h)); cipher.encrypt(output, input, inputSize); cipher.computeTag(output + inputSize, 16); mixHash(output, inputSize + 16); @@ -148,11 +149,12 @@ int NoiseSymmetricState_ChaChaPoly_SHA256::decryptAndHash if (inputSize < 16 || outputSize < (inputSize - 16)) return -1; outputSize = inputSize - 16; - mixHash(input, inputSize); ChaChaPoly cipher; uint64_t iv = htole64(st.n); cipher.setKey(st.key, 32); cipher.setIV((const uint8_t *)&iv, sizeof(iv)); + cipher.addAuthData(st.h, sizeof(st.h)); + mixHash(input, inputSize); cipher.decrypt(output, input, outputSize); if (cipher.checkTag(input + outputSize, 16)) { ++st.n; diff --git a/libraries/NoiseProtocol/src/Noise_IK.cpp b/libraries/NoiseProtocol/src/Noise_IK.cpp index 1b6d036b..b400e149 100644 --- a/libraries/NoiseProtocol/src/Noise_IK.cpp +++ b/libraries/NoiseProtocol/src/Noise_IK.cpp @@ -54,6 +54,8 @@ void NoiseHandshakeState_IK::writeTokens (NoiseHandshakeState::Packet &packet, uint8_t msgnum) { if (msgnum == 0) { + premessage(packet, Noise::RemoteStaticPublicKey, + Noise::LocalStaticPublicKey); write_e(packet); write_es(packet); write_s(packet); @@ -70,6 +72,8 @@ void NoiseHandshakeState_IK::readTokens (NoiseHandshakeState::Packet &packet, uint8_t msgnum) { if (msgnum == 0) { + premessage(packet, Noise::RemoteStaticPublicKey, + Noise::LocalStaticPublicKey); read_e(packet); read_es(packet); read_s(packet); diff --git a/libraries/NoiseProtocol/src/Noise_XXfallback.cpp b/libraries/NoiseProtocol/src/Noise_XXfallback.cpp index 56f27f40..c383c922 100644 --- a/libraries/NoiseProtocol/src/Noise_XXfallback.cpp +++ b/libraries/NoiseProtocol/src/Noise_XXfallback.cpp @@ -69,12 +69,6 @@ bool NoiseHandshakeState_XXfallback::startFallback // Start the new handshake. 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. setState(party == Noise::Initiator ? Noise::Read : Noise::Write); return true; @@ -91,6 +85,8 @@ void NoiseHandshakeState_XXfallback::writeTokens (NoiseHandshakeState::Packet &packet, uint8_t msgnum) { if (msgnum == 0) { + premessage(packet, Noise::LocalEphemPublicKey, + Noise::RemoteEphemPublicKey); write_e(packet); write_ee(packet); write_s(packet); @@ -106,6 +102,8 @@ void NoiseHandshakeState_XXfallback::readTokens (NoiseHandshakeState::Packet &packet, uint8_t msgnum) { if (msgnum == 0) { + premessage(packet, Noise::LocalEphemPublicKey, + Noise::RemoteEphemPublicKey); read_e(packet); read_ee(packet); read_s(packet);