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

Define the NoiseClient and NoiseServer API's

This commit is contained in:
Rhys Weatherley 2018-06-30 04:15:43 +10:00
parent ff04a61efa
commit 3b5da67919
26 changed files with 2011 additions and 34 deletions

View File

@ -85,6 +85,7 @@ SOURCES += \
NoiseCipherState_AESGCM.cpp \
NoiseCipherState_ChaChaPoly.cpp \
NoiseCipherState.cpp \
NoiseClient.cpp \
NoiseDHState_Curve25519.cpp \
NoiseDHState.cpp \
NoiseHandshakeState.cpp \
@ -92,12 +93,13 @@ SOURCES += \
Noise_IK_25519_ChaChaPoly_BLAKE2s.cpp \
Noise_IK_25519_ChaChaPoly_SHA256.cpp \
Noise_IK.cpp \
NoiseNamespace.cpp \
NoiseLinkOptions.cpp \
Noise_NNpsk0_25519_AESGCM_SHA256.cpp \
Noise_NNpsk0_25519_ChaChaPoly_BLAKE2s.cpp \
Noise_NNpsk0_25519_ChaChaPoly_SHA256.cpp \
Noise_NNpsk0.cpp \
NoiseProtobufs.cpp \
NoiseServer.cpp \
NoiseSymmetricState_AESGCM_SHA256.cpp \
NoiseSymmetricState_ChaChaPoly_BLAKE2s.cpp \
NoiseSymmetricState_ChaChaPoly_SHA256.cpp \

55
host/emulation/Client.h Normal file
View File

@ -0,0 +1,55 @@
/*
Client.h - Base class that provides Client
Copyright (c) 2011 Adrian McEwen. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef client_h
#define client_h
// XXX: This file has been hacked up to make it compile in the
// emulation environment. Not intended for real work.
//#include "Print.h"
//#include "Stream.h"
//#include "IPAddress.h"
#include <stdint.h>
#include <stddef.h>
typedef uint32_t IPAddress;
class Client /*: public Stream*/ {
public:
virtual ~Client() {}
virtual int connect(IPAddress ip, uint16_t port) =0;
virtual int connect(const char *host, uint16_t port) =0;
virtual size_t write(uint8_t) =0;
virtual size_t write(const uint8_t *buf, size_t size) =0;
virtual int available() = 0;
virtual int read() = 0;
virtual int read(uint8_t *buf, size_t size) = 0;
virtual int peek() = 0;
virtual void flush() = 0;
virtual void stop() = 0;
virtual uint8_t connected() = 0;
virtual operator bool() = 0;
//protected:
// uint8_t* rawIPAddress(IPAddress& addr) { return addr.raw_address(); };
};
#endif

33
host/emulation/Server.h Normal file
View File

@ -0,0 +1,33 @@
/*
Server.h - Base class that provides Server
Copyright (c) 2011 Adrian McEwen. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef server_h
#define server_h
// XXX: This file has been hacked up to make it compile in the
// emulation environment. Not intended for real work.
//#include "Print.h"
class Server /*: public Print*/ {
public:
virtual void begin() =0;
};
#endif

View File

@ -196,6 +196,11 @@ KeyRingClass KeyRing;
* primary remote device that this device will be communicating with.
*/
/**
* \var KeyRingClass::PreSharedKeyDefault
* \brief Identifier for the default pre-shared symmetric key for the device.
*/
/**
* \var KeyRingClass::EthernetMACAddress
* \brief Identifier for storing a generated MAC address into the other

View File

@ -65,6 +65,8 @@ public:
static const uint16_t LocalEd25519Default = 0x4501; // 'E', 0x01
static const uint16_t RemoteEd25519Default = 0x6501; // 'e', 0x01
static const uint16_t PreSharedKeyDefault = 0x5001; // 'P', 0x01
static const uint16_t EthernetMACAddress = 0x4D01; // 'M', 0x01
private:

View File

@ -1,2 +1,6 @@
NoiseClient KEYWORD1
NoiseEthernetClient KEYWORD1
NoiseWiFiClient KEYWORD1
NoiseServer KEYWORD1
NoiseEthernetServer KEYWORD1
NoiseWiFiServer KEYWORD1

View File

@ -1,7 +1,7 @@
{
"name": "NoiseProtocol",
"version": "0.2.0",
"keywords": "NoiseClient,NoiseServer",
"keywords": "NoiseClient,NoiseServer,NoiseEthernetClient,NoiseEthernetServer,NoiseWiFiClient,NoiseWiFiServer",
"description": "Noise protocol for creating secure sessions",
"authors":
{

View File

@ -0,0 +1,489 @@
/*
* 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 "NoiseClient.h"
/**
* \class NoiseClient NoiseClient.h <NoiseClient.h>
* \brief Network class for connecting to a peer using the Noise protocol.
*/
/**
* \var NoiseEthernetClient
* \brief Convenience type for communicating via Noise over an Ethernet link.
*/
/**
* \var NoiseWiFiClient
* \brief Convenience type for communicating via Noise over a Wi-Fi link.
*/
/** @cond noise_client_private */
// Standard Arduino network error codes for connect().
#define NOISE_CLIENT_SUCCESS 1
#define NOISE_CLIENT_FAILED 0
#define NOISE_CLIENT_TIMED_OUT -1
#define NOISE_CLIENT_INVALID_SERVER -2
#define NOISE_CLIENT_TRUNCATED -3
#define NOISE_CLIENT_INVALID_RESPONSE -4
/** @endcond */
/**
* \brief Constructs a new Noise network client.
*
* \param networkClient The underlying transport client; usually an instance
* of EthernetClient or WiFiClient.
*
* The new object will take owership of \a networkClient and will delete it
* when this object is destroyed.
*/
NoiseClient::NoiseClient(Client *networkClient)
: refCount(1)
, net(networkClient)
, opts(0)
{
}
/**
* \brief Destroys this Noise network client.
*/
NoiseClient::~NoiseClient()
{
delete net;
delete opts;
}
/**
* \brief Gets the options block for this client so that the application
* can modify the options for this client only.
*
* \return A pointer to the options block.
*
* Normally the options for clients are set on the global NoiseClientOptions
* object, which then apply to all clients on the device. If this function
* is called, then the options in NoiseClientOptions are copied and the
* application can then make local modifications specific to this session.
*
* The options can be modified up until the point that connect() is
* called. After that, whatever options were set on the client become
* permanent for the session. Any modifications that are made to the
* options will be ignored until the next call to connect() on this object.
*
* For incoming connections, use NoiseServerOptions and NoiseServer::options()
* instead.
*
* \sa NoiseServer::options()
*/
NoiseLinkOptions *NoiseClient::options()
{
// The caller may be modifying the options, so we need to clone
// the global options block to avoid making modifications to it.
if (opts)
return opts;
opts = new NoiseLinkOptions();
opts->copyFrom(NoiseClientOptions);
return opts;
}
/**
* \brief Gets a constant reference to the options block for this client.
*
* \return A constant pointer to the options block.
*/
const NoiseLinkOptions *NoiseClient::constOptions() const
{
// Avoid cloning the global options block if we don't need to.
if (opts)
return opts;
return &NoiseClientOptions;
}
/**
* \fn Client *NoiseClient::network() const
* \brief Gets the underlying network client.
*
* \return The underlying network client for this Noise client;
* usually an instance of EthernetClient or WiFiClient.
*
* Applications normally won't need to use this unless they need to
* adjust the raw socket options on the transport.
*/
/**
* \brief Gets the status of the connection.
*
* \return Noise::Closed if the connection has closed or it hasn't started yet.
* \return Noise::Connecting if the connection is in the process of connecting
* to the remote host, but the secure handshake has not completed yet.
* \return Noise::Connected if the connection is active and ready to
* send and receive data.
* \return Noise::Closing if the remote end of the connection has been
* closed but there are still bytes left in the receive buffer. Any data
* that is transmitted in this state will be discarded.
* \return HandshakeFailed if the client failed to establish a secure
* connection with the remote party.
*
* \sa connected()
*/
Noise::ConnectionStatus NoiseClient::connectionStatus() const
{
// TODO
if (!net)
return Noise::Closed;
return net->connected() ? Noise::Closed : Noise::Connected;
}
/**
* \brief Clears security-sensitive data from this object.
*
* If the connection is active, then calling this function will stop() it.
*/
void NoiseClient::clear()
{
stop();
if (opts)
opts->clear();
// TODO
}
/**
* \brief Gets the public key of the remote party that this client
* is communicating with.
*
* \param value Buffer to receive the remote public key.
* \param maxSize Maximum size of the \a value buffer.
*
* \return The number of bytes that were written to the \a value buffer if
* the remote public key is available.
* \return 0 if the remote public key is not available yet.
* \return 0 if \a maxSize is too small to contain the key.
* \return 0 if the protocol does not make use of remote public keys,
* such as the "NNpsk0" pattern.
*
* The remote public key is available once the handshake completes
* and when connected() becomes true. Typically an application will use
* this to verify the identity of the party they are communicating with.
*
* \sa getHandshakeHash()
*/
size_t NoiseClient::getRemotePublicKey(void *value, size_t maxSize) const
{
// TODO
return 0;
}
/**
* \brief Gets the handshake hash at the end of the handshake.
*
* \param value Buffer to receive the handshake hash.
* \param maxSize Maximum size of the \a value buffer.
*
* \return The number of bytes that were written to the \a value buffer if
* the handshake hash is available.
* \return 0 if the handshake hash is not available yet.
* \return 0 if \a maxSize is too small to contain the handshake hash.
*
* The handshake hash is a value defined by the Noise specification that
* is unique for every connection, and which can be used to secure higher
* level authentication protocols. See the Noise specification for details.
*
* \sa getRemotePublicKey()
*/
size_t NoiseClient::getHandshakeHash(void *value, size_t maxSize) const
{
// TODO: modify NoiseHandshakeState API to use the maxSize idiom.
return 0;
}
/**
* \brief Connects to a remote host using an IP address and port number.
*
* \param ip The IP address of the remote host.
* \param port The port number on the remote host.
*
* \return 1 if the connection was initiated.
* \return 0 if the connection attempt failed.
* \return A negative code on error.
*
* This function establishes the basic connection but the security handshake
* will not be complete when this function returns. The connection will not
* be ready send or receive until conected() returns true. Data that is
* sent before then will be discarded.
*
* \sa connected()
*/
int NoiseClient::connect(IPAddress ip, uint16_t port)
{
// Stop the previous connection first.
stop();
// Fail if the underlying network client is not ready.
if (!net || !(net->operator bool()))
return NOISE_CLIENT_FAILED;
// Attempt to connect to the remote host using the underlying network.
int ret = net->connect(ip, port);
if (ret <= 0)
return ret;
// Start the handshake running.
return connect();
}
/**
* \brief Connects to a remote host using a host name and port number.
*
* \param host The name of the remote host.
* \param port The port number on the remote host.
*
* \return 1 if the connection was initiated.
* \return 0 if the connection attempt failed.
* \return A negative code on error.
*
* This function establishes the basic connection but the security handshake
* will not be complete when this function returns. The connection will not
* be ready send or receive until conected() returns true. Data that is
* sent before then will be discarded.
*
* \sa connected()
*/
int NoiseClient::connect(const char *host, uint16_t port)
{
// Stop the previous connection first.
stop();
// Fail if the underlying network client is not ready.
if (!net || !(net->operator bool()))
return NOISE_CLIENT_FAILED;
// Attempt to connect to the remote host using the underlying network.
int ret = net->connect(host, port);
if (ret <= 0)
return ret;
// Start the handshake running.
return connect();
}
/**
* \brief Internal implementation of the public connect() methods that
* is called after the basic network link is established.
*
* \return 1 if the connection was initiated.
* \return 0 if the connection attempt failed.
* \return A negative code on error.
*/
int NoiseClient::connect()
{
// TODO
return NOISE_CLIENT_SUCCESS;
}
/**
* \brief Writes a single byte to the connection.
*
* \param data The byte to write.
*
* \return 1 if the byte was written successfully, 0 if the byte was
* not written.
*
* If the security handshake has not completed or the connection has been
* lost, then the write will be rejected with a 0 return value.
*
* This function may not send the byte to the other party immediately.
* It may be queued up until the next call to flush(), read(), or
* available().
*
* \sa flush(), read(), available()
*/
size_t NoiseClient::write(uint8_t data)
{
// TODO
return 0;
}
/**
* \brief Writes a buffer of bytes to the connection.
*
* \param buf Points to the start of the buffer.
* \param size The number of bytes in the buffer.
*
* \return The number of bytes that were written, which may be 0 if
* the connection is not ready to accept data.
*
* If the security handshake has not completed or the connection has been
* lost, then the write will be rejected with a 0 return value.
*
* This function may not send the data to the other party immediately.
* It may be queued up until the next call to flush(), read(), or
* available().
*
* \sa flush(), read()
*/
size_t NoiseClient::write(const uint8_t *buf, size_t size)
{
// TODO
return size;
}
/**
* \brief Gets the number of bytes that are available to read().
*
* \return The number of bytes that are available.
*
* This function will implicitly perform a flush() to send any outstanding
* data in the internal write buffer to the other party.
*
* \sa read(), flush()
*/
int NoiseClient::available()
{
flush();
// TODO
return 0;
}
/**
* \brief Reads a single byte from the connection if one is ready.
*
* \return The next byte or -1 if no bytes are ready to read.
*
* This function will implicitly perform a flush() to send any outstanding
* data in the internal write buffer to the other party.
*
* \sa write(), available(), flush(), peek()
*/
int NoiseClient::read()
{
flush();
// TODO
return -1;
}
/**
* \brief Reads a buffer of bytes from the connection if data is ready.
*
* \param buf Points to the buffer to read into.
* \param size Maximum number of bytes that can be read into the buffer.
*
* This function will implicitly perform a flush() to send any outstanding
* data in the internal write buffer to the other party.
*
* \sa write(), available(), flush()
*/
int NoiseClient::read(uint8_t *buf, size_t size)
{
flush();
// TODO
return 0;
}
/**
* \brief Peeks at the next byte without removing it from the receive buffer.
*
* \return The next byte or -1 if no bytes are ready to read.
*
* This function will implicitly perform a flush() to send any outstanding
* data in the internal write buffer to the other party.
*
* \sa read()
*/
int NoiseClient::peek()
{
flush();
// TODO
return -1;
}
/**
* \brief Flushes pending data in internal buffers to the other party.
*/
void NoiseClient::flush()
{
// TODO
}
/**
* \brief Closes the connection and stops it.
*/
void NoiseClient::stop()
{
// TODO
}
/**
* \brief Determine if this client is connected to the remote peer and
* ready to send or receive application data.
*
* \return Returns non-zero if the client is connected and the security
* handshake has completed successfully; zero if not.
*
* This function can be used to determine if the connection is ready
* to send and receive application data. Requests to read() or write()
* before this point will fail.
*
* The connectionStatus() function can be used to get more detailed
* information on the state of the connection.
*
* \sa connectionStatus()
*/
uint8_t NoiseClient::connected()
{
Noise::ConnectionStatus status = connectionStatus();
return status == Noise::Connected || status == Noise::Closing;
}
/**
* \brief Determine if this network client is ready to make connections.
*
* \return Returns true if the client is ready, false otherwise.
*/
NoiseClient::operator bool()
{
if (!net)
return false;
return net->operator bool();
}
void NoiseClient::ref(NoiseClient *client)
{
if (client)
++(client->refCount);
}
void NoiseClient::deref(NoiseClient *client)
{
if (client && --(client->refCount) <= 0)
delete client;
}
bool NoiseClient::accept(const NoiseLinkOptions &options)
{
// Make a copy of the incoming server options.
if (!opts)
opts = new NoiseLinkOptions();
opts->copyFrom(options);
// TODO
return false;
}

View File

@ -0,0 +1,193 @@
/*
* 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_CLIENT_h
#define NOISE_CLIENT_h
#include "NoiseLinkOptions.h"
#include <Client.h>
class NoiseClient : public Client
{
private:
// Disable the copy operators. NoiseEthernetClient and NoiseWiFiClient
// should be used instead for safe copiable network client objects.
NoiseClient(const NoiseClient &other) {}
NoiseClient &operator=(const NoiseClient &other) { return *this; }
public:
explicit NoiseClient(Client *networkClient);
virtual ~NoiseClient();
NoiseLinkOptions *options();
Client *network() const { return net; }
Noise::ConnectionStatus connectionStatus() const;
void clear();
// TODO: How to check for known remote public keys in a simple way?
size_t getRemotePublicKey(void *value, size_t maxSize) const;
size_t getHandshakeHash(void *value, size_t maxSize) const;
// Standard API for Arduino network clients.
int connect(IPAddress ip, uint16_t port);
int connect(const char *host, uint16_t port);
size_t write(uint8_t data);
size_t write(const uint8_t *buf, size_t size);
int available();
int read();
int read(uint8_t *buf, size_t size);
int peek();
void flush();
void stop();
uint8_t connected();
operator bool();
/** @cond noise_client_internal */
// Internal helper functions for NoiseClientWrapper and NoiseServer.
// Not part of the public API that applications should be using.
static void ref(NoiseClient *client);
static void deref(NoiseClient *client);
bool accept(const NoiseLinkOptions &options);
/** @endcond */
private:
int refCount;
Client *net;
NoiseLinkOptions *opts;
const NoiseLinkOptions *constOptions() const;
int connect();
};
/** @cond noise_client_wrapper */
// Nelper template for safely creating and copying instances of NoiseClient for
// specific underlying network stacks such as EthernetClient or WiFiClient.
template <typename T>
class NoiseClientWrapper : public Client
{
public:
inline NoiseClientWrapper() : d(0) {}
inline NoiseClientWrapper(const NoiseClientWrapper<T> &other)
: d(other.d) { NoiseClient::ref(d); }
inline NoiseClientWrapper(NoiseClient *client) : d(client) {}
inline ~NoiseClientWrapper() { NoiseClient::deref(d); }
inline NoiseClientWrapper<T> &operator=(const NoiseClientWrapper<T> &other)
{
NoiseClient::ref(other.d);
NoiseClient::deref(d);
d = other.d;
return *this;
}
inline NoiseLinkOptions *options()
{
return d ? d->options() : &NoiseClientOptions;
}
inline T *network() const
{
return d ? (T *)(d->network()) : 0;
}
int connect(IPAddress ip, uint16_t port)
{
if (!d)
d = new NoiseClient(new T());
return d->connect(ip, port);
}
int connect(const char *host, uint16_t port)
{
if (!d)
d = new NoiseClient(new T());
return d->connect(host, port);
}
size_t write(uint8_t data)
{
return d ? d->write(data) : 0;
}
size_t write(const uint8_t *buf, size_t size)
{
return d ? d->write(buf, size) : 0;
}
int available()
{
return d ? d->available() : 0;
}
int read()
{
return d ? d->read() : -1;
}
int read(uint8_t *buf, size_t size)
{
return d ? d->read(buf, size) : -1;
}
int peek()
{
return d ? d->peek() : -1;
}
void flush()
{
if (d)
d->flush();
}
void stop()
{
if (d)
d->stop();
}
uint8_t connected()
{
return d ? d->connected() : false;
}
operator bool()
{
return d ? (d->operator bool()) : false;
}
private:
NoiseClient *d;
};
/** @endcond */
#define NoiseEthernetClient NoiseClientWrapper<EthernetClient>
#define NoiseWiFiClient NoiseClientWrapper<WiFiClient>
#endif

View File

