almost finished documenting, moved SSLSession implementation into it's own file, fixed infinite loop when overflowing m_iobuf
This commit is contained in:
parent
cd94d0bf3b
commit
648104c7e3
8 changed files with 265 additions and 250 deletions
260
src/SSLClient.h
260
src/SSLClient.h
|
@ -18,20 +18,6 @@
|
|||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SSLCLient.h
|
||||
*
|
||||
* This library was created to provide SSL functionality to the {@link https://learn.adafruit.com/adafruit-wiz5500-wiznet-ethernet-featherwing/overview}
|
||||
* Adafruit Ethernet shield. Since this shield does not implement SSL functionality on
|
||||
* its own, we need to use an external library: in this case BearSSL {@link https://bearssl.org/},
|
||||
* which is also used in the Arduino ESP8266 core. SSLClient will serve to implement the
|
||||
* BearSSL functionality inbetween EthernetClient and the User, such that the user.
|
||||
*
|
||||
* This file specifically controls the class templating used to allow SSLClient to interface
|
||||
* with all of the CLient-based classes. To see details on the implementations of the functions
|
||||
* in SSLClient, please see {@link ./SSLClientImpl.h}.
|
||||
*/
|
||||
|
||||
#include <type_traits>
|
||||
#include "Client.h"
|
||||
#include "SSLClientImpl.h"
|
||||
|
@ -41,27 +27,13 @@
|
|||
#define SSLClient_H_
|
||||
|
||||
/**
|
||||
* \brief The main SSLClient class
|
||||
*
|
||||
* TODO: fix this blurb
|
||||
*
|
||||
* This class serves as a templating proxy class for the SSLClientImpl to do the real work.
|
||||
*
|
||||
* A problem arose when writing this class: I wanted the user to be able to construct
|
||||
* this class in a single line of code (e.g. SSLClient(EthernetClient())), but I also
|
||||
* wanted to avoid the use of dynamic memory if possible. In an attempt to solve this
|
||||
* problem I used a templated classes. However, becuase of the Arduino build process
|
||||
* this meant that the implementations for all the functions had to be in a header
|
||||
* file (a weird effect of using templated classes and linking) which would slow down
|
||||
* the build quite a bit. As a comprimise, I instead decided to build the main class (SSLCLient)
|
||||
* as a templated class, and have use a not templated implementation class (SSLClientImpl)
|
||||
* that would be able to reside in a seperate file. This gets the best of both worlds
|
||||
* from the client side, however from the developer side it can be a bit confusing.
|
||||
* @brief The main SSLClient class
|
||||
* Check out README.md for more info.
|
||||
*/
|
||||
|
||||
template <class C, size_t SessionCache = 1>
|
||||
class SSLClient : public SSLClientImpl {
|
||||
/**
|
||||
/*
|
||||
* static checks
|
||||
* I'm a java developer, so I want to ensure that my inheritance is safe.
|
||||
* These checks ensure that all the functions we use on class C are
|
||||
|
@ -81,22 +53,20 @@ static_assert(SessionCache <= 3, "You need to decrease the size of m_iobuf in or
|
|||
|
||||
public:
|
||||
/**
|
||||
* @brief copies the client object, and passes the various parameters to the SSLCLientImpl functions.
|
||||
*
|
||||
* We copy the client because we aren't sure the Client object
|
||||
* is going to exists past the inital creation of the SSLClient.
|
||||
* @brief Initialize SSLClient with all of the prerequisites needed.
|
||||
*
|
||||
* @pre You will need to generate an array of trust_anchors (root certificates)
|
||||
* based off of the domains you want to make SSL connections to. Check out the
|
||||
* Wiki on the pycert-bearssl tool for a simple way to do this.
|
||||
* TrustAnchors.md file for more info.
|
||||
* @pre The analog_pin should be set to input.
|
||||
*
|
||||
* @param client The base network device to create an SSL socket on. This object will be copied
|
||||
* and the copy will be stored in SSLClient.
|
||||
* @param trust_anchors Trust anchors used in the verification
|
||||
* of the SSL server certificate, generated using the `brssl` command
|
||||
* line utility. For more information see the samples or bearssl.org
|
||||
* @param trust_anchors_num The number of trust anchors stored
|
||||
* @param analog_pin An analog pin to pull random bytes from, used in seeding the RNG
|
||||
* @param debug whether to enable or disable debug logging, must be constexpr
|
||||
* of the SSL server certificate. Check out TrustAnchors.md for more info.
|
||||
* @param trust_anchors_num The number of objects in the trust_anchors array.
|
||||
* @param analog_pin An analog pin to pull random bytes from, used in seeding the RNG.
|
||||
* @param debug whether to enable or disable debug logging.
|
||||
*/
|
||||
explicit SSLClient(const C& client, const br_x509_trust_anchor *trust_anchors, const size_t trust_anchors_num, const int analog_pin, const DebugLevel debug = SSL_WARN)
|
||||
: SSLClientImpl(trust_anchors, trust_anchors_num, analog_pin, debug)
|
||||
|
@ -113,93 +83,81 @@ public:
|
|||
//========================================
|
||||
|
||||
/**
|
||||
* @brief Connect over SSL to a host specified by an ip address
|
||||
* @brief Connect over SSL to a host specified by an IP address.
|
||||
*
|
||||
* SSLClient::connect(host, port) should be preffered over this function,
|
||||
* SSLClient::connect(host, port) should be preferred over this function,
|
||||
* as verifying the domain name is a step in ensuring the certificate is
|
||||
* legitimate, which is important to the security of the device. Additionally,
|
||||
* SSL sessions cannot be resumed, which can drastically increase initial
|
||||
* connect time.
|
||||
*
|
||||
* This function initializes EthernetClient by calling EthernetClient::connect
|
||||
* with the parameters supplied, then once the socket is open initializes
|
||||
* the appropriete bearssl contexts. Due to the design of the SSL standard,
|
||||
* This function initializes the socket by calling m_client::connect(IPAddress, uint16_t)
|
||||
* with the parameters supplied, then once the socket uses BearSSL to
|
||||
* to complete a SSL handshake. Due to the design of the SSL standard,
|
||||
* this function will probably take an extended period (1-4sec) to negotiate
|
||||
* the handshake and finish the connection. This function runs until the SSL
|
||||
* handshake succeeds or fails, as found in most Arduino libraries.
|
||||
* handshake succeeds or fails.
|
||||
*
|
||||
* The implementation for this function can be found in SSLClientImpl::connect_impl(IPAddress, uint16_t)
|
||||
* SSL requires the client to generate some random bits (to be later combined
|
||||
* with some random bits from the server), so SSLClient uses the least significant
|
||||
* bits from the analog pin supplied in the constructor. The random bits are generated
|
||||
* from 16 consecutive analogReads, and given to BearSSL before the handshake
|
||||
* starts.
|
||||
*
|
||||
* @pre The underlying client object (passed in through the ctor) in a non-
|
||||
* error state, and must be able to access the server being connected to.
|
||||
* @pre SSLCLient can only have one connection at a time, so the client
|
||||
* object must not already have a socket open.
|
||||
* @pre There must be sufficient memory availible on the device to verify
|
||||
* The implementation for this function can be found in SSLClientImpl::connect_impl(IPAddress, uint16_t).
|
||||
*
|
||||
* @pre The underlying client object (passed in through the constructor) is in a non-
|
||||
* error state, and must be able to access the IP.
|
||||
* @pre SSLClient can only have one connection at a time, so the client
|
||||
* object must not already be connected.
|
||||
* @pre There must be sufficient memory available on the device to verify
|
||||
* the certificate (if the free memory drops below 8000 bytes during certain
|
||||
* points in the connection, SSLCLient will fail).
|
||||
* @pre There must be a trust anchor given to the ctor that corresponds to
|
||||
* points in the connection, SSLClient will fail).
|
||||
* @pre There must be a trust anchor given to the constructor that corresponds to
|
||||
* the certificate provided by the IP address being connected to. For more
|
||||
* information check out the wiki on the pycert-bearssl tool.
|
||||
* @pre The analog pin passed to the ctor must be set to input, and must
|
||||
* be wired to something sort of random (floating is fine).
|
||||
* information check out TrustAnchors.md .
|
||||
*
|
||||
* @param ip The ip address to connect to
|
||||
* @param ip The IP address to connect to
|
||||
* @param port the port to connect to
|
||||
* @returns 1 if success, 0 if failure (as found in EthernetClient)
|
||||
* @returns 1 if success, 0 if failure
|
||||
*/
|
||||
virtual int connect(IPAddress ip, uint16_t port) { return connect_impl(ip, port); }
|
||||
|
||||
/**
|
||||
* @brief Connect over SSL using connect(ip, port), using a DNS lookup to
|
||||
* get the IP Address first.
|
||||
* @brief Connect over SSL to a host specified by a hostname.
|
||||
*
|
||||
* This function initializes EthernetClient by calling EthernetClient::connect
|
||||
* This function initializes the socket by calling m_client::connect(const char*, uint16_t)
|
||||
* with the parameters supplied, then once the socket is open uses BearSSL to
|
||||
* to complete a SSL handshake. This function runs until the SSL handshake
|
||||
* succeeds or fails, as found in most Arduino libraries.
|
||||
* succeeds or fails.
|
||||
*
|
||||
* SSL requires the client to generate some random bits (to be later combined
|
||||
* with some random bits from the server), so SSLClient uses the least signinigant
|
||||
* bits from the analog pin supplied in the ctor. The random bits are generated
|
||||
* with some random bits from the server), so SSLClient uses the least significant
|
||||
* bits from the analog pin supplied in the constructor. The random bits are generated
|
||||
* from 16 consecutive analogReads, and given to BearSSL before the handshake
|
||||
* starts.
|
||||
*
|
||||
* Due to the design of the SSL standard, this function will probably take an
|
||||
* extended period (1-4sec) to negotiate the handshake and finish the
|
||||
* connection. Since the hostname is provided, however, BearSSL is able to keep
|
||||
* a session cache of the clients we have connected to. This should reduce
|
||||
* connection time to about 100-200ms. In order to use this feature, the website
|
||||
* you are connecting to must support it (most do by default), you must
|
||||
* reuse the same SSLClient object, and you must reconnect to the same server.
|
||||
* SSLClient automatcally stores an IP address and hostname in each session,
|
||||
* ensuring that if you call connect("www.google.com") SSLClient will use a
|
||||
* cached IP address instead of another DNS lookup. Because some websites have
|
||||
* multiple servers on a single IP address (github.com is an example), however,
|
||||
* you may find that even if you are connecting to the same host the connection
|
||||
* does not resume. This is a flaw in the SSL session protocol, and has been
|
||||
* resolved in future versions. On top of all that, SSL sessions can expire
|
||||
* based on server criteria, which will result in a regular connection time.
|
||||
* Because of all these factors, it is generally prudent to assume the
|
||||
* connection will not be resumed, and go from there.
|
||||
* This function will usually take around 4-10 seconds. If possible, this function
|
||||
* also attempts to resume the SSL session if one is present matching the hostname
|
||||
* string, which will reduce connection time to 100-500ms. To read more about this
|
||||
* functionality, check out Session Caching in the README.
|
||||
*
|
||||
* The implementation for this function can be found in SSLClientImpl::connect_impl(const char*, uint16_t)
|
||||
*
|
||||
* @pre The underlying client object (passed in through the ctor) in a non-
|
||||
* error state, and must be able to access the server being connected to.
|
||||
* @pre SSLCLient can only have one connection at a time, so the client
|
||||
* object must not already have a socket open.
|
||||
* @pre There must be sufficient memory availible on the device to verify
|
||||
* @pre The underlying client object (passed in through the constructor) is in a non-
|
||||
* error state, and must be able to access the IP.
|
||||
* @pre SSLClient can only have one connection at a time, so the client
|
||||
* object must not already be connected.
|
||||
* @pre There must be sufficient memory available on the device to verify
|
||||
* the certificate (if the free memory drops below 8000 bytes during certain
|
||||
* points in the connection, SSLCLient will fail).
|
||||
* @pre There must be a trust anchor given to the ctor that corresponds to
|
||||
* points in the connection, SSLClient will fail).
|
||||
* @pre There must be a trust anchor given to the constructor that corresponds to
|
||||
* the certificate provided by the IP address being connected to. For more
|
||||
* information check out the wiki on the pycert-bearssl tool.
|
||||
* @pre The analog pin passed to the ctor must be set to input, and must
|
||||
* be wired to something sort of random (floating is fine).
|
||||
* information check out TrustAnchors.md .
|
||||
*
|
||||
* @param host The cstring host ("www.google.com")
|
||||
* @param port the port to connect to (443)
|
||||
* @returns 1 of success, 0 if failure (as found in EthernetClient).
|
||||
* @param host The hostname as a null-terminated c-string ("www.google.com")
|
||||
* @param port The port to connect to on the host (443 for HTTPS)
|
||||
* @returns 1 of success, 0 if failure
|
||||
*/
|
||||
virtual int connect(const char *host, uint16_t port) { return connect_impl(host, port); }
|
||||
|
||||
|
@ -208,36 +166,14 @@ public:
|
|||
/**
|
||||
* @brief Write some bytes to the SSL connection
|
||||
*
|
||||
* Assuming all preconditions are met, this function waits for BearSSL
|
||||
* to be ready for data to be sent, then writes data to the BearSSL IO
|
||||
* buffer, BUT does not initally send the data. Insead, it is
|
||||
* then checked if the BearSSL IO buffer is full, and if so, this function
|
||||
* waits until BearSSL has flushed the buffer (written it to the
|
||||
* network client) and fills the buffer again. If the function finds
|
||||
* that the BearSSL buffer is not full, it returns the number of
|
||||
* bytes written. In other words, this function will only write data
|
||||
* to the network if the BearSSL IO buffer is full. Instead, you must call
|
||||
* SSLClient::availible or SSLClient::flush, which will detect that
|
||||
* the buffer is ready for writing, and will write the data to the network.
|
||||
*
|
||||
* This was implemented as a buffered function because users of Arduino Client
|
||||
* libraries will often write to the network as such:
|
||||
* @code{.cpp}
|
||||
* Client client;
|
||||
* ...
|
||||
* client.println("GET /asciilogo.txt HTTP/1.1");
|
||||
* client.println("Host: arduino.cc");
|
||||
* client.println("Connection: close");
|
||||
* while (!client.available()) { ... }
|
||||
* ...
|
||||
* @endcode
|
||||
* This is fine with most network clients. With SSL, however, if we are encryting and
|
||||
* writing to the network every write() call this will result in a lot of
|
||||
* small encryption tasks. Encryption takes a lot of time and code, and in general
|
||||
* the larger the batch we can do it in the better. For this reason, write()
|
||||
* implicitly buffers until SSLClient::availible is called, or until the buffer is full.
|
||||
* If you would like to trigger a network write manually without using the SSLClient::available,
|
||||
* you can also call SSLClient::flush, which will write all data and return when finished.
|
||||
* Assuming all preconditions are met, this function writes data to the BearSSL IO
|
||||
* buffer, BUT does not initially send the data. Instead, you must call
|
||||
* SSLClient::available or SSLClient::flush, which will detect that
|
||||
* the buffer is ready for writing, and will write the data to the network.
|
||||
* Alternatively, if this function is requested to write a larger amount of data than SSLClientImpl::m_iobuf
|
||||
* can handle, data will be written to the network in pages the size of SSLClientImpl::m_iobuf until
|
||||
* all the data in buf is sent--attempting to keep all writes to the network grouped together. For information
|
||||
* on why this is the case check out README.md .
|
||||
*
|
||||
* The implementation for this function can be found in SSLClientImpl::write_impl(const uint8_t*, size_t)
|
||||
*
|
||||
|
@ -256,18 +192,18 @@ public:
|
|||
* @brief Returns the number of bytes availible to read from the SSL Socket
|
||||
*
|
||||
* This function updates the state of the SSL engine (including writing any data,
|
||||
* see SSLClient::write) and as a result should be called periodically when writing
|
||||
* or expecting data. Additionally, since this function returns zero if there are
|
||||
* no bytes and if SSLClient::connected is false (this same behavior is found
|
||||
* see SSLClient::write) and as a result should be called periodically when expecting data.
|
||||
* Additionally, since if there are no bytes and if SSLClient::connected is false
|
||||
* this function returns zero (this same behavior is found
|
||||
* in EthernetClient), it is prudent to ensure in your own code that the
|
||||
* preconditions are met before checking this function to prevent an ambigious
|
||||
* preconditions are met before checking this function to prevent an ambiguous
|
||||
* result.
|
||||
*
|
||||
* The implementation for this function can be found in SSLClientImpl::available
|
||||
*
|
||||
* @pre SSLClient::connected must be true.
|
||||
*
|
||||
* @returns The number of bytes availible (can be zero), or zero if any of the pre
|
||||
* @returns The number of bytes available (can be zero), or zero if any of the pre
|
||||
* conditions aren't satisfied.
|
||||
*/
|
||||
virtual int available() { return available_impl(); }
|
||||
|
@ -280,38 +216,14 @@ public:
|
|||
/**
|
||||
* @brief Read size bytes from the SSL socket buffer, copying them into *buf, and return the number of bytes read.
|
||||
*
|
||||
* This function checks if bytes are ready to be read by calling SSLClient::availible,
|
||||
* and if so copies size number of bytes from the IO buffer into the buf pointer, and deletes
|
||||
* that number of bytes from the SSLClient buffer. Data read using this function will not
|
||||
* This function checks if bytes are ready to be read by calling SSLClient::available,
|
||||
* and if so copies size number of bytes from the IO buffer into the buf pointer.
|
||||
* Data read using this function will not
|
||||
* include any SSL or socket commands, as the Client and BearSSL will capture those and
|
||||
* process them seperatley.
|
||||
* process them separately.
|
||||
*
|
||||
* It should be noted that a common problem I encountered with SSL connections is
|
||||
* buffer overflow, caused by the server sending too much data at once. This problem
|
||||
* is caused by the microcontroller being unable to copy and decrypt data faster
|
||||
* than it is being recieved, forcing some data to be discarded. This usually puts BearSSL
|
||||
* in an invalid state in which it is unable to recover, causing SSLClient to close
|
||||
* the connection with a write error. If you are experiencing frequent timeout problems,
|
||||
* this could be the reason why.
|
||||
*
|
||||
* In order to remedy this problem the device must be able to read the data faster than
|
||||
* it is being recieved, or have a cache large enough to store the entire recieve payload.
|
||||
* Since SSL's encryption forces the device to read slowly, this means we must increase
|
||||
* the cache size. Depending on your platform, there are a number of ways this can be
|
||||
* done:
|
||||
* - Sometimes your communication sheild will have an internal buffer, which can be expanded
|
||||
* through the driver code. This is the case with the Arduino Ethernet library (in the form
|
||||
* of the MAX_SOCK_NUM and ETHERNET_LARGE_BUFFERS macros), however the library must be
|
||||
* modified for the change to take effect.
|
||||
* - SSLClient has an internal buffer SSLClientImpl::m_iobuf, which can be expanded. This will have very
|
||||
* limited usefulness, however, as BearSSL limits the amount of data that can be processed
|
||||
* based on the stage in the SSL handshake.
|
||||
* - If none of the above are viable, it is possible to implement your own Client class which
|
||||
* has an internal buffer much larger than both the driver and BearSSL. This would require
|
||||
* in-depth knowlege of programming and the communication shield you are working with.
|
||||
* Another important question to ask with this problem is: do I need to acsess this website?
|
||||
* Often times there are other ways to get data that we need that do the same thing,
|
||||
* and these other ways may offer smaller and more managable response payloads.
|
||||
* If you find that you are having a lot of timeout errors, SSLClient may be experiencing a buffer
|
||||
* overflow. Checkout README.md for more information.
|
||||
*
|
||||
* The implementation for this function can be found in SSLClientImpl::read_impl(uint8_t*, size_t)
|
||||
*
|
||||
|
@ -328,15 +240,15 @@ public:
|
|||
* @brief view the first byte of the buffer, without removing it from the SSLClient Buffer
|
||||
* The implementation for this function can be found in SSLClientImpl::peek
|
||||
* @pre SSLClient::available must be >0
|
||||
* @returns The first byte recieved, or -1 if the preconditions are not satisfied (warning:
|
||||
* do not use if your data may be -1, as the return value is ambigious)
|
||||
* @returns The first byte received, or -1 if the preconditions are not satisfied (warning:
|
||||
* do not use if your data may be -1, as the return value is ambiguous)
|
||||
*/
|
||||
virtual int peek() { return peek_impl(); }
|
||||
|
||||
/**
|
||||
* @brief Force writing the buffered bytes from SSLClient::write to the network.
|
||||
* This function is blocking until all bytes from the buffer are written. For
|
||||
* an explanation of how writing with SSLClient works, please see SSLCLient::write.
|
||||
* an explanation of how writing with SSLClient works, please see SSLClient::write.
|
||||
* The implementation for this function can be found in SSLClientImpl::flush.
|
||||
*/
|
||||
virtual void flush() { return flush_impl(); }
|
||||
|
@ -353,9 +265,9 @@ public:
|
|||
/**
|
||||
* @brief Check if the device is connected.
|
||||
* Use this function to determine if SSLClient is still connected and a SSL connection is active.
|
||||
* It should be noted that SSLClient::availible should be prefered over this function for rapid
|
||||
* polling--both functions send and recieve data to the Client device, however SSLClient::availible
|
||||
* has some delays built in to protect the Client device from being polled too frequently.
|
||||
* It should be noted that SSLClient::available should be preferred over this function for rapid
|
||||
* polling--both functions send and receive data with the SSLClient::m_client device, however SSLClient::available
|
||||
* has some delays built in to protect SSLClient::m_client from being polled too frequently.
|
||||
*
|
||||
* The implementation for this function can be found in SSLClientImpl::connected_impl.
|
||||
*
|
||||
|
@ -368,16 +280,16 @@ public:
|
|||
//========================================
|
||||
|
||||
/**
|
||||
* @brief Get a sesssion reference corressponding to a host and IP, or a reference to a emptey session if none exist
|
||||
* @brief Get a session reference corresponding to a host and IP, or a reference to a empty session if none exist
|
||||
*
|
||||
* If no session corresponding to the host and ip exist, then this function will cycle through
|
||||
* sessions in a rotating order. This allows the ssession cache to continuially store sessions,
|
||||
* If no session corresponding to the host and IP exist, then this function will cycle through
|
||||
* sessions in a rotating order. This allows the session cache to continually store sessions,
|
||||
* however it will also result in old sessions being cleared and returned. In general, it is a
|
||||
* good idea to use a SessionCache size equal to the number of domains you plan on connecting to.
|
||||
*
|
||||
* The implementation for this function can be found at SSLClientImpl::get_session_impl.
|
||||
*
|
||||
* @param host A hostname c string, or NULL if one is not availible
|
||||
* @param host A hostname c string, or NULL if one is not available
|
||||
* @param ip An IP address
|
||||
* @returns A reference to an SSLSession object
|
||||
*/
|
||||
|
@ -388,13 +300,13 @@ public:
|
|||
*
|
||||
* The implementation for this function can be found at SSLClientImpl::remove_session_impl.
|
||||
*
|
||||
* @param host A hostname c string, or NULL if one is not availible
|
||||
* @param host A hostname c string, or NULL if one is not available
|
||||
* @param ip An IP address
|
||||
*/
|
||||
virtual void removeSession(const char* host, const IPAddress& addr) { return remove_session_impl(host, addr); }
|
||||
|
||||
/**
|
||||
* @brief Get the meximum number of SSL sessions that can be stored at once
|
||||
* @brief Get the maximum number of SSL sessions that can be stored at once
|
||||
* @returns The SessionCache template parameter.
|
||||
*/
|
||||
virtual size_t getSessionCount() const { return SessionCache; }
|
||||
|
@ -437,7 +349,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
/** @brief returns a refernence to the client object stored in this class. Take care not to break it. */
|
||||
/** @brief returns a reference to the client object stored in this class. Take care not to break it. */
|
||||
C& getClient() { return m_client; }
|
||||
|
||||
protected:
|
||||
|
|
|
@ -135,6 +135,7 @@ size_t SSLClientImpl::write_impl(const uint8_t *buf, size_t size) {
|
|||
size_t alen;
|
||||
unsigned char *br_buf = br_ssl_engine_sendapp_buf(&m_sslctx.eng, &alen);
|
||||
size_t cur_idx = 0;
|
||||
bool did_overflow = false;
|
||||
// while there are still elements to write
|
||||
while (cur_idx < size) {
|
||||
// run until the ssl socket is ready to write, unless we've already written
|
||||
|
@ -152,8 +153,13 @@ size_t SSLClientImpl::write_impl(const uint8_t *buf, size_t size) {
|
|||
// so we only send the smallest of the buffer size or our data size - how much we've already sent
|
||||
const size_t cpamount = m_write_idx + (size - cur_idx) > alen ? alen : size - cur_idx;
|
||||
memcpy(br_buf + m_write_idx, buf + cur_idx, cpamount);
|
||||
// if we filled the buffer, reset m_write_idx
|
||||
if (cpamount == alen) m_write_idx = 0;
|
||||
// if we filled the buffer, reset m_write_idx, and mark the data for sending
|
||||
// or if we've overflowed since we're writing to the network already we may as well finish
|
||||
if (cpamount == alen || did_overflow) {
|
||||
m_write_idx = 0;
|
||||
br_ssl_engine_sendapp_ack(&m_sslctx.eng, alen);
|
||||
did_overflow = true;
|
||||
}
|
||||
// else increment
|
||||
else m_write_idx += cpamount;
|
||||
// increment the buffer pointer
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
* @brief Static constants defining the possible errors encountered.
|
||||
*
|
||||
* If SSLClient encounters an error, it will generally output
|
||||
* logs into the serial moniter. If you need a way of programmatically
|
||||
* logs into the serial monitor. If you need a way of programmatically
|
||||
* checking the errors, you can do so with SSLClient::getWriteError(),
|
||||
* which will return one of these values.
|
||||
*/
|
||||
|
@ -44,7 +44,7 @@ enum Error {
|
|||
SSL_CLIENT_WRTIE_ERROR,
|
||||
/** An internal error occurred with BearSSL, check logs for diagnosis. */
|
||||
SSL_BR_WRITE_ERROR,
|
||||
/** An internal error occured with SSLClient, and you probably need to submit an issue on Github. */
|
||||
/** An internal error occurred with SSLClient, and you probably need to submit an issue on Github. */
|
||||
SSL_INTERNAL_ERROR,
|
||||
/** SSLClient detected that there was not enough memory (>8000 bytes) to continue. */
|
||||
SSL_OUT_OF_MEMORY
|
||||
|
@ -53,47 +53,24 @@ enum Error {
|
|||
/**
|
||||
* @brief Level of verbosity used in logging for SSLClient.
|
||||
*
|
||||
* Use these values when initializing SSLCLient to set how many logs you
|
||||
* would like to see in the Serial moniter.
|
||||
* Use these values when initializing SSLClient to set how many logs you
|
||||
* would like to see in the Serial monitor.
|
||||
*/
|
||||
enum DebugLevel {
|
||||
/** No logging output */
|
||||
SSL_NONE = 0,
|
||||
/** Only output errors that result in connection failure */
|
||||
SSL_ERROR = 1,
|
||||
/** Ouput errors and warnings (useful when just starting to develop) */
|
||||
/** Output errors and warnings (useful when just starting to develop) */
|
||||
SSL_WARN = 2,
|
||||
/** Output errors, warnings, and internal information (very verbose) */
|
||||
SSL_INFO = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* On error, any function in this class will terminate the socket.
|
||||
* TODO: Write what this is */
|
||||
|
||||
/** @brief Implementation code to be inherited by SSLClient */
|
||||
class SSLClientImpl : public Client {
|
||||
public:
|
||||
/**
|
||||
* @brief initializes SSL contexts for bearSSL
|
||||
*
|
||||
* @pre You will need to generate an array of trust_anchors (root certificates)
|
||||
* based off of the domains you want to make SSL connections to. Check out the
|
||||
* Wiki on the pycert-bearssl tool for a simple way to do this.
|
||||
* @pre The analog_pin should be set to input.
|
||||
* @pre The session_ray must be an array of the size returned by SSLClient::getSessionCount()
|
||||
* filled with SSLSession objects.
|
||||
*
|
||||
* @post set_client must be called immediatly after to set the client class
|
||||
* pointer and Session pointer.
|
||||
*
|
||||
* @param trust_anchors Trust anchors used in the verification
|
||||
* of the SSL server certificate, generated using the `brssl` command
|
||||
* line utility. For more information see the samples or bearssl.org
|
||||
* @param trust_anchors_num The number of trust anchors stored
|
||||
* @param analog_pin An analog pin to pull random bytes from, used in seeding the RNG
|
||||
* @param session_ray A pointer to the array of SSLSessions created by SSLClient
|
||||
* @param debug whether to enable or disable debug logging, must be constexpr
|
||||
*/
|
||||
/** See SSLClient::SSLClient */
|
||||
explicit SSLClientImpl(const br_x509_trust_anchor *trust_anchors,
|
||||
const size_t trust_anchors_num, const int analog_pin, const DebugLevel debug);
|
||||
|
||||
|
@ -185,7 +162,7 @@ private:
|
|||
int m_start_ssl(const char* host, SSLSession& ssl_ses);
|
||||
/** run the bearssl engine until a certain state */
|
||||
int m_run_until(const unsigned target);
|
||||
/** proxy for availble that returns the state */
|
||||
/** proxy for available that returns the state */
|
||||
unsigned m_update_engine();
|
||||
/** utility function to find a session index based off of a host and IP */
|
||||
int m_get_session_index(const char* host, const IPAddress& addr) const;
|
||||
|
|
24
src/SSLSession.cpp
Normal file
24
src/SSLSession.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "SSLSession.h"
|
||||
|
||||
/* See SSLSession.h */
|
||||
void SSLSession::set_parameters(const IPAddress& ip, const char* hostname) {
|
||||
// copy the hostname
|
||||
if (hostname != NULL) m_hostname = hostname;
|
||||
// or if there's no hostname, clear the string
|
||||
else m_hostname = "";
|
||||
// and the IP address
|
||||
m_ip = ip;
|
||||
// check if both values are valid, and if so set valid to true
|
||||
if (m_ip != INADDR_NONE && session_id_len > 0
|
||||
&& (hostname == NULL || m_hostname)) m_valid_session = true;
|
||||
// else clear
|
||||
else clear_parameters();
|
||||
}
|
||||
|
||||
/* see SSLSession.h */
|
||||
void SSLSession::clear_parameters() {
|
||||
// clear the hostname , ip, and valid session flags
|
||||
m_hostname = "";
|
||||
m_ip = INADDR_NONE;
|
||||
m_valid_session = false;
|
||||
}
|
|
@ -26,6 +26,8 @@
|
|||
*/
|
||||
|
||||
#include "bearssl.h"
|
||||
#include "Arduino.h"
|
||||
#include "IPAddress.h"
|
||||
|
||||
#ifndef SSLSession_H_
|
||||
#define SSLSession_H_
|
||||
|
@ -104,19 +106,7 @@ public:
|
|||
* Take care that this value is corrent, SSLSession performs no validation
|
||||
* of the hostname.
|
||||
*/
|
||||
void set_parameters(const IPAddress& ip, const char* hostname = NULL) {
|
||||
// copy the hostname
|
||||
if (hostname != NULL) m_hostname = hostname;
|
||||
// or if there's no hostname, clear the string
|
||||
else m_hostname = "";
|
||||
// and the IP address
|
||||
m_ip = ip;
|
||||
// check if both values are valid, and if so set valid to true
|
||||
if (m_ip != INADDR_NONE && session_id_len > 0
|
||||
&& (hostname == NULL || m_hostname)) m_valid_session = true;
|
||||
// else clear
|
||||
else clear_parameters();
|
||||
}
|
||||
void set_parameters(const IPAddress& ip, const char* hostname = NULL);
|
||||
|
||||
/**
|
||||
* @brief delete the parameters and invalidate the session
|
||||
|
@ -124,12 +114,7 @@ public:
|
|||
* this function preserves the String object, allowing it
|
||||
* to better handle the dynamic memory needed.
|
||||
*/
|
||||
void clear_parameters() {
|
||||
// clear the hostname , ip, and valid session flags
|
||||
m_hostname = "";
|
||||
m_ip = INADDR_NONE;
|
||||
m_valid_session = false;
|
||||
}
|
||||
void clear_parameters();
|
||||
|
||||
/** @brief returns a pointer to the ::br_ssl_session_parameters component of this class */
|
||||
br_ssl_session_parameters* to_br_session() { return (br_ssl_session_parameters *)this; }
|
||||
|
@ -142,4 +127,6 @@ private:
|
|||
IPAddress m_ip;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* SSLSession_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue