1064 lines
38 KiB
C
1064 lines
38 KiB
C
/* ENC28J60 Example
|
||
|
||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||
|
||
Unless required by applicable law or agreed to in writing, this
|
||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||
CONDITIONS OF ANY KIND, either express or implied.
|
||
*/
|
||
|
||
/*
|
||
*
|
||
* see https://www.wolfssl.com/docs/quickstart/
|
||
**/
|
||
|
||
#define WOLFSSL_ESPIDF
|
||
#define WOLFSSL_ESPWROOM32
|
||
#define WOLFSSL_USER_SETTINGS
|
||
|
||
/* the usual suspects */
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
|
||
#include "sdkconfig.h"
|
||
#include "freertos/FreeRTOS.h"
|
||
#include "freertos/task.h"
|
||
#include "esp_netif.h"
|
||
#include "esp_eth.h"
|
||
#include "esp_event.h"
|
||
#include "esp_log.h"
|
||
#include "driver/gpio.h"
|
||
#include "enc28j60.h"
|
||
#include "driver/spi_master.h"
|
||
|
||
#include "nvs_flash.h"
|
||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||
// #include "protocol_examples_common.h"
|
||
#endif
|
||
|
||
/* socket includes */
|
||
#include "lwip/netdb.h"
|
||
#include "lwip/sockets.h"
|
||
|
||
/* time */
|
||
#include <lwip/apps/sntp.h>
|
||
|
||
/* wolfSSL */
|
||
#include <wolfssl/wolfcrypt/settings.h> // make sure this appears before any other wolfSSL headers
|
||
#include <wolfssl/ssl.h>
|
||
|
||
#ifdef WOLFSSL_TRACK_MEMORY
|
||
#include <wolfssl/wolfcrypt/mem_track.h>
|
||
#endif
|
||
|
||
/**
|
||
******************************************************************************
|
||
******************************************************************************
|
||
** USER APPLICATION SETTINGS BEGIN
|
||
******************************************************************************
|
||
******************************************************************************
|
||
**/
|
||
|
||
/* ENC28J60 doesn't burn any factory MAC address, we need to set it manually.
|
||
02:00:00 is a Locally Administered OUI range so should not be used except when testing on a LAN under your control.
|
||
*/
|
||
uint8_t myMacAddress[] = {
|
||
0x02,
|
||
0x00,
|
||
0x00,
|
||
0x12,
|
||
0x34,
|
||
0x56
|
||
};
|
||
|
||
/* ESP lwip */
|
||
#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY
|
||
|
||
#define DEFAULT_PORT 11111
|
||
|
||
#define TLS_SMP_CLIENT_TASK_NAME "tls_server_example"
|
||
#define TLS_SMP_CLIENT_TASK_WORDS 10240
|
||
#define TLS_SMP_CLIENT_TASK_PRIORITY 8
|
||
|
||
|
||
|
||
/* include certificates. Note that there is an experiation date!
|
||
*
|
||
* See also https://github.com/wolfSSL/wolfssl/blob/master/wolfssl/certs_test.h
|
||
|
||
for example:
|
||
|
||
#define USE_CERT_BUFFERS_2048
|
||
#include <wolfssl/certs_test.h>
|
||
*/
|
||
|
||
/*
|
||
* for reference, embedded client uses:
|
||
*
|
||
* #include "embedded_CA_FILE.h"
|
||
* #include "embedded_CERT_FILE.h"
|
||
* #include "embedded_KEY_FILE.h"
|
||
*/
|
||
|
||
/*
|
||
server file system versions:
|
||
#define CA_FILE "../../../../certs/client-cert.pem"
|
||
#define CERT_FILE "../../../../certs/server-cert.pem"
|
||
#define KEY_FILE "../../../../certs/server-key.pem"
|
||
*/
|
||
|
||
#include "embedded_CLIENT_CERT_FILE.h"
|
||
#include "embedded_SERVER_CERT_FILE.h"
|
||
#include "embedded_SERVER_KEY_FILE.h"
|
||
|
||
|
||
static const char *TAG = "eth_example";
|
||
|
||
// see https://tf.nist.gov/tf-cgi/servers.cgi
|
||
const int NTP_SERVER_COUNT = 3;
|
||
const char* ntpServerList[] = {
|
||
"pool.ntp.org",
|
||
"time.nist.gov",
|
||
"utcnist.colorado.edu"
|
||
};
|
||
const char * TIME_ZONE = "PST-8";
|
||
const long gmtOffset_sec = 3600;
|
||
const int daylightOffset_sec = 3600;
|
||
|
||
TickType_t DelayTicks = 5000 / portTICK_PERIOD_MS;
|
||
/**
|
||
******************************************************************************
|
||
******************************************************************************
|
||
** USER SETTINGS END
|
||
******************************************************************************
|
||
******************************************************************************
|
||
**/
|
||
|
||
|
||
|
||
#ifdef HAVE_SIGNAL
|
||
static void sig_handler(const int sig) {
|
||
// fprintf(stderr, "SIGINT handled = %d.\n", sig);
|
||
|
||
mShutdown = 1;
|
||
if (mConnd != SOCKET_INVALID) {
|
||
close(mConnd); /* Close the connection to the client */
|
||
mConnd = SOCKET_INVALID;
|
||
}
|
||
if (mSockfd != SOCKET_INVALID) {
|
||
close(mSockfd); /* Close the socket listening for clients */
|
||
mSockfd = SOCKET_INVALID;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
static void LogSocketError(const char* fmt, int err)
|
||
{
|
||
char err_msg[128];
|
||
XSNPRINTF(err_msg, sizeof(err_msg), fmt, err);
|
||
WOLFSSL_ERROR_MSG(err_msg);
|
||
}
|
||
|
||
int tls_smp_server_task() {
|
||
int ret = WOLFSSL_SUCCESS; /* assume success until proven wrong */
|
||
int sockfd = 0; /* the socket that will carry our secure connection */
|
||
struct sockaddr_in servAddr;
|
||
const int BUFF_SIZE = 256;
|
||
char buff[BUFF_SIZE];
|
||
size_t len; /* we'll be looking at the length of messages sent and received */
|
||
|
||
struct sockaddr_in clientAddr;
|
||
socklen_t size = sizeof(clientAddr);
|
||
const char* reply = "I hear ya fa shizzle!\n";
|
||
int on;
|
||
|
||
static int mConnd = SOCKET_INVALID;
|
||
static int mShutdown = 0;
|
||
|
||
/* declare wolfSSL objects */
|
||
WOLFSSL_CTX *ctx = NULL; /* the wolfSSL context object*/
|
||
WOLFSSL *ssl = NULL; /* although called "ssl" is is the secure object for reading and writings data*/
|
||
|
||
#ifdef HAVE_SIGNAL
|
||
signal(SIGINT, sig_handler);
|
||
#endif
|
||
|
||
#ifdef DEBUG_WOLFSSL
|
||
wolfSSL_Debugging_ON();
|
||
WOLFSSL_MSG("Debug ON v0.2b");
|
||
//ShowCiphers();
|
||
#endif /* DEBUG_WOLFSSL */
|
||
|
||
#ifndef WOLFSSL_TLS13
|
||
ret = WOLFSSL_FAILURE;
|
||
WOLFSSL_ERROR_MSG("ERROR: Example requires TLS v1.3.\n");
|
||
#endif /* WOLFSSL_TLS13 */
|
||
|
||
/* Initialize the server address struct with zeros */
|
||
XMEMSET(&servAddr, 0, sizeof(servAddr));
|
||
|
||
/* Fill in the server address */
|
||
servAddr.sin_family = AF_INET; /* using IPv4 */
|
||
servAddr.sin_port = htons(DEFAULT_PORT); /* on DEFAULT_PORT */
|
||
servAddr.sin_addr.s_addr = INADDR_ANY; /* from anywhere */
|
||
|
||
/*
|
||
***************************************************************************
|
||
* Create a socket that uses an internet IPv4 address,
|
||
* Sets the socket to be stream based (TCP),
|
||
* 0 means choose the default protocol.
|
||
*
|
||
* #include <sys/socket.h>
|
||
*
|
||
* int socket(int domain, int type, int protocol);
|
||
*
|
||
* The socket() function shall create an unbound socket in a communications
|
||
* domain, and return a file descriptor that can be used in later function
|
||
* calls that operate on sockets.
|
||
*
|
||
* The socket() function takes the following arguments:
|
||
* domain Specifies the communications domain in which a
|
||
* socket is to be created.
|
||
* type Specifies the type of socket to be created.
|
||
* protocol Specifies a particular protocol to be used with the socket.
|
||
* Specifying a protocol of 0 causes socket() to use an
|
||
* unspecified default protocol appropriate for the
|
||
* requested socket type.
|
||
*
|
||
* The domain argument specifies the address family used in the
|
||
* communications domain. The address families supported by the system
|
||
* are implementation-defined.
|
||
*
|
||
* Symbolic constants that can be used for the domain argument are
|
||
* defined in the <sys/socket.h> header.
|
||
*
|
||
* The type argument specifies the socket type, which determines the semantics
|
||
* of communication over the socket. The following socket types are defined;
|
||
* implementations may specify additional socket types:
|
||
*
|
||
* SOCK_STREAM Provides sequenced, reliable, bidirectional,
|
||
* connection-mode byte streams, and may provide a
|
||
* transmission mechanism for out-of-band data.
|
||
* SOCK_DGRAM Provides datagrams, which are connectionless-mode,
|
||
* unreliable messages of fixed maximum length.
|
||
* SOCK_SEQPACKET Provides sequenced, reliable, bidirectional,
|
||
* connection-mode transmission paths for records.
|
||
* A record can be sent using one or more output
|
||
* operations and received using one or more input
|
||
* operations, but a single operation never transfers
|
||
* part of more than one record. Record boundaries
|
||
* are visible to the receiver via the MSG_EOR flag.
|
||
*
|
||
* If the protocol argument is non-zero, it shall
|
||
* specify a protocol that is supported by the address
|
||
* family. If the protocol argument is zero, the default
|
||
* protocol for this address family and type shall be
|
||
* used. The protocols supported by the system are
|
||
* implementation-defined.
|
||
*
|
||
* The process may need to have appropriate privileges to use the socket() function or to create some sockets.
|
||
*
|
||
* Return Value
|
||
* Upon successful completion, socket() shall return a non-negative integer,
|
||
* the socket file descriptor. Otherwise, a value of -1 shall be returned
|
||
* and errno set to indicate the error.
|
||
*
|
||
* Errors; The socket() function shall fail if:
|
||
*
|
||
* EAFNOSUPPORT The implementation does not support the specified address family.
|
||
* EMFILE No more file descriptors are available for this process.
|
||
* ENFILE No more file descriptors are available for the system.
|
||
* EPROTONOSUPPORT The protocol is not supported by the address family, or the protocol is not supported by the implementation.
|
||
* EPROTOTYPE The socket type is not supported by the protocol.
|
||
*
|
||
* The socket() function may fail if:
|
||
*
|
||
* EACCES The process does not have appropriate privileges.
|
||
* ENOBUFS Insufficient resources were available in the system to perform the operation.
|
||
* ENOMEM Insufficient memory was available to fulfill the request.
|
||
*
|
||
* see: https://linux.die.net/man/3/socket
|
||
***************************************************************************
|
||
*/
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
/* Upon successful completion, socket() shall return
|
||
* a non-negative integer, the socket file descriptor.
|
||
*/
|
||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||
if (sockfd >= 0) {
|
||
WOLFSSL_MSG("socket creation successful\n");
|
||
}
|
||
else {
|
||
LogSocketError("ERROR: failed to create a socket (errno = %d).\n", errno);
|
||
ret = WOLFSSL_FAILURE;
|
||
}
|
||
}
|
||
else {
|
||
WOLFSSL_ERROR_MSG("Skipping socket create.\n");
|
||
}
|
||
|
||
|
||
/*
|
||
***************************************************************************
|
||
* set SO_REUSEADDR on socket
|
||
*
|
||
* #include <sys/types.h>
|
||
* # include <sys / socket.h>
|
||
* int getsockopt(int sockfd,
|
||
* int level,
|
||
* int optname,
|
||
* void *optval,
|
||
* socklen_t *optlen); int setsockopt(int sockfd,
|
||
* int level,
|
||
* int optname,
|
||
* const void *optval,
|
||
* socklen_t optlen);
|
||
*
|
||
* setsockopt() manipulates options for the socket referred to by the file
|
||
* descriptor sockfd. Options may exist at multiple protocol levels; they
|
||
* are always present at the uppermost socket level.
|
||
*
|
||
* When manipulating socket options, the level at which the option resides
|
||
* and the name of the option must be specified. To manipulate options at
|
||
* the sockets API level, level is specified as SOL_SOCKET. To manipulate
|
||
* options at any other level the protocol number of the appropriate
|
||
* protocol controlling the option is supplied. For example, to indicate
|
||
* that an option is to be interpreted by the TCP protocol, level should
|
||
* be set to the protocol number of TCP
|
||
*
|
||
* Return Value
|
||
* On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
|
||
*
|
||
* Errors
|
||
* EBADF The argument sockfd is not a valid descriptor.
|
||
* EFAULT The address pointed to by optval is not in a valid part of the process address space. For getsockopt(), this error may also be returned if optlen is not in a valid part of the process address space.
|
||
* EINVAL optlen invalid in setsockopt(). In some cases this error can also occur for an invalid value in optval (e.g., for the IP_ADD_MEMBERSHIP option described in ip(7)).
|
||
* ENOPROTOOPT The option is unknown at the level indicated.
|
||
* ENOTSOCK The argument sockfd is a file, not a socket.
|
||
*
|
||
* see: https://linux.die.net/man/2/setsockopt
|
||
***************************************************************************
|
||
*/
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
/* make sure server is setup for reuse addr/port */
|
||
on = 1;
|
||
int soc_ret = setsockopt(sockfd,
|
||
SOL_SOCKET,
|
||
SO_REUSEADDR,
|
||
(char*)&on,
|
||
(socklen_t)sizeof(on));
|
||
|
||
if (soc_ret == 0) {
|
||
WOLFSSL_MSG("setsockopt re-use addr successful\n");
|
||
}
|
||
else {
|
||
LogSocketError("ERROR: failed to setsockopt addr on socket (errno = %d).\n", errno);
|
||
ret = WOLFSSL_FAILURE;
|
||
}
|
||
}
|
||
else {
|
||
WOLFSSL_ERROR_MSG("Skipping setsockopt addr\n");
|
||
}
|
||
|
||
#ifdef SO_REUSEPORT
|
||
/* see above for details on getsockopt */
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
int soc_ret = setsockopt(sockfd,
|
||
SOL_SOCKET,
|
||
SO_REUSEPORT,
|
||
(char*)&on,
|
||
(socklen_t)sizeof(on));
|
||
|
||
if (soc_ret == 0) {
|
||
WOLFSSL_MSG("setsockopt re-use port successful\n");
|
||
}
|
||
else {
|
||
LogSocketError("ERROR: failed to setsockopt port on socket (errno = %d). >> IGNORED << \n", errno);
|
||
}
|
||
}
|
||
else {
|
||
WOLFSSL_ERROR_MSG("Skipping setsockopt port\n");
|
||
}
|
||
#else
|
||
WOLFSSL_MSG("SO_REUSEPORT not configured for setsockopt to re-use port\n");
|
||
#endif
|
||
|
||
/*
|
||
***************************************************************************
|
||
* #include <sys/types.h>
|
||
* #include <sys/socket.h>
|
||
*
|
||
* int bind(int sockfd,
|
||
* const struct sockaddr *addr,
|
||
* socklen_t addrlen);
|
||
*
|
||
* Description
|
||
*
|
||
* When a socket is created with socket(2), it exists in a name
|
||
* space(address family) but has no address assigned to it.
|
||
*
|
||
* bind() assigns the address specified by addr to the socket referred to
|
||
* by the file descriptor sockfd.addrlen specifies the size, in bytes, of
|
||
* the address structure pointed to by addr.Traditionally, this operation
|
||
* is called "assigning a name to a socket".
|
||
*
|
||
* It is normally necessary to assign a local address using bind() before
|
||
* a SOCK_STREAM socket may receive connections.
|
||
*
|
||
* Return Value
|
||
* On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
|
||
*
|
||
* Errors
|
||
* EACCES The address is protected, and the user is not the superuser.
|
||
* EADDRINUSE The given address is already in use.
|
||
* EBADF sockfd is not a valid descriptor.
|
||
* EINVAL The socket is already bound to an address.
|
||
* ENOTSOCK sockfd is a descriptor for a file, not a socket.
|
||
*
|
||
* see: https://linux.die.net/man/2/bind
|
||
*
|
||
* https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/lwip.html
|
||
***************************************************************************
|
||
*/
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
/* Bind the server socket to our port */
|
||
int soc_ret = bind(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr));
|
||
if (soc_ret > -1) {
|
||
WOLFSSL_MSG("socket bind successful\n");
|
||
}
|
||
else {
|
||
LogSocketError("ERROR: failed to bind to socket (errno = %d).\n", errno);
|
||
ret = WOLFSSL_FAILURE;
|
||
}
|
||
}
|
||
|
||
/*
|
||
***************************************************************************
|
||
* Listen for a new connection, allow 5 pending connections
|
||
*
|
||
* #include <sys/types.h>
|
||
* #include <sys/socket.h>
|
||
* int listen(int sockfd, int backlog);
|
||
*
|
||
* Description
|
||
*
|
||
* listen() marks the socket referred to by sockfd as a passive socket,
|
||
* that is, as a socket that will be used to accept incoming connection
|
||
* requests using accept.
|
||
*
|
||
* The sockfd argument is a file descriptor that refers to a socket of
|
||
* type SOCK_STREAM or SOCK_SEQPACKET.
|
||
*
|
||
* The backlog argument defines the maximum length to which the queue of
|
||
* pending connections for sockfd may grow.If a connection request arrives
|
||
* when the queue is full, the client may receive an error with an indication
|
||
* of ECONNREFUSED or, if the underlying protocol supports retransmission,
|
||
* the request may be ignored so that a later reattempt at connection
|
||
* succeeds.
|
||
*
|
||
* Return Value
|
||
* On success, zero is returned.
|
||
* On Error, -1 is returned, and errno is set appropriately.
|
||
* Errors
|
||
* EADDRINUSE Another socket is already listening on the same port.
|
||
* EBADF The argument sockfd is not a valid descriptor.
|
||
* ENOTSOCK The argument sockfd is not a socket.
|
||
* EOPNOTSUPP The socket is not of a type that supports the listen() operation.
|
||
*
|
||
* ses: https://linux.die.net/man/2/listen
|
||
*/
|
||
|
||
if(ret == WOLFSSL_SUCCESS) {
|
||
int soc_ret = listen(sockfd, 5);
|
||
if (soc_ret > -1) {
|
||
WOLFSSL_MSG("socket listen successful\n");
|
||
}
|
||
else {
|
||
LogSocketError("ERROR: failed to listen to socket (errno = %d).\n", errno);
|
||
ret = WOLFSSL_FAILURE;
|
||
}
|
||
}
|
||
|
||
/*
|
||
***************************************************************************
|
||
* Initialize wolfSSL
|
||
*
|
||
* WOLFSSL_API int wolfSSL_Init (void)
|
||
*
|
||
* Initializes the wolfSSL library for use. Must be called once per
|
||
* application and before any other call to the library.
|
||
*
|
||
* Returns
|
||
* SSL_SUCCESS If successful the call will return.
|
||
* BAD_MUTEX_E is an error that may be returned.
|
||
* WC_INIT_E wolfCrypt initialization error returned.
|
||
*
|
||
* see: https://www.wolfssl.com/doxygen/group__TLS.html#gae2a25854de5230820a6edf16281d8fd7
|
||
***************************************************************************
|
||
*/
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
/* only proceed if the prior step was successful */
|
||
WOLFSSL_MSG("calling wolfSSL_Init");
|
||
ret = wolfSSL_Init();
|
||
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
WOLFSSL_MSG("wolfSSL_Init successful\n");
|
||
}
|
||
else {
|
||
WOLFSSL_ERROR_MSG("ERROR: wolfSSL_Init failed\n");
|
||
}
|
||
}
|
||
else {
|
||
WOLFSSL_ERROR_MSG("Skipping wolfSSL_Init\n");
|
||
}
|
||
|
||
/*
|
||
***************************************************************************
|
||
* Create and initialize WOLFSSL_CTX (aka the context)
|
||
*
|
||
* WOLFSSL_API WOLFSSL_CTX* wolfSSL_CTX_new (WOLFSSL_METHOD *)
|
||
*
|
||
* This function creates a new SSL context, taking a desired
|
||
* SSL/TLS protocol method for input.
|
||
*
|
||
* Returns
|
||
* pointer If successful the call will return a pointer to the newly-created WOLFSSL_CTX.
|
||
* NULL upon failure.
|
||
*
|
||
* Parameters
|
||
* method pointer to the desired WOLFSSL_METHOD to use for the SSL context.
|
||
* This is created using one of the wolfSSLvXX_XXXX_method() functions to
|
||
* specify SSL/TLS/DTLS protocol level.
|
||
*
|
||
* see https://www.wolfssl.com/doxygen/group__Setup.html#gadfa552e771944a6a1102aa43f45378b5
|
||
***************************************************************************
|
||
*/
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
WOLFSSL_METHOD* method = wolfTLSv1_3_server_method();
|
||
WOLFSSL_MSG("calling wolfTLSv1_3_server_method");
|
||
if (method == NULL) {
|
||
WOLFSSL_ERROR_MSG("ERROR : failed to get wolfTLSv1_3_server_method.\n");
|
||
ret = WOLFSSL_FAILURE;
|
||
}
|
||
else {
|
||
WOLFSSL_MSG("calling wolfSSL_CTX_new");
|
||
ctx = wolfSSL_CTX_new(method);
|
||
|
||
if (ctx == NULL) {
|
||
WOLFSSL_ERROR_MSG("ERROR : failed to create WOLFSSL_CTX\n");
|
||
ret = WOLFSSL_FAILURE;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
WOLFSSL_ERROR_MSG("skipping wolfSSL_CTX_new\n");
|
||
}
|
||
|
||
/*
|
||
***************************************************************************
|
||
* load CERT_FILE
|
||
*
|
||
*
|
||
* WOLFSSL_API int wolfSSL_use_certificate_buffer (WOLFSSL * ,
|
||
* const unsigned char * ,
|
||
* long,
|
||
* int
|
||
* )
|
||
*
|
||
* The wolfSSL_use_certificate_buffer() function loads a certificate buffer
|
||
* into the WOLFSSL object. It behaves like the non-buffered version, only
|
||
* differing in its ability to be called with a buffer as input instead of
|
||
* a file. The buffer is provided by the in argument of size sz.
|
||
*
|
||
* format specifies the format type of the buffer; SSL_FILETYPE_ASN1 or
|
||
* SSL_FILETYPE_PEM. Please see the examples for proper usage.
|
||
*
|
||
* Returns
|
||
* SSL_SUCCESS upon success.
|
||
* SSL_BAD_FILETYPE will be returned if the file is the wrong format.
|
||
* SSL_BAD_FILE will be returned if the file doesn’t exist, can’t be read, or is corrupted.
|
||
* MEMORY_E will be returned if an out of memory condition occurs.
|
||
* ASN_INPUT_E will be returned if Base16 decoding fails on the file.
|
||
*
|
||
* Parameters
|
||
* ssl pointer to the SSL session, created with wolfSSL_new().
|
||
* in buffer containing certificate to load.
|
||
* sz size of the certificate located in buffer.
|
||
* format format of the certificate to be loaded. Possible values are SSL_FILETYPE_ASN1 or SSL_FILETYPE_PEM.
|
||
*
|
||
*
|
||
* Pay attention to expiration dates and the current date setting
|
||
*
|
||
* see https://www.wolfssl.com/doxygen/group__CertsKeys.html#gaf4e8d912f3fe2c37731863e1cad5c97e
|
||
***************************************************************************
|
||
*/
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
WOLFSSL_MSG("Loading cert");
|
||
ret = wolfSSL_CTX_use_certificate_buffer(ctx,
|
||
CERT_FILE,
|
||
sizeof_CERT_FILE(),
|
||
WOLFSSL_FILETYPE_PEM);
|
||
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
WOLFSSL_MSG("wolfSSL_CTX_use_certificate_buffer successful\n");
|
||
}
|
||
else {
|
||
WOLFSSL_ERROR_MSG("ERROR: wolfSSL_CTX_use_certificate_buffer failed\n");
|
||
}
|
||
}
|
||
else {
|
||
WOLFSSL_ERROR_MSG("skipping wolfSSL_CTX_use_certificate_buffer\n");
|
||
}
|
||
|
||
|
||
/*
|
||
***************************************************************************
|
||
* Load client private key into WOLFSSL_CTX
|
||
*
|
||
* wolfSSL_CTX_use_PrivateKey_buffer()
|
||
*
|
||
* WOLFSSL_API int wolfSSL_CTX_use_PrivateKey_buffer(WOLFSSL_CTX *,
|
||
* const unsigned char *,
|
||
* long,
|
||
* int
|
||
* )
|
||
*
|
||
* This function loads a private key buffer into the SSL Context.
|
||
* It behaves like the non-buffered version, only differing in its
|
||
* ability to be called with a buffer as input instead of a file.
|
||
*
|
||
* The buffer is provided by the in argument of size sz. format
|
||
* specifies the format type of the buffer;
|
||
* SSL_FILETYPE_ASN1 or SSL_FILETYPE_PEM.
|
||
*
|
||
* Please see the examples for proper usage.
|
||
*
|
||
* Returns
|
||
* SSL_SUCCESS upon success
|
||
* SSL_BAD_FILETYPE will be returned if the file is the wrong format.
|
||
* SSL_BAD_FILE will be returned if the file doesn’t exist, can’t be read, or is corrupted.
|
||
* MEMORY_E will be returned if an out of memory condition occurs.
|
||
* ASN_INPUT_E will be returned if Base16 decoding fails on the file.
|
||
* NO_PASSWORD will be returned if the key file is encrypted but no password is provided.
|
||
*
|
||
* Parameters
|
||
* ctx pointer to the SSL context, created with wolfSSL_CTX_new().
|
||
* inthe input buffer containing the private key to be loaded.
|
||
*
|
||
* sz the size of the input buffer.
|
||
*
|
||
* format the format of the private key located in the input buffer(in).
|
||
* Possible values are SSL_FILETYPE_ASN1 or SSL_FILETYPE_PEM.
|
||
*
|
||
* see: https://www.wolfssl.com/doxygen/group__CertsKeys.html#ga71850887b87138b7c2d794bf6b1eafab
|
||
***************************************************************************
|
||
*/
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
ret = wolfSSL_CTX_use_PrivateKey_buffer(ctx,
|
||
KEY_FILE,
|
||
sizeof_KEY_FILE(),
|
||
WOLFSSL_FILETYPE_PEM);
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
WOLFSSL_MSG("wolfSSL_CTX_use_PrivateKey_buffer successful\n");
|
||
}
|
||
else {
|
||
/* TODO fetch and print expiration date since it is a common fail */
|
||
WOLFSSL_ERROR_MSG("ERROR: wolfSSL_CTX_use_PrivateKey_buffer failed\n");
|
||
}
|
||
}
|
||
else {
|
||
WOLFSSL_ERROR_MSG("Skipping wolfSSL_CTX_use_PrivateKey_buffer\n");
|
||
}
|
||
|
||
|
||
/*
|
||
***************************************************************************
|
||
* Load CA certificate into WOLFSSL_CTX
|
||
*
|
||
* wolfSSL_CTX_load_verify_buffer()
|
||
* WOLFSSL_API int wolfSSL_CTX_load_verify_buffer(WOLFSSL_CTX *,
|
||
* const unsigned char *,
|
||
* long,
|
||
* int
|
||
* )
|
||
*
|
||
* This function loads a CA certificate buffer into the WOLFSSL Context.
|
||
* It behaves like the non-buffered version, only differing in its ability
|
||
* to be called with a buffer as input instead of a file. The buffer is
|
||
* provided by the in argument of size sz. format specifies the format type
|
||
* of the buffer; SSL_FILETYPE_ASN1 or SSL_FILETYPE_PEM. More than one
|
||
* CA certificate may be loaded per buffer as long as the format is in PEM.
|
||
*
|
||
* Please see the examples for proper usage.
|
||
*
|
||
* Returns
|
||
*
|
||
* SSL_SUCCESS upon success
|
||
* SSL_BAD_FILETYPE will be returned if the file is the wrong format.
|
||
* SSL_BAD_FILE will be returned if the file doesn’t exist, can’t be read, or is corrupted.
|
||
* MEMORY_E will be returned if an out of memory condition occurs.
|
||
* ASN_INPUT_E will be returned if Base16 decoding fails on the file.
|
||
* BUFFER_E will be returned if a chain buffer is bigger than the receiving buffer.
|
||
*
|
||
* Parameters
|
||
*
|
||
* ctx pointer to the SSL context, created with wolfSSL_CTX_new().
|
||
* in pointer to the CA certificate buffer.
|
||
* sz size of the input CA certificate buffer, in.
|
||
* format format of the buffer certificate, either SSL_FILETYPE_ASN1 or SSL_FILETYPE_PEM.
|
||
*
|
||
* see https://www.wolfssl.com/doxygen/group__CertsKeys.html#gaa37539cce3388c628ac4672cf5606785
|
||
***************************************************************************
|
||
*/
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
ret = wolfSSL_CTX_load_verify_buffer(ctx, CA_FILE, sizeof_CA_FILE(), WOLFSSL_FILETYPE_PEM);
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
WOLFSSL_MSG("wolfSSL_CTX_load_verify_buffer successful\n");
|
||
}
|
||
else {
|
||
WOLFSSL_ERROR_MSG("ERROR: wolfSSL_CTX_load_verify_buffer failed\n");
|
||
}
|
||
}
|
||
else {
|
||
WOLFSSL_ERROR_MSG("skipping wolfSSL_CTX_load_verify_buffer\n");
|
||
}
|
||
|
||
/* Require mutual authentication */
|
||
wolfSSL_CTX_set_verify(ctx,
|
||
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
||
NULL);
|
||
|
||
|
||
|
||
|
||
/* Continue to accept clients until mShutdown is issued */
|
||
while (!mShutdown && (ret == WOLFSSL_SUCCESS)) {
|
||
WOLFSSL_MSG("Waiting for a connection...\n");
|
||
|
||
/* Accept client connections */
|
||
if ((mConnd = accept(sockfd, (struct sockaddr*)&clientAddr, &size))
|
||
== -1) {
|
||
WOLFSSL_ERROR_MSG("ERROR: failed socket accept\n");
|
||
ret = WOLFSSL_FAILURE;
|
||
break;
|
||
}
|
||
|
||
/* Create a WOLFSSL object */
|
||
if ((ssl = wolfSSL_new(ctx)) == NULL) {
|
||
WOLFSSL_ERROR_MSG("ERROR: failed wolfSSL_new during loop\n");
|
||
ret = WOLFSSL_FAILURE;
|
||
close(mConnd);
|
||
mConnd = SOCKET_INVALID;
|
||
break;
|
||
}
|
||
|
||
/* Attach wolfSSL to the socket */
|
||
wolfSSL_set_fd(ssl, mConnd);
|
||
|
||
#ifdef HAVE_SECRET_CALLBACK
|
||
/* required for getting random used */
|
||
wolfSSL_KeepArrays(ssl);
|
||
|
||
/* optional logging for wireshark */
|
||
wolfSSL_set_tls13_secret_cb(ssl,
|
||
Tls13SecretCallback,
|
||
(void*)WOLFSSL_SSLKEYLOGFILE_OUTPUT);
|
||
#endif
|
||
|
||
/* Establish TLS connection */
|
||
if ((ret = wolfSSL_accept(ssl)) != WOLFSSL_SUCCESS) {
|
||
WOLFSSL_ERROR_MSG("ERROR: wolfSSL_accept\n");
|
||
ret = WOLFSSL_FAILURE;
|
||
}
|
||
else {
|
||
WOLFSSL_MSG("Client connected successfully\n");
|
||
}
|
||
|
||
#ifdef HAVE_SECRET_CALLBACK
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
wolfSSL_FreeArrays(ssl);
|
||
}
|
||
#endif
|
||
|
||
/* Read the client data into our buff array */
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
XMEMSET(buff, 0, sizeof(buff));
|
||
if (wolfSSL_read(ssl, buff, sizeof(buff) - 1) <= 0) {
|
||
WOLFSSL_ERROR_MSG("ERROR: failed to read\n");
|
||
ret = WOLFSSL_FAILURE;
|
||
}
|
||
}
|
||
|
||
if (ret == WOLFSSL_SUCCESS) {
|
||
/* Check for server shutdown command */
|
||
if (XSTRNCMP(buff, "shutdown", 8) == 0) {
|
||
mShutdown = 1;
|
||
}
|
||
|
||
/* Write our reply into buff */
|
||
XMEMSET(buff, 0, sizeof(buff));
|
||
XMEMCPY(buff, reply, XSTRLEN(reply));
|
||
len = XSTRLEN(buff);
|
||
|
||
/* Reply back to the client */
|
||
if (wolfSSL_write(ssl, buff, len) != len) {
|
||
WOLFSSL_ERROR_MSG("ERROR: failed to write\n");
|
||
ret = WOLFSSL_FAILURE;
|
||
}
|
||
}
|
||
|
||
/* Cleanup after this connection */
|
||
wolfSSL_shutdown(ssl);
|
||
if (ssl) {
|
||
wolfSSL_free(ssl); /* Free the wolfSSL object */
|
||
ssl = NULL;
|
||
}
|
||
if (mConnd != SOCKET_INVALID) {
|
||
close(mConnd); /* Close the connection to the client */
|
||
mConnd = SOCKET_INVALID;
|
||
}
|
||
|
||
/* A handshake (wolfSSL_accept) or read/write failure on this client's
|
||
* connection shouldn't stop the server from accepting the next one.
|
||
* Fatal setup failures above (socket accept/wolfSSL_new) already break
|
||
* out of the loop. */
|
||
ret = WOLFSSL_SUCCESS;
|
||
}
|
||
|
||
WOLFSSL_MSG("Shutdown complete\n");
|
||
|
||
/*
|
||
***************************************************************************
|
||
*
|
||
* Cleanup and return
|
||
*
|
||
***************************************************************************
|
||
*/
|
||
if (mConnd != SOCKET_INVALID) {
|
||
close(mConnd); /* Close the connection to the client */
|
||
mConnd = SOCKET_INVALID;
|
||
}
|
||
|
||
if (sockfd != SOCKET_INVALID) {
|
||
close(sockfd); /* Close the socket listening for clients */
|
||
sockfd = SOCKET_INVALID;
|
||
}
|
||
|
||
if (ssl) {
|
||
wolfSSL_free(ssl); /* Free the wolfSSL object */
|
||
}
|
||
|
||
|
||
if (ctx) {
|
||
wolfSSL_CTX_free(ctx); /* Free the wolfSSL context object */
|
||
}
|
||
|
||
wolfSSL_Cleanup(); /* Cleanup the wolfSSL environment */
|
||
|
||
return ret;
|
||
}
|
||
|
||
|
||
/** Event handler for Ethernet events */
|
||
static void eth_event_handler(void *arg,
|
||
esp_event_base_t event_base,
|
||
int32_t event_id,
|
||
void *event_data) {
|
||
uint8_t mac_addr[6] = { 0 };
|
||
/* we can get the ethernet driver handle from event data */
|
||
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
|
||
|
||
switch (event_id) {
|
||
case ETHERNET_EVENT_CONNECTED:
|
||
/*
|
||
* see ESP-IDF 5.0 note at
|
||
* https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/ethernet.html
|
||
*
|
||
* esp_eth_ioctl() third argument could take int (bool) number as an input in some cases.
|
||
* However, it was not properly documented and, in addition, the number had to be “unnaturally”
|
||
* type casted to void * datatype to prevent compiler warnings as shown in below example:
|
||
*
|
||
* esp_eth_ioctl(eth_handle, ETH_CMD_S_FLOW_CTRL, (void *)true);
|
||
*
|
||
* This could lead to misuse of the esp_eth_ioctl(). Therefore, ESP-IDF 5.0 unified usage of
|
||
* esp_eth_ioctl(). Its third argument now always acts as pointer to a memory location of specific
|
||
* type from/to where the configuration option is read/stored.
|
||
*
|
||
* TODO Migrate Ethernet Drivers to ESP-IDF 5.0
|
||
|
||
eth_duplex_t new_duplex_mode = ETH_DUPLEX_HALF;
|
||
esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &new_duplex_mode);
|
||
|
||
*/
|
||
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
|
||
|
||
ESP_LOGI(TAG, "Ethernet Link Up");
|
||
ESP_LOGI(TAG,
|
||
"Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
|
||
mac_addr[0],
|
||
mac_addr[1],
|
||
mac_addr[2],
|
||
mac_addr[3],
|
||
mac_addr[4],
|
||
mac_addr[5]);
|
||
break;
|
||
|
||
case ETHERNET_EVENT_DISCONNECTED:
|
||
ESP_LOGI(TAG, "Ethernet Link Down");
|
||
break;
|
||
|
||
case ETHERNET_EVENT_START:
|
||
ESP_LOGI(TAG, "Ethernet Started");
|
||
break;
|
||
|
||
case ETHERNET_EVENT_STOP:
|
||
ESP_LOGI(TAG, "Ethernet Stopped");
|
||
break;
|
||
|
||
default:
|
||
ESP_LOGI(TAG, "Other");
|
||
break;
|
||
}
|
||
}
|
||
|
||
/** Event handler for IP_EVENT_ETH_GOT_IP */
|
||
static void got_ip_event_handler(void *arg,
|
||
esp_event_base_t event_base,
|
||
int32_t event_id,
|
||
void *event_data) {
|
||
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
|
||
const esp_netif_ip_info_t *ip_info = &event->ip_info;
|
||
|
||
ESP_LOGI(TAG, "Ethernet Got IP Address");
|
||
ESP_LOGI(TAG, "~~~~~~~~~~~");
|
||
ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
|
||
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
|
||
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
|
||
ESP_LOGI(TAG, "~~~~~~~~~~~");
|
||
}
|
||
|
||
int set_time() {
|
||
/* we'll also return a result code of zero */
|
||
int res = 0;
|
||
|
||
//*ideally, we'd like to set time from network, but let's set a default time, just in case */
|
||
struct tm timeinfo;
|
||
timeinfo.tm_year = 2022 - 1900;
|
||
timeinfo.tm_mon = 3;
|
||
timeinfo.tm_mday = 15;
|
||
timeinfo.tm_hour = 8;
|
||
timeinfo.tm_min = 03;
|
||
timeinfo.tm_sec = 10;
|
||
time_t t;
|
||
t = mktime(&timeinfo);
|
||
|
||
struct timeval now = { .tv_sec = t };
|
||
settimeofday(&now, NULL);
|
||
|
||
/* set timezone */
|
||
setenv("TZ", TIME_ZONE, 1);
|
||
tzset();
|
||
|
||
/* next, let's setup NTP time servers
|
||
*
|
||
* see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/system_time.html#sntp-time-synchronization
|
||
*/
|
||
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||
|
||
int i = 0;
|
||
for (i = 0; i < NTP_SERVER_COUNT; i++) {
|
||
const char* thisServer = ntpServerList[i];
|
||
if (XSTRNCMP(thisServer, "\x00", 1)) {
|
||
/* just in case we run out of NTP servers */
|
||
break;
|
||
}
|
||
sntp_setservername(i, thisServer);
|
||
}
|
||
sntp_init();
|
||
return res;
|
||
}
|
||
|
||
|
||
|
||
int init_ENC28J60() {
|
||
ESP_ERROR_CHECK(gpio_install_isr_service(0));
|
||
// Initialize TCP/IP network interface (should be called only once in application)
|
||
ESP_ERROR_CHECK(esp_netif_init());
|
||
// Create default event loop that running in background
|
||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
|
||
esp_netif_t *eth_netif = esp_netif_new(&netif_cfg);
|
||
|
||
|
||
spi_bus_config_t buscfg = {
|
||
.miso_io_num = CONFIG_EXAMPLE_ENC28J60_MISO_GPIO,
|
||
.mosi_io_num = CONFIG_EXAMPLE_ENC28J60_MOSI_GPIO,
|
||
.sclk_io_num = CONFIG_EXAMPLE_ENC28J60_SCLK_GPIO,
|
||
.quadwp_io_num = -1,
|
||
.quadhd_io_num = -1,
|
||
};
|
||
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ENC28J60_SPI_HOST, &buscfg, 1));
|
||
/* ENC28J60 ethernet driver is based on spi driver */
|
||
spi_device_interface_config_t devcfg = {
|
||
.command_bits = 3,
|
||
.address_bits = 5,
|
||
.mode = 0,
|
||
.clock_speed_hz = CONFIG_EXAMPLE_ENC28J60_SPI_CLOCK_MHZ * 1000 * 1000,
|
||
.spics_io_num = CONFIG_EXAMPLE_ENC28J60_CS_GPIO,
|
||
.queue_size = 20
|
||
};
|
||
spi_device_handle_t spi_handle = NULL;
|
||
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ENC28J60_SPI_HOST, &devcfg, &spi_handle));
|
||
|
||
eth_enc28j60_config_t enc28j60_config = ETH_ENC28J60_DEFAULT_CONFIG(spi_handle);
|
||
enc28j60_config.int_gpio_num = CONFIG_EXAMPLE_ENC28J60_INT_GPIO;
|
||
|
||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||
mac_config.smi_mdc_gpio_num = -1; // ENC28J60 doesn't have SMI interface
|
||
mac_config.smi_mdio_gpio_num = -1;
|
||
esp_eth_mac_t *mac = esp_eth_mac_new_enc28j60(&enc28j60_config, &mac_config);
|
||
|
||
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
||
phy_config.autonego_timeout_ms = 0; // ENC28J60 doesn't support auto-negotiation
|
||
phy_config.reset_gpio_num = -1; // ENC28J60 doesn't have a pin to reset internal PHY
|
||
esp_eth_phy_t *phy = esp_eth_phy_new_enc28j60(&phy_config);
|
||
|
||
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
|
||
esp_eth_handle_t eth_handle = NULL;
|
||
ESP_ERROR_CHECK(esp_eth_driver_install(ð_config, ð_handle));
|
||
|
||
|
||
mac->set_addr(mac, myMacAddress);
|
||
|
||
|
||
/* attach Ethernet driver to TCP/IP stack */
|
||
ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)));
|
||
|
||
/* Register user defined event handers
|
||
* "ensure that they register the user event handlers as the last thing prior to starting the Ethernet driver."
|
||
*/
|
||
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL));
|
||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));
|
||
|
||
/* start Ethernet driver state machine */
|
||
ESP_ERROR_CHECK(esp_eth_start(eth_handle));
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
void app_main(void) {
|
||
init_ENC28J60();
|
||
|
||
// one of the most important aspects of security is the time and date values
|
||
set_time();
|
||
|
||
for (;;) {
|
||
ESP_LOGI(TAG, "main loop");
|
||
vTaskDelay(DelayTicks ? DelayTicks : 1); /* Minimum delay = 1 tick */
|
||
tls_smp_server_task();
|
||
}
|
||
}
|