@ -0,0 +1,867 @@
/*
* 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 "NoiseLinkOptions.h"
#include "NoiseHandshakeState.h"
#include "Crypto.h"
#include "RNG.h"
#include "Curve25519.h"
#include <string.h>
/**
* \class NoiseLinkOptions NoiseLinkOptions.h <NoiseLinkOptions.h>
* \brief Collection of options for NoiseLink clients and servers.
*
* The global objects NoiseClientOptions and NoiseServerOptions are
* used to set the initial options for Noise sessions created by
* NoiseClient and NoiseServer respectively. They are usually populated
* at system startup.
*
* \code
* void setup()
* {
* ...
*
* // Load the device's local key pair from the key ring. Otherwise,
* // generate a new key pair and store it in the key ring.
* if (!NoiseClientOptions.load(Noise::LocalStaticKeyPair)) {
* Serial.println("Generating a new key pair, please wait ...");
* NoiseClientOptions.generate(Noise::LocalStaticKeyPair);
* }
*
* ...
* }
* \endcode
*
* If you are using the "IK" pattern to connect to a remote device,
* then you also need to load the public key of the remote device:
*
* \code
* void setup()
* {
* ...
*
* // Load the public key of the remote device we are communicating with.
* if (!NoiseClientOptions.load(Noise::RemoteStaticPublicKey)) {
* Serial.println("Do not know the identity of the remote device");
* ...
* }
*
* ...
* }
* \endcode
*
* If you want to be more clever, you can try connecting with the "XX"
* pattern if the identity of the remote device is unknown and then
* record the public key for use with "IK" in subsequent sessions.
*
* Keys do not have to be stored in the key ring. If you have obtained a
* remote public key through other means, you can pass it to the options
* block with setParameter():
*
* \code
* uint8_t remoteKey[32] = {...};
*
* NoiseClientOptions.setParameter(Noise::RemoteStaticPublicKey, remoteKey, 32);
* \endcode
*
* At present, NoiseLinkOptions only supports Curve25519 keys and PSK's.
* Support for other Noise DH algorithms is not implemented yet.
*/
/**
* \brief Default options to use when establishing an outgoing session
* from a client device using NoiseClient.
*/
NoiseLinkOptions NoiseClientOptions;
/**
* \brief Default options to use when establishing an incoming session
* on a server device using NoiseServer.
*/
NoiseLinkOptions NoiseServerOptions;
/** @cond noise_link_options_private */
#define HAVE_KEY_PAIR_25519 0x01
#define HAVE_REMOTE_KEY_25519 0x02
#define HAVE_PSK 0x04
#define MAX_PROTOCOLS 4
class NoiseLinkOptionsPrivate
{
public:
NoiseLinkOptionsPrivate()
: allowAliases(true)
, padding(Noise::NoPadding)
, sendSize(NOISE_DEFAULT_BUFSIZ)
, recvSize(NOISE_DEFAULT_BUFSIZ)
, numProtocols(0)
{
st.flags = 0;
}
~NoiseLinkOptionsPrivate() { clean(&st, sizeof(st)); }
void clear() { clean(&st, sizeof(st)); }
void copyFrom(const NoiseLinkOptionsPrivate *other)
{
memcpy(&st, &(other->st), sizeof(st));
allowAliases = other->allowAliases;
padding = other->padding;
sendSize = other->sendSize;
recvSize = other->recvSize;
numProtocols = other->numProtocols;
memcpy(protocols, other->protocols,
numProtocols * sizeof(protocols[0]));
}
struct {
uint8_t flags;
uint8_t keyPair25519[64];
uint8_t remoteKey25519[32];
uint8_t psk[32];
} st;
bool allowAliases;
Noise::Padding padding;
uint16_t sendSize;
uint16_t recvSize;
size_t numProtocols;
const NoiseProtocolDescriptor *protocols[MAX_PROTOCOLS];
};
/** @endcond */
/**
* \brief Constructs a new options block.
*/
NoiseLinkOptions::NoiseLinkOptions()
: d(new NoiseLinkOptionsPrivate())
{
}
/**
* \brief Destroys this options block.
*/
NoiseLinkOptions::~NoiseLinkOptions()
{
delete d;
}
/**
* \brief Sets a parameter within this options block.
*
* \param id Identifies the parameter.
* \param value Points to the value to set for the parameter.
* \param size Size of the parameter value in bytes.
*
* \return Returns true if the parameter is set; false if the parameter
* \a id is not supported; or false if the \a size is not valid for the
* parameter.
*
* If the \a id is for the private key component of a key pair, then the
* public key component will be generated from \a value. Returns false if
* it is not possible to generate the public key component from the
* private key component.
*
* \sa getParameter(), getParameterSize()
*/
bool NoiseLinkOptions::setParameter(Noise::Parameter id, const void *value, size_t size)
{
if (!value)
return false;
switch (id) {
case Noise::LocalStaticKeyPair:
case Noise::LocalStatic25519KeyPair:
if (size != 64)
break;
memcpy(d->st.keyPair25519, value, 64);
d->st.flags |= HAVE_KEY_PAIR_25519;
return true;
case Noise::LocalStaticPrivateKey:
case Noise::LocalStatic25519PrivateKey:
if (size != 32)
break;
memcpy(d->st.keyPair25519, value, 32);
d->st.keyPair25519[0] &= 0xF8;
d->st.keyPair25519[31] = (d->st.keyPair25519[31] & 0x7F) | 0x40;
Curve25519::eval(d->st.keyPair25519 + 32, d->st.keyPair25519, 0);
d->st.flags |= HAVE_KEY_PAIR_25519;
return true;
case Noise::RemoteStatic25519PublicKey:
case Noise::RemoteStaticPublicKey:
if (size != 32)
break;
memcpy(d->st.remoteKey25519, value, 32);
d->st.flags |= HAVE_REMOTE_KEY_25519;
return true;
case Noise::PreSharedKey:
if (size != 32)
break;
memcpy(d->st.psk, value, 32);
d->st.flags |= HAVE_PSK;
return true;
default: break;
}
return false;
}
/**
* \brief Gets the value associated from within this options block.
*
* \param id Identifies the parameter.
* \param value Points to the buffer to write the parameter value to.
* \param maxSize Maximum size of the \a value buffer in bytes.
*
* \return The number of bytes that were written to \a value.
* \return 0 if \a id is not valid.
* \return 0 if \a maxSize is not large enough to hold the parameter value.
* \return 0 if the parameter \a id has not been set yet.
*
* The getParameterSize() function can be used to determine the actual
* size of the parameter so that the caller can allocate a buffer large
* enough to contain it ahead of time.
*
* \sa setParameter(), getParameterSize()
*/
size_t NoiseLinkOptions::getParameter(Noise::Parameter id, void *value, size_t maxSize) const
{
if (!value)
return 0;
switch (id) {
case Noise::LocalStaticKeyPair:
case Noise::LocalStatic25519KeyPair:
if (maxSize < 64 || !(d->st.flags & HAVE_KEY_PAIR_25519))
break;
memcpy(value, d->st.keyPair25519, 64);
return 64;
case Noise::LocalStaticPrivateKey:
case Noise::LocalStatic25519PrivateKey:
if (maxSize < 32 || !(d->st.flags & HAVE_KEY_PAIR_25519))
break;
memcpy(value, d->st.keyPair25519, 32);
return 32;
case Noise::LocalStatic25519PublicKey:
case Noise::LocalStaticPublicKey:
if (maxSize < 32 || !(d->st.flags & HAVE_KEY_PAIR_25519))
break;
memcpy(value, d->st.keyPair25519 + 32, 32);
return 32;
case Noise::RemoteStatic25519PublicKey:
case Noise::RemoteStaticPublicKey:
if (maxSize < 32 || !(d->st.flags & HAVE_KEY_PAIR_25519))
break;
memcpy(value, d->st.remoteKey25519, 32);
return 32;
case Noise::PreSharedKey:
if (maxSize < 32 || !(d->st.flags & HAVE_PSK))
break;
memcpy(value, d->st.psk, 32);
return 32;
default: break;
}
return 0;
}
/**
* \brief Gets the size of a parameter within this options block.
*
* \param id Identifies the parameter.
*
* \return The parameter's size, or zero if \a id is not recognised.
*
* If the parameter is variable-length, then this function will return
* the actual length of the value, which may be zero if the value has
* not been set yet.
*
* \sa getParameter(), setParameter(), hasParameter()
*/
size_t NoiseLinkOptions::getParameterSize(Noise::Parameter id) const
{
switch (id) {
case Noise::LocalStaticKeyPair:
case Noise::LocalStatic25519KeyPair:
return 64;
case Noise::LocalStaticPrivateKey:
case Noise::LocalStatic25519PrivateKey:
case Noise::LocalStaticPublicKey:
case Noise::LocalStatic25519PublicKey:
case Noise::RemoteStatic25519PublicKey:
case Noise::RemoteStaticPublicKey:
case Noise::PreSharedKey:
return 32;
default:
return 0;
}
}
/**
* \brief Determine if a parameter has a value set within this options block.
*
* \param id Identifies the parameter.
*
* \return Returns true if \a id has a value; false if \a id does not
* have a value; false if \a id is not valid.
*
* \sa getParameterSize(), getParameter(), removeParameter()
*/
bool NoiseLinkOptions::hasParameter(Noise::Parameter id) const
{
switch (id) {
case Noise::LocalStaticKeyPair:
case Noise::LocalStatic25519KeyPair:
case Noise::LocalStaticPrivateKey:
case Noise::LocalStatic25519PrivateKey:
case Noise::LocalStaticPublicKey:
case Noise::LocalStatic25519PublicKey:
return (d->st.flags & HAVE_KEY_PAIR_25519) != 0;
case Noise::RemoteStatic25519PublicKey:
case Noise::RemoteStaticPublicKey:
return (d->st.flags & HAVE_REMOTE_KEY_25519) != 0;
case Noise::PreSharedKey:
return (d->st.flags & HAVE_PSK) != 0;
default:
return false;
}
}
/**
* \brief Removes a parameter from this options block.
*
* \param id Identifies the parameter. If the parameter is unknown,
* the request will be ignored.
*
* The clear() function will remove all parameters in a single call.
*
* \sa clear(), hasParameter()
*/
void NoiseLinkOptions::removeParameter(Noise::Parameter id)
{
switch (id) {
case Noise::LocalStaticKeyPair:
case Noise::LocalStatic25519KeyPair:
case Noise::LocalStaticPrivateKey:
case Noise::LocalStatic25519PrivateKey:
case Noise::LocalStaticPublicKey:
case Noise::LocalStatic25519PublicKey:
clean(d->st.keyPair25519, 64);
d->st.flags &= ~HAVE_KEY_PAIR_25519;
break;
case Noise::RemoteStatic25519PublicKey:
case Noise::RemoteStaticPublicKey:
clean(d->st.remoteKey25519, 32);
d->st.flags &= ~HAVE_REMOTE_KEY_25519;
break;
case Noise::PreSharedKey:
clean(d->st.psk, 32);
d->st.flags &= ~HAVE_PSK;
break;
default: break;
}
}
/**
* \brief Loads a parameter from the device's key ring.
*
* \param id Identifies the parameter to load.
* \param keyRingId Identifier of the stored key within the key ring,
* or zero to use the default key ring identifier for \a id.
*
* \return Returns true if the parameter was loaded.
* \return Returns false if \a id is not a valid parameter.
* \return Returns false if \a keyRingId does not have a key stored for it.
*
* The following example loads the Curve25519 key pair for the local
* device and the public key for the remote device that we are expecting
* to communicate with. If the local device does not have a key pair,
* then this example will generate a new one:
*
* \code
* if (!NoiseClientOptions.load(Noise::LocalStaticKeyPair)) {
* Serial.println("Generating a new key pair, please wait ...");
* NoiseClientOptions.generate(Noise::LocalStaticKeyPair);
* }
* NoiseClientOptions.load(Noise::RemoteStaticPublicKey);
* \endcode
*
* If the key ring is protected by an encryption key or passphrase,
* then it is assumed that the key ring was already unlocked by a
* previous call to KeyRing.begin().
*
* \sa generate(), save()
*/
bool NoiseLinkOptions::load(Noise::Parameter id, uint16_t keyRingId)
{
switch (id) {
case Noise::LocalStaticKeyPair:
case Noise::LocalStatic25519KeyPair:
case Noise::LocalStaticPrivateKey:
case Noise::LocalStatic25519PrivateKey:
case Noise::LocalStaticPublicKey:
case Noise::LocalStatic25519PublicKey:
if (!keyRingId)
keyRingId = KeyRingClass::LocalCurve25519Default;
if (KeyRing.getLocalKeyPair(keyRingId, d->st.keyPair25519, 64) == 64) {
d->st.flags |= HAVE_KEY_PAIR_25519;
return true;
}
clean(d->st.keyPair25519, 64);
d->st.flags &= ~HAVE_KEY_PAIR_25519;
break;
case Noise::RemoteStatic25519PublicKey:
case Noise::RemoteStaticPublicKey:
if (!keyRingId)
keyRingId = KeyRingClass::RemoteCurve25519Default;
if (KeyRing.getRemotePublicKey
(keyRingId, d->st.remoteKey25519, 32) == 32) {
d->st.flags |= HAVE_REMOTE_KEY_25519;
return true;
}
clean(d->st.remoteKey25519, 32);
d->st.flags &= ~HAVE_REMOTE_KEY_25519;
break;
case Noise::PreSharedKey:
if (!keyRingId)
keyRingId = KeyRingClass::PreSharedKeyDefault;
if (KeyRing.getSharedSymmetricKey(keyRingId, d->st.psk, 32) == 32) {
d->st.flags |= HAVE_PSK;
return true;
}
clean(d->st.psk, 32);
d->st.flags &= ~HAVE_PSK;
break;
default: break;
}
return false;
}
/**
* \brief Saves a parameter into the device's key ring.
*
* \param id Identifies the parameter to save.
* \param keyRingId Identifier of the stored key within the key ring,
* or zero to use the default key ring identifier for \a id.
*
* \return Returns true if the parameter was saved.
* \return Returns false if \a id is not a valid parameter or it does
* not currently have a value in this options block.
* \return Returns false if the key ring does not have enough space
* to store the parameter.
*
* If the key ring is protected by an encryption key or passphrase,
* then it is assumed that the key ring was already unlocked by a
* previous call to KeyRing.begin().
*
* \sa load(), generate()
*/
bool NoiseLinkOptions::save(Noise::Parameter id, uint16_t keyRingId)
{
switch (id) {
case Noise::LocalStaticKeyPair:
case Noise::LocalStatic25519KeyPair:
case Noise::LocalStaticPrivateKey:
case Noise::LocalStatic25519PrivateKey:
case Noise::LocalStaticPublicKey:
case Noise::LocalStatic25519PublicKey:
if (!keyRingId)
keyRingId = KeyRingClass::LocalCurve25519Default;
if ((d->st.flags & HAVE_KEY_PAIR_25519) == 0)
break;
return KeyRing.setLocalKeyPair(keyRingId, d->st.keyPair25519, 64);
case Noise::RemoteStatic25519PublicKey:
case Noise::RemoteStaticPublicKey:
if (!keyRingId)
keyRingId = KeyRingClass::RemoteCurve25519Default;
if ((d->st.flags & HAVE_REMOTE_KEY_25519) == 0)
break;
return KeyRing.setRemotePublicKey(keyRingId, d->st.remoteKey25519, 32);
case Noise::PreSharedKey:
if (!keyRingId)
keyRingId = KeyRingClass::PreSharedKeyDefault;
if ((d->st.flags & HAVE_PSK) == 0)
break;
return KeyRing.setSharedSymmetricKey(keyRingId, d->st.psk, 32);
default: break;
}
return false;
}
/**
* \brief Generates a new local key pair for the device and saves it
* into the device's key ring.
*
* \param id Identifies the key pair to be generated.
* \param keyRingId Identifier for the saved key within the key ring,
* or zero to use the default key ring identifier for \a id.
* \param wait Set to true to wait for the system random number generator
* to have enough entropy to safely generate the key; false to immediately
* generate the key with whatever entropy is available at the moment.
* The default value for this parameter is true.
*
* \return Returns true if the new key pair was generated and saved.
* \return Returns false if \a id does not specify a valid key pair name.
* \return Returns false if there is not enough space left in the
* key ring to save the new key.
*
* The new key pair will also be loaded into this options block, ready for use.
*
* If the key ring is protected by an encryption key or passphrase,
* then it is assumed that the key ring was already unlocked by a
* previous call to KeyRing.begin().
*
* \note If \a wait is true then this function may block for a long
* time on some devices to accumulate enough entropy to safely generate
* the key pair. Alternatively, the application can poll
* \link RNGClass::available() RNG.available()\endlink itself and call
* this function with \a wait set to false when ready; allowing the
* application to perform other setup tasks while waiting.
*
* \sa load(), save()
*/
bool NoiseLinkOptions::generate
(Noise::Parameter id, uint16_t keyRingId, bool wait)
{
switch (id) {
case Noise::LocalStaticKeyPair:
case Noise::LocalStatic25519KeyPair:
case Noise::LocalStaticPrivateKey:
case Noise::LocalStatic25519PrivateKey:
case Noise::LocalStaticPublicKey:
case Noise::LocalStatic25519PublicKey:
if (!keyRingId)
keyRingId = KeyRingClass::LocalCurve25519Default;
while (wait && !RNG.available(32)) {
RNG.loop();
crypto_feed_watchdog();
}
Curve25519::generateKeyPair(d->st.keyPair25519);
if (KeyRing.setLocalKeyPair(keyRingId, d->st.keyPair25519, 64)) {
d->st.flags |= HAVE_KEY_PAIR_25519;
return true;
}
clean(d->st.keyPair25519, 64);
d->st.flags &= ~HAVE_KEY_PAIR_25519;
break;
default: break;
}
return false;
}
/**
* \brief Gets the number of protocols that have been added to this
* options block.
*
* \return The number of protocols.
*
* \sa protocolAt(), addProtocol(), removeProtocol()
*/
size_t NoiseLinkOptions::protocolCount() const
{
return d->numProtocols;
}
/**
* \brief Gets a specific protocol from this options block.
*
* \param index The index of the protocol between 0 and protocolCount() - 1.
*
* \return A pointer to the protocol descriptor or NULL if \a index
* is out of range.
*
* \sa protocolCount(), addProtocol(), removeProtocol()
*/
const NoiseProtocolDescriptor *NoiseLinkOptions::protocolAt(size_t index) const
{
if (index < d->numProtocols)
return d->protocols[index];
else
return 0;
}
/**
* \brief Adds a protocol to this options block.
*
* \param protocol The new protocol to add.
*
* The caller can add a maximum of 4 protocols to an options block.
* The first protocol added is the one used for outgoing connections
* using NoiseClient. All protocols can be accepted for incoming
* connections using NoiseServer.
*
* \sa removeProtocol(), protocolCount(), protocolAt()
*/
void NoiseLinkOptions::addProtocol(const NoiseProtocolDescriptor &protocol)
{
if (d->numProtocols < MAX_PROTOCOLS)
d->protocols[(d->numProtocols)++] = &protocol;
}
/**
* \brief Removes a protocol from this options block.
*
* \param protocol The protocol to remove.
*
* \sa addProtocol(), protocolCount(), protocolAt()
*/
void NoiseLinkOptions::removeProtocol(const NoiseProtocolDescriptor &protocol)
{
for (size_t index = 0; index < d->numProtocols; ++index) {
if (d->protocols[index] == &protocol) {
memmove(d->protocols + index, d->protocols + index + 1,
(d->numProtocols - index - 1) * sizeof(d->protocols[0]));
--(d->numProtocols);
return;
}
}
}
/**
* \brief Gets the size of the send buffer when a session is in progress.
*
* \return The size of the send buffer between 128 and 2048; default is 512.
*
* \sa setSendBufferSize(), receiveBufferSize()
*/
size_t NoiseLinkOptions::sendBufferSize() const
{
return d->sendSize;
}
/**
* \brief Sets the size of the send buffer when a session is in progress.
*
* \param size The size of the send buffer between 128 and 2048.
*
* \sa sendBufferSize(), setReceiveBufferSize(), setBufferSize()
*/
void NoiseLinkOptions::setSendBufferSize(size_t size)
{
if (size < NOISE_MIN_BUFSIZ)
size = NOISE_MIN_BUFSIZ;
else if (size > NOISE_MAX_BUFSIZ)
size = NOISE_MAX_BUFSIZ;
d->sendSize = size;
}
/**
* \brief Gets the size of the receive buffer when a session is in progress.
*
* \return The size of the receive buffer between 128 and 2048; default is 512.
*
* \sa setReceiveBufferSize(), sendBufferSize()
*/
size_t NoiseLinkOptions::receiveBufferSize() const
{
return d->recvSize;
}
/**
* \brief Sets the size of the receive buffer when a session is in progress.
*
* \param size The size of the receive buffer between 128 and 2048.
*
* \sa receiveBufferSize(), setSendBufferSize(), setBufferSize()
*/
void NoiseLinkOptions::setReceiveBufferSize(size_t size)
{
if (size < NOISE_MIN_BUFSIZ)
size = NOISE_MIN_BUFSIZ;
else if (size > NOISE_MAX_BUFSIZ)
size = NOISE_MAX_BUFSIZ;
d->recvSize = size;
}
/**
* \fn void NoiseLinkOptions::setBufferSize(size_t size)
* \brief Sets the size of the send and receive buffers to the same value.
*
* \param size The size of the send and receive buffers between 128 and 2048.
*
* \sa setSendBufferSize(), setReceiveBufferSize()
*/
/**
* \brief Determine if NoiseTinyLink protocol name aliases are allowed.
*
* \return Returns true if protocol name aliases are allowed or false if not.
* The default is true.
*
* Protocol name aliases are strings like "2" instead of a full protocol
* name like "Noise_XX_25519_ChaChaPoly_SHA256". This can save bytes on
* the network when making an outgoing connection using NoiseClient.
*
* Incoming connections via NoiseServer can use either full names or aliases.
* This option has no effect on NoiseServer.
*
* If a protocol does not have a name alias, the full name will be used
* regardless of this option's setting.
*
* If your device is communicating with a server that is running the full
* version of NoiseLink, then it may not have support for protocol name
* aliases. In that case, use setAllowAliases(false) to force the use
* of the full protocol name when communicating with that server.
*
* \sa setAllowAliases()
*/
bool NoiseLinkOptions::allowAliases() const
{
return d->allowAliases;
}
/**
* \brief Enables or disables the use of NoiseTinyLink protocol name aliases.
*
* \param allow Set to true if protocol name aliases are allowed; false if not.
*
* \sa allowAliases()
*/
void NoiseLinkOptions::setAllowAliases(bool allow)
{
d->allowAliases = allow;
}
/**
* \brief Gets the type of padding to use on plaintext before encryption.
*
* \return The type of padding; default is Noise::NoPadding.
*
* \sa setPadding()
*/
Noise::Padding NoiseLinkOptions::padding() const
{
return d->padding;
}
/**
* \brief Sets the type of padding to use on plaintext before encryption.
*
* \param padding The type of padding; Noise::NoPadding, Noise::ZeroPadding,
* or Noise::RandomPadding.
*
* If \a padding is Noise::ZeroPadding, then the plaintext on outgoing
* packets will be padded with zeroes to sendBufferSize() before encryption.
*
* If \a padding is Noise::RandomPadding, then the plaintext on outgoing
* packets will be padded with random data to sendBufferSize() before
* encryption.
*
* \sa setPadding()
*/
void NoiseLinkOptions::setPadding(Noise::Padding padding)
{
d->padding = padding;
}
/**
* \brief Copies all values from another options block into this one.
*
* \param options The other options block to copy from.
*/
void NoiseLinkOptions::copyFrom(const NoiseLinkOptions &options)
{
if (d != options.d)
d->copyFrom(options.d);
}
/**
* \brief Destroys all security-sensitive data within this options block.
*
* If you only need to destroy a specific parameter (e.g. the local key pair),
* then you can use removeParameter() instead.
*
* \sa removeParameter()
*/
void NoiseLinkOptions::clear()
{
d->clear();
}
/**
* \brief Creates a Noise handshake object for a specific protocol.
*
* \param party The party to create the handshake for; one of
* Noise::Initiator or Noise::Responder.
* \param protocolIndex The index of the protocol in this options block.
*
* \return A pointer to a new handshake object, populated with the keys
* that are needed to initialize the handshake.
* \return Returns NULL if \a protocolIndex is invalid or there are
* insufficient keys in this options block to initialize the handshake.
*
* \sa protocolAt()
*/
NoiseHandshakeState *NoiseLinkOptions::createHandshake
(Noise::Party party, size_t protocolIndex) const
{
// Find the protocol to be created.
const NoiseProtocolDescriptor *desc = protocolAt(protocolIndex);
if (!desc)
return 0;
// Verify that we have sufficient parameters to start the protocol.
unsigned short flags;
if (party == Noise::Initiator)
flags = desc->initiatorFlags;
else
flags = desc->responderFlags;
if ((flags & NOISE_PROTOCOL_NEEDS_LOCAL_STATIC) != 0 &&
(d->st.flags & HAVE_KEY_PAIR_25519) == 0)
return 0;
if ((flags & NOISE_PROTOCOL_NEEDS_REMOTE_STATIC) != 0 &&
(d->st.flags & HAVE_REMOTE_KEY_25519) == 0)
return 0;
if ((flags & NOISE_PROTOCOL_NEEDS_PSK) != 0 &&
(d->st.flags & HAVE_PSK) == 0)
return 0;
// Create the handshake object.
NoiseHandshakeState *handshake = desc->createHandshake();
if (!handshake)
return 0;
// Add all required keys. It is possible that this may fail if the
// protocol uses a different DH algorithm than Curve25519 or the
// flags in the protocol descriptor were incorrect for the handshake.
bool ok = true;
if ((flags & NOISE_PROTOCOL_NEEDS_LOCAL_STATIC) != 0) {
if (!handshake->setParameter
(Noise::LocalStatic25519KeyPair, d->st.keyPair25519, 64))
ok = false;
}
if (ok && (flags & NOISE_PROTOCOL_NEEDS_REMOTE_STATIC) != 0) {
if (!handshake->setParameter
(Noise::RemoteStatic25519PublicKey, d->st.remoteKey25519, 32))
ok = false;
}
if (ok && (flags & NOISE_PROTOCOL_NEEDS_PSK) != 0) {
if (!handshake->setParameter(Noise::PreSharedKey, d->st.psk, 32))
ok = false;
}
if (!ok) {
delete handshake;
return 0;
}
// Ready to go.
return handshake;
}

