mirror of
https://github.com/taigrr/arduinolibs
synced 2025-01-18 04:33:12 -08:00
Mini protobufs implementation to support NoiseLink
This commit is contained in:
parent
dd9c99ff07
commit
e4b90184fd
@ -97,6 +97,7 @@ SOURCES += \
|
|||||||
Noise_NNpsk0_25519_ChaChaPoly_BLAKE2s.cpp \
|
Noise_NNpsk0_25519_ChaChaPoly_BLAKE2s.cpp \
|
||||||
Noise_NNpsk0_25519_ChaChaPoly_SHA256.cpp \
|
Noise_NNpsk0_25519_ChaChaPoly_SHA256.cpp \
|
||||||
Noise_NNpsk0.cpp \
|
Noise_NNpsk0.cpp \
|
||||||
|
NoiseProtobufs.cpp \
|
||||||
NoiseProtocolDescriptor.cpp \
|
NoiseProtocolDescriptor.cpp \
|
||||||
NoiseSymmetricState_AESGCM_SHA256.cpp \
|
NoiseSymmetricState_AESGCM_SHA256.cpp \
|
||||||
NoiseSymmetricState_ChaChaPoly_BLAKE2s.cpp \
|
NoiseSymmetricState_ChaChaPoly_BLAKE2s.cpp \
|
||||||
|
679
libraries/NoiseProtocol/src/NoiseProtobufs.cpp
Normal file
679
libraries/NoiseProtocol/src/NoiseProtobufs.cpp
Normal file
@ -0,0 +1,679 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 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 "NoiseProtobufs.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class NoiseProtobufFormatter NoiseProtobufs.h <NoiseProtobufs.h>
|
||||||
|
* \brief Formats data into a buffer using the protobufs format.
|
||||||
|
*
|
||||||
|
* This class provides support for creating messages in the protobufs
|
||||||
|
* format. Fields in a message consist of a tag followed by a value.
|
||||||
|
*
|
||||||
|
* This implementation provides a subset of the full protobufs encoding,
|
||||||
|
* limited to the types bool, uint32, uint64, sint32, sint64, string,
|
||||||
|
* bytes, and embedded messages. In addition, tag numbers are limited
|
||||||
|
* to unsigned 8-bit values.
|
||||||
|
*
|
||||||
|
* Reference: https://developers.google.com/protocol-buffers/docs/encoding
|
||||||
|
*
|
||||||
|
* \sa NoiseProtobufParser
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \type NoiseProtobufTag_t
|
||||||
|
* \brief Integer type that defines a protobuf tag number (unsigned 8-bit).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Constructs a new protobuf formatter.
|
||||||
|
*
|
||||||
|
* \param data Points to the buffer to format fields into.
|
||||||
|
* \param maxSize Maximum size of the buffer in bytes.
|
||||||
|
*/
|
||||||
|
NoiseProtobufFormatter::NoiseProtobufFormatter(void *data, size_t maxSize)
|
||||||
|
: buf((uint8_t *)data)
|
||||||
|
, maxsz(maxSize)
|
||||||
|
, pos(0)
|
||||||
|
, err(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Destroys this protobuf formatter.
|
||||||
|
*/
|
||||||
|
NoiseProtobufFormatter::~NoiseProtobufFormatter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn size_t NoiseProtobufFormatter::size() const
|
||||||
|
* \brief Returns the final size of the data after all fields have been added.
|
||||||
|
*
|
||||||
|
* \return The number of bytes that were written to the buffer.
|
||||||
|
*
|
||||||
|
* If an error has occurred, then size() will be invalid.
|
||||||
|
*
|
||||||
|
* \sa error()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn bool NoiseProtobufFormatter::error() const
|
||||||
|
* \brief Determine if an error occurred during formatting.
|
||||||
|
*
|
||||||
|
* \return Returns true if the buffer ran out of space while trying to
|
||||||
|
* write the fields; false if formatting was successful.
|
||||||
|
*
|
||||||
|
* \sa size()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn void NoiseProtobufFormatter::addBool(NoiseProtobufTag_t tag, bool value)
|
||||||
|
* \brief Adds a boolean field.
|
||||||
|
*
|
||||||
|
* \param tag Tag number for the field.
|
||||||
|
* \param value Boolean value for the field.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn void NoiseProtobufFormatter::addUInt32(NoiseProtobufTag_t tag, uint32_t value)
|
||||||
|
* \brief Adds an unsigned 32-bit integer field.
|
||||||
|
*
|
||||||
|
* \param tag Tag number for the field.
|
||||||
|
* \param value Unsigned 32-bit value for the field.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn void NoiseProtobufFormatter::addUInt64(NoiseProtobufTag_t tag, uint64_t value)
|
||||||
|
* \brief Adds an unsigned 64-bit integer field.
|
||||||
|
*
|
||||||
|
* \param tag Tag number for the field.
|
||||||
|
* \param value Unsigned 64-bit value for the field.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn void NoiseProtobufFormatter::addInt32(NoiseProtobufTag_t tag, int32_t value)
|
||||||
|
* \brief Adds a signed 32-bit integer field.
|
||||||
|
*
|
||||||
|
* \param tag Tag number for the field.
|
||||||
|
* \param value Signed 32-bit value for the field.
|
||||||
|
*
|
||||||
|
* This function uses the ZigZag encoding for the signed integer value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn void NoiseProtobufFormatter::addInt64(NoiseProtobufTag_t tag, int64_t value)
|
||||||
|
* \brief Adds a signed 64-bit integer field.
|
||||||
|
*
|
||||||
|
* \param tag Tag number for the field.
|
||||||
|
* \param value Signed 64-bit value for the field.
|
||||||
|
*
|
||||||
|
* This function uses the ZigZag encoding for the signed integer value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Adds an arbitrary byte array as a field.
|
||||||
|
*
|
||||||
|
* \param tag Tag number for the field.
|
||||||
|
* \param data Points to the array of bytes for the field's value.
|
||||||
|
* \param len Number of bytes in the array.
|
||||||
|
*/
|
||||||
|
void NoiseProtobufFormatter::addBytes
|
||||||
|
(NoiseProtobufTag_t tag, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
if (!data && len > 0) {
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addHeader(tag, 2);
|
||||||
|
if (sizeof(len) <= 4)
|
||||||
|
addVarUInt32(len);
|
||||||
|
else
|
||||||
|
addVarUInt64(len);
|
||||||
|
if (!err) {
|
||||||
|
if (len <= (maxsz - pos)) {
|
||||||
|
memcpy(buf + pos, data, len);
|
||||||
|
pos += len;
|
||||||
|
} else {
|
||||||
|
err = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Adds a NUL-terminated string as a field.
|
||||||
|
*
|
||||||
|
* \param tag Tag number for the field.
|
||||||
|
* \param str String value for the field, NUL-terminated.
|
||||||
|
*
|
||||||
|
* If \a str is NULL, then an empty string will be added.
|
||||||
|
*/
|
||||||
|
void NoiseProtobufFormatter::addString(NoiseProtobufTag_t tag, const char *str)
|
||||||
|
{
|
||||||
|
addBytes(tag, str, str ? strlen(str) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn void NoiseProtobufFormatter::addString(NoiseProtobufTag_t tag, const char *str, size_t len)
|
||||||
|
* \brief Adds a length-delimited string as a field.
|
||||||
|
*
|
||||||
|
* \param tag Tag number for the field.
|
||||||
|
* \param str String value for the field.
|
||||||
|
* \param len Number of bytes in the string value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ADD_BYTE(b) \
|
||||||
|
do { \
|
||||||
|
if (!err) { \
|
||||||
|
if (pos < maxsz) { \
|
||||||
|
buf[pos++] = (uint8_t)(b); \
|
||||||
|
} else { \
|
||||||
|
err = true; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Starts an embedded message.
|
||||||
|
*
|
||||||
|
* \param tag Tag number for the embedded message.
|
||||||
|
*
|
||||||
|
* \return A reference to the start of the embedded message, to be passed
|
||||||
|
* to endEmbedded() when the application has finished adding nested fields.
|
||||||
|
*
|
||||||
|
* \note The size of the embedded data is limited to no more than 65535 bytes,
|
||||||
|
* to simplify the implementation of endEmbedded(). Since Noise messages are
|
||||||
|
* also limited to no more than 65535 bytes, this shouldn't be a problem
|
||||||
|
* in practice.
|
||||||
|
*
|
||||||
|
* \sa endEmbedded()
|
||||||
|
*/
|
||||||
|
size_t NoiseProtobufFormatter::startEmbedded(NoiseProtobufTag_t tag)
|
||||||
|
{
|
||||||
|
addHeader(tag, 2);
|
||||||
|
size_t start = pos;
|
||||||
|
ADD_BYTE(0); // Placeholder to be filled in by endEmbedded().
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Ends an embedded message and returns to the previous level.
|
||||||
|
*
|
||||||
|
* \param start The value that was returned from startEmbedded() at the
|
||||||
|
* beginning of the embedded message.
|
||||||
|
*
|
||||||
|
* \sa startEmbedded()
|
||||||
|
*/
|
||||||
|
void NoiseProtobufFormatter::endEmbedded(size_t start)
|
||||||
|
{
|
||||||
|
// If an error already occurred, then bail out.
|
||||||
|
if (err)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If the starting position is invalid, then bail out.
|
||||||
|
if (start >= pos) {
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the actual length of the embedded message.
|
||||||
|
size_t len = pos - start - 1;
|
||||||
|
|
||||||
|
// Encode the length as 1, 2, or 3 bytes. For the 2 and 3 byte
|
||||||
|
// versions we need to shift the message data up to make room.
|
||||||
|
if (len < (1U << 7)) {
|
||||||
|
buf[start] = (uint8_t)len;
|
||||||
|
} else if (len < (1U << 14)) {
|
||||||
|
if (pos < maxsz) {
|
||||||
|
memmove(buf + start + 2, buf + start + 1, len);
|
||||||
|
++pos;
|
||||||
|
buf[start] = (uint8_t)(len | 0x80);
|
||||||
|
buf[start + 1] = (uint8_t)(len >> 7);
|
||||||
|
} else {
|
||||||
|
err = true;
|
||||||
|
}
|
||||||
|
} else if (len < (1U << 16)) {
|
||||||
|
if ((pos + 1) < maxsz) {
|
||||||
|
memmove(buf + start + 3, buf + start + 1, len);
|
||||||
|
pos += 2;
|
||||||
|
buf[start] = (uint8_t)(len | 0x80);
|
||||||
|
buf[start + 1] = (uint8_t)((len >> 7) | 0x80);
|
||||||
|
buf[start + 2] = (uint8_t)(len >> 14);
|
||||||
|
} else {
|
||||||
|
err = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn void NoiseProtobufFormatter::addHeader(NoiseProtobufTag_t tag, uint8_t wireType)
|
||||||
|
* \brief Adds a protobuf tag header.
|
||||||
|
*
|
||||||
|
* \param tag Tag number for the field.
|
||||||
|
* \param wireType Protobuf wire type for the field; 0 for integer, 2 for
|
||||||
|
* length-delimited.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Adds an unsigned 14-bit value as a varint.
|
||||||
|
*
|
||||||
|
* \param value Unsigned 14-bit value to add.
|
||||||
|
*/
|
||||||
|
void NoiseProtobufFormatter::addVarUInt14(uint16_t value)
|
||||||
|
{
|
||||||
|
if (value < (1U << 7)) {
|
||||||
|
ADD_BYTE(value);
|
||||||
|
} else {
|
||||||
|
ADD_BYTE(value | 0x80);
|
||||||
|
ADD_BYTE(value >> 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Adds an unsigned 32-bit value as a varint.
|
||||||
|
*
|
||||||
|
* \param value Unsigned 32-bit value to add.
|
||||||
|
*/
|
||||||
|
void NoiseProtobufFormatter::addVarUInt32(uint32_t value)
|
||||||
|
{
|
||||||
|
if (value < (1U << 7)) {
|
||||||
|
ADD_BYTE(value);
|
||||||
|
} else if (value < (1U << 14)) {
|
||||||
|
ADD_BYTE(value | 0x80);
|
||||||
|
ADD_BYTE(value >> 7);
|
||||||
|
} else if (value < (1U << 21)) {
|
||||||
|
ADD_BYTE(value | 0x80);
|
||||||
|
ADD_BYTE((value >> 7) | 0x80);
|
||||||
|
ADD_BYTE(value >> 14);
|
||||||
|
} else if (value < (1U << 28)) {
|
||||||
|
ADD_BYTE(value | 0x80);
|
||||||
|
ADD_BYTE((value >> 7) | 0x80);
|
||||||
|
ADD_BYTE((value >> 14) | 0x80);
|
||||||
|
ADD_BYTE(value >> 21);
|
||||||
|
} else {
|
||||||
|
ADD_BYTE(value | 0x80);
|
||||||
|
ADD_BYTE((value >> 7) | 0x80);
|
||||||
|
ADD_BYTE((value >> 14) | 0x80);
|
||||||
|
ADD_BYTE((value >> 21) | 0x80);
|
||||||
|
ADD_BYTE(value >> 28);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Adds an unsigned 64-bit value as a varint.
|
||||||
|
*
|
||||||
|
* \param value Unsigned 64-bit value to add.
|
||||||
|
*/
|
||||||
|
void NoiseProtobufFormatter::addVarUInt64(uint64_t value)
|
||||||
|
{
|
||||||
|
if (value < 0x100000000ULL) {
|
||||||
|
addVarUInt32((uint32_t)value);
|
||||||
|
} else {
|
||||||
|
while (value >= (1ULL << 7)) {
|
||||||
|
ADD_BYTE(value | 0x80);
|
||||||
|
value >>= 7;
|
||||||
|
}
|
||||||
|
ADD_BYTE(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class NoiseProtobufParser NoiseProtobufs.h <NoiseProtobufs.h>
|
||||||
|
* \brief Parses data from a buffer using the protobufs format.
|
||||||
|
*
|
||||||
|
* This class provides support for parsing messages in the protobufs
|
||||||
|
* format. Fields in a message consist of a tag followed by a value.
|
||||||
|
*
|
||||||
|
* This implementation provides a subset of the full protobufs encoding,
|
||||||
|
* limited to the types bool, uint32, uint64, sint32, sint64, string,
|
||||||
|
* bytes, and embedded messages. In addition, tag numbers are limited
|
||||||
|
* to unsigned 8-bit values.
|
||||||
|
*
|
||||||
|
* Reference: https://developers.google.com/protocol-buffers/docs/encoding
|
||||||
|
*
|
||||||
|
* \sa NoiseProtobufFormatter
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Constructs a new protobuf parser for reading the contents
|
||||||
|
* of a buffer.
|
||||||
|
*
|
||||||
|
* \param data Points to the buffer to parse.
|
||||||
|
* \param size Size of the buffer in bytes.
|
||||||
|
*/
|
||||||
|
NoiseProtobufParser::NoiseProtobufParser(const void *data, size_t size)
|
||||||
|
: buf((const uint8_t *)data)
|
||||||
|
, pos(0)
|
||||||
|
, end(size)
|
||||||
|
, ptr(0)
|
||||||
|
, value(0)
|
||||||
|
, wire(0xFF)
|
||||||
|
, err(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Destroys this protobuf parser.
|
||||||
|
*/
|
||||||
|
NoiseProtobufParser::~NoiseProtobufParser()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn bool NoiseProtobufParser::error() const
|
||||||
|
* \brief Determine if an error occurred during parsing.
|
||||||
|
*
|
||||||
|
* \return Returns true if malformed data was found while parsing,
|
||||||
|
* false if parsing was successful.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reads the next field from a protobuf-formatted message.
|
||||||
|
*
|
||||||
|
* \param tag Returns the tag number for the next field.
|
||||||
|
*
|
||||||
|
* \return Returns true if a new field was found, or false at the end
|
||||||
|
* of the message. Also returns false if an error occurs.
|
||||||
|
*/
|
||||||
|
bool NoiseProtobufParser::readNext(NoiseProtobufTag_t &tag)
|
||||||
|
{
|
||||||
|
tag = 0;
|
||||||
|
if (err || pos >= end)
|
||||||
|
return false;
|
||||||
|
uint64_t header = readVarint();
|
||||||
|
if (err)
|
||||||
|
return false;
|
||||||
|
if (header >= (256U << 3)) {
|
||||||
|
err = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tag = (NoiseProtobufTag_t)(header >> 3);
|
||||||
|
wire = (uint8_t)(header & 0x07);
|
||||||
|
if (wire == 0) {
|
||||||
|
value = readVarint();
|
||||||
|
return !err;
|
||||||
|
} else if (wire == 2) {
|
||||||
|
value = readVarint();
|
||||||
|
if (err)
|
||||||
|
return false;
|
||||||
|
if (value > (end - pos)) {
|
||||||
|
err = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ptr = pos;
|
||||||
|
pos += (size_t)value;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
err = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reads a boolean value from the current field.
|
||||||
|
*
|
||||||
|
* \return The boolean value in the current field.
|
||||||
|
*
|
||||||
|
* This function will raise an error() if the current field is not boolean.
|
||||||
|
*/
|
||||||
|
bool NoiseProtobufParser::readBool()
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
return false;
|
||||||
|
if (wire != 0) {
|
||||||
|
err = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return value != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reads an unsigned 32-bit integer value from the current field.
|
||||||
|
*
|
||||||
|
* \return The integer value in the current field.
|
||||||
|
*
|
||||||
|
* This function will raise an error() if the current field is not an
|
||||||
|
* integer or the value is out of range.
|
||||||
|
*
|
||||||
|
* \sa readUInt64(), readInt32()
|
||||||
|
*/
|
||||||
|
uint32_t NoiseProtobufParser::readUInt32()
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
return 0;
|
||||||
|
if (wire != 0 || value >= 0x100000000ULL) {
|
||||||
|
err = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (uint32_t)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reads an unsigned 64-bit integer value from the current field.
|
||||||
|
*
|
||||||
|
* \return The integer value in the current field.
|
||||||
|
*
|
||||||
|
* This function will raise an error() if the current field is not an integer.
|
||||||
|
*
|
||||||
|
* \sa readUInt32(), readInt64()
|
||||||
|
*/
|
||||||
|
uint64_t NoiseProtobufParser::readUInt64()
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
return 0;
|
||||||
|
if (wire != 0) {
|
||||||
|
err = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reads a signed 32-bit integer value from the current field.
|
||||||
|
*
|
||||||
|
* \return The integer value in the current field.
|
||||||
|
*
|
||||||
|
* This function uses the ZigZag encoding for the signed integer value.
|
||||||
|
* An error() is raised if the current field is not an integer or the value
|
||||||
|
* is out of range.
|
||||||
|
*
|
||||||
|
* \sa readInt64(), readUInt32()
|
||||||
|
*/
|
||||||
|
int32_t NoiseProtobufParser::readInt32()
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
return 0;
|
||||||
|
if (wire != 0 || value >= 0x100000000ULL) {
|
||||||
|
err = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ((int32_t)(value >> 1)) ^ (-((int32_t)(value & 0x01)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reads a signed 64-bit integer value from the current field.
|
||||||
|
*
|
||||||
|
* \return The integer value in the current field.
|
||||||
|
*
|
||||||
|
* This function uses the ZigZag encoding for the signed integer value.
|
||||||
|
* An error() is raised if the current field is not an integer.
|
||||||
|
*
|
||||||
|
* \sa readInt32(), readUInt64()
|
||||||
|
*/
|
||||||
|
int64_t NoiseProtobufParser::readInt64()
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
return 0;
|
||||||
|
if (wire != 0) {
|
||||||
|
err = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ((int64_t)(value >> 1)) ^ (-((int64_t)(value & 0x01)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reads a string value from the current field.
|
||||||
|
*
|
||||||
|
* \param str Returns a pointer to the start of the string. This points
|
||||||
|
* directly into the original buffer and will not be NUL-terminated.
|
||||||
|
*
|
||||||
|
* \return The length of the string in bytes.
|
||||||
|
*
|
||||||
|
* This function will raise an error() if the current field is not
|
||||||
|
* length-delimited.
|
||||||
|
*
|
||||||
|
* \sa readBytes()
|
||||||
|
*/
|
||||||
|
size_t NoiseProtobufParser::readString(const char * &str)
|
||||||
|
{
|
||||||
|
if (err) {
|
||||||
|
str = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (wire != 2) {
|
||||||
|
err = true;
|
||||||
|
str = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
str = (const char *)(buf + ptr);
|
||||||
|
return (size_t)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reads a byte array from the current field.
|
||||||
|
*
|
||||||
|
* \param str Returns a pointer to the start of the byte array.
|
||||||
|
*
|
||||||
|
* \return The length of the byte array in bytes.
|
||||||
|
*
|
||||||
|
* This function will raise an error() if the current field is not
|
||||||
|
* length-delimited.
|
||||||
|
*
|
||||||
|
* \sa readString()
|
||||||
|
*/
|
||||||
|
size_t NoiseProtobufParser::readBytes(const void * &data)
|
||||||
|
{
|
||||||
|
if (err) {
|
||||||
|
data = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (wire != 2) {
|
||||||
|
err = true;
|
||||||
|
data = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
data = (const void *)(buf + ptr);
|
||||||
|
return (size_t)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Starts parsing an embedded message.
|
||||||
|
*
|
||||||
|
* \return A value to pass to endEmbedded() to pop out a level at the
|
||||||
|
* end of the embedded message.
|
||||||
|
*
|
||||||
|
* This function will raise an error() if the current field is not
|
||||||
|
* length-delimited.
|
||||||
|
*
|
||||||
|
* The following is an example of descending into an embedded message
|
||||||
|
* to parse all of the fields, and then returning to the previous
|
||||||
|
* message level at the end:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* size_t posn = parser.startEmbedded();
|
||||||
|
* while (parser.readNext(tag)) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* parser.endEmbedded(posn);
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* \sa endEmbedded()
|
||||||
|
*/
|
||||||
|
size_t NoiseProtobufParser::startEmbedded()
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
return 0;
|
||||||
|
if (wire != 2) {
|
||||||
|
err = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t prev = end;
|
||||||
|
end = pos;
|
||||||
|
pos = ptr;
|
||||||
|
wire = 0xFF;
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Ends parsing of an embedded message.
|
||||||
|
*
|
||||||
|
* \param posn The position in the outer message to return to. This must be
|
||||||
|
* the return value from startEmbedded() for the same nesting level.
|
||||||
|
*
|
||||||
|
* \sa startEmbedded()
|
||||||
|
*/
|
||||||
|
void NoiseProtobufParser::endEmbedded(size_t posn)
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
return;
|
||||||
|
if (posn < end) {
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pos = end;
|
||||||
|
end = posn;
|
||||||
|
wire = 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reads a variable-length integer from the message.
|
||||||
|
*
|
||||||
|
* \return The integer value.
|
||||||
|
*/
|
||||||
|
uint64_t NoiseProtobufParser::readVarint()
|
||||||
|
{
|
||||||
|
uint64_t value = 0;
|
||||||
|
unsigned bit = 0;
|
||||||
|
while (pos < end && (buf[pos] & 0x80) != 0) {
|
||||||
|
value |= ((uint64_t)(buf[pos] & 0x7F)) << bit;
|
||||||
|
bit += 7;
|
||||||
|
if (bit >= 63) {
|
||||||
|
err = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
if (pos >= end) {
|
||||||
|
err = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (bit >= 63 && (buf[pos] & 0xFE) != 0) {
|
||||||
|
err = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
value |= ((uint64_t)(buf[pos] & 0x7F)) << bit;
|
||||||
|
++pos;
|
||||||
|
return value;
|
||||||
|
}
|
205
libraries/NoiseProtocol/src/NoiseProtobufs.h
Normal file
205
libraries/NoiseProtocol/src/NoiseProtobufs.h
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NOISE_PROTOBUFS_h
|
||||||
|
#define NOISE_PROTOBUFS_h
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef uint8_t NoiseProtobufTag_t;
|
||||||
|
|
||||||
|
namespace Noise
|
||||||
|
{
|
||||||
|
// Tags for all NoiseLink negotiation options, for convenience.
|
||||||
|
// Not all of these are used for the NoiseTinyLink profile but we
|
||||||
|
// provide them in case we need more of full NoiseLink in the future.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Protobuf tags for NoiseLink session negotiation requests
|
||||||
|
* from the client.
|
||||||
|
*/
|
||||||
|
enum NegotiationRequest
|
||||||
|
{
|
||||||
|
ReqServerName = 1, /**< Server name */
|
||||||
|
ReqInitialProtocol = 2, /**< Initial Noise protocol to try */
|
||||||
|
ReqSwitchProtocol = 3, /**< List of supported fallback protocols */
|
||||||
|
ReqRetryProtocol = 4, /**< List of other non-fallback protocols */
|
||||||
|
ReqRejectedProtocol = 5, /**< Previous protocol rejected by server */
|
||||||
|
ReqPSKId = 6 /**< Identifier for the client's PSK */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Protobuf tags for NoiseLink session negotiation responses
|
||||||
|
* from the server.
|
||||||
|
*/
|
||||||
|
enum NegotiationResponse
|
||||||
|
{
|
||||||
|
RespSwitchProtocol = 3, /**< Switch to a fallback protocol */
|
||||||
|
RespRetryProtocol = 4, /**< Retry with a non-fallback protocol */
|
||||||
|
RespRejected = 5 /**< Server totally rejected the client */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Protobuf tags for NoiseLink options that may appear in
|
||||||
|
* handshake message payloads.
|
||||||
|
*/
|
||||||
|
enum PayloadOptions
|
||||||
|
{
|
||||||
|
OptEvidenceReqType = 1, /**< Evidence request type */
|
||||||
|
OptEvidenceBlobType = 2, /**< Evidence blob type */
|
||||||
|
OptEvidenceBlob = 3, /**< Evidence blob */
|
||||||
|
OptPSKId = 4, /**< Identifier for the client's PSK */
|
||||||
|
OptTransport = 5 /**< Embedded transport options */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Protobuf tags for NoiseLink transport options that may
|
||||||
|
* appear in handshake message payloads within an embedded message
|
||||||
|
* with the tag number OptTransport.
|
||||||
|
*/
|
||||||
|
enum TransportOptions
|
||||||
|
{
|
||||||
|
TrMaxSendLength = 1, /**< Maximum send buffer length */
|
||||||
|
TrMaxRecvLength = 2, /**< Maximum receive buffer length */
|
||||||
|
TrContinuousRekey = 3, /**< Rekey after every message */
|
||||||
|
TrShortTerminated = 4 /**< Terminate session on short message */
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class NoiseProtobufFormatter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NoiseProtobufFormatter(void *data, size_t maxSize);
|
||||||
|
~NoiseProtobufFormatter();
|
||||||
|
|
||||||
|
size_t size() const { return pos; }
|
||||||
|
bool error() const { return err; }
|
||||||
|
|
||||||
|
void addBool(NoiseProtobufTag_t tag, bool value);
|
||||||
|
|
||||||
|
void addUInt32(NoiseProtobufTag_t tag, uint32_t value);
|
||||||
|
void addUInt64(NoiseProtobufTag_t tag, uint64_t value);
|
||||||
|
void addInt32(NoiseProtobufTag_t tag, int32_t value);
|
||||||
|
void addInt64(NoiseProtobufTag_t tag, int64_t value);
|
||||||
|
|
||||||
|
void addBytes(NoiseProtobufTag_t tag, const void *data, size_t len);
|
||||||
|
|
||||||
|
void addString(NoiseProtobufTag_t tag, const char *str);
|
||||||
|
void addString(NoiseProtobufTag_t tag, const char *str, size_t len);
|
||||||
|
|
||||||
|
size_t startEmbedded(NoiseProtobufTag_t tag);
|
||||||
|
void endEmbedded(size_t start);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t *buf;
|
||||||
|
size_t maxsz;
|
||||||
|
size_t pos;
|
||||||
|
bool err;
|
||||||
|
|
||||||
|
void addHeader(NoiseProtobufTag_t tag, uint8_t wireType);
|
||||||
|
void addVarUInt14(uint16_t value);
|
||||||
|
void addVarUInt32(uint32_t value);
|
||||||
|
void addVarUInt64(uint64_t value);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NoiseProtobufParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NoiseProtobufParser(const void *data, size_t size);
|
||||||
|
~NoiseProtobufParser();
|
||||||
|
|
||||||
|
bool error() const { return err; }
|
||||||
|
|
||||||
|
bool readNext(NoiseProtobufTag_t &tag);
|
||||||
|
|
||||||
|
bool readBool();
|
||||||
|
uint32_t readUInt32();
|
||||||
|
uint64_t readUInt64();
|
||||||
|
int32_t readInt32();
|
||||||
|
int64_t readInt64();
|
||||||
|
|
||||||
|
size_t readString(const char * &str);
|
||||||
|
size_t readBytes(const void * &data);
|
||||||
|
|
||||||
|
size_t startEmbedded();
|
||||||
|
void endEmbedded(size_t posn);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const uint8_t *buf;
|
||||||
|
size_t pos;
|
||||||
|
size_t end;
|
||||||
|
size_t ptr;
|
||||||
|
uint64_t value;
|
||||||
|
uint8_t wire;
|
||||||
|
bool err;
|
||||||
|
|
||||||
|
uint64_t readVarint();
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void NoiseProtobufFormatter::addHeader
|
||||||
|
(NoiseProtobufTag_t tag, uint8_t wireType)
|
||||||
|
{
|
||||||
|
addVarUInt14((((uint16_t)tag) << 3) | wireType);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void NoiseProtobufFormatter::addBool(NoiseProtobufTag_t tag, bool value)
|
||||||
|
{
|
||||||
|
addHeader(tag, 0);
|
||||||
|
addVarUInt14(value ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void NoiseProtobufFormatter::addUInt32
|
||||||
|
(NoiseProtobufTag_t tag, uint32_t value)
|
||||||
|
{
|
||||||
|
addHeader(tag, 0);
|
||||||
|
addVarUInt32(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void NoiseProtobufFormatter::addUInt64
|
||||||
|
(NoiseProtobufTag_t tag, uint64_t value)
|
||||||
|
{
|
||||||
|
addHeader(tag, 0);
|
||||||
|
addVarUInt64(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void NoiseProtobufFormatter::addInt32
|
||||||
|
(NoiseProtobufTag_t tag, int32_t value)
|
||||||
|
{
|
||||||
|
addHeader(tag, 0);
|
||||||
|
addVarUInt32((uint32_t)((value << 1) ^ (value >> 31)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void NoiseProtobufFormatter::addInt64
|
||||||
|
(NoiseProtobufTag_t tag, int64_t value)
|
||||||
|
{
|
||||||
|
addHeader(tag, 0);
|
||||||
|
addVarUInt64((uint64_t)((value << 1) ^ (value >> 63)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void NoiseProtobufFormatter::addString
|
||||||
|
(NoiseProtobufTag_t tag, const char *str, size_t len)
|
||||||
|
{
|
||||||
|
addBytes(tag, str, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user