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:
parent
fa1400ea83
commit
8fd4a994dc
@ -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 $<
|
||||
|
||||
|
1
host/Crypto/noise/.gitignore
vendored
Normal file
1
host/Crypto/noise/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
test-vector
|
8
host/Crypto/noise/README
Normal file
8
host/Crypto/noise/README
Normal 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.
|
253
host/Crypto/noise/json-reader.cpp
Normal file
253
host/Crypto/noise/json-reader.cpp
Normal 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);
|
||||
}
|
75
host/Crypto/noise/json-reader.h
Normal file
75
host/Crypto/noise/json-reader.h
Normal 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
|
815
host/Crypto/noise/test-vector.cpp
Normal file
815
host/Crypto/noise/test-vector.cpp
Normal 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;
|
||||
}
|
331
host/Crypto/noise/test-vectors.txt
Normal file
331
host/Crypto/noise/test-vectors.txt
Normal 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"
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user