View File

@ -0,0 +1,94 @@
/*
* 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_LINK_OPTIONS_h
#define NOISE_LINK_OPTIONS_h
#include "NoiseNamespace.h"
#include "NoiseProtocolDescriptor.h"
#include "KeyRing.h"
/** Default send/receive buffer size for NoiseLink sessions */
#define NOISE_DEFAULT_BUFSIZ 512
/** Minimum send/receive buffer size that we allow for NoiseLink sessions */
#define NOISE_MIN_BUFSIZ 128
/** Maximum send/receive buffer size that we allow for NoiseLink sessions */
#define NOISE_MAX_BUFSIZ 2048
class NoiseLinkOptionsPrivate;
class NoiseLinkOptions
{
private:
// Disable the copy operators.
NoiseLinkOptions(const NoiseLinkOptions &other) {}
NoiseLinkOptions &operator=(const NoiseLinkOptions &other) { return *this; }
public:
NoiseLinkOptions();
~NoiseLinkOptions();
bool setParameter(Noise::Parameter id, const void *value, size_t size);
size_t getParameter(Noise::Parameter id, void *value, size_t maxSize) const;
size_t getParameterSize(Noise::Parameter id) const;
bool hasParameter(Noise::Parameter id) const;
void removeParameter(Noise::Parameter id);
bool load(Noise::Parameter id, uint16_t keyRingId = 0);
bool save(Noise::Parameter id, uint16_t keyRingId = 0);
bool generate(Noise::Parameter id, uint16_t keyRingId = 0, bool wait = true);
size_t protocolCount() const;
const NoiseProtocolDescriptor *protocolAt(size_t index) const;
void addProtocol(const NoiseProtocolDescriptor &protocol);
void removeProtocol(const NoiseProtocolDescriptor &protocol);
size_t sendBufferSize() const;
void setSendBufferSize(size_t size);
size_t receiveBufferSize() const;
void setReceiveBufferSize(size_t size);
void setBufferSize(size_t size)
{ setSendBufferSize(size); setReceiveBufferSize(size); }
bool allowAliases() const;
void setAllowAliases(bool allow);
Noise::Padding padding() const;
void setPadding(Noise::Padding padding);
void copyFrom(const NoiseLinkOptions &options);
void clear();
NoiseHandshakeState *createHandshake
(Noise::Party party, size_t protocolIndex) const;
private:
NoiseLinkOptionsPrivate *d;
};
extern NoiseLinkOptions NoiseClientOptions;
extern NoiseLinkOptions NoiseServerOptions;
#endif

View File

@ -1,28 +0,0 @@
/*
* 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 "NoiseNamespace.h"
/**
* \namespace Noise
* \brief Common definitions for the Noise protocol.
*/

View File

@ -23,8 +23,10 @@
#ifndef NOISE_NAMESPACE_h
#define NOISE_NAMESPACE_h
#include <inttypes.h>
/**
* \namespace Noise
* \brief Common definitions for the Noise protocol.
*/
namespace Noise
{
/**
@ -77,6 +79,28 @@ namespace Noise
RemoteEphem25519PublicKey = 207 /**< Remote ephemeral Curve25519 public key */
};
/**
* \brief Type of padding to apply to plaintext before encryption.
*/
enum Padding
{
NoPadding, /**< No padding */
ZeroPadding, /**< Pad with zeroes to the maximum length */
RandomPadding /**< Pad with random data to the maximum length */
};
/**
* \brief Status of a NoiseClient connection.
*/
enum ConnectionStatus
{
Closed, /**< Connection is closed or session hasn't started */
Connecting, /**< Connecting, handshake is not complete yet */
Connected, /**< Connected, ready to transmit/receive data */
Closing, /**< Connection is closing, but still unread data */
HandshakeFailed /**< Failed to establish a secure connection */
};
}; // namespace Noise
#endif

View File

@ -37,4 +37,7 @@
#include "Noise_XX_25519_ChaChaPoly_BLAKE2s.h"
#include "Noise_XX_25519_ChaChaPoly_SHA256.h"
#include "NoiseClient.h"
#include "NoiseServer.h"
#endif

View File

@ -37,8 +37,13 @@ class NoiseHandshakeState;
*/
struct NoiseProtocolDescriptor
{
/** Flags that define the properties and required keys for the protocol */
unsigned flags;
/** Flags that define the properties and required keys for the protocol
* when the handshake is created from the initiator side. */
unsigned short initiatorFlags;
/** Flags that define the properties and required keys for the protocol
* when the handshake is created from the responder side. */
unsigned short responderFlags;
/** Full Noise protocol name; e.g. "Noise_XX_25519_ChaChaPoly_BLAKE2s" */
const char *protocolName;

View File

@ -0,0 +1,144 @@
/*
* 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 "NoiseServer.h"
/**
* \class NoiseServer NoiseServer.h <NoiseServer.h>
* \brief Network server that uses Noise to secure incoming connections.
*
* This class is a template that wraps an underlying network server
* interface to layer the Noise protocol on top to provide security.
* Normally an application will use the NoiseEthernetServer or
* NoiseWiFiServer template instances to create a network server.
*
* Incoming Noise connections are secured using the options within
* the global NoiseServerOptions instance.
*
* \code
* #include <NoiseProtocol.h>
* #include <Ethernet.h>
* #include <RNG.h>
*
* uint8_t mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
* NoiseEthernetServer server(4433);
*
* void setup() {
* // Initialise the system random number generator.
* RNG.begin("MyApp 1.0");
*
* // Start the Ethernet interface.
* Ethernet.begin(mac);
*
* // Load or generate the Curve25519 key pair for this device.
* if (!NoiseServerOptions.load(Noise::LocalStaticKeyPair))
* NoiseServerOptions.generate(Noise::LocalStaticKeyPair);
*
* // Specify the protocols that we permit incoming connections to use.
* NoiseServerOptions.addProtocol(Noise_XX_25519_ChaChaPoly_BLAKE2s);
* NoiseServerOptions.addProtocol(Noise_XX_25519_AESGCM_SHA256);
*
* // Limit packet payloads to 128 bytes and pad to full length.
* NoiseServerOptions.setBufferSize(128);
* NoiseServerOptions.setPadding(Noise::RandomPadding);
*
* // Start Ethernet server operations on the port.
* server.begin();
* }
*
* void loop() {
* NoiseEthernetClient client = server.available();
* if (client) {
* // Handle the incoming connection.
* ...
* }
* }
* \endcode
*/
/**
* \fn NoiseServer::NoiseServer(uint16_t port)
* \brief Constructs a new network server that uses Noise.
*
* \param port The network port number to bind to on the underlying
* network interface when begin() is called.
*
* This will also construct the underlying network interface, which is
* usually an instance of EthernetServer or WiFiServer.
*/
/**
* \fn NoiseServer::~NoiseServer()
* \brief Destroys this Noise-based network server.
*
* This will also destroy the underlying network interface.
*/
/**
* \fn ServerT *NoiseServer::network()
* \brief Returns a reference to the underlying network interface.
*
* \return The underlying network server object; usually something like
* EthernetServer or WiFiServer.
*
* This function is intended to let the application adjust custom settings
* on the underlying network interface prior to calling begin():
*
* \code
* NoiseWiFiServer server;
* server.network()->setNoDelay(true);
* server.begin();
* \endcode
*
* \sa begin()
*/
/**
* \fn void NoiseServer::begin()
* \brief Begins network server operations.
*
* \sa available()
*/
/**
* \fn NoiseClientWrapper<ClientT> NoiseServer::available()
* \brief Checks for the next available incoming client.
*
* \return The next available incoming client, which may be false if there
* is no client available yet. Typically an instance of NoiseEthernetClient
* or NoiseWiFiClient depending upon the underlying network interface type.
*
* \code
* NoiseEthernetServer server;
* server.begin();
* ...
* void loop() {
* NoiseEthernetClient client = server.available();
* if (client) {
* // Handle the new client.
* ...
* }
* }
* \endcode
*
* \sa begin()
*/

View File

@ -0,0 +1,76 @@
/*
* 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_SERVER_h
#define NOISE_SERVER_h
#include "NoiseClient.h"
#include <Server.h>
template <typename ServerT, typename ClientT>
class NoiseServer : public Server
{
private:
// Disable the copy operators.
NoiseServer(const NoiseServer<ServerT, ClientT> &other) {}
NoiseServer &operator=(const NoiseServer<ServerT, ClientT> &other) { return *this; }
public:
explicit NoiseServer(uint16_t port) : net(port) {}
virtual ~NoiseServer() {}
inline ServerT *network() { return &net; }
// Standard Arduino Server interface.
inline void begin() { net.begin(); }
NoiseClientWrapper<ClientT> available();
/** @cond noise_server_write */
// Disable the write() functions from the base Print class.
// "Write to all clients" behaviour is not supported.
size_t write(uint8_t) { return 0; }
size_t write(const uint8_t *buffer, size_t size) { return 0; }
/** @endcond */
private:
ServerT net;
};
template <typename ServerT, typename ClientT>
NoiseClientWrapper<ClientT> NoiseServer<ServerT, ClientT>::available()
{
ClientT client = net.available();
if (client) {
ClientT *networkClient = new ClientT();
*networkClient = client;
NoiseClient *noiseClient = new NoiseClient(networkClient);
if (noiseClient->accept(NoiseServerOptions))
return NoiseClientWrapper<ClientT>(noiseClient);
delete networkClient;
}
return NoiseClientWrapper<ClientT>();
}
#define NoiseEthernetServer NoiseServer<EthernetServer, EthernetClient>
#define NoiseWiFiServer NoiseServer<WiFiServer, WiFiClient>
#endif

View File

@ -56,6 +56,7 @@ static NoiseHandshakeState *Noise_IK_25519_AESGCM_SHA256_createHandshake()
*/
const NoiseProtocolDescriptor Noise_IK_25519_AESGCM_SHA256 = {
NOISE_PROTOCOL_NEEDS_LOCAL_STATIC | NOISE_PROTOCOL_NEEDS_REMOTE_STATIC,
NOISE_PROTOCOL_NEEDS_LOCAL_STATIC,
Noise_IK_25519_AESGCM_SHA256_Name,
0,
Noise_IK_25519_AESGCM_SHA256_createHandshake

View File

@ -56,6 +56,7 @@ static NoiseHandshakeState *Noise_IK_25519_ChaChaPoly_BLAKE2s_createHandshake()
*/
const NoiseProtocolDescriptor Noise_IK_25519_ChaChaPoly_BLAKE2s = {
NOISE_PROTOCOL_NEEDS_LOCAL_STATIC | NOISE_PROTOCOL_NEEDS_REMOTE_STATIC,
NOISE_PROTOCOL_NEEDS_LOCAL_STATIC,
Noise_IK_25519_ChaChaPoly_BLAKE2s_Name,
0,
Noise_IK_25519_ChaChaPoly_BLAKE2s_createHandshake

View File

@ -56,6 +56,7 @@ static NoiseHandshakeState *Noise_IK_25519_ChaChaPoly_SHA256_createHandshake()
*/
const NoiseProtocolDescriptor Noise_IK_25519_ChaChaPoly_SHA256 = {
NOISE_PROTOCOL_NEEDS_LOCAL_STATIC | NOISE_PROTOCOL_NEEDS_REMOTE_STATIC,
NOISE_PROTOCOL_NEEDS_LOCAL_STATIC,
Noise_IK_25519_ChaChaPoly_SHA256_Name,
0,
Noise_IK_25519_ChaChaPoly_SHA256_createHandshake

View File

@ -55,6 +55,7 @@ static NoiseHandshakeState *Noise_NNpsk0_25519_AESGCM_SHA256_createHandshake()
* \brief Protocol descriptor for "Noise_NNps0_25519_AESGCM_SHA256".
*/
const NoiseProtocolDescriptor Noise_NNpsk0_25519_AESGCM_SHA256 = {
NOISE_PROTOCOL_NEEDS_PSK,
NOISE_PROTOCOL_NEEDS_PSK,
Noise_NNpsk0_25519_AESGCM_SHA256_Name,
0,

View File

@ -55,6 +55,7 @@ static NoiseHandshakeState *Noise_NNpsk0_25519_ChaChaPoly_BLAKE2s_createHandshak
* \brief Protocol descriptor for "Noise_NNps0_25519_ChaChaPoly_BLAKE2s".
*/
const NoiseProtocolDescriptor Noise_NNpsk0_25519_ChaChaPoly_BLAKE2s = {
NOISE_PROTOCOL_NEEDS_PSK,
NOISE_PROTOCOL_NEEDS_PSK,
Noise_NNpsk0_25519_ChaChaPoly_BLAKE2s_Name,
0,

View File

@ -55,6 +55,7 @@ static NoiseHandshakeState *Noise_NNpsk0_25519_ChaChaPoly_SHA256_createHandshake
* \brief Protocol descriptor for "Noise_NNps0_25519_ChaChaPoly_SHA256".
*/
const NoiseProtocolDescriptor Noise_NNpsk0_25519_ChaChaPoly_SHA256 = {
NOISE_PROTOCOL_NEEDS_PSK,
NOISE_PROTOCOL_NEEDS_PSK,
Noise_NNpsk0_25519_ChaChaPoly_SHA256_Name,
0,

View File

@ -55,6 +55,7 @@ static NoiseHandshakeState *Noise_XX_25519_AESGCM_SHA256_createHandshake()
* \brief Protocol descriptor for "Noise_XX_25519_AESGCM_SHA256".
*/
const NoiseProtocolDescriptor Noise_XX_25519_AESGCM_SHA256 = {
NOISE_PROTOCOL_NEEDS_LOCAL_STATIC,
NOISE_PROTOCOL_NEEDS_LOCAL_STATIC,
Noise_XX_25519_AESGCM_SHA256_Name,
"1",

View File

@ -55,6 +55,7 @@ static NoiseHandshakeState *Noise_XX_25519_ChaChaPoly_BLAKE2s_createHandshake()
* \brief Protocol descriptor for "Noise_XX_25519ChaChaPoly_BLAKE2s".
*/
const NoiseProtocolDescriptor Noise_XX_25519_ChaChaPoly_BLAKE2s = {
NOISE_PROTOCOL_NEEDS_LOCAL_STATIC,
NOISE_PROTOCOL_NEEDS_LOCAL_STATIC,
Noise_XX_25519_ChaChaPoly_BLAKE2s_Name,
"3",

View File

@ -55,6 +55,7 @@ static NoiseHandshakeState *Noise_XX_25519_ChaChaPoly_SHA256_createHandshake()
* \brief Protocol descriptor for "Noise_XX_25519ChaChaPoly_SHA256".
*/
const NoiseProtocolDescriptor Noise_XX_25519_ChaChaPoly_SHA256 = {
NOISE_PROTOCOL_NEEDS_LOCAL_STATIC,
NOISE_PROTOCOL_NEEDS_LOCAL_STATIC,
Noise_XX_25519_ChaChaPoly_SHA256_Name,
"2",