Commit e905c24d authored by Johny Mattsson's avatar Johny Mattsson
Browse files

Removed unsused, now incompatible SSL code.

parent 21c4e110
/******************************************************************************
* Copyright 2013-2014 Espressif Systems (Wuxi)
*
* FileName: espconn_ssl.c
*
* Description: ssl encrypt interface
*
* Modification history:
* 2014/3/31, v1.0 create this file.
*******************************************************************************/
#include "lwip/netif.h"
#include "netif/etharp.h"
#include "lwip/tcp.h"
#include "lwip/ip.h"
#include "lwip/init.h"
#include "lwip/tcp_impl.h"
#include "ssl/ssl_os_port.h"
#include "ssl/app/espconn_ssl.h"
#include "ets_sys.h"
#include "os_type.h"
//#include "os.h"
#include "lwip/app/espconn.h"
struct pbuf *psslpbuf = NULL;
extern espconn_msg *plink_active;
static err_t espconn_ssl_crecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
static err_t espconn_ssl_srecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
static void espconn_ssl_sclose(void *arg, struct tcp_pcb *pcb);
static void espconn_ssl_cclose(void *arg, struct tcp_pcb *pcb);
/////////////////////////////common function///////////////////////////////////
/******************************************************************************
* FunctionName : display_session_id
* Description : Display what session id we have.
* Parameters :
* Returns :
*******************************************************************************/
static void ICACHE_FLASH_ATTR display_session_id(SSL *ssl)
{
int i;
const uint8_t *session_id = ssl_get_session_id(ssl);
int sess_id_size = ssl_get_session_id_size(ssl);
if (sess_id_size > 0) {
ssl_printf("-----BEGIN SSL SESSION PARAMETERS-----\n");
for (i = 0; i < sess_id_size; i++) {
ssl_printf("%02x", session_id[i]);
}
ssl_printf("\n-----END SSL SESSION PARAMETERS-----\n");
//TTY_FLUSH();
}
}
/******************************************************************************
* FunctionName : display_cipher
* Description : Display what cipher we are using
* Parameters :
* Returns :
*******************************************************************************/
static void ICACHE_FLASH_ATTR display_cipher(SSL *ssl)
{
ssl_printf("CIPHER is ");
switch (ssl_get_cipher_id(ssl)) {
case SSL_AES128_SHA:
ssl_printf("AES128-SHA");
break;
case SSL_AES256_SHA:
ssl_printf("AES256-SHA");
break;
case SSL_RC4_128_SHA:
ssl_printf("RC4-SHA");
break;
case SSL_RC4_128_MD5:
ssl_printf("RC4-MD5");
break;
default:
ssl_printf("Unknown - %d", ssl_get_cipher_id(ssl));
break;
}
ssl_printf("\n");
//TTY_FLUSH();
}
/******************************************************************************
* FunctionName : espconn_ssl_reconnect
* Description : reconnect with host
* Parameters : arg -- Additional argument to pass to the callback function
* Returns : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_ssl_reconnect(void *arg)
{
espconn_msg *pssl_recon = arg;
struct espconn *espconn = NULL;
ssl_msg *pssl = NULL;
sint8 ssl_reerr = 0;
if (pssl_recon != NULL) {
espconn = pssl_recon->preverse;
if (pssl_recon->pespconn != NULL){
if (espconn != NULL){
/*espconn_copy_partial(espconn, pssl_recon->pespconn);
if (pssl_recon->pespconn->proto.tcp != NULL){
os_free(pssl_recon->pespconn->proto.tcp);
pssl_recon->pespconn->proto.tcp = NULL;
}
os_free(pssl_recon->pespconn);
pssl_recon->pespconn = NULL;*/
espconn = pssl_recon->preverse;
} else {
espconn = pssl_recon->pespconn;
}
}
pssl = pssl_recon->pssl;
ssl_reerr = pssl_recon->pcommon.err;
if (pssl != NULL) {
if (pssl->ssl) {
ssl_free(pssl->ssl);
}
if (pssl->ssl_ctx) {
ssl_ctx_free(pssl->ssl_ctx);
}
os_free(pssl);
pssl = NULL;
pssl_recon->pssl = pssl;
}
os_free(pssl_recon);
pssl_recon = NULL;
if (espconn ->proto.tcp->reconnect_callback != NULL) {
espconn ->proto.tcp->reconnect_callback(espconn, ssl_reerr);
}
} else {
ssl_printf("espconn_ssl_reconnect err\n");
}
}
/******************************************************************************
* FunctionName : espconn_ssl_dissuccessful
* Description : as
* Parameters :
* Returns :
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_ssl_dissuccessful(void *arg)
{
espconn_msg *pdiscon = arg;
struct espconn *espconn = NULL;
struct tcp_pcb *pcb = NULL;
ssl_msg *pssl = NULL;
if (pdiscon != NULL) {
espconn = pdiscon->preverse;
if (pdiscon->pespconn != NULL){
if (espconn != NULL){
/*espconn_copy_partial(espconn, pdiscon->pespconn);
if (pdiscon->pespconn->proto.tcp != NULL){
os_free(pdiscon->pespconn->proto.tcp);
pdiscon->pespconn->proto.tcp = NULL;
}
os_free(pdiscon->pespconn);
pdiscon->pespconn = NULL;*/
espconn = pdiscon->preverse;
} else{
espconn = pdiscon->pespconn;
}
pcb = pdiscon->pcommon.pcb;
tcp_arg(pcb, NULL);
tcp_err(pcb, NULL);
}
pssl = pdiscon->pssl;
if (pssl != NULL) {
if (pssl->ssl) {
ssl_free(pssl->ssl);
}
if (pssl->ssl_ctx) {
ssl_ctx_free(pssl->ssl_ctx);
}
os_free(pssl);
pssl = NULL;
pdiscon->pssl = pssl;
}
os_free(pdiscon);
pdiscon = NULL;
if (espconn ->proto.tcp->disconnect_callback != NULL) {
espconn ->proto.tcp->disconnect_callback(espconn);
}
} else {
espconn_printf("espconn_ssl_dissuccessful err\n");
}
}
/******************************************************************************
* FunctionName : espconn_ssl_write
* Description : sent data for client or server
* Parameters : void *arg -- client or server to send
* uint8* psent -- Data to send
* uint16 length -- Length of data to send
* Returns : none
*******************************************************************************/
void ICACHE_FLASH_ATTR
espconn_ssl_sent(void *arg, uint8 *psent, uint16 length)
{
espconn_msg *pssl_sent = arg;
struct tcp_pcb *pcb = NULL;
ssl_msg *pssl = NULL;
u16_t len = 0;
int res = 0;
ssl_printf("espconn_ssl_sent pcb %p psent %p length %d\n", arg, psent, length);
if (pssl_sent == NULL || psent == NULL || length == 0) {
return;
}
pcb = pssl_sent->pcommon.pcb;
pssl = pssl_sent->pssl;
if (RT_MAX_PLAIN_LENGTH < length) {
len = RT_MAX_PLAIN_LENGTH;
} else {
len = length;
}
if (pssl != NULL) {
if (pssl->ssl != NULL) {
pssl->ssl->SslClient_pcb = pcb;
res = ssl_write(pssl->ssl, psent, len);
pssl_sent->pcommon.ptrbuf = psent + len;
pssl_sent->pcommon.cntr = length - len;
}
}
}
/******************************************************************************
* FunctionName : espconn_sent_packet
* Description : sent data for client or server
* Parameters : void *arg -- client or server to send
* uint8* psent -- Data to send
* uint16 length -- Length of data to send
* Returns : none
*******************************************************************************/
void ICACHE_FLASH_ATTR
espconn_sent_packet(struct tcp_pcb *pcb, uint8 *psent, uint16 length)
{
err_t err = 0;
u16_t len = 0;
if (pcb == NULL || psent == NULL || length == 0) {
return;
}
if (tcp_sndbuf(pcb) < length) {
len = tcp_sndbuf(pcb);
} else {
len = length;
}
if (len > (2 * pcb->mss)) {
len = 2 * pcb->mss;
}
do {
err = tcp_write(pcb, psent, len, 0);
if (err == ERR_MEM) {
len /= 2;
}
} while (err == ERR_MEM && len > 1);
if (err == ERR_OK) {
err = tcp_output(pcb);
}
}
////////////////////////////////client function////////////////////////////////
/******************************************************************************
* FunctionName : espconn_ssl_cclose_cb
* Description : as
* Parameters :
* Returns :
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_ssl_cclose_cb(void *arg)
{
static uint16 timecount = 0;
espconn_msg *pcclose_cb = arg;
if (pcclose_cb == NULL) {
return;
}
struct tcp_pcb *pcb = pcclose_cb->pcommon.pcb;
ssl_printf("espconn_ssl_cclose_cb %d %d\n", pcb->state, pcb->nrtx);
if (pcb->state == TIME_WAIT || pcb->state == CLOSED) {
pcclose_cb->pespconn ->state = ESPCONN_CLOSE;
/*remove the node from the client's active connection list*/
espconn_list_delete(&plink_active, pcclose_cb);
espconn_ssl_dissuccessful((void *)pcclose_cb);
} else {
os_timer_arm(&pcclose_cb->pcommon.ptimer, TCP_FAST_INTERVAL, 0);
}
}
/******************************************************************************
* FunctionName : espconn_sslclient_close
* Description : The connection shall be actively closed.
* Parameters : pcb -- Additional argument to pass to the callback function
* pcb -- the pcb to close
* Returns : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_ssl_cclose(void *arg, struct tcp_pcb *pcb)
{
espconn_msg *pcclose = arg;
os_timer_disarm(&pcclose->pcommon.ptimer);
os_timer_setfn(&pcclose->pcommon.ptimer, espconn_ssl_cclose_cb, pcclose);
os_timer_arm(&pcclose->pcommon.ptimer, TCP_FAST_INTERVAL, 0);
tcp_recv(pcb, NULL);
pcclose->pcommon.err = tcp_close(pcb);
ssl_printf("espconn_ssl_cclose %d\n", pcclose->pcommon.err);
if (pcclose->pcommon.err != ERR_OK) {
/* closing failed, try again later */
tcp_recv(pcb, espconn_ssl_crecv);
} else {
tcp_sent(pcb, NULL);
tcp_poll(pcb, NULL, 0);
}
}
/******************************************************************************
* FunctionName : espconn_sslclient_sent
* Description : Data has been sent and acknowledged by the remote host.
* This means that more data can be sent.
* Parameters : arg -- Additional argument to pass to the callback function
* pcb -- The connection pcb for which data has been acknowledged
* len -- The amount of bytes acknowledged
* Returns : ERR_OK: try to send some data by calling tcp_output
* ERR_ABRT: if you have called tcp_abort from within the function!
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_ssl_csent(void *arg, struct tcp_pcb *pcb, u16_t len)
{
espconn_msg *psent = arg;
ssl_msg *pssl = psent->pssl;
psent->pcommon.pcb = pcb;
if (pssl->quiet == true) {
int pkt_size = pssl->ssl->bm_index + SSL_RECORD_SIZE;
u16_t max_len = 2 * pcb->mss;
pssl->pkt_length += len;
ssl_printf("espconn_ssl_csent %d %d %d\n", len, pssl->pkt_length, pkt_size);
if (pssl->pkt_length == pkt_size){
pssl->ssl->bm_index = 0;
pssl->pkt_length = 0;
if (psent->pcommon.cntr == 0) {
psent->pespconn->state = ESPCONN_CONNECT;
if (psent->pespconn->sent_callback != NULL) {
psent->pespconn->sent_callback(psent->pespconn);
}
} else {
espconn_ssl_sent(psent, psent->pcommon.ptrbuf, psent->pcommon.cntr);
}
} else {
if (len == max_len){
espconn_sent_packet(pcb, &pssl->ssl->bm_all_data[pssl->pkt_length], pkt_size - pssl->pkt_length);
}
}
} else {
ssl_printf("espconn_ssl_csent %p %p %d\n", pcb, pssl->ssl->bm_all_data, len);
}
return ERR_OK;
}
/******************************************************************************
* FunctionName : espconn_sslclient_recv
* Description : Data has been received on this pcb.
* Parameters : arg -- Additional argument to pass to the callback function
* pcb -- The connection pcb which received data
* p -- The received data (or NULL when the connection has been closed!)
* err -- An error code if there has been an error receiving
* Returns : ERR_ABRT: if you have called tcp_abort from within the function!
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_ssl_crecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
u16_t ret = 0;
espconn_msg *precv = arg;
ssl_msg *pssl = precv->pssl;
ssl_printf("espconn_ssl_crecv %d %p %p\n", __LINE__, pssl->ssl, p);
if (p != NULL) {
tcp_recved(pcb, p ->tot_len);
if (pssl->ssl == NULL) {
pbuf_free(p);
} else {
pssl->ssl->ssl_pbuf = p;
if (ssl_handshake_status(pssl->ssl) != SSL_OK) {
ret = ssl_read(pssl->ssl, NULL);
pbuf_free(p);
if (ret != SSL_OK){
os_printf("client handshake failed\n");
espconn_ssl_cclose(arg, pcb);
}
}
if (ssl_handshake_status(pssl->ssl) == SSL_OK) {
if (!pssl->quiet) {
ssl_printf("client handshake need size %d\n", system_get_free_heap_size());
const char *common_name = ssl_get_cert_dn(pssl->ssl,
SSL_X509_CERT_COMMON_NAME);
if (common_name) {
ssl_printf("Common Name:\t\t\t%s\n", common_name);
}
display_session_id(pssl->ssl);
display_cipher(pssl->ssl);
pssl->quiet = true;
os_printf("client handshake ok!\n");
REG_CLR_BIT(0x3ff00014, BIT(0));
ets_update_cpu_frequency(80);
precv->pespconn->state = ESPCONN_CONNECT;
precv->pcommon.pcb = pcb;
pbuf_free(p);
if (precv->pespconn->proto.tcp->connect_callback != NULL) {
precv->pespconn->proto.tcp->connect_callback(precv->pespconn);
}
} else {
uint8_t *read_buf = NULL;
ret = ssl_read(pssl->ssl, &read_buf);
precv->pespconn->state = ESPCONN_READ;
precv->pcommon.pcb = pcb;
pbuf_free(p);
if (precv->pespconn->recv_callback != NULL && read_buf != NULL) {
precv->pespconn->recv_callback(precv->pespconn, read_buf, ret);
}
precv->pespconn->state = ESPCONN_CONNECT;
}
}
}
}
if (err == ERR_OK && p == NULL) {
espconn_ssl_cclose(precv, pcb);
}
return ERR_OK;
}
/******************************************************************************
* FunctionName : espconn_client_err
* Description : The pcb had an error and is already deallocated.
* The argument might still be valid (if != NULL).
* Parameters : arg -- Additional argument to pass to the callback function
* err -- Error code to indicate why the pcb has been closed
* Returns : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_ssl_cerr(void *arg, err_t err)
{
espconn_msg *pssl_cerr = arg;
struct tcp_pcb *pcb = NULL;
LWIP_UNUSED_ARG(err);
if (pssl_cerr != NULL) {
os_timer_disarm(&pssl_cerr->pcommon.ptimer);
pcb = pssl_cerr->pcommon.pcb;
pssl_cerr->pespconn->state = ESPCONN_CLOSE;
espconn_printf("espconn_ssl_cerr %d %d %d\n", pcb->state, pcb->nrtx, err);
/*remove the node from the client's active connection list*/
espconn_list_delete(&plink_active, pssl_cerr);
if (err == ERR_ABRT) {
switch (pcb->state) {
case SYN_SENT:
if (pcb->nrtx == TCP_SYNMAXRTX) {
pssl_cerr->pcommon.err = ESPCONN_CONN;
} else {
pssl_cerr->pcommon.err = err;
}
break;
case ESTABLISHED:
if (pcb->nrtx == TCP_MAXRTX) {
pssl_cerr->pcommon.err = ESPCONN_TIMEOUT;
} else {
pssl_cerr->pcommon.err = err;
}
break;
case FIN_WAIT_1:
if (pcb->nrtx == TCP_MAXRTX) {
pssl_cerr->pcommon.err = ESPCONN_CLSD;
} else {
pssl_cerr->pcommon.err = err;
}
break;
case FIN_WAIT_2:
pssl_cerr->pcommon.err = ESPCONN_CLSD;
break;
case CLOSED:
pssl_cerr->pcommon.err = ESPCONN_CONN;
break;
default :
break;
}
} else {
pssl_cerr->pcommon.err = err;
}
os_timer_setfn(&pssl_cerr->pcommon.ptimer, espconn_ssl_reconnect, pssl_cerr);
os_timer_arm(&pssl_cerr->pcommon.ptimer, 10, 0);
}
}
#if 0
/******************************************************************************
* FunctionName : espconn_ssl_cpoll
* Description : The poll function is called every 3nd second.
* If there has been no data sent (which resets the retries) in 3 seconds, close.
* If the last portion of a file has not been sent in 3 seconds, close.
*
* This could be increased, but we don't want to waste resources for bad connections.
* Parameters : arg -- Additional argument to pass to the callback function
* pcb -- The connection pcb for which data has been acknowledged
* Returns : ERR_OK: try to send some data by calling tcp_output
* ERR_ABRT: if you have called tcp_abort from within the function!
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_ssl_cpoll(void *arg, struct tcp_pcb *pcb)
{
ssl_printf("espconn_ssl_cpoll %p %d\n", pcb, pcb->state);
struct espconn *espconn = arg;
if (arg == NULL) {
tcp_abandon(pcb, 0);
tcp_poll(pcb, NULL, 0);
return ERR_ABRT;
}
if (pcb ->state == ESTABLISHED) {
espconn->recv_check ++;
if (espconn ->recv_check == 0x05){
//tcp_poll(pcb, espconn_ssl_cpoll, 0);
espconn->recv_check = 0;
espconn_ssl_cclose(arg, pcb);
}
} else {
//tcp_poll(pcb, espconn_ssl_cpoll, 0);
espconn_ssl_cclose(arg, pcb);
}
return ERR_OK;
}
#endif
/******************************************************************************
* FunctionName : espconn_sslclient_connect
* Description : A new incoming connection has been connected.
* Parameters : arg -- Additional argument to pass to the callback function
* tpcb -- The connection pcb which is connected
* err -- An unused error code, always ERR_OK currently
* Returns : connection result
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_ssl_connect(void *arg, struct tcp_pcb *tpcb, err_t err)
{
espconn_msg *pconnect = arg;
ssl_msg *pssl = NULL;
uint32_t options;
options = SSL_SERVER_VERIFY_LATER | SSL_DISPLAY_CERTS | SSL_NO_DEFAULT_KEY;
ssl_printf("espconn_ssl_connect %p %p %p %d\n", tpcb, arg, pespconn->psecure, system_get_free_heap_size());
//if (pespconn->psecure != NULL){
// return ERR_ISCONN;
//}
pconnect->pcommon.pcb = tpcb;
pssl = (ssl_msg *)os_zalloc(sizeof(ssl_msg));
pconnect->pssl = pssl;
if (pssl == NULL) {
return ERR_MEM;
}
REG_SET_BIT(0x3ff00014, BIT(0));
ets_update_cpu_frequency(160);
os_printf("client handshake start.\n");
pssl->quiet = false;
pssl->ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_CLNT_SESS);
if (pssl->ssl_ctx == NULL) {
return ERR_MEM;
}
ssl_printf("espconn_ssl_client ssl_ctx %p\n", pssl->ssl_ctx);
pssl->ssl = SSLClient_new(pssl->ssl_ctx, tpcb, NULL, 0);
if (pssl->ssl == NULL) {
return ERR_MEM;
}
tcp_arg(tpcb, arg);
tcp_sent(tpcb, espconn_ssl_csent);
tcp_recv(tpcb, espconn_ssl_crecv);
//tcp_poll(tpcb, espconn_ssl_cpoll, 6);
return ERR_OK;
}
/******************************************************************************
* FunctionName : espconn_ssl_disconnect
* Description : A new incoming connection has been disconnected.
* Parameters : espconn -- the espconn used to disconnect with host
* Returns : none
*******************************************************************************/
void ICACHE_FLASH_ATTR espconn_ssl_disconnect(espconn_msg *pdis)
{
if (pdis != NULL) {
if (pdis->preverse == NULL)
espconn_ssl_cclose(pdis, pdis->pcommon.pcb);
else
espconn_ssl_sclose(pdis, pdis->pcommon.pcb);
} else {
ssl_printf("espconn_ssl_disconnect err.\n");
}
}
/******************************************************************************
* FunctionName : espconn_ssl_client
* Description : Initialize the client: set up a connect PCB and bind it to
* the defined port
* Parameters : espconn -- the espconn used to build client
* Returns : none
*******************************************************************************/
sint8 ICACHE_FLASH_ATTR
espconn_ssl_client(struct espconn *espconn)
{
struct tcp_pcb *pcb;
struct ip_addr ipaddr;
espconn_msg *pclient = NULL;
pclient = plink_active;
while(pclient != NULL){
if (pclient->pssl != NULL)
return ESPCONN_ISCONN;
pclient = pclient->pnext;
}
pclient = (espconn_msg *)os_zalloc(sizeof(espconn_msg));
if (pclient == NULL){
return ESPCONN_MEM;
}
IP4_ADDR(&ipaddr, espconn->proto.tcp->remote_ip[0],
espconn->proto.tcp->remote_ip[1],
espconn->proto.tcp->remote_ip[2],
espconn->proto.tcp->remote_ip[3]);
pcb = tcp_new();
if (pcb == NULL) {
espconn ->state = ESPCONN_NONE;
os_free(pclient);
pclient = NULL;
return ESPCONN_MEM;
} else {
/*insert the node to the active connection list*/
espconn_list_creat(&plink_active, pclient);
tcp_arg(pcb, (void *)pclient);
tcp_err(pcb, espconn_ssl_cerr);
pclient->preverse = NULL;
pclient->pespconn = espconn;
pclient->pespconn->state = ESPCONN_WAIT;
pclient->pcommon.pcb = pcb;
tcp_bind(pcb, IP_ADDR_ANY, pclient->pespconn->proto.tcp->local_port);
pclient->pcommon.err = tcp_connect(pcb, &ipaddr, pclient->pespconn->proto.tcp->remote_port, espconn_ssl_connect);
return ESPCONN_OK;
}
}
/////////////////////////////server's function/////////////////////////////////
/******************************************************************************
* FunctionName : espconn_ssl_sclose_cb
* Description : as
* Parameters :
* Returns :
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_ssl_sclose_cb(void *arg)
{
static uint16 timecount = 0;
espconn_msg *psclose_cb = arg;
if (psclose_cb == NULL) {
return;
}
struct tcp_pcb *pcb = psclose_cb->pcommon.pcb;
ssl_printf("espconn_ssl_sclose_cb %d %d\n", pcb->state, pcb->nrtx);
if (pcb->state == CLOSED || pcb->state == TIME_WAIT) {
psclose_cb ->pespconn ->state = ESPCONN_CLOSE;
psclose_cb->pespconn->link_cnt --;
/*remove the node from the server's active connection list*/
espconn_list_delete(&plink_active, psclose_cb);
espconn_ssl_dissuccessful((void *)psclose_cb);
} else {
os_timer_arm(&psclose_cb->pcommon.ptimer, TCP_FAST_INTERVAL, 0);
}
}
/******************************************************************************
* FunctionName : espconn_sslclient_close
* Description : The connection shall be actively closed.
* Parameters : pcb -- Additional argument to pass to the callback function
* pcb -- the pcb to close
* Returns : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_ssl_sclose(void *arg, struct tcp_pcb *pcb)
{
espconn_msg *psclose = arg;
os_timer_disarm(&psclose->pcommon.ptimer);
os_timer_setfn(&psclose->pcommon.ptimer, espconn_ssl_sclose_cb, psclose);
os_timer_arm(&psclose->pcommon.ptimer, TCP_FAST_INTERVAL, 0);
tcp_recv(pcb, NULL);
psclose->pcommon.err = tcp_close(pcb);
if (psclose->pcommon.err != ERR_OK) {
/* closing failed, try again later */
tcp_recv(pcb, espconn_ssl_srecv);
} else {
tcp_sent(pcb, NULL);
tcp_poll(pcb, NULL, 0);
}
}
/******************************************************************************
* FunctionName : espconn_sslclient_sent
* Description : Data has been sent and acknowledged by the remote host.
* This means that more data can be sent.
* Parameters : arg -- Additional argument to pass to the callback function
* pcb -- The connection pcb for which data has been acknowledged
* len -- The amount of bytes acknowledged
* Returns : ERR_OK: try to send some data by calling tcp_output
* ERR_ABRT: if you have called tcp_abort from within the function!
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_ssl_ssent(void *arg, struct tcp_pcb *pcb, u16_t len)
{
espconn_msg *psent = arg;
ssl_msg *pssl = psent->pssl;
psent->pcommon.pcb = pcb;
psent->pcommon.recv_check = 0;
if (ssl_handshake_status(pssl->ssl) == SSL_OK) {
if (!pssl->quiet) {
ssl_printf("espconn_ssl_ssent %p %d\n",pcb, system_get_free_heap_size());
const char *common_name = ssl_get_cert_dn(pssl->ssl, SSL_X509_CERT_COMMON_NAME);
if (common_name) {
ssl_printf("Common Name:\t\t\t%s\n", common_name);
}
display_session_id(pssl->ssl);
display_cipher(pssl->ssl);
pssl->quiet = true;
os_printf("server handshake ok!\n");
REG_CLR_BIT(0x3ff00014, BIT(0));
ets_update_cpu_frequency(80);
psent->pespconn->state = ESPCONN_CONNECT;
if (psent->pespconn->proto.tcp->connect_callback != NULL) {
psent->pespconn->proto.tcp->connect_callback(psent->pespconn);
}
} else {
int pkt_size = pssl->ssl->bm_index + SSL_RECORD_SIZE;
u16_t max_len = 2 * pcb->mss;
pssl->pkt_length += len;
ssl_printf("espconn_ssl_ssent %d %d %d\n", len, pssl->pkt_length, pkt_size);
if (pssl->pkt_length == pkt_size){
pssl->ssl->bm_index = 0;
pssl->pkt_length = 0;
if (psent->pcommon.cntr == 0) {
psent->pespconn->state = ESPCONN_CONNECT;
if (psent->pespconn->sent_callback != NULL) {
psent->pespconn->sent_callback(psent->pespconn);
}
} else {
espconn_ssl_sent(psent, psent->pcommon.ptrbuf, psent->pcommon.cntr);
}
} else {
if (len == max_len){
espconn_sent_packet(pcb, &pssl->ssl->bm_all_data[pssl->pkt_length], pkt_size - pssl->pkt_length);
}
}
}
} else {
ssl_printf("espconn_ssl_ssent %p %p %d\n",pcb, pssl->ssl->bm_all_data, len);
}
return ERR_OK;
}
/******************************************************************************
* FunctionName : espconn_sslclient_recv
* Description : Data has been received on this pcb.
* Parameters : arg -- Additional argument to pass to the callback function
* pcb -- The connection pcb which received data
* p -- The received data (or NULL when the connection has been closed!)
* err -- An error code if there has been an error receiving
* Returns : ERR_ABRT: if you have called tcp_abort from within the function!
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_ssl_srecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
u16_t ret = 0;
espconn_msg *precv = arg;
ssl_msg *pssl = precv->pssl;
ssl_printf("espconn_ssl_srecv %d %p %p\n", __LINE__, pcb, p);
if (p != NULL) {
tcp_recved(pcb, p ->tot_len);
precv->pcommon.recv_check = 0;
if (pssl->ssl == NULL) {
pbuf_free(p);
} else {
pssl->ssl->ssl_pbuf = p;
if (ssl_handshake_status(pssl->ssl) != SSL_OK) {
ret = ssl_read(pssl->ssl, NULL);
pbuf_free(p);
if (ret != SSL_OK){
os_printf("server handshake failed.\n");
espconn_ssl_sclose(arg, pcb);
}
} else {
uint8_t *read_buf = NULL;
ret = ssl_read(pssl->ssl, &read_buf);
precv->pespconn->state = ESPCONN_READ;
precv->pcommon.pcb = pcb;
pbuf_free(p);
if (precv->pespconn->recv_callback != NULL && read_buf != NULL) {
precv->pespconn->recv_callback(precv->pespconn, read_buf, ret);
}
precv->pespconn->state = ESPCONN_CONNECT;
}
}
}
if (err == ERR_OK && p == NULL) {
espconn_ssl_sclose(precv, pcb);
}
return ERR_OK;
}
/******************************************************************************
* FunctionName : espconn_server_poll
* Description : The poll function is called every 3nd second.
* If there has been no data sent (which resets the retries) in 3 seconds, close.
* If the last portion of a file has not been sent in 3 seconds, close.
*
* This could be increased, but we don't want to waste resources for bad connections.
* Parameters : arg -- Additional argument to pass to the callback function
* pcb -- The connection pcb for which data has been acknowledged
* Returns : ERR_OK: try to send some data by calling tcp_output
* ERR_ABRT: if you have called tcp_abort from within the function!
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_ssl_spoll(void *arg, struct tcp_pcb *pcb)
{
ssl_printf("espconn_ssl_spoll %p %d\n", pcb, pcb->state);
espconn_msg *pspoll = arg;
if (arg == NULL) {
tcp_abandon(pcb, 0);
tcp_poll(pcb, NULL, 0);
return ERR_ABRT;
}
if (pcb ->state == ESTABLISHED) {
pspoll ->pcommon.recv_check ++;
if (pspoll ->pcommon.recv_check == pspoll ->pcommon.timeout){
tcp_poll(pcb, NULL, 0);
pspoll ->pcommon.recv_check = 0;
espconn_ssl_sclose(arg, pcb);
}
} else {
tcp_poll(pcb, NULL, 0);
espconn_ssl_sclose(arg, pcb);
}
return ERR_OK;
}
/******************************************************************************
* FunctionName : esponn_server_err
* Description : The pcb had an error and is already deallocated.
* The argument might still be valid (if != NULL).
* Parameters : arg -- Additional argument to pass to the callback function
* err -- Error code to indicate why the pcb has been closed
* Returns : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_ssl_serr(void *arg, err_t err)
{
espconn_msg *pserr = arg;
struct tcp_pcb *pcb = NULL;
LWIP_UNUSED_ARG(err);
if (pserr != NULL) {
os_timer_disarm(&pserr->pcommon.ptimer);
pcb = pserr->pcommon.pcb;
pserr->pespconn->state = ESPCONN_CLOSE;
/*remove the node from the server's active connection list*/
espconn_list_delete(&plink_active, pserr);
if (err == ERR_ABRT) {
switch (pcb->state) {
case SYN_RCVD:
if (pcb->nrtx == TCP_SYNMAXRTX) {
pserr->pcommon.err = ESPCONN_CONN;
} else {
pserr->pcommon.err = err;
}
break;
case ESTABLISHED:
if (pcb->nrtx == TCP_MAXRTX) {
pserr->pcommon.err = ESPCONN_TIMEOUT;
} else {
pserr->pcommon.err = err;
}
break;
case CLOSE_WAIT:
if (pcb->nrtx == TCP_MAXRTX) {
pserr->pcommon.err = ESPCONN_CLSD;
} else {
pserr->pcommon.err = err;
}
break;
case LAST_ACK:
pserr->pcommon.err = ESPCONN_CLSD;
break;
case CLOSED:
pserr->pcommon.err = ESPCONN_CONN;
break;
default :
break;
}
} else {
pserr->pcommon.err = err;
}
os_timer_setfn(&pserr->pcommon.ptimer, espconn_ssl_reconnect, pserr);
os_timer_arm(&pserr->pcommon.ptimer, 10, 0);
}
}
/******************************************************************************
* FunctionName : espconn_tcp_accept
* Description : A new incoming connection has been accepted.
* Parameters : arg -- Additional argument to pass to the callback function
* pcb -- The connection pcb which is accepted
* err -- An unused error code, always ERR_OK currently
* Returns : acception result
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_ssl_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
struct espconn *espconn = arg;
ssl_msg *pssl = NULL;
espconn_msg *paccept = NULL;
remot_info *pinfo = NULL;
ssl_printf("espconn_ssl_accept %p %p %p %d\n", pcb, arg, espconn->psecure, system_get_free_heap_size());
LWIP_UNUSED_ARG(err);
paccept = (espconn_msg *)os_zalloc(sizeof(espconn_msg));
tcp_arg(pcb, paccept);
tcp_err(pcb, espconn_ssl_serr);
if (paccept == NULL)
return ERR_MEM;
/*insert the node to the active connection list*/
espconn_list_creat(&plink_active, paccept);
paccept->preverse = espconn;
paccept->pespconn = espconn;
paccept->pcommon.timeout = 0x0a;
paccept->pcommon.pcb = pcb;
paccept->pcommon.remote_port = pcb->remote_port;
paccept->pcommon.remote_ip[0] = ip4_addr1_16(&pcb->remote_ip);
paccept->pcommon.remote_ip[1] = ip4_addr2_16(&pcb->remote_ip);
paccept->pcommon.remote_ip[2] = ip4_addr3_16(&pcb->remote_ip);
paccept->pcommon.remote_ip[3] = ip4_addr4_16(&pcb->remote_ip);
os_memcpy(espconn->proto.tcp->remote_ip, paccept->pcommon.remote_ip, 4);
espconn->proto.tcp->remote_port = pcb->remote_port;
espconn_get_connection_info(espconn, &pinfo , ESPCONN_SSL);
if (espconn->link_cnt == 0x01)
return ERR_ISCONN;
pssl = (ssl_msg *)os_zalloc(sizeof(ssl_msg));
paccept->pssl = pssl;
if (pssl == NULL) {
return ERR_MEM;
}
REG_SET_BIT(0x3ff00014, BIT(0));
ets_update_cpu_frequency(160);
os_printf("server handshake start.\n");
pssl->quiet = false;
pssl->ssl_ctx = ssl_ctx_new(SSL_DISPLAY_CERTS, SSL_DEFAULT_SVR_SESS);
if (pssl->ssl_ctx == NULL) {
ssl_printf("Error: Server context is invalid\n");
return ERR_MEM;
}
ssl_printf("Server context %p\n", pssl->ssl_ctx);
pssl->ssl = sslserver_new(pssl->ssl_ctx, pcb);
if (pssl->ssl == NULL) {
ssl_printf("Error: Server ssl connection is invalid\n");
return ERR_MEM;
}
tcp_sent(pcb, espconn_ssl_ssent);
tcp_recv(pcb, espconn_ssl_srecv);
tcp_poll(pcb, espconn_ssl_spoll, 2);
return ERR_OK;
}
/******************************************************************************
* FunctionName : espconn_ssl_server
* Description : as
* Parameters :
* Returns :
*******************************************************************************/
sint8 ICACHE_FLASH_ATTR espconn_ssl_server(struct espconn *espconn)
{
struct tcp_pcb *pcb;
pcb = tcp_new();
if (pcb == NULL) {
espconn ->state = ESPCONN_NONE;
return ESPCONN_MEM;
} else {
tcp_bind(pcb, IP_ADDR_ANY, espconn->proto.tcp->local_port);
pcb = tcp_listen(pcb);
if (pcb != NULL) {
espconn ->state = ESPCONN_LISTEN;
tcp_arg(pcb, (void *)espconn);
tcp_accept(pcb, espconn_ssl_accept);
return ESPCONN_OK;
} else {
espconn ->state = ESPCONN_NONE;
return ESPCONN_MEM;
}
}
}
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
GEN_LIBS = libsslcrypto.a
endif
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* AES implementation - this is a small code version. There are much faster
* versions around but they are much larger in size (i.e. they use large
* submix tables).
*/
//#include <string.h>
#include "lwip/opt.h"
#include "lwip/def.h"
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_crypto.h"
/* all commented out in skeleton mode */
#ifndef CONFIG_SSL_SKELETON_MODE
#define rot1(x) (((x) << 24) | ((x) >> 8))
#define rot2(x) (((x) << 16) | ((x) >> 16))
#define rot3(x) (((x) << 8) | ((x) >> 24))
/*
* This cute trick does 4 'mul by two' at once. Stolen from
* Dr B. R. Gladman <brg@gladman.uk.net> but I'm sure the u-(u>>7) is
* a standard graphics trick
* The key to this is that we need to xor with 0x1b if the top bit is set.
* a 1xxx xxxx 0xxx 0xxx First we mask the 7bit,
* b 1000 0000 0000 0000 then we shift right by 7 putting the 7bit in 0bit,
* c 0000 0001 0000 0000 we then subtract (c) from (b)
* d 0111 1111 0000 0000 and now we and with our mask
* e 0001 1011 0000 0000
*/
#define mt 0x80808080
#define ml 0x7f7f7f7f
#define mh 0xfefefefe
#define mm 0x1b1b1b1b
#define mul2(x,t) ((t)=((x)&mt), \
((((x)+(x))&mh)^(((t)-((t)>>7))&mm)))
#define inv_mix_col(x,f2,f4,f8,f9) (\
(f2)=mul2(x,f2), \
(f4)=mul2(f2,f4), \
(f8)=mul2(f4,f8), \
(f9)=(x)^(f8), \
(f8)=((f2)^(f4)^(f8)), \
(f2)^=(f9), \
(f4)^=(f9), \
(f8)^=rot3(f2), \
(f8)^=rot2(f4), \
(f8)^rot1(f9))
/*
* AES S-box
*/
static const uint8_t aes_sbox[256] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR =
{
0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,
0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76,
0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,
0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0,
0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,
0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15,
0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,
0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75,
0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,
0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84,
0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,
0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF,
0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,
0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8,
0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,
0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2,
0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,
0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73,
0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,
0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB,
0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,
0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79,
0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,
0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08,
0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,
0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A,
0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,
0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E,
0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,
0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF,
0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,
0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16,
};
/*
* AES is-box
*/
static const uint8_t aes_isbox[256] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR =
{
0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,
0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,
0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,
0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,
0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,
0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,
0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,
0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,
0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,
0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,
0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,
0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,
0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,
0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,
0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,
0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,
0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,
0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,
0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,
0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,
0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,
0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,
0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,
0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,
0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,
0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,
0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,
0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,
0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,
0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,
0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,
0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d
};
static const unsigned char Rcon[30]=
{
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,
0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f,
0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4,
0xb3,0x7d,0xfa,0xef,0xc5,0x91,
};
/* ----- static functions ----- */
static void AES_encrypt(const AES_CTX *ctx, uint32_t *data);
static void AES_decrypt(const AES_CTX *ctx, uint32_t *data);
/* Perform doubling in Galois Field GF(2^8) using the irreducible polynomial
x^8+x^4+x^3+x+1 */
static unsigned char ICACHE_FLASH_ATTR AES_xtime(uint32_t x)
{
return (x&0x80) ? (x<<1)^0x1b : x<<1;
}
/**
* Set up AES with the key/iv and cipher size.
*/
void ICACHE_FLASH_ATTR AES_set_key(AES_CTX *ctx, const uint8_t *key,
const uint8_t *iv, AES_MODE mode)
{
int i, ii;
uint32_t *W, tmp, tmp2;
const unsigned char *ip;
int words;
switch (mode)
{
case AES_MODE_128:
i = 10;
words = 4;
break;
case AES_MODE_256:
i = 14;
words = 8;
break;
default: /* fail silently */
return;
}
ctx->rounds = i;
ctx->key_size = words;
W = ctx->ks;
for (i = 0; i < words; i+=2)
{
W[i+0]= ((uint32_t)key[ 0]<<24)|
((uint32_t)key[ 1]<<16)|
((uint32_t)key[ 2]<< 8)|
((uint32_t)key[ 3] );
W[i+1]= ((uint32_t)key[ 4]<<24)|
((uint32_t)key[ 5]<<16)|
((uint32_t)key[ 6]<< 8)|
((uint32_t)key[ 7] );
key += 8;
}
ip = Rcon;
ii = 4 * (ctx->rounds+1);
for (i = words; i<ii; i++)
{
tmp = W[i-1];
if ((i % words) == 0)
{
// tmp2 =(uint32_t)aes_sbox[(tmp )&0xff]<< 8;
// tmp2|=(uint32_t)aes_sbox[(tmp>> 8)&0xff]<<16;
// tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<24;
// tmp2|=(uint32_t)aes_sbox[(tmp>>24) ];
tmp2 =((uint32_t)byte_of_aligned_array(aes_sbox,(tmp )&0xff))<< 8;
tmp2|=((uint32_t)byte_of_aligned_array(aes_sbox,(tmp>> 8)&0xff))<<16;
tmp2|=((uint32_t)byte_of_aligned_array(aes_sbox,(tmp>>16)&0xff))<<24;
tmp2|=((uint32_t)byte_of_aligned_array(aes_sbox,(tmp>>24) ));
tmp=tmp2^(((unsigned int)*ip)<<24);
ip++;
}
if ((words == 8) && ((i % words) == 4))
{
// tmp2 =(uint32_t)aes_sbox[(tmp )&0xff] ;
// tmp2|=(uint32_t)aes_sbox[(tmp>> 8)&0xff]<< 8;
// tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<16;
// tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]<<24;
tmp2 =((uint32_t)byte_of_aligned_array(aes_sbox,(tmp )&0xff)) ;
tmp2|=((uint32_t)byte_of_aligned_array(aes_sbox,(tmp>> 8)&0xff))<< 8;
tmp2|=((uint32_t)byte_of_aligned_array(aes_sbox,(tmp>>16)&0xff))<<16;
tmp2|=((uint32_t)byte_of_aligned_array(aes_sbox,(tmp>>24) ))<<24;
tmp=tmp2;
}
W[i]=W[i-words]^tmp;
}
/* copy the iv across */
os_memcpy(ctx->iv, iv, 16);
}
/**
* Change a key for decryption.
*/
void ICACHE_FLASH_ATTR AES_convert_key(AES_CTX *ctx)
{
int i;
uint32_t *k,w,t1,t2,t3,t4;
k = ctx->ks;
k += 4;
for (i= ctx->rounds*4; i > 4; i--)
{
w= *k;
w = inv_mix_col(w,t1,t2,t3,t4);
*k++ =w;
}
}
/**
* Encrypt a byte sequence (with a block size 16) using the AES cipher.
*/
void ICACHE_FLASH_ATTR AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length)
{
int i;
uint32_t tin[4], tout[4], iv[4];
os_memcpy(iv, ctx->iv, AES_IV_SIZE);
for (i = 0; i < 4; i++)
tout[i] = ntohl(iv[i]);
for (length -= AES_BLOCKSIZE; length >= 0; length -= AES_BLOCKSIZE)
{
uint32_t msg_32[4];
uint32_t out_32[4];
os_memcpy(msg_32, msg, AES_BLOCKSIZE);
msg += AES_BLOCKSIZE;
for (i = 0; i < 4; i++)
tin[i] = ntohl(msg_32[i])^tout[i];
AES_encrypt(ctx, tin);
for (i = 0; i < 4; i++)
{
tout[i] = tin[i];
out_32[i] = htonl(tout[i]);
}
os_memcpy(out, out_32, AES_BLOCKSIZE);
out += AES_BLOCKSIZE;
}
for (i = 0; i < 4; i++)
iv[i] = htonl(tout[i]);
os_memcpy(ctx->iv, iv, AES_IV_SIZE);
}
/**
* Decrypt a byte sequence (with a block size 16) using the AES cipher.
*/
void ICACHE_FLASH_ATTR AES_cbc_decrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length)
{
int i;
uint32_t tin[4], xor[4], tout[4], data[4], iv[4];
os_memcpy(iv, ctx->iv, AES_IV_SIZE);
for (i = 0; i < 4; i++)
xor[i] = ntohl(iv[i]);
for (length -= 16; length >= 0; length -= 16)
{
uint32_t msg_32[4];
uint32_t out_32[4];
os_memcpy(msg_32, msg, AES_BLOCKSIZE);
msg += AES_BLOCKSIZE;
for (i = 0; i < 4; i++)
{
tin[i] = ntohl(msg_32[i]);
data[i] = tin[i];
}
AES_decrypt(ctx, data);
for (i = 0; i < 4; i++)
{
tout[i] = data[i]^xor[i];
xor[i] = tin[i];
out_32[i] = htonl(tout[i]);
}
os_memcpy(out, out_32, AES_BLOCKSIZE);
out += AES_BLOCKSIZE;
}
for (i = 0; i < 4; i++)
iv[i] = htonl(xor[i]);
os_memcpy(ctx->iv, iv, AES_IV_SIZE);
}
/**
* Encrypt a single block (16 bytes) of data
*/
static void ICACHE_FLASH_ATTR AES_encrypt(const AES_CTX *ctx, uint32_t *data)
{
/* To make this code smaller, generate the sbox entries on the fly.
* This will have a really heavy effect upon performance.
*/
uint32_t tmp[4];
uint32_t tmp1, old_a0, a0, a1, a2, a3, row;
int curr_rnd;
int rounds = ctx->rounds;
const uint32_t *k = ctx->ks;
/* Pre-round key addition */
for (row = 0; row < 4; row++)
data[row] ^= *(k++);
/* Encrypt one block. */
for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++)
{
/* Perform ByteSub and ShiftRow operations together */
for (row = 0; row < 4; row++)
{
// a0 = (uint32_t)aes_sbox[(data[row%4]>>24)&0xFF];
// a1 = (uint32_t)aes_sbox[(data[(row+1)%4]>>16)&0xFF];
// a2 = (uint32_t)aes_sbox[(data[(row+2)%4]>>8)&0xFF];
// a3 = (uint32_t)aes_sbox[(data[(row+3)%4])&0xFF];
a0 = (uint32_t)(byte_of_aligned_array(aes_sbox,(data[row%4]>>24)&0xFF));
a1 = (uint32_t)(byte_of_aligned_array(aes_sbox,(data[(row+1)%4]>>16)&0xFF));
a2 = (uint32_t)(byte_of_aligned_array(aes_sbox,(data[(row+2)%4]>>8)&0xFF));
a3 = (uint32_t)(byte_of_aligned_array(aes_sbox,(data[(row+3)%4])&0xFF));
/* Perform MixColumn iff not last round */
if (curr_rnd < (rounds - 1))
{
tmp1 = a0 ^ a1 ^ a2 ^ a3;
old_a0 = a0;
a0 ^= tmp1 ^ AES_xtime(a0 ^ a1);
a1 ^= tmp1 ^ AES_xtime(a1 ^ a2);
a2 ^= tmp1 ^ AES_xtime(a2 ^ a3);
a3 ^= tmp1 ^ AES_xtime(a3 ^ old_a0);
}
tmp[row] = ((a0 << 24) | (a1 << 16) | (a2 << 8) | a3);
}
/* KeyAddition - note that it is vital that this loop is separate from
the MixColumn operation, which must be atomic...*/
for (row = 0; row < 4; row++)
data[row] = tmp[row] ^ *(k++);
}
}
/**
* Decrypt a single block (16 bytes) of data
*/
static void ICACHE_FLASH_ATTR AES_decrypt(const AES_CTX *ctx, uint32_t *data)
{
uint32_t tmp[4];
uint32_t xt0,xt1,xt2,xt3,xt4,xt5,xt6;
uint32_t a0, a1, a2, a3, row;
int curr_rnd;
int rounds = ctx->rounds;
const uint32_t *k = ctx->ks + ((rounds+1)*4);
/* pre-round key addition */
for (row=4; row > 0;row--)
data[row-1] ^= *(--k);
/* Decrypt one block */
for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++)
{
/* Perform ByteSub and ShiftRow operations together */
for (row = 4; row > 0; row--)
{
// a0 = aes_isbox[(data[(row+3)%4]>>24)&0xFF];
// a1 = aes_isbox[(data[(row+2)%4]>>16)&0xFF];
// a2 = aes_isbox[(data[(row+1)%4]>>8)&0xFF];
// a3 = aes_isbox[(data[row%4])&0xFF];
a0 = byte_of_aligned_array(aes_isbox,(data[(row+3)%4]>>24)&0xFF);
a1 = byte_of_aligned_array(aes_isbox,(data[(row+2)%4]>>16)&0xFF);
a2 = byte_of_aligned_array(aes_isbox,(data[(row+1)%4]>>8)&0xFF);
a3 = byte_of_aligned_array(aes_isbox,(data[row%4])&0xFF);
/* Perform MixColumn iff not last round */
if (curr_rnd<(rounds-1))
{
/* The MDS cofefficients (0x09, 0x0B, 0x0D, 0x0E)
are quite large compared to encryption; this
operation slows decryption down noticeably. */
xt0 = AES_xtime(a0^a1);
xt1 = AES_xtime(a1^a2);
xt2 = AES_xtime(a2^a3);
xt3 = AES_xtime(a3^a0);
xt4 = AES_xtime(xt0^xt1);
xt5 = AES_xtime(xt1^xt2);
xt6 = AES_xtime(xt4^xt5);
xt0 ^= a1^a2^a3^xt4^xt6;
xt1 ^= a0^a2^a3^xt5^xt6;
xt2 ^= a0^a1^a3^xt4^xt6;
xt3 ^= a0^a1^a2^xt5^xt6;
tmp[row-1] = ((xt0<<24)|(xt1<<16)|(xt2<<8)|xt3);
}
else
tmp[row-1] = ((a0<<24)|(a1<<16)|(a2<<8)|a3);
}
for (row = 4; row > 0; row--)
data[row-1] = tmp[row-1] ^ *(--k);
}
}
#endif
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @defgroup bigint_api Big Integer API
* @brief The bigint implementation as used by the axTLS project.
*
* The bigint library is for RSA encryption/decryption as well as signing.
* This code tries to minimise use of malloc/free by maintaining a small
* cache. A bigint context may maintain state by being made "permanent".
* It be be later released with a bi_depermanent() and bi_free() call.
*
* It supports the following reduction techniques:
* - Classical
* - Barrett
* - Montgomery
*
* It also implements the following:
* - Karatsuba multiplication
* - Squaring
* - Sliding window exponentiation
* - Chinese Remainder Theorem (implemented in rsa.c).
*
* All the algorithms used are pretty standard, and designed for different
* data bus sizes. Negative numbers are not dealt with at all, so a subtraction
* may need to be tested for negativity.
*
* This library steals some ideas from Jef Poskanzer
* <http://cs.marlboro.edu/term/cs-fall02/algorithms/crypto/RSA/bigint>
* and GMP <http://www.swox.com/gmp>. It gets most of its implementation
* detail from "The Handbook of Applied Cryptography"
* <http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf>
* @{
*/
//#include <stdlib.h>
//#include <limits.h>
//#include <string.h>
//#include <stdio.h>
//#include <time.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_bigint.h"
//#include "os.h"
#include "lwip/mem.h"
#define V1 v->comps[v->size-1] /**< v1 for division */
#define V2 v->comps[v->size-2] /**< v2 for division */
#define U(j) tmp_u->comps[tmp_u->size-j-1] /**< uj for division */
#define Q(j) quotient->comps[quotient->size-j-1] /**< qj for division */
static bigint *bi_int_multiply(BI_CTX *ctx, bigint *bi, comp i);
static bigint *bi_int_divide(BI_CTX *ctx, bigint *biR, comp denom);
static bigint *alloc(BI_CTX *ctx, int size);
static bigint *trim(bigint *bi);
static void more_comps(bigint *bi, int n);
#if defined(CONFIG_BIGINT_KARATSUBA) || defined(CONFIG_BIGINT_BARRETT) || \
defined(CONFIG_BIGINT_MONTGOMERY)
static bigint *comp_right_shift(bigint *biR, int num_shifts);
static bigint *comp_left_shift(bigint *biR, int num_shifts);
#endif
#ifdef CONFIG_BIGINT_CHECK_ON
static void check(const bigint *bi);
#else
#define check(A) /**< disappears in normal production mode */
#endif
/**
* @brief Start a new bigint context.
* @return A bigint context.
*/
BI_CTX * ICACHE_FLASH_ATTR bi_initialize(void)
{
/* calloc() sets everything to zero */
BI_CTX *ctx = (BI_CTX *)os_zalloc(sizeof(BI_CTX));
/* the radix */
ctx->bi_radix = alloc(ctx, 2);
ctx->bi_radix->comps[0] = 0;
ctx->bi_radix->comps[1] = 1;
bi_permanent(ctx->bi_radix);
return ctx;
}
/**
* @brief Close the bigint context and free any resources.
*
* Free up any used memory - a check is done if all objects were not
* properly freed.
* @param ctx [in] The bigint session context.
*/
void ICACHE_FLASH_ATTR bi_terminate(BI_CTX *ctx)
{
bi_depermanent(ctx->bi_radix);
bi_free(ctx, ctx->bi_radix);
if (ctx->active_count != 0)
{
#ifdef CONFIG_SSL_FULL_MODE
ssl_printf("bi_terminate: there were %d un-freed bigints\n",
ctx->active_count);
#endif
return; /* wujg : org ---> abort(); */
}
bi_clear_cache(ctx);
os_free(ctx);
}
/**
*@brief Clear the memory cache.
*/
void ICACHE_FLASH_ATTR bi_clear_cache(BI_CTX *ctx)
{
bigint *p, *pn;
if (ctx->free_list == NULL)
return;
for (p = ctx->free_list; p != NULL; p = pn)
{
pn = p->next;
os_free(p->comps);
os_free(p);
}
ctx->free_count = 0;
ctx->free_list = NULL;
}
/**
* @brief Increment the number of references to this object.
* It does not do a full copy.
* @param bi [in] The bigint to copy.
* @return A reference to the same bigint.
*/
bigint * ICACHE_FLASH_ATTR bi_copy(bigint *bi)
{
check(bi);
if (bi->refs != PERMANENT)
bi->refs++;
return bi;
}
/**
* @brief Simply make a bigint object "unfreeable" if bi_free() is called on it.
*
* For this object to be freed, bi_depermanent() must be called.
* @param bi [in] The bigint to be made permanent.
*/
void ICACHE_FLASH_ATTR bi_permanent(bigint *bi)
{
check(bi);
if (bi->refs != 1)
{
#ifdef CONFIG_SSL_FULL_MODE
ssl_printf("bi_permanent: refs was not 1\n");
#endif
return; /* wujg : org ----> abort(); */
}
bi->refs = PERMANENT;
}
/**
* @brief Take a permanent object and make it eligible for freedom.
* @param bi [in] The bigint to be made back to temporary.
*/
void ICACHE_FLASH_ATTR bi_depermanent(bigint *bi)
{
check(bi);
if (bi->refs != PERMANENT)
{
#ifdef CONFIG_SSL_FULL_MODE
ssl_printf("bi_depermanent: bigint was not permanent\n");
#endif
return; /* wujg : org ----> abort(); */
}
bi->refs = 1;
}
/**
* @brief Free a bigint object so it can be used again.
*
* The memory itself it not actually freed, just tagged as being available
* @param ctx [in] The bigint session context.
* @param bi [in] The bigint to be freed.
*/
void ICACHE_FLASH_ATTR bi_free(BI_CTX *ctx, bigint *bi)
{
check(bi);
if (bi->refs == PERMANENT)
{
return;
}
if (--bi->refs > 0)
{
return;
}
bi->next = ctx->free_list;
ctx->free_list = bi;
ctx->free_count++;
if (--ctx->active_count < 0)
{
#ifdef CONFIG_SSL_FULL_MODE
ssl_printf("bi_free: active_count went negative "
"- double-freed bigint?\n");
#endif
return; /* wujg : org ----> abort(); */
}
}
/**
* @brief Convert an (unsigned) integer into a bigint.
* @param ctx [in] The bigint session context.
* @param i [in] The (unsigned) integer to be converted.
*
*/
bigint * ICACHE_FLASH_ATTR int_to_bi(BI_CTX *ctx, comp i)
{
bigint *biR = alloc(ctx, 1);
biR->comps[0] = i;
return biR;
}
/**
* @brief Do a full copy of the bigint object.
* @param ctx [in] The bigint session context.
* @param bi [in] The bigint object to be copied.
*/
bigint * ICACHE_FLASH_ATTR bi_clone(BI_CTX *ctx, const bigint *bi)
{
bigint *biR = alloc(ctx, bi->size);
check(bi);
os_memcpy(biR->comps, bi->comps, bi->size*COMP_BYTE_SIZE);
return biR;
}
/**
* @brief Perform an addition operation between two bigints.
* @param ctx [in] The bigint session context.
* @param bia [in] A bigint.
* @param bib [in] Another bigint.
* @return The result of the addition.
*/
bigint * ICACHE_FLASH_ATTR bi_add(BI_CTX *ctx, bigint *bia, bigint *bib)
{
int n;
comp carry = 0;
comp *pa, *pb;
check(bia);
check(bib);
n = max(bia->size, bib->size);
more_comps(bia, n+1);
more_comps(bib, n);
pa = bia->comps;
pb = bib->comps;
do
{
comp sl, rl, cy1;
sl = *pa + *pb++;
rl = sl + carry;
cy1 = sl < *pa;
carry = cy1 | (rl < sl);
*pa++ = rl;
} while (--n != 0);
*pa = carry; /* do overflow */
bi_free(ctx, bib);
return trim(bia);
}
/**
* @brief Perform a subtraction operation between two bigints.
* @param ctx [in] The bigint session context.
* @param bia [in] A bigint.
* @param bib [in] Another bigint.
* @param is_negative [out] If defined, indicates that the result was negative.
* is_negative may be null.
* @return The result of the subtraction. The result is always positive.
*/
bigint * ICACHE_FLASH_ATTR bi_subtract(BI_CTX *ctx,
bigint *bia, bigint *bib, int *is_negative)
{
int n = bia->size;
comp *pa, *pb, carry = 0;
check(bia);
check(bib);
more_comps(bib, n);
pa = bia->comps;
pb = bib->comps;
do
{
comp sl, rl, cy1;
sl = *pa - *pb++;
rl = sl - carry;
cy1 = sl > *pa;
carry = cy1 | (rl > sl);
*pa++ = rl;
} while (--n != 0);
if (is_negative) /* indicate a negative result */
{
*is_negative = carry;
}
bi_free(ctx, trim(bib)); /* put bib back to the way it was */
return trim(bia);
}
/**
* Perform a multiply between a bigint an an (unsigned) integer
*/
static bigint * ICACHE_FLASH_ATTR bi_int_multiply(BI_CTX *ctx, bigint *bia, comp b)
{
int j = 0, n = bia->size;
bigint *biR = alloc(ctx, n + 1);
comp carry = 0;
comp *r = biR->comps;
comp *a = bia->comps;
check(bia);
/* clear things to start with */
os_memset(r, 0, ((n+1)*COMP_BYTE_SIZE));
do
{
long_comp tmp = *r + (long_comp)a[j]*b + carry;
*r++ = (comp)tmp; /* downsize */
carry = (comp)(tmp >> COMP_BIT_SIZE);
} while (++j < n);
*r = carry;
bi_free(ctx, bia);
return trim(biR);
}
/**
* @brief Does both division and modulo calculations.
*
* Used extensively when doing classical reduction.
* @param ctx [in] The bigint session context.
* @param u [in] A bigint which is the numerator.
* @param v [in] Either the denominator or the modulus depending on the mode.
* @param is_mod [n] Determines if this is a normal division (0) or a reduction
* (1).
* @return The result of the division/reduction.
*/
bigint * ICACHE_FLASH_ATTR bi_divide(BI_CTX *ctx, bigint *u, bigint *v, int is_mod)
{
int n = v->size, m = u->size-n;
int j = 0, orig_u_size = u->size;
uint8_t mod_offset = ctx->mod_offset;
comp d;
bigint *quotient, *tmp_u;
comp q_dash;
check(u);
check(v);
/* if doing reduction and we are < mod, then return mod */
if (is_mod && bi_compare(v, u) > 0)
{
bi_free(ctx, v);
return u;
}
quotient = alloc(ctx, m+1);
tmp_u = alloc(ctx, n+1);
v = trim(v); /* make sure we have no leading 0's */
d = (comp)((long_comp)COMP_RADIX/(V1+1));
/* clear things to start with */
os_memset(quotient->comps, 0, ((quotient->size)*COMP_BYTE_SIZE));
/* normalise */
if (d > 1)
{
u = bi_int_multiply(ctx, u, d);
if (is_mod)
{
v = ctx->bi_normalised_mod[mod_offset];
}
else
{
v = bi_int_multiply(ctx, v, d);
}
}
if (orig_u_size == u->size) /* new digit position u0 */
{
more_comps(u, orig_u_size + 1);
}
do
{
/* get a temporary short version of u */
os_memcpy(tmp_u->comps, &u->comps[u->size-n-1-j], (n+1)*COMP_BYTE_SIZE);
/* calculate q' */
if (U(0) == V1)
{
q_dash = COMP_RADIX-1;
}
else
{
q_dash = (comp)(((long_comp)U(0)*COMP_RADIX + U(1))/V1);
if (v->size > 1 && V2)
{
/* we are implementing the following:
if (V2*q_dash > (((U(0)*COMP_RADIX + U(1) -
q_dash*V1)*COMP_RADIX) + U(2))) ... */
comp inner = (comp)((long_comp)COMP_RADIX*U(0) + U(1) -
(long_comp)q_dash*V1);
if ((long_comp)V2*q_dash > (long_comp)inner*COMP_RADIX + U(2))
{
q_dash--;
}
}
}
/* multiply and subtract */
if (q_dash)
{
int is_negative;
tmp_u = bi_subtract(ctx, tmp_u,
bi_int_multiply(ctx, bi_copy(v), q_dash), &is_negative);
more_comps(tmp_u, n+1);
Q(j) = q_dash;
/* add back */
if (is_negative)
{
Q(j)--;
tmp_u = bi_add(ctx, tmp_u, bi_copy(v));
/* lop off the carry */
tmp_u->size--;
v->size--;
}
}
else
{
Q(j) = 0;
}
/* copy back to u */
os_memcpy(&u->comps[u->size-n-1-j], tmp_u->comps, (n+1)*COMP_BYTE_SIZE);
} while (++j <= m);
bi_free(ctx, tmp_u);
bi_free(ctx, v);
if (is_mod) /* get the remainder */
{
bi_free(ctx, quotient);
return bi_int_divide(ctx, trim(u), d);
}
else /* get the quotient */
{
bi_free(ctx, u);
return trim(quotient);
}
}
/*
* Perform an integer divide on a bigint.
*/
static bigint * ICACHE_FLASH_ATTR bi_int_divide(BI_CTX *ctx, bigint *biR, comp denom)
{
int i = biR->size - 1;
long_comp r = 0;
check(biR);
do
{
r = (r<<COMP_BIT_SIZE) + biR->comps[i];
biR->comps[i] = (comp)(r / denom);
r %= denom;
} while (--i >= 0);
return trim(biR);
}
#ifdef CONFIG_BIGINT_MONTGOMERY
/**
* There is a need for the value of integer N' such that B^-1(B-1)-N^-1N'=1,
* where B^-1(B-1) mod N=1. Actually, only the least significant part of
* N' is needed, hence the definition N0'=N' mod b. We reproduce below the
* simple algorithm from an article by Dusse and Kaliski to efficiently
* find N0' from N0 and b */
static comp ICACHE_FLASH_ATTR modular_inverse(bigint *bim)
{
int i;
comp t = 1;
comp two_2_i_minus_1 = 2; /* 2^(i-1) */
long_comp two_2_i = 4; /* 2^i */
comp N = bim->comps[0];
for (i = 2; i <= COMP_BIT_SIZE; i++)
{
if ((long_comp)N*t%two_2_i >= two_2_i_minus_1)
{
t += two_2_i_minus_1;
}
two_2_i_minus_1 <<= 1;
two_2_i <<= 1;
}
return (comp)(COMP_RADIX-t);
}
#endif
#if defined(CONFIG_BIGINT_KARATSUBA) || defined(CONFIG_BIGINT_BARRETT) || \
defined(CONFIG_BIGINT_MONTGOMERY)
/**
* Take each component and shift down (in terms of components)
*/
static bigint * ICACHE_FLASH_ATTR comp_right_shift(bigint *biR, int num_shifts)
{
int i = biR->size-num_shifts;
comp *x = biR->comps;
comp *y = &biR->comps[num_shifts];
check(biR);
if (i <= 0) /* have we completely right shifted? */
{
biR->comps[0] = 0; /* return 0 */
biR->size = 1;
return biR;
}
do
{
*x++ = *y++;
} while (--i > 0);
biR->size -= num_shifts;
return biR;
}
/**
* Take each component and shift it up (in terms of components)
*/
static bigint * ICACHE_FLASH_ATTR comp_left_shift(bigint *biR, int num_shifts)
{
int i = biR->size-1;
comp *x, *y;
check(biR);
if (num_shifts <= 0)
{
return biR;
}
more_comps(biR, biR->size + num_shifts);
x = &biR->comps[i+num_shifts];
y = &biR->comps[i];
do
{
*x-- = *y--;
} while (i--);
os_memset(biR->comps, 0, num_shifts*COMP_BYTE_SIZE); /* zero LS comps */
return biR;
}
#endif
/**
* @brief Allow a binary sequence to be imported as a bigint.
* @param ctx [in] The bigint session context.
* @param data [in] The data to be converted.
* @param size [in] The number of bytes of data.
* @return A bigint representing this data.
*/
bigint * ICACHE_FLASH_ATTR bi_import(BI_CTX *ctx, const uint8_t *data, int size)
{
bigint *biR = alloc(ctx, (size+COMP_BYTE_SIZE-1)/COMP_BYTE_SIZE);
int i, j = 0, offset = 0;
os_memset(biR->comps, 0, biR->size*COMP_BYTE_SIZE);
for (i = size-1; i >= 0; i--)
{
biR->comps[offset] += data[i] << (j*8);
if (++j == COMP_BYTE_SIZE)
{
j = 0;
offset ++;
}
}
return trim(biR);
}
#ifdef CONFIG_SSL_FULL_MODE
/**
* @brief The testharness uses this code to import text hex-streams and
* convert them into bigints.
* @param ctx [in] The bigint session context.
* @param data [in] A string consisting of hex characters. The characters must
* be in upper case.
* @return A bigint representing this data.
*/
bigint * ICACHE_FLASH_ATTR bi_str_import(BI_CTX *ctx, const char *data)
{
int size = os_strlen(data);
bigint *biR = alloc(ctx, (size+COMP_NUM_NIBBLES-1)/COMP_NUM_NIBBLES);
int i, j = 0, offset = 0;
os_memset(biR->comps, 0, biR->size*COMP_BYTE_SIZE);
for (i = size-1; i >= 0; i--)
{
int num = (data[i] <= '9') ? (data[i] - '0') : (data[i] - 'A' + 10);
biR->comps[offset] += num << (j*4);
if (++j == COMP_NUM_NIBBLES)
{
j = 0;
offset ++;
}
}
return biR;
}
void ICACHE_FLASH_ATTR bi_print(const char *label, bigint *x)
{
int i, j;
if (x == NULL)
{
ssl_printf("%s: (null)\n", label);
return;
}
ssl_printf("%s: (size %d)\n", label, x->size);
for (i = x->size-1; i >= 0; i--)
{
for (j = COMP_NUM_NIBBLES-1; j >= 0; j--)
{
comp mask = 0x0f << (j*4);
comp num = (x->comps[i] & mask) >> (j*4);
os_putc((num <= 9) ? (num + '0') : (num + 'A' - 10));
}
}
ssl_printf("\n");
}
#endif
/**
* @brief Take a bigint and convert it into a byte sequence.
*
* This is useful after a decrypt operation.
* @param ctx [in] The bigint session context.
* @param x [in] The bigint to be converted.
* @param data [out] The converted data as a byte stream.
* @param size [in] The maximum size of the byte stream. Unused bytes will be
* zeroed.
*/
void ICACHE_FLASH_ATTR bi_export(BI_CTX *ctx, bigint *x, uint8_t *data, int size)
{
int i, j, k = size-1;
check(x);
os_memset(data, 0, size); /* ensure all leading 0's are cleared */
for (i = 0; i < x->size; i++)
{
for (j = 0; j < COMP_BYTE_SIZE; j++)
{
comp mask = 0xff << (j*8);
int num = (x->comps[i] & mask) >> (j*8);
data[k--] = num;
if (k < 0)
{
goto buf_done;
}
}
}
buf_done:
bi_free(ctx, x);
}
/**
* @brief Pre-calculate some of the expensive steps in reduction.
*
* This function should only be called once (normally when a session starts).
* When the session is over, bi_free_mod() should be called. bi_mod_power()
* relies on this function being called.
* @param ctx [in] The bigint session context.
* @param bim [in] The bigint modulus that will be used.
* @param mod_offset [in] There are three moduluii that can be stored - the
* standard modulus, and its two primes p and q. This offset refers to which
* modulus we are referring to.
* @see bi_free_mod(), bi_mod_power().
*/
void ICACHE_FLASH_ATTR bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset)
{
int k = bim->size;
comp d = (comp)((long_comp)COMP_RADIX/(bim->comps[k-1]+1));
#ifdef CONFIG_BIGINT_MONTGOMERY
bigint *R, *R2;
#endif
ctx->bi_mod[mod_offset] = bim;
bi_permanent(ctx->bi_mod[mod_offset]);
ctx->bi_normalised_mod[mod_offset] = bi_int_multiply(ctx, bim, d);
bi_permanent(ctx->bi_normalised_mod[mod_offset]);
#if defined(CONFIG_BIGINT_MONTGOMERY)
/* set montgomery variables */
R = comp_left_shift(bi_clone(ctx, ctx->bi_radix), k-1); /* R */
R2 = comp_left_shift(bi_clone(ctx, ctx->bi_radix), k*2-1); /* R^2 */
ctx->bi_RR_mod_m[mod_offset] = bi_mod(ctx, R2); /* R^2 mod m */
ctx->bi_R_mod_m[mod_offset] = bi_mod(ctx, R); /* R mod m */
bi_permanent(ctx->bi_RR_mod_m[mod_offset]);
bi_permanent(ctx->bi_R_mod_m[mod_offset]);
ctx->N0_dash[mod_offset] = modular_inverse(ctx->bi_mod[mod_offset]);
#elif defined (CONFIG_BIGINT_BARRETT)
ctx->bi_mu[mod_offset] =
bi_divide(ctx, comp_left_shift(
bi_clone(ctx, ctx->bi_radix), k*2-1), ctx->bi_mod[mod_offset], 0);
bi_permanent(ctx->bi_mu[mod_offset]);
#endif
}
/**
* @brief Used when cleaning various bigints at the end of a session.
* @param ctx [in] The bigint session context.
* @param mod_offset [in] The offset to use.
* @see bi_set_mod().
*/
void ICACHE_FLASH_ATTR bi_free_mod(BI_CTX *ctx, int mod_offset)
{
bi_depermanent(ctx->bi_mod[mod_offset]);
bi_free(ctx, ctx->bi_mod[mod_offset]);
#if defined (CONFIG_BIGINT_MONTGOMERY)
bi_depermanent(ctx->bi_RR_mod_m[mod_offset]);
bi_depermanent(ctx->bi_R_mod_m[mod_offset]);
bi_free(ctx, ctx->bi_RR_mod_m[mod_offset]);
bi_free(ctx, ctx->bi_R_mod_m[mod_offset]);
#elif defined(CONFIG_BIGINT_BARRETT)
bi_depermanent(ctx->bi_mu[mod_offset]);
bi_free(ctx, ctx->bi_mu[mod_offset]);
#endif
bi_depermanent(ctx->bi_normalised_mod[mod_offset]);
bi_free(ctx, ctx->bi_normalised_mod[mod_offset]);
}
/**
* Perform a standard multiplication between two bigints.
*
* Barrett reduction has no need for some parts of the product, so ignore bits
* of the multiply. This routine gives Barrett its big performance
* improvements over Classical/Montgomery reduction methods.
*/
static bigint * ICACHE_FLASH_ATTR regular_multiply(BI_CTX *ctx, bigint *bia, bigint *bib,
int inner_partial, int outer_partial)
{
int i = 0, j;
int n = bia->size;
int t = bib->size;
bigint *biR = alloc(ctx, n + t);
comp *sr = biR->comps;
comp *sa = bia->comps;
comp *sb = bib->comps;
check(bia);
check(bib);
/* clear things to start with */
os_memset(biR->comps, 0, ((n+t)*COMP_BYTE_SIZE));
do
{
long_comp tmp;
comp carry = 0;
int r_index = i;
j = 0;
if (outer_partial && outer_partial-i > 0 && outer_partial < n)
{
r_index = outer_partial-1;
j = outer_partial-i-1;
}
do
{
if (inner_partial && r_index >= inner_partial)
{
break;
}
tmp = sr[r_index] + ((long_comp)sa[j])*sb[i] + carry;
sr[r_index++] = (comp)tmp; /* downsize */
carry = tmp >> COMP_BIT_SIZE;
} while (++j < n);
sr[r_index] = carry;
} while (++i < t);
bi_free(ctx, bia);
bi_free(ctx, bib);
return trim(biR);
}
#ifdef CONFIG_BIGINT_KARATSUBA
/*
* Karatsuba improves on regular multiplication due to only 3 multiplications
* being done instead of 4. The additional additions/subtractions are O(N)
* rather than O(N^2) and so for big numbers it saves on a few operations
*/
static bigint * ICACHE_FLASH_ATTR karatsuba(BI_CTX *ctx, bigint *bia, bigint *bib, int is_square)
{
bigint *x0, *x1;
bigint *p0, *p1, *p2;
int m;
if (is_square)
{
m = (bia->size + 1)/2;
}
else
{
m = (max(bia->size, bib->size) + 1)/2;
}
x0 = bi_clone(ctx, bia);
x0->size = m;
x1 = bi_clone(ctx, bia);
comp_right_shift(x1, m);
bi_free(ctx, bia);
/* work out the 3 partial products */
if (is_square)
{
p0 = bi_square(ctx, bi_copy(x0));
p2 = bi_square(ctx, bi_copy(x1));
p1 = bi_square(ctx, bi_add(ctx, x0, x1));
}
else /* normal multiply */
{
bigint *y0, *y1;
y0 = bi_clone(ctx, bib);
y0->size = m;
y1 = bi_clone(ctx, bib);
comp_right_shift(y1, m);
bi_free(ctx, bib);
p0 = bi_multiply(ctx, bi_copy(x0), bi_copy(y0));
p2 = bi_multiply(ctx, bi_copy(x1), bi_copy(y1));
p1 = bi_multiply(ctx, bi_add(ctx, x0, x1), bi_add(ctx, y0, y1));
}
p1 = bi_subtract(ctx,
bi_subtract(ctx, p1, bi_copy(p2), NULL), bi_copy(p0), NULL);
comp_left_shift(p1, m);
comp_left_shift(p2, 2*m);
return bi_add(ctx, p1, bi_add(ctx, p0, p2));
}
#endif
/**
* @brief Perform a multiplication operation between two bigints.
* @param ctx [in] The bigint session context.
* @param bia [in] A bigint.
* @param bib [in] Another bigint.
* @return The result of the multiplication.
*/
bigint * ICACHE_FLASH_ATTR bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib)
{
check(bia);
check(bib);
#ifdef CONFIG_BIGINT_KARATSUBA
if (min(bia->size, bib->size) < MUL_KARATSUBA_THRESH)
{
return regular_multiply(ctx, bia, bib, 0, 0);
}
return karatsuba(ctx, bia, bib, 0);
#else
return regular_multiply(ctx, bia, bib, 0, 0);
#endif
}
#ifdef CONFIG_BIGINT_SQUARE
/*
* Perform the actual square operion. It takes into account overflow.
*/
static bigint * ICACHE_FLASH_ATTR regular_square(BI_CTX *ctx, bigint *bi)
{
int t = bi->size;
int i = 0, j;
bigint *biR = alloc(ctx, t*2+1);
comp *w = biR->comps;
comp *x = bi->comps;
long_comp carry;
os_memset(w, 0, biR->size*COMP_BYTE_SIZE);
do
{
long_comp tmp = w[2*i] + (long_comp)x[i]*x[i];
w[2*i] = (comp)tmp;
carry = tmp >> COMP_BIT_SIZE;
for (j = i+1; j < t; j++)
{
uint8_t c = 0;
long_comp xx = (long_comp)x[i]*x[j];
if ((COMP_MAX-xx) < xx)
c = 1;
tmp = (xx<<1);
if ((COMP_MAX-tmp) < w[i+j])
c = 1;
tmp += w[i+j];
if ((COMP_MAX-tmp) < carry)
c = 1;
tmp += carry;
w[i+j] = (comp)tmp;
carry = tmp >> COMP_BIT_SIZE;
if (c)
carry += COMP_RADIX;
}
tmp = w[i+t] + carry;
w[i+t] = (comp)tmp;
w[i+t+1] = tmp >> COMP_BIT_SIZE;
} while (++i < t);
bi_free(ctx, bi);
return trim(biR);
}
/**
* @brief Perform a square operation on a bigint.
* @param ctx [in] The bigint session context.
* @param bia [in] A bigint.
* @return The result of the multiplication.
*/
bigint * ICACHE_FLASH_ATTR bi_square(BI_CTX *ctx, bigint *bia)
{
check(bia);
#ifdef CONFIG_BIGINT_KARATSUBA
if (bia->size < SQU_KARATSUBA_THRESH)
{
return regular_square(ctx, bia);
}
return karatsuba(ctx, bia, NULL, 1);
#else
return regular_square(ctx, bia);
#endif
}
#endif
/**
* @brief Compare two bigints.
* @param bia [in] A bigint.
* @param bib [in] Another bigint.
* @return -1 if smaller, 1 if larger and 0 if equal.
*/
int ICACHE_FLASH_ATTR bi_compare(bigint *bia, bigint *bib)
{
int r, i;
check(bia);
check(bib);
if (bia->size > bib->size)
r = 1;
else if (bia->size < bib->size)
r = -1;
else
{
comp *a = bia->comps;
comp *b = bib->comps;
/* Same number of components. Compare starting from the high end
* and working down. */
r = 0;
i = bia->size - 1;
do
{
if (a[i] > b[i])
{
r = 1;
break;
}
else if (a[i] < b[i])
{
r = -1;
break;
}
} while (--i >= 0);
}
return r;
}
/*
* Allocate and zero more components. Does not consume bi.
*/
static void ICACHE_FLASH_ATTR more_comps(bigint *bi, int n)
{
if (n > bi->max_comps)
{
bi->max_comps = max(bi->max_comps * 2, n);
bi->comps = (comp*)os_realloc(bi->comps, bi->max_comps * COMP_BYTE_SIZE);
}
if (n > bi->size)
{
os_memset(&bi->comps[bi->size], 0, (n-bi->size)*COMP_BYTE_SIZE);
}
bi->size = n;
}
/*
* Make a new empty bigint. It may just use an old one if one is available.
* Otherwise get one off the heap.
*/
static bigint * ICACHE_FLASH_ATTR alloc(BI_CTX *ctx, int size)
{
bigint *biR;
/* Can we recycle an old bigint? */
if (ctx->free_list != NULL)
{
biR = ctx->free_list;
ctx->free_list = biR->next;
ctx->free_count--;
if (biR->refs != 0)
{
#ifdef CONFIG_SSL_FULL_MODE
ssl_printf("alloc: refs was not 0\n");
#endif
return; /* wujg : org ----> abort(); */
}
more_comps(biR, size);
}
else
{
/* No free bigints available - create a new one. */
biR = (bigint *)os_malloc(sizeof(bigint));
biR->comps = (comp*)os_malloc(size * COMP_BYTE_SIZE);
biR->max_comps = size; /* give some space to spare */
}
biR->size = size;
biR->refs = 1;
biR->next = NULL;
ctx->active_count++;
return biR;
}
/*
* Work out the highest '1' bit in an exponent. Used when doing sliding-window
* exponentiation.
*/
static int ICACHE_FLASH_ATTR find_max_exp_index(bigint *biexp)
{
int i = COMP_BIT_SIZE-1;
comp shift = COMP_RADIX/2;
comp test = biexp->comps[biexp->size-1]; /* assume no leading zeroes */
check(biexp);
do
{
if (test & shift)
{
return i+(biexp->size-1)*COMP_BIT_SIZE;
}
shift >>= 1;
} while (i-- != 0);
return -1; /* error - must have been a leading 0 */
}
/*
* Is a particular bit is an exponent 1 or 0? Used when doing sliding-window
* exponentiation.
*/
static int ICACHE_FLASH_ATTR exp_bit_is_one(bigint *biexp, int offset)
{
comp test = biexp->comps[offset / COMP_BIT_SIZE];
int num_shifts = offset % COMP_BIT_SIZE;
comp shift = 1;
int i;
check(biexp);
for (i = 0; i < num_shifts; i++)
{
shift <<= 1;
}
return (test & shift) != 0;
}
#ifdef CONFIG_BIGINT_CHECK_ON
/*
* Perform a sanity check on bi.
*/
static void ICACHE_FLASH_ATTR check(const bigint *bi)
{
if (bi->refs <= 0)
{
ssl_printf("check: zero or negative refs in bigint\n");
return; /* wujg : org ----> abort(); */
}
if (bi->next != NULL)
{
ssl_printf("check: attempt to use a bigint from "
"the free list\n");
return; /* wujg : org ----> abort(); */
}
}
#endif
/*
* Delete any leading 0's (and allow for 0).
*/
static bigint * ICACHE_FLASH_ATTR trim(bigint *bi)
{
check(bi);
while (bi->comps[bi->size-1] == 0 && bi->size > 1)
{
bi->size--;
}
return bi;
}
#if defined(CONFIG_BIGINT_MONTGOMERY)
/**
* @brief Perform a single montgomery reduction.
* @param ctx [in] The bigint session context.
* @param bixy [in] A bigint.
* @return The result of the montgomery reduction.
*/
bigint * ICACHE_FLASH_ATTR bi_mont(BI_CTX *ctx, bigint *bixy)
{
int i = 0, n;
uint8_t mod_offset = ctx->mod_offset;
bigint *bim = ctx->bi_mod[mod_offset];
comp mod_inv = ctx->N0_dash[mod_offset];
check(bixy);
if (ctx->use_classical) /* just use classical instead */
{
return bi_mod(ctx, bixy);
}
n = bim->size;
do
{
bixy = bi_add(ctx, bixy, comp_left_shift(
bi_int_multiply(ctx, bim, bixy->comps[i]*mod_inv), i));
} while (++i < n);
comp_right_shift(bixy, n);
if (bi_compare(bixy, bim) >= 0)
{
bixy = bi_subtract(ctx, bixy, bim, NULL);
}
return bixy;
}
#elif defined(CONFIG_BIGINT_BARRETT)
/*
* Stomp on the most significant components to give the illusion of a "mod base
* radix" operation
*/
static bigint * ICACHE_FLASH_ATTR comp_mod(bigint *bi, int mod)
{
check(bi);
if (bi->size > mod)
{
bi->size = mod;
}
return bi;
}
/**
* @brief Perform a single Barrett reduction.
* @param ctx [in] The bigint session context.
* @param bi [in] A bigint.
* @return The result of the Barrett reduction.
*/
bigint * ICACHE_FLASH_ATTR bi_barrett(BI_CTX *ctx, bigint *bi)
{
bigint *q1, *q2, *q3, *r1, *r2, *r;
uint8_t mod_offset = ctx->mod_offset;
bigint *bim = ctx->bi_mod[mod_offset];
int k = bim->size;
check(bi);
check(bim);
/* use Classical method instead - Barrett cannot help here */
if (bi->size > k*2)
{
return bi_mod(ctx, bi);
}
q1 = comp_right_shift(bi_clone(ctx, bi), k-1);
/* do outer partial multiply */
q2 = regular_multiply(ctx, q1, ctx->bi_mu[mod_offset], 0, k-1);
q3 = comp_right_shift(q2, k+1);
r1 = comp_mod(bi, k+1);
/* do inner partial multiply */
r2 = comp_mod(regular_multiply(ctx, q3, bim, k+1, 0), k+1);
r = bi_subtract(ctx, r1, r2, NULL);
/* if (r >= m) r = r - m; */
if (bi_compare(r, bim) >= 0)
{
r = bi_subtract(ctx, r, bim, NULL);
}
return r;
}
#endif /* CONFIG_BIGINT_BARRETT */
#ifdef CONFIG_BIGINT_SLIDING_WINDOW
/*
* Work out g1, g3, g5, g7... etc for the sliding-window algorithm
*/
static void ICACHE_FLASH_ATTR precompute_slide_window(BI_CTX *ctx, int window, bigint *g1)
{
int k = 1, i;
bigint *g2;
for (i = 0; i < window-1; i++) /* compute 2^(window-1) */
{
k <<= 1;
}
ctx->g = (bigint **)os_malloc(k*sizeof(bigint *));
ctx->g[0] = bi_clone(ctx, g1);
bi_permanent(ctx->g[0]);
g2 = bi_residue(ctx, bi_square(ctx, ctx->g[0])); /* g^2 */
for (i = 1; i < k; i++)
{
ctx->g[i] = bi_residue(ctx, bi_multiply(ctx, ctx->g[i-1], bi_copy(g2)));
bi_permanent(ctx->g[i]);
}
bi_free(ctx, g2);
ctx->window = k;
}
#endif
/**
* @brief Perform a modular exponentiation.
*
* This function requires bi_set_mod() to have been called previously. This is
* one of the optimisations used for performance.
* @param ctx [in] The bigint session context.
* @param bi [in] The bigint on which to perform the mod power operation.
* @param biexp [in] The bigint exponent.
* @return The result of the mod exponentiation operation
* @see bi_set_mod().
*/
bigint * ICACHE_FLASH_ATTR bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp)
{
int i = find_max_exp_index(biexp), j, window_size = 1;
bigint *biR = int_to_bi(ctx, 1);
#if defined(CONFIG_BIGINT_MONTGOMERY)
uint8_t mod_offset = ctx->mod_offset;
if (!ctx->use_classical)
{
/* preconvert */
bi = bi_mont(ctx,
bi_multiply(ctx, bi, ctx->bi_RR_mod_m[mod_offset])); /* x' */
bi_free(ctx, biR);
biR = ctx->bi_R_mod_m[mod_offset]; /* A */
}
#endif
check(bi);
check(biexp);
#ifdef CONFIG_BIGINT_SLIDING_WINDOW
for (j = i; j > 32; j /= 5) /* work out an optimum size */
window_size++;
/* work out the slide constants */
precompute_slide_window(ctx, window_size, bi);
#else /* just one constant */
ctx->g = (bigint **)os_malloc(sizeof(bigint *));
ctx->g[0] = bi_clone(ctx, bi);
ctx->window = 1;
bi_permanent(ctx->g[0]);
#endif
/* if sliding-window is off, then only one bit will be done at a time and
* will reduce to standard left-to-right exponentiation */
do
{
if (exp_bit_is_one(biexp, i))
{
int l = i-window_size+1;
int part_exp = 0;
if (l < 0) /* LSB of exponent will always be 1 */
l = 0;
else
{
while (exp_bit_is_one(biexp, l) == 0)
l++; /* go back up */
}
/* build up the section of the exponent */
for (j = i; j >= l; j--)
{
biR = bi_residue(ctx, bi_square(ctx, biR));
if (exp_bit_is_one(biexp, j))
part_exp++;
if (j != l)
part_exp <<= 1;
}
part_exp = (part_exp-1)/2; /* adjust for array */
biR = bi_residue(ctx, bi_multiply(ctx, biR, ctx->g[part_exp]));
i = l-1;
}
else /* square it */
{
biR = bi_residue(ctx, bi_square(ctx, biR));
i--;
}
} while (i >= 0);
/* cleanup */
for (i = 0; i < ctx->window; i++)
{
bi_depermanent(ctx->g[i]);
bi_free(ctx, ctx->g[i]);
}
os_free(ctx->g);
bi_free(ctx, bi);
bi_free(ctx, biexp);
#if defined CONFIG_BIGINT_MONTGOMERY
return ctx->use_classical ? biR : bi_mont(ctx, biR); /* convert back */
#else /* CONFIG_BIGINT_CLASSICAL or CONFIG_BIGINT_BARRETT */
return biR;
#endif
}
#ifdef CONFIG_SSL_CERT_VERIFICATION
/**
* @brief Perform a modular exponentiation using a temporary modulus.
*
* We need this function to check the signatures of certificates. The modulus
* of this function is temporary as it's just used for authentication.
* @param ctx [in] The bigint session context.
* @param bi [in] The bigint to perform the exp/mod.
* @param bim [in] The temporary modulus.
* @param biexp [in] The bigint exponent.
* @return The result of the mod exponentiation operation
* @see bi_set_mod().
*/
bigint * ICACHE_FLASH_ATTR bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp)
{
bigint *biR, *tmp_biR;
/* Set up a temporary bigint context and transfer what we need between
* them. We need to do this since we want to keep the original modulus
* which is already in this context. This operation is only called when
* doing peer verification, and so is not expensive :-) */
BI_CTX *tmp_ctx = bi_initialize();
bi_set_mod(tmp_ctx, bi_clone(tmp_ctx, bim), BIGINT_M_OFFSET);
tmp_biR = bi_mod_power(tmp_ctx,
bi_clone(tmp_ctx, bi),
bi_clone(tmp_ctx, biexp));
biR = bi_clone(ctx, tmp_biR);
bi_free(tmp_ctx, tmp_biR);
bi_free_mod(tmp_ctx, BIGINT_M_OFFSET);
bi_terminate(tmp_ctx);
bi_free(ctx, bi);
bi_free(ctx, bim);
bi_free(ctx, biexp);
return biR;
}
#endif
#ifdef CONFIG_BIGINT_CRT
/**
* @brief Use the Chinese Remainder Theorem to quickly perform RSA decrypts.
*
* @param ctx [in] The bigint session context.
* @param bi [in] The bigint to perform the exp/mod.
* @param dP [in] CRT's dP bigint
* @param dQ [in] CRT's dQ bigint
* @param p [in] CRT's p bigint
* @param q [in] CRT's q bigint
* @param qInv [in] CRT's qInv bigint
* @return The result of the CRT operation
*/
bigint * ICACHE_FLASH_ATTR bi_crt(BI_CTX *ctx, bigint *bi,
bigint *dP, bigint *dQ,
bigint *p, bigint *q, bigint *qInv)
{
bigint *m1, *m2, *h;
/* Montgomery has a condition the 0 < x, y < m and these products violate
* that condition. So disable Montgomery when using CRT */
#if defined(CONFIG_BIGINT_MONTGOMERY)
ctx->use_classical = 1;
#endif
ctx->mod_offset = BIGINT_P_OFFSET;
m1 = bi_mod_power(ctx, bi_copy(bi), dP);
ctx->mod_offset = BIGINT_Q_OFFSET;
m2 = bi_mod_power(ctx, bi, dQ);
h = bi_subtract(ctx, bi_add(ctx, m1, p), bi_copy(m2), NULL);
h = bi_multiply(ctx, h, qInv);
ctx->mod_offset = BIGINT_P_OFFSET;
h = bi_residue(ctx, h);
#if defined(CONFIG_BIGINT_MONTGOMERY)
ctx->use_classical = 0; /* reset for any further operation */
#endif
return bi_add(ctx, m2, bi_multiply(ctx, q, h));
}
#endif
/** @} */
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Some misc. routines to help things out
*/
#include <stdlib.h>
//#include <string.h>
//#include <stdarg.h>
//#include <stdio.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_crypto_misc.h"
#ifdef CONFIG_WIN32_USE_CRYPTO_LIB
#include "wincrypt.h"
#endif
#ifndef WIN32
static int rng_fd = -1;
#elif defined(CONFIG_WIN32_USE_CRYPTO_LIB)
static HCRYPTPROV gCryptProv;
#endif
#if (!defined(CONFIG_USE_DEV_URANDOM) && !defined(CONFIG_WIN32_USE_CRYPTO_LIB))
/* change to processor registers as appropriate */
#define ENTROPY_POOL_SIZE 32
#define ENTROPY_COUNTER1 ((((uint64_t)tv.tv_sec)<<32) | tv.tv_usec)
#define ENTROPY_COUNTER2 rand()
static uint8_t entropy_pool[ENTROPY_POOL_SIZE];
#endif
const char * const unsupported_str = "Error: Feature not supported\n";
#ifndef CONFIG_SSL_SKELETON_MODE
/**
* Retrieve a file and put it into memory
* @return The size of the file, or -1 on failure.
*/
int get_file(const char *filename, uint8_t **buf)
{
#if 0
int total_bytes = 0;
int bytes_read = 0;
int filesize;
FILE *stream = fopen(filename, "rb");
if (stream == NULL)
{
#ifdef CONFIG_SSL_FULL_MODE
printf("file '%s' does not exist\n", filename); //TTY_FLUSH();
#endif
return -1;
}
/* Win CE doesn't support stat() */
fseek(stream, 0, SEEK_END);
filesize = ftell(stream);
*buf = (uint8_t *)os_malloc(filesize);
fseek(stream, 0, SEEK_SET);
do
{
bytes_read = fread(*buf+total_bytes, 1, filesize-total_bytes, stream);
total_bytes += bytes_read;
} while (total_bytes < filesize && bytes_read > 0);
fclose(stream);
return filesize;
#endif
return 0;
}
#endif
/**
* Initialise the Random Number Generator engine.
* - On Win32 use the platform SDK's crypto engine.
* - On Linux use /dev/urandom
* - If none of these work then use a custom RNG.
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR RNG_initialize()
{
#if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM)
rng_fd = ax_open("/dev/urandom", O_RDONLY);
#elif defined(WIN32) && defined(CONFIG_WIN32_USE_CRYPTO_LIB)
if (!CryptAcquireContext(&gCryptProv,
NULL, NULL, PROV_RSA_FULL, 0))
{
if (GetLastError() == NTE_BAD_KEYSET &&
!CryptAcquireContext(&gCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET))
{
printf("CryptoLib: %x\n", unsupported_str, GetLastError());
exit(1);
}
}
#else
/* start of with a stack to copy across */
int i;
os_memcpy(entropy_pool, &i, ENTROPY_POOL_SIZE);
srand((unsigned int)&i);
#endif
}
/**
* If no /dev/urandom, then initialise the RNG with something interesting.
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR RNG_custom_init(const uint8_t *seed_buf, int size)
{
#if defined(WIN32) || defined(CONFIG_WIN32_USE_CRYPTO_LIB)
int i;
for (i = 0; i < ENTROPY_POOL_SIZE && i < size; i++)
entropy_pool[i] ^= seed_buf[i];
#endif
}
/**
* Terminate the RNG engine.
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR RNG_terminate(void)
{
#ifndef WIN32
// close(rng_fd);
#elif defined(CONFIG_WIN32_USE_CRYPTO_LIB)
CryptReleaseContext(gCryptProv, 0);
#endif
}
/**
* Set a series of bytes with a random number. Individual bytes can be 0
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR get_random(int num_rand_bytes, uint8_t *rand_data)
{
#if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM)
/* use the Linux default */
read(rng_fd, rand_data, num_rand_bytes); /* read from /dev/urandom */
#elif defined(WIN32) && defined(CONFIG_WIN32_USE_CRYPTO_LIB)
/* use Microsoft Crypto Libraries */
CryptGenRandom(gCryptProv, num_rand_bytes, rand_data);
#else /* nothing else to use, so use a custom RNG */
/* The method we use when we've got nothing better. Use RC4, time
and a couple of random seeds to generate a random sequence */
RC4_CTX rng_ctx;
struct timeval tv;
MD5_CTX rng_digest_ctx;
uint8_t digest[MD5_SIZE];
uint64_t *ep;
int i;
/* A proper implementation would use counters etc for entropy */
// gettimeofday(&tv, NULL);
ep = (uint64_t *)entropy_pool;
ep[0] ^= ENTROPY_COUNTER1;
ep[1] ^= ENTROPY_COUNTER2;
/* use a digested version of the entropy pool as a key */
MD5_Init(&rng_digest_ctx);
MD5_Update(&rng_digest_ctx, entropy_pool, ENTROPY_POOL_SIZE);
MD5_Final(digest, &rng_digest_ctx);
/* come up with the random sequence */
RC4_setup(&rng_ctx, digest, MD5_SIZE); /* use as a key */
os_memcpy(rand_data, entropy_pool, num_rand_bytes < ENTROPY_POOL_SIZE ?
num_rand_bytes : ENTROPY_POOL_SIZE);
RC4_crypt(&rng_ctx, rand_data, rand_data, num_rand_bytes);
/* move things along */
for (i = ENTROPY_POOL_SIZE-1; i >= MD5_SIZE ; i--)
entropy_pool[i] = entropy_pool[i-MD5_SIZE];
/* insert the digest at the start of the entropy pool */
os_memcpy(entropy_pool, digest, MD5_SIZE);
#endif
}
/**
* Set a series of bytes with a random number. Individual bytes are not zero.
*/
void ICACHE_FLASH_ATTR get_random_NZ(int num_rand_bytes, uint8_t *rand_data)
{
int i;
get_random(num_rand_bytes, rand_data);
for (i = 0; i < num_rand_bytes; i++)
{
while (rand_data[i] == 0) /* can't be 0 */
rand_data[i] = (uint8_t)(rand());
}
}
/**
* Some useful diagnostic routines
*/
#if defined(CONFIG_SSL_FULL_MODE) || defined(CONFIG_DEBUG)
int hex_finish;
int hex_index;
static void ICACHE_FLASH_ATTR print_hex_init(int finish)
{
hex_finish = finish;
hex_index = 0;
}
static void ICACHE_FLASH_ATTR print_hex(uint8_t hex)
{
static int column;
if (hex_index == 0)
{
column = 0;
}
ssl_printf("%02x ", hex);
if (++column == 8)
{
ssl_printf(": ");
}
else if (column >= 16)
{
ssl_printf("\n");
column = 0;
}
if (++hex_index >= hex_finish && column > 0)
{
ssl_printf("\n");
}
}
/**
* Spit out a blob of data for diagnostics. The data is is a nice column format
* for easy reading.
*
* @param format [in] The string (with possible embedded format characters)
* @param size [in] The number of numbers to print
* @param data [in] The start of data to use
* @param ... [in] Any additional arguments
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR print_blob(const char *format,
const uint8_t *data, int size, ...)
{
// int i;
// char tmp[80];
// va_list(ap);
// va_start(ap, size);
// sprintf(tmp, "%s\n", format);
// vprintf(tmp, ap);
// print_hex_init(size);
// for (i = 0; i < size; i++)
// {
// print_hex(data[i]);
// }
// va_end(ap);
// TTY_FLUSH();
}
#elif defined(WIN32)
/* VC6.0 doesn't handle variadic macros */
EXP_FUNC void STDCALL print_blob(const char *format, const unsigned char *data,
int size, ...) {}
#endif
#if defined(CONFIG_SSL_HAS_PEM) || defined(CONFIG_HTTP_HAS_AUTHORIZATION)
/* base64 to binary lookup table */
static const uint8_t map[128] =
{
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 255, 255, 255, 255, 255
};
EXP_FUNC int STDCALL ICACHE_FLASH_ATTR base64_decode(const char *in, int len,
uint8_t *out, int *outlen)
{
int g, t, x, y, z;
uint8_t c;
int ret = -1;
g = 3;
for (x = y = z = t = 0; x < len; x++)
{
if ((c = map[in[x]&0x7F]) == 0xff)
continue;
if (c == 254) /* this is the end... */
{
c = 0;
if (--g < 0)
goto error;
}
else if (g != 3) /* only allow = at end */
goto error;
t = (t<<6) | c;
if (++y == 4)
{
out[z++] = (uint8_t)((t>>16)&255);
if (g > 1)
out[z++] = (uint8_t)((t>>8)&255);
if (g > 2)
out[z++] = (uint8_t)(t&255);
y = t = 0;
}
/* check that we don't go past the output buffer */
if (z > *outlen)
goto error;
}
if (y != 0)
goto error;
*outlen = z;
ret = 0;
error:
#ifdef CONFIG_SSL_FULL_MODE
if (ret < 0)
ssl_printf("Error: Invalid base64\n"); //TTY_FLUSH();
#endif
//TTY_FLUSH();
return ret;
}
#endif
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* HMAC implementation - This code was originally taken from RFC2104
* See http://www.ietf.org/rfc/rfc2104.txt and
* http://www.faqs.org/rfcs/rfc2202.html
*/
//#include <string.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_crypto.h"
/**
* Perform HMAC-MD5
* NOTE: does not handle keys larger than the block size.
*/
void ICACHE_FLASH_ATTR ssl_hmac_md5(const uint8_t *msg, int length, const uint8_t *key,
int key_len, uint8_t *digest)
{
MD5_CTX context;
uint8_t k_ipad[64];
uint8_t k_opad[64];
int i;
os_memset(k_ipad, 0, sizeof k_ipad);
os_memset(k_opad, 0, sizeof k_opad);
os_memcpy(k_ipad, key, key_len);
os_memcpy(k_opad, key, key_len);
for (i = 0; i < 64; i++)
{
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
MD5_Init(&context);
MD5_Update(&context, k_ipad, 64);
MD5_Update(&context, msg, length);
MD5_Final(digest, &context);
MD5_Init(&context);
MD5_Update(&context, k_opad, 64);
MD5_Update(&context, digest, MD5_SIZE);
MD5_Final(digest, &context);
}
/**
* Perform HMAC-SHA1
* NOTE: does not handle keys larger than the block size.
*/
void ICACHE_FLASH_ATTR ssl_hmac_sha1(const uint8_t *msg, int length, const uint8_t *key,
int key_len, uint8_t *digest)
{
SHA1_CTX context;
uint8_t k_ipad[64];
uint8_t k_opad[64];
int i;
os_memset(k_ipad, 0, sizeof k_ipad);
os_memset(k_opad, 0, sizeof k_opad);
os_memcpy(k_ipad, key, key_len);
os_memcpy(k_opad, key, key_len);
for (i = 0; i < 64; i++)
{
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
SHA1_Init(&context);
SHA1_Update(&context, k_ipad, 64);
SHA1_Update(&context, msg, length);
SHA1_Final(digest, &context);
SHA1_Init(&context);
SHA1_Update(&context, k_opad, 64);
SHA1_Update(&context, digest, SHA1_SIZE);
SHA1_Final(digest, &context);
}
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* RFC 1115/1319 compliant MD2 implementation
* The MD2 algorithm was designed by Ron Rivest in 1989.
*
* http://www.ietf.org/rfc/rfc1115.txt
* http://www.ietf.org/rfc/rfc1319.txt
*/
//#include <string.h>
//#include <stdio.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_crypto.h"
//#include "os.h"
#include "lwip/mem.h"
/**
* This code is only here to enable the verification of Verisign root
* certificates. So only enable it for verification mode.
*/
#ifdef CONFIG_SSL_CERT_VERIFICATION
static const uint8_t PI_SUBST[256] =
{
0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, 0x3D, 0x36,
0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13, 0x62, 0xA7, 0x05, 0xF3,
0xC0, 0xC7, 0x73, 0x8C, 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C,
0x82, 0xCA, 0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16,
0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12, 0xBE, 0x4E,
0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49, 0xA0, 0xFB, 0xF5, 0x8E,
0xBB, 0x2F, 0xEE, 0x7A, 0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2,
0x07, 0x3F, 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21,
0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27, 0x35, 0x3E,
0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03, 0xFF, 0x19, 0x30, 0xB3,
0x48, 0xA5, 0xB5, 0xD1, 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56,
0xAA, 0xC6, 0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6,
0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1, 0x45, 0x9D,
0x70, 0x59, 0x64, 0x71, 0x87, 0x20, 0x86, 0x5B, 0xCF, 0x65,
0xE6, 0x2D, 0xA8, 0x02, 0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0,
0xB9, 0xF6, 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F,
0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A, 0xC3, 0x5C,
0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26, 0x2C, 0x53, 0x0D, 0x6E,
0x85, 0x28, 0x84, 0x09, 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81,
0x4D, 0x52, 0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA,
0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A, 0x78, 0x88,
0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D, 0xE9, 0xCB, 0xD5, 0xFE,
0x3B, 0x00, 0x1D, 0x39, 0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58,
0xD0, 0xE4, 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A,
0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A, 0xDB, 0x99,
0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14
};
/*
* MD2 context setup
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR MD2_Init(MD2_CTX *ctx)
{
os_memset(ctx, 0, sizeof *ctx);
}
static void ICACHE_FLASH_ATTR md2_process(MD2_CTX *ctx)
{
int i, j;
uint8_t t = 0;
for (i = 0; i < 16; i++)
{
ctx->state[i + 16] = ctx->buffer[i];
ctx->state[i + 32] = ctx->buffer[i] ^ ctx->state[i];
}
for (i = 0; i < 18; i++)
{
for (j = 0; j < 48; j++)
t = (ctx->state[j] ^= PI_SUBST[t]);
t = (t + i) & 0xFF;
}
t = ctx->cksum[15];
for (i = 0; i < 16; i++)
t = (ctx->cksum[i] ^= PI_SUBST[ctx->buffer[i] ^ t]);
}
/*
* MD2 process buffer
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR MD2_Update(MD2_CTX *ctx, const uint8_t *input, int ilen)
{
int fill;
while (ilen > 0)
{
if (ctx->left + ilen > 16)
fill = 16 - ctx->left;
else
fill = ilen;
os_memcpy(ctx->buffer + ctx->left, input, fill);
ctx->left += fill;
input += fill;
ilen -= fill;
if (ctx->left == 16)
{
ctx->left = 0;
md2_process(ctx);
}
}
}
/*
* MD2 final digest
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR MD2_Final(uint8_t *output, MD2_CTX *ctx)
{
int i;
uint8_t x;
x = (uint8_t)(16 - ctx->left);
for (i = ctx->left; i < 16; i++)
ctx->buffer[i] = x;
md2_process(ctx);
os_memcpy(ctx->buffer, ctx->cksum, 16);
md2_process(ctx);
os_memcpy(output, ctx->state, 16);
}
#endif
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* This file implements the MD5 algorithm as defined in RFC1321
*/
//#include <string.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_crypto.h"
//#include "os.h"
#include "lwip/mem.h"
/* Constants for MD5Transform routine.
*/
#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21
/* ----- static functions ----- */
static void MD5Transform(uint32_t state[4], const uint8_t block[64]);
static void Encode(uint8_t *output, uint32_t *input, uint32_t len);
static void Decode(uint32_t *output, const uint8_t *input, uint32_t len);
static const uint8_t PADDING[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* F, G, H and I are basic MD5 functions.
*/
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
/* ROTATE_LEFT rotates x left n bits. */
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
Rotation is separate from addition to prevent recomputation. */
#define FF(a, b, c, d, x, s, ac) { \
(a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) { \
(a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) { \
(a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) { \
(a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
/**
* MD5 initialization - begins an MD5 operation, writing a new ctx.
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR MD5_Init(MD5_CTX *ctx)
{
ctx->count[0] = ctx->count[1] = 0;
/* Load magic initialization constants.
*/
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xefcdab89;
ctx->state[2] = 0x98badcfe;
ctx->state[3] = 0x10325476;
}
/**
* Accepts an array of octets as the next portion of the message.
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR MD5_Update(MD5_CTX *ctx, const uint8_t * msg, int len)
{
uint32_t x;
int i, partLen;
/* Compute number of bytes mod 64 */
x = (uint32_t)((ctx->count[0] >> 3) & 0x3F);
/* Update number of bits */
if ((ctx->count[0] += ((uint32_t)len << 3)) < ((uint32_t)len << 3))
ctx->count[1]++;
ctx->count[1] += ((uint32_t)len >> 29);
partLen = 64 - x;
/* Transform as many times as possible. */
if (len >= partLen)
{
os_memcpy(&ctx->buffer[x], msg, partLen);
MD5Transform(ctx->state, ctx->buffer);
for (i = partLen; i + 63 < len; i += 64)
MD5Transform(ctx->state, &msg[i]);
x = 0;
}
else
i = 0;
/* Buffer remaining input */
os_memcpy(&ctx->buffer[x], &msg[i], len-i);
}
/**
* Return the 128-bit message digest into the user's array
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR MD5_Final(uint8_t *digest, MD5_CTX *ctx)
{
uint8_t bits[8];
uint32_t x, padLen;
/* Save number of bits */
Encode(bits, ctx->count, 8);
/* Pad out to 56 mod 64.
*/
x = (uint32_t)((ctx->count[0] >> 3) & 0x3f);
padLen = (x < 56) ? (56 - x) : (120 - x);
MD5_Update(ctx, PADDING, padLen);
/* Append length (before padding) */
MD5_Update(ctx, bits, 8);
/* Store state in digest */
Encode(digest, ctx->state, MD5_SIZE);
}
/**
* MD5 basic transformation. Transforms state based on block.
*/
static void ICACHE_FLASH_ATTR MD5Transform(uint32_t state[4], const uint8_t block[64])
{
uint32_t a = state[0], b = state[1], c = state[2],
d = state[3], x[MD5_SIZE];
Decode(x, block, 64);
/* Round 1 */
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
/* Round 2 */
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
/* Round 3 */
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
/* Round 4 */
II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}
/**
* Encodes input (uint32_t) into output (uint8_t). Assumes len is
* a multiple of 4.
*/
static void ICACHE_FLASH_ATTR Encode(uint8_t *output, uint32_t *input, uint32_t len)
{
uint32_t i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
{
output[j] = (uint8_t)(input[i] & 0xff);
output[j+1] = (uint8_t)((input[i] >> 8) & 0xff);
output[j+2] = (uint8_t)((input[i] >> 16) & 0xff);
output[j+3] = (uint8_t)((input[i] >> 24) & 0xff);
}
}
/**
* Decodes input (uint8_t) into output (uint32_t). Assumes len is
* a multiple of 4.
*/
static void ICACHE_FLASH_ATTR Decode(uint32_t *output, const uint8_t *input, uint32_t len)
{
uint32_t i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j+1]) << 8) |
(((uint32_t)input[j+2]) << 16) | (((uint32_t)input[j+3]) << 24);
}
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* An implementation of the RC4/ARC4 algorithm.
* Originally written by Christophe Devine.
*/
//#include <string.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_crypto.h"
/**
* Get ready for an encrypt/decrypt operation
*/
void ICACHE_FLASH_ATTR RC4_setup(RC4_CTX *ctx, const uint8_t *key, int length)
{
int i, j = 0, k = 0, a;
uint8_t *m;
ctx->x = 0;
ctx->y = 0;
m = ctx->m;
for (i = 0; i < 256; i++)
m[i] = i;
for (i = 0; i < 256; i++)
{
a = m[i];
j = (uint8_t)(j + a + key[k]);
m[i] = m[j];
m[j] = a;
if (++k >= length)
k = 0;
}
}
/**
* Perform the encrypt/decrypt operation (can use it for either since
* this is a stream cipher).
* NOTE: *msg and *out must be the same pointer (performance tweak)
*/
void ICACHE_FLASH_ATTR RC4_crypt(RC4_CTX *ctx, const uint8_t *msg, uint8_t *out, int length)
{
int i;
uint8_t *m, x, y, a, b;
x = ctx->x;
y = ctx->y;
m = ctx->m;
for (i = 0; i < length; i++)
{
a = m[++x];
y += a;
m[x] = b = m[y];
m[y] = a;
out[i] ^= m[(uint8_t)(a + b)];
}
ctx->x = x;
ctx->y = y;
}
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Implements the RSA public encryption algorithm. Uses the bigint library to
* perform its calculations.
*/
//#include <stdio.h>
//#include <string.h>
//#include <time.h>
//#include <stdlib.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_crypto.h"
//#include "os.h"
#include "lwip/mem.h"
void ICACHE_FLASH_ATTR RSA_priv_key_new(RSA_CTX **ctx,
const uint8_t *modulus, int mod_len,
const uint8_t *pub_exp, int pub_len,
const uint8_t *priv_exp, int priv_len
#if CONFIG_BIGINT_CRT
, const uint8_t *p, int p_len,
const uint8_t *q, int q_len,
const uint8_t *dP, int dP_len,
const uint8_t *dQ, int dQ_len,
const uint8_t *qInv, int qInv_len
#endif
)
{
RSA_CTX *rsa_ctx;
BI_CTX *bi_ctx;
RSA_pub_key_new(ctx, modulus, mod_len, pub_exp, pub_len);
rsa_ctx = *ctx;
bi_ctx = rsa_ctx->bi_ctx;
rsa_ctx->d = bi_import(bi_ctx, priv_exp, priv_len);
bi_permanent(rsa_ctx->d);
#ifdef CONFIG_BIGINT_CRT
rsa_ctx->p = bi_import(bi_ctx, p, p_len);
rsa_ctx->q = bi_import(bi_ctx, q, q_len);
rsa_ctx->dP = bi_import(bi_ctx, dP, dP_len);
rsa_ctx->dQ = bi_import(bi_ctx, dQ, dQ_len);
rsa_ctx->qInv = bi_import(bi_ctx, qInv, qInv_len);
bi_permanent(rsa_ctx->dP);
bi_permanent(rsa_ctx->dQ);
bi_permanent(rsa_ctx->qInv);
bi_set_mod(bi_ctx, rsa_ctx->p, BIGINT_P_OFFSET);
bi_set_mod(bi_ctx, rsa_ctx->q, BIGINT_Q_OFFSET);
#endif
}
void ICACHE_FLASH_ATTR RSA_pub_key_new(RSA_CTX **ctx,
const uint8_t *modulus, int mod_len,
const uint8_t *pub_exp, int pub_len)
{
RSA_CTX *rsa_ctx;
BI_CTX *bi_ctx;
if (*ctx) /* if we load multiple certs, dump the old one */
RSA_free(*ctx);
bi_ctx = bi_initialize();
*ctx = (RSA_CTX *)os_zalloc(sizeof(RSA_CTX));
rsa_ctx = *ctx;
rsa_ctx->bi_ctx = bi_ctx;
rsa_ctx->num_octets = mod_len;
rsa_ctx->m = bi_import(bi_ctx, modulus, mod_len);
bi_set_mod(bi_ctx, rsa_ctx->m, BIGINT_M_OFFSET);
rsa_ctx->e = bi_import(bi_ctx, pub_exp, pub_len);
bi_permanent(rsa_ctx->e);
}
/**
* Free up any RSA context resources.
*/
void ICACHE_FLASH_ATTR RSA_free(RSA_CTX *rsa_ctx)
{
BI_CTX *bi_ctx;
if (rsa_ctx == NULL) /* deal with ptrs that are null */
return;
bi_ctx = rsa_ctx->bi_ctx;
bi_depermanent(rsa_ctx->e);
bi_free(bi_ctx, rsa_ctx->e);
bi_free_mod(rsa_ctx->bi_ctx, BIGINT_M_OFFSET);
if (rsa_ctx->d)
{
bi_depermanent(rsa_ctx->d);
bi_free(bi_ctx, rsa_ctx->d);
#ifdef CONFIG_BIGINT_CRT
bi_depermanent(rsa_ctx->dP);
bi_depermanent(rsa_ctx->dQ);
bi_depermanent(rsa_ctx->qInv);
bi_free(bi_ctx, rsa_ctx->dP);
bi_free(bi_ctx, rsa_ctx->dQ);
bi_free(bi_ctx, rsa_ctx->qInv);
bi_free_mod(rsa_ctx->bi_ctx, BIGINT_P_OFFSET);
bi_free_mod(rsa_ctx->bi_ctx, BIGINT_Q_OFFSET);
#endif
}
bi_terminate(bi_ctx);
os_free(rsa_ctx);
}
/**
* @brief Use PKCS1.5 for decryption/verification.
* @param ctx [in] The context
* @param in_data [in] The data to encrypt (must be < modulus size-11)
* @param out_data [out] The encrypted data.
* @param is_decryption [in] Decryption or verify operation.
* @return The number of bytes that were originally encrypted. -1 on error.
* @see http://www.rsasecurity.com/rsalabs/node.asp?id=2125
*/
int ICACHE_FLASH_ATTR RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data,
uint8_t *out_data, int is_decryption)
{
const int byte_size = ctx->num_octets;
int i, size;
bigint *decrypted_bi, *dat_bi;
uint8_t *block = (uint8_t *)os_malloc(byte_size);
os_memset(out_data, 0, byte_size); /* initialise */
/* decrypt */
dat_bi = bi_import(ctx->bi_ctx, in_data, byte_size);
#ifdef CONFIG_SSL_CERT_VERIFICATION
decrypted_bi = is_decryption ? /* decrypt or verify? */
RSA_private(ctx, dat_bi) : RSA_public(ctx, dat_bi);
#else /* always a decryption */
decrypted_bi = RSA_private(ctx, dat_bi);
#endif
/* convert to a normal block */
bi_export(ctx->bi_ctx, decrypted_bi, block, byte_size);
i = 10; /* start at the first possible non-padded byte */
#ifdef CONFIG_SSL_CERT_VERIFICATION
if (is_decryption == 0) /* PKCS1.5 signing pads with "0xff"s */
{
while (block[i++] == 0xff && i < byte_size);
if (block[i-2] != 0xff)
i = byte_size; /*ensure size is 0 */
}
else /* PKCS1.5 encryption padding is random */
#endif
{
while (block[i++] && i < byte_size);
}
size = byte_size - i;
/* get only the bit we want */
if (size > 0)
os_memcpy(out_data, &block[i], size);
os_free(block);
return size ? size : -1;
}
/**
* Performs m = c^d mod n
*/
bigint *ICACHE_FLASH_ATTR RSA_private(const RSA_CTX *c, bigint *bi_msg)
{
#ifdef CONFIG_BIGINT_CRT
return bi_crt(c->bi_ctx, bi_msg, c->dP, c->dQ, c->p, c->q, c->qInv);
#else
BI_CTX *ctx = c->bi_ctx;
ctx->mod_offset = BIGINT_M_OFFSET;
return bi_mod_power(ctx, bi_msg, c->d);
#endif
}
#ifdef CONFIG_SSL_FULL_MODE
/**
* Used for diagnostics.
*/
void ICACHE_FLASH_ATTR RSA_print(const RSA_CTX *rsa_ctx)
{
if (rsa_ctx == NULL)
return;
ssl_printf("----------------- RSA DEBUG ----------------\n");
ssl_printf("Size:\t%d\n", rsa_ctx->num_octets);
bi_print("Modulus", rsa_ctx->m);
bi_print("Public Key", rsa_ctx->e);
bi_print("Private Key", rsa_ctx->d);
}
#endif
#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_GENERATE_X509_CERT)
/**
* Performs c = m^e mod n
*/
bigint *ICACHE_FLASH_ATTR RSA_public(const RSA_CTX * c, bigint *bi_msg)
{
c->bi_ctx->mod_offset = BIGINT_M_OFFSET;
return bi_mod_power(c->bi_ctx, bi_msg, c->e);
}
/**
* Use PKCS1.5 for encryption/signing.
* see http://www.rsasecurity.com/rsalabs/node.asp?id=2125
*/
int ICACHE_FLASH_ATTR RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len,
uint8_t *out_data, int is_signing)
{
int byte_size = ctx->num_octets;
int num_pads_needed = byte_size-in_len-3;
bigint *dat_bi, *encrypt_bi;
/* note: in_len+11 must be > byte_size */
out_data[0] = 0; /* ensure encryption block is < modulus */
if (is_signing)
{
out_data[1] = 1; /* PKCS1.5 signing pads with "0xff"'s */
os_memset(&out_data[2], 0xff, num_pads_needed);
}
else /* randomize the encryption padding with non-zero bytes */
{
out_data[1] = 2;
get_random_NZ(num_pads_needed, &out_data[2]);
}
out_data[2+num_pads_needed] = 0;
os_memcpy(&out_data[3+num_pads_needed], in_data, in_len);
/* now encrypt it */
dat_bi = bi_import(ctx->bi_ctx, out_data, byte_size);
encrypt_bi = is_signing ? RSA_private(ctx, dat_bi) :
RSA_public(ctx, dat_bi);
bi_export(ctx->bi_ctx, encrypt_bi, out_data, byte_size);
/* save a few bytes of memory */
bi_clear_cache(ctx->bi_ctx);
return byte_size;
}
#endif /* CONFIG_SSL_CERT_VERIFICATION */
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* SHA1 implementation - as defined in FIPS PUB 180-1 published April 17, 1995.
* This code was originally taken from RFC3174
*/
//#include <string.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_crypto.h"
//#include "os.h"
#include "lwip/mem.h"
/*
* Define the SHA1 circular left shift macro
*/
#define SHA1CircularShift(bits,word) \
(((word) << (bits)) | ((word) >> (32-(bits))))
/* ----- static functions ----- */
static void SHA1PadMessage(SHA1_CTX *ctx);
static void SHA1ProcessMessageBlock(SHA1_CTX *ctx);
/**
* Initialize the SHA1 context
*/
void ICACHE_FLASH_ATTR SHA1_Init(SHA1_CTX *ctx)
{
ctx->Length_Low = 0;
ctx->Length_High = 0;
ctx->Message_Block_Index = 0;
ctx->Intermediate_Hash[0] = 0x67452301;
ctx->Intermediate_Hash[1] = 0xEFCDAB89;
ctx->Intermediate_Hash[2] = 0x98BADCFE;
ctx->Intermediate_Hash[3] = 0x10325476;
ctx->Intermediate_Hash[4] = 0xC3D2E1F0;
}
/**
* Accepts an array of octets as the next portion of the message.
*/
void ICACHE_FLASH_ATTR SHA1_Update(SHA1_CTX *ctx, const uint8_t *msg, int len)
{
while (len--)
{
ctx->Message_Block[ctx->Message_Block_Index++] = (*msg & 0xFF);
ctx->Length_Low += 8;
if (ctx->Length_Low == 0)
ctx->Length_High++;
if (ctx->Message_Block_Index == 64)
SHA1ProcessMessageBlock(ctx);
msg++;
}
}
/**
* Return the 160-bit message digest into the user's array
*/
void ICACHE_FLASH_ATTR SHA1_Final(uint8_t *digest, SHA1_CTX *ctx)
{
int i;
SHA1PadMessage(ctx);
os_memset(ctx->Message_Block, 0, 64);
ctx->Length_Low = 0; /* and clear length */
ctx->Length_High = 0;
for (i = 0; i < SHA1_SIZE; i++)
{
digest[i] = ctx->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) );
}
}
/**
* Process the next 512 bits of the message stored in the array.
*/
static void ICACHE_FLASH_ATTR SHA1ProcessMessageBlock(SHA1_CTX *ctx)
{
const uint32_t K[] = { /* Constants defined in SHA-1 */
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6
};
int t; /* Loop counter */
uint32_t temp; /* Temporary word value */
uint32_t W[80]; /* Word sequence */
uint32_t A, B, C, D, E; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for (t = 0; t < 16; t++)
{
W[t] = ctx->Message_Block[t * 4] << 24;
W[t] |= ctx->Message_Block[t * 4 + 1] << 16;
W[t] |= ctx->Message_Block[t * 4 + 2] << 8;
W[t] |= ctx->Message_Block[t * 4 + 3];
}
for (t = 16; t < 80; t++)
{
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
}
A = ctx->Intermediate_Hash[0];
B = ctx->Intermediate_Hash[1];
C = ctx->Intermediate_Hash[2];
D = ctx->Intermediate_Hash[3];
E = ctx->Intermediate_Hash[4];
for (t = 0; t < 20; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | ((~B) & D)) + E + W[t] + K[0];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for (t = 20; t < 40; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for (t = 40; t < 60; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for (t = 60; t < 80; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
ctx->Intermediate_Hash[0] += A;
ctx->Intermediate_Hash[1] += B;
ctx->Intermediate_Hash[2] += C;
ctx->Intermediate_Hash[3] += D;
ctx->Intermediate_Hash[4] += E;
ctx->Message_Block_Index = 0;
}
/*
* According to the standard, the message must be padded to an even
* 512 bits. The first padding bit must be a '1'. The last 64
* bits represent the length of the original message. All bits in
* between should be 0. This function will pad the message
* according to those rules by filling the Message_Block array
* accordingly. It will also call the ProcessMessageBlock function
* provided appropriately. When it returns, it can be assumed that
* the message digest has been computed.
*
* @param ctx [in, out] The SHA1 context
*/
static void ICACHE_FLASH_ATTR SHA1PadMessage(SHA1_CTX *ctx)
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
if (ctx->Message_Block_Index > 55)
{
ctx->Message_Block[ctx->Message_Block_Index++] = 0x80;
while(ctx->Message_Block_Index < 64)
{
ctx->Message_Block[ctx->Message_Block_Index++] = 0;
}
SHA1ProcessMessageBlock(ctx);
while (ctx->Message_Block_Index < 56)
{
ctx->Message_Block[ctx->Message_Block_Index++] = 0;
}
}
else
{
ctx->Message_Block[ctx->Message_Block_Index++] = 0x80;
while(ctx->Message_Block_Index < 56)
{
ctx->Message_Block[ctx->Message_Block_Index++] = 0;
}
}
/*
* Store the message length as the last 8 octets
*/
ctx->Message_Block[56] = ctx->Length_High >> 24;
ctx->Message_Block[57] = ctx->Length_High >> 16;
ctx->Message_Block[58] = ctx->Length_High >> 8;
ctx->Message_Block[59] = ctx->Length_High;
ctx->Message_Block[60] = ctx->Length_Low >> 24;
ctx->Message_Block[61] = ctx->Length_Low >> 16;
ctx->Message_Block[62] = ctx->Length_Low >> 8;
ctx->Message_Block[63] = ctx->Length_Low;
SHA1ProcessMessageBlock(ctx);
}
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
GEN_LIBS = libsslssl.a
endif
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Some primitive asn methods for extraction ASN.1 data.
*/
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
//#include <time.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_crypto.h"
#include "ssl/ssl_crypto_misc.h"
//#include "os.h"
#include "lwip/mem.h"
struct tm
{
int tm_sec; /* Seconds. [0-60] (1 leap second) */
int tm_min; /* Minutes. [0-59] */
int tm_hour; /* Hours. [0-23] */
int tm_mday; /* Day. [1-31] */
int tm_mon; /* Month. [0-11] */
int tm_year; /* Year - 1900. */
int tm_wday; /* Day of week. [0-6] */
int tm_yday; /* Days in year.[0-365] */
int tm_isdst; /* DST. [-1/0/1]*/
#ifdef __USE_BSD
long int tm_gmtoff; /* Seconds east of UTC. */
__const char *tm_zone; /* Timezone abbreviation. */
#else
long int __tm_gmtoff; /* Seconds east of UTC. */
__const char *__tm_zone; /* Timezone abbreviation. */
#endif
};
#define SIG_OID_PREFIX_SIZE 8
#define SIG_IIS6_OID_SIZE 5
#define SIG_SUBJECT_ALT_NAME_SIZE 3
/* Must be an RSA algorithm with either SHA1 or MD5 for verifying to work */
static const uint8_t sig_oid_prefix[SIG_OID_PREFIX_SIZE] =
{
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01
};
static const uint8_t sig_sha1WithRSAEncrypt[SIG_IIS6_OID_SIZE] =
{
0x2b, 0x0e, 0x03, 0x02, 0x1d
};
static const uint8_t sig_subject_alt_name[SIG_SUBJECT_ALT_NAME_SIZE] =
{
0x55, 0x1d, 0x11
};
/* CN, O, OU */
static const uint8_t g_dn_types[] = { 3, 10, 11 };
int ICACHE_FLASH_ATTR get_asn1_length(const uint8_t *buf, int *offset)
{
int len, i;
if (!(buf[*offset] & 0x80)) /* short form */
{
len = buf[(*offset)++];
}
else /* long form */
{
int length_bytes = buf[(*offset)++]&0x7f;
len = 0;
for (i = 0; i < length_bytes; i++)
{
len <<= 8;
len += buf[(*offset)++];
}
}
return len;
}
/**
* Skip the ASN1.1 object type and its length. Get ready to read the object's
* data.
*/
int ICACHE_FLASH_ATTR asn1_next_obj(const uint8_t *buf, int *offset, int obj_type)
{
if (buf[*offset] != obj_type)
return X509_NOT_OK;
(*offset)++;
return get_asn1_length(buf, offset);
}
/**
* Skip over an ASN.1 object type completely. Get ready to read the next
* object.
*/
int ICACHE_FLASH_ATTR asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type)
{
int len;
if (buf[*offset] != obj_type)
return X509_NOT_OK;
(*offset)++;
len = get_asn1_length(buf, offset);
*offset += len;
return 0;
}
/**
* Read an integer value for ASN.1 data
* Note: This function allocates memory which must be freed by the user.
*/
int ICACHE_FLASH_ATTR asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object)
{
int len;
if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0)
goto end_int_array;
if (len > 1 && buf[*offset] == 0x00) /* ignore the negative byte */
{
len--;
(*offset)++;
}
*object = (uint8_t *)os_malloc(len);
os_memcpy(*object, &buf[*offset], len);
*offset += len;
end_int_array:
return len;
}
/**
* Get all the RSA private key specifics from an ASN.1 encoded file
*/
int ICACHE_FLASH_ATTR asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx)
{
int offset = 7;
uint8_t *modulus = NULL, *priv_exp = NULL, *pub_exp = NULL;
int mod_len, priv_len, pub_len;
#ifdef CONFIG_BIGINT_CRT
uint8_t *p = NULL, *q = NULL, *dP = NULL, *dQ = NULL, *qInv = NULL;
int p_len, q_len, dP_len, dQ_len, qInv_len;
#endif
/* not in der format */
if (buf[0] != ASN1_SEQUENCE) /* basic sanity check */
{
#ifdef CONFIG_SSL_FULL_MODE
ssl_printf("Error: This is not a valid ASN.1 file\n");
#endif
return X509_INVALID_PRIV_KEY;
}
/* Use the private key to mix up the RNG if possible. */
RNG_custom_init(buf, len);
mod_len = asn1_get_int(buf, &offset, &modulus);
pub_len = asn1_get_int(buf, &offset, &pub_exp);
priv_len = asn1_get_int(buf, &offset, &priv_exp);
if (mod_len <= 0 || pub_len <= 0 || priv_len <= 0)
return X509_INVALID_PRIV_KEY;
#ifdef CONFIG_BIGINT_CRT
p_len = asn1_get_int(buf, &offset, &p);
q_len = asn1_get_int(buf, &offset, &q);
dP_len = asn1_get_int(buf, &offset, &dP);
dQ_len = asn1_get_int(buf, &offset, &dQ);
qInv_len = asn1_get_int(buf, &offset, &qInv);
if (p_len <= 0 || q_len <= 0 || dP_len <= 0 || dQ_len <= 0 || qInv_len <= 0)
return X509_INVALID_PRIV_KEY;
RSA_priv_key_new(rsa_ctx,
modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len,
p, p_len, q, p_len, dP, dP_len, dQ, dQ_len, qInv, qInv_len);
os_free(p);
os_free(q);
os_free(dP);
os_free(dQ);
os_free(qInv);
#else
RSA_priv_key_new(rsa_ctx,
modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len);
#endif
os_free(modulus);
os_free(priv_exp);
os_free(pub_exp);
return X509_OK;
}
/**
* Get the time of a certificate. Ignore hours/minutes/seconds.
*/
static int ICACHE_FLASH_ATTR asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t)
{
int ret = X509_NOT_OK, len, t_offset;
struct tm tm;
if (buf[(*offset)++] != ASN1_UTC_TIME)
goto end_utc_time;
len = get_asn1_length(buf, offset);
t_offset = *offset;
os_memset(&tm, 0, sizeof(struct tm));
tm.tm_year = (buf[t_offset] - '0')*10 + (buf[t_offset+1] - '0');
if (tm.tm_year <= 50) /* 1951-2050 thing */
{
tm.tm_year += 100;
}
tm.tm_mon = (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0') - 1;
tm.tm_mday = (buf[t_offset+4] - '0')*10 + (buf[t_offset+5] - '0');
// wujg : pass compile first
// *t = mktime(&tm);
*offset += len;
ret = X509_OK;
end_utc_time:
return ret;
}
/**
* Get the version type of a certificate (which we don't actually care about)
*/
int ICACHE_FLASH_ATTR asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
{
int ret = X509_NOT_OK;
(*offset) += 2; /* get past explicit tag */
if (asn1_skip_obj(cert, offset, ASN1_INTEGER))
goto end_version;
ret = X509_OK;
end_version:
return ret;
}
/**
* Retrieve the notbefore and notafter certificate times.
*/
int ICACHE_FLASH_ATTR asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
{
return (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
asn1_get_utc_time(cert, offset, &x509_ctx->not_before) ||
asn1_get_utc_time(cert, offset, &x509_ctx->not_after));
}
/**
* Get the components of a distinguished name
*/
static int ICACHE_FLASH_ATTR asn1_get_oid_x520(const uint8_t *buf, int *offset)
{
int dn_type = 0;
int len;
if ((len = asn1_next_obj(buf, offset, ASN1_OID)) < 0)
goto end_oid;
/* expect a sequence of 2.5.4.[x] where x is a one of distinguished name
components we are interested in. */
if (len == 3 && buf[(*offset)++] == 0x55 && buf[(*offset)++] == 0x04)
dn_type = buf[(*offset)++];
else
{
*offset += len; /* skip over it */
}
end_oid:
return dn_type;
}
/**
* Obtain an ASN.1 printable string type.
*/
static int ICACHE_FLASH_ATTR asn1_get_printable_str(const uint8_t *buf, int *offset, char **str)
{
int len = X509_NOT_OK;
int asn1_type = buf[*offset];
/* some certs have this awful crud in them for some reason */
if (asn1_type != ASN1_PRINTABLE_STR &&
asn1_type != ASN1_PRINTABLE_STR2 &&
asn1_type != ASN1_TELETEX_STR &&
asn1_type != ASN1_IA5_STR &&
asn1_type != ASN1_UNICODE_STR)
goto end_pnt_str;
(*offset)++;
len = get_asn1_length(buf, offset);
if (asn1_type == ASN1_UNICODE_STR)
{
int i;
*str = (char *)os_malloc(len/2+1); /* allow for null */
for (i = 0; i < len; i += 2)
(*str)[i/2] = buf[*offset + i + 1];
(*str)[len/2] = 0; /* null terminate */
}
else
{
*str = (char *)os_malloc(len+1); /* allow for null */
os_memcpy(*str, &buf[*offset], len);
(*str)[len] = 0; /* null terminate */
}
*offset += len;
end_pnt_str:
return len;
}
/**
* Get the subject name (or the issuer) of a certificate.
*/
int ICACHE_FLASH_ATTR asn1_name(const uint8_t *cert, int *offset, char *dn[])
{
int ret = X509_NOT_OK;
int dn_type;
char *tmp;
if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0)
goto end_name;
while (asn1_next_obj(cert, offset, ASN1_SET) >= 0)
{
int i, found = 0;
if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
(dn_type = asn1_get_oid_x520(cert, offset)) < 0)
goto end_name;
tmp = NULL;
if (asn1_get_printable_str(cert, offset, &tmp) < 0)
{
os_free(tmp);
goto end_name;
}
/* find the distinguished named type */
for (i = 0; i < X509_NUM_DN_TYPES; i++)
{
if (dn_type == g_dn_types[i])
{
if (dn[i] == NULL)
{
dn[i] = tmp;
found = 1;
break;
}
}
}
if (found == 0) /* not found so get rid of it */
{
os_free(tmp);
}
}
ret = X509_OK;
end_name:
return ret;
}
/**
* Read the modulus and public exponent of a certificate.
*/
int ICACHE_FLASH_ATTR asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
{
int ret = X509_NOT_OK, mod_len, pub_len;
uint8_t *modulus = NULL, *pub_exp = NULL;
if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
asn1_skip_obj(cert, offset, ASN1_SEQUENCE) ||
asn1_next_obj(cert, offset, ASN1_BIT_STRING) < 0)
goto end_pub_key;
(*offset)++; /* ignore the padding bit field */
if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0)
goto end_pub_key;
mod_len = asn1_get_int(cert, offset, &modulus);
pub_len = asn1_get_int(cert, offset, &pub_exp);
RSA_pub_key_new(&x509_ctx->rsa_ctx, modulus, mod_len, pub_exp, pub_len);
os_free(modulus);
os_free(pub_exp);
ret = X509_OK;
end_pub_key:
return ret;
}
#ifdef CONFIG_SSL_CERT_VERIFICATION
/**
* Read the signature of the certificate.
*/
int ICACHE_FLASH_ATTR asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
{
int ret = X509_NOT_OK;
if (cert[(*offset)++] != ASN1_BIT_STRING)
goto end_sig;
x509_ctx->sig_len = get_asn1_length(cert, offset)-1;
(*offset)++; /* ignore bit string padding bits */
x509_ctx->signature = (uint8_t *)os_malloc(x509_ctx->sig_len);
os_memcpy(x509_ctx->signature, &cert[*offset], x509_ctx->sig_len);
*offset += x509_ctx->sig_len;
ret = X509_OK;
end_sig:
return ret;
}
/*
* Compare 2 distinguished name components for equality
* @return 0 if a match
*/
static int ICACHE_FLASH_ATTR asn1_compare_dn_comp(const char *dn1, const char *dn2)
{
int ret;
if (dn1 == NULL && dn2 == NULL)
ret = 0;
else
ret = (dn1 && dn2) ? os_strcmp(dn1, dn2) : 1;
return ret;
}
/**
* Clean up all of the CA certificates.
*/
void ICACHE_FLASH_ATTR remove_ca_certs(CA_CERT_CTX *ca_cert_ctx)
{
int i = 0;
if (ca_cert_ctx == NULL)
return;
while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
{
x509_free(ca_cert_ctx->cert[i]);
ca_cert_ctx->cert[i++] = NULL;
}
os_free(ca_cert_ctx);
}
/*
* Compare 2 distinguished names for equality
* @return 0 if a match
*/
int ICACHE_FLASH_ATTR asn1_compare_dn(char * const dn1[], char * const dn2[])
{
int i;
for (i = 0; i < X509_NUM_DN_TYPES; i++)
{
if (asn1_compare_dn_comp(dn1[i], dn2[i]))
return 1;
}
return 0; /* all good */
}
int ICACHE_FLASH_ATTR asn1_find_oid(const uint8_t* cert, int* offset,
const uint8_t* oid, int oid_length)
{
int seqlen;
if ((seqlen = asn1_next_obj(cert, offset, ASN1_SEQUENCE))> 0)
{
int end = *offset + seqlen;
while (*offset < end)
{
int type = cert[(*offset)++];
int length = get_asn1_length(cert, offset);
int noffset = *offset + length;
if (type == ASN1_SEQUENCE)
{
type = cert[(*offset)++];
length = get_asn1_length(cert, offset);
if (type == ASN1_OID && length == oid_length &&
os_memcmp(cert + *offset, oid, oid_length) == 0)
{
*offset += oid_length;
return 1;
}
}
*offset = noffset;
}
}
return 0;
}
int ICACHE_FLASH_ATTR asn1_find_subjectaltname(const uint8_t* cert, int offset)
{
if (asn1_find_oid(cert, &offset, sig_subject_alt_name,
SIG_SUBJECT_ALT_NAME_SIZE))
{
return offset;
}
return 0;
}
#endif /* CONFIG_SSL_CERT_VERIFICATION */
/**
* Read the signature type of the certificate. We only support RSA-MD5 and
* RSA-SHA1 signature types.
*/
int ICACHE_FLASH_ATTR asn1_signature_type(const uint8_t *cert,
int *offset, X509_CTX *x509_ctx)
{
int ret = X509_NOT_OK, len;
if (cert[(*offset)++] != ASN1_OID)
goto end_check_sig;
len = get_asn1_length(cert, offset);
if (len == 5 && os_memcmp(sig_sha1WithRSAEncrypt, &cert[*offset],
SIG_IIS6_OID_SIZE) == 0)
{
x509_ctx->sig_type = SIG_TYPE_SHA1;
}
else
{
if (os_memcmp(sig_oid_prefix, &cert[*offset], SIG_OID_PREFIX_SIZE))
goto end_check_sig; /* unrecognised cert type */
x509_ctx->sig_type = cert[*offset + SIG_OID_PREFIX_SIZE];
}
*offset += len;
asn1_skip_obj(cert, offset, ASN1_NULL); /* if it's there */
ret = X509_OK;
end_check_sig:
return ret;
}
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "ssl/ssl_config.h"
#ifdef CONFIG_SSL_GENERATE_X509_CERT
#include <string.h>
#include <stdlib.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_ssl.h"
/**
* Generate a basic X.509 certificate
*/
static uint8_t ICACHE_FLASH_ATTR set_gen_length(int len, uint8_t *buf, int *offset)
{
if (len < 0x80) /* short form */
{
buf[(*offset)++] = len;
return 1;
}
else /* long form */
{
int i, length_bytes = 0;
if (len & 0x00FF0000)
length_bytes = 3;
else if (len & 0x0000FF00)
length_bytes = 2;
else if (len & 0x000000FF)
length_bytes = 1;
buf[(*offset)++] = 0x80 + length_bytes;
for (i = length_bytes-1; i >= 0; i--)
{
buf[*offset+i] = len & 0xFF;
len >>= 8;
}
*offset += length_bytes;
return length_bytes+1;
}
}
static int ICACHE_FLASH_ATTR pre_adjust_with_size(uint8_t type,
int *seq_offset, uint8_t *buf, int *offset)
{
buf[(*offset)++] = type;
*seq_offset = *offset;
*offset += 4; /* fill in later */
return *offset;
}
static void ICACHE_FLASH_ATTR adjust_with_size(int seq_size, int seq_start,
uint8_t *buf, int *offset)
{
uint8_t seq_byte_size;
int orig_seq_size = seq_size;
int orig_seq_start = seq_start;
seq_size = *offset-seq_size;
seq_byte_size = set_gen_length(seq_size, buf, &seq_start);
if (seq_byte_size != 4)
{
memmove(&buf[orig_seq_start+seq_byte_size],
&buf[orig_seq_size], seq_size);
*offset -= 4-seq_byte_size;
}
}
static void ICACHE_FLASH_ATTR gen_serial_number(uint8_t *buf, int *offset)
{
static const uint8_t ser_oid[] = { ASN1_INTEGER, 1, 0x7F };
memcpy(&buf[*offset], ser_oid , sizeof(ser_oid));
*offset += sizeof(ser_oid);
}
static void ICACHE_FLASH_ATTR gen_signature_alg(uint8_t *buf, int *offset)
{
/* OBJECT IDENTIFIER sha1withRSAEncryption (1 2 840 113549 1 1 5) */
static const uint8_t sig_oid[] =
{
ASN1_SEQUENCE, 0x0d, ASN1_OID, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
ASN1_NULL, 0x00
};
memcpy(&buf[*offset], sig_oid, sizeof(sig_oid));
*offset += sizeof(sig_oid);
}
static int ICACHE_FLASH_ATTR gen_dn(const char *name, uint8_t dn_type,
uint8_t *buf, int *offset)
{
int ret = X509_OK;
int name_size = strlen(name);
if (name_size > 0x70) /* just too big */
{
ret = X509_NOT_OK;
goto error;
}
buf[(*offset)++] = ASN1_SET;
set_gen_length(9+name_size, buf, offset);
buf[(*offset)++] = ASN1_SEQUENCE;
set_gen_length(7+name_size, buf, offset);
buf[(*offset)++] = ASN1_OID;
buf[(*offset)++] = 3;
buf[(*offset)++] = 0x55;
buf[(*offset)++] = 0x04;
buf[(*offset)++] = dn_type;
buf[(*offset)++] = ASN1_PRINTABLE_STR;
buf[(*offset)++] = name_size;
strcpy(&buf[*offset], name);
*offset += name_size;
error:
return ret;
}
static int ICACHE_FLASH_ATTR gen_issuer(const char * dn[], uint8_t *buf, int *offset)
{
int ret = X509_OK;
int seq_offset;
int seq_size = pre_adjust_with_size(
ASN1_SEQUENCE, &seq_offset, buf, offset);
char fqdn[128];
/* we need the common name, so if not configured, work out the fully
* qualified domain name */
if (dn[X509_COMMON_NAME] == NULL || strlen(dn[X509_COMMON_NAME]) == 0)
{
int fqdn_len;
gethostname(fqdn, sizeof(fqdn));
fqdn_len = strlen(fqdn);
fqdn[fqdn_len++] = '.';
getdomainname(&fqdn[fqdn_len], sizeof(fqdn)-fqdn_len);
fqdn_len = strlen(fqdn);
if (fqdn[fqdn_len-1] == '.') /* ensure '.' is not last char */
fqdn[fqdn_len-1] = 0;
dn[X509_COMMON_NAME] = fqdn;
}
if ((ret = gen_dn(dn[X509_COMMON_NAME], 3, buf, offset)))
goto error;
if (dn[X509_ORGANIZATION] != NULL && strlen(dn[X509_ORGANIZATION]) > 0)
{
if ((ret = gen_dn(dn[X509_ORGANIZATION], 10, buf, offset)))
goto error;
}
if (dn[X509_ORGANIZATIONAL_UNIT] != NULL &&
strlen(dn[X509_ORGANIZATIONAL_UNIT]) > 0)
{
if ((ret = gen_dn(dn[X509_ORGANIZATIONAL_UNIT], 11, buf, offset)))
goto error;
}
adjust_with_size(seq_size, seq_offset, buf, offset);
error:
return ret;
}
static void ICACHE_FLASH_ATTR gen_utc_time(uint8_t *buf, int *offset)
{
static const uint8_t time_seq[] =
{
ASN1_SEQUENCE, 30,
ASN1_UTC_TIME, 13,
'0', '7', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z',
ASN1_UTC_TIME, 13, /* make it good for 30 or so years */
'3', '8', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z'
};
/* fixed time */
memcpy(&buf[*offset], time_seq, sizeof(time_seq));
*offset += sizeof(time_seq);
}
static void ICACHE_FLASH_ATTR gen_pub_key2(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset)
{
static const uint8_t pub_key_seq[] =
{
ASN1_INTEGER, 0x03, 0x01, 0x00, 0x01 /* INTEGER 65537 */
};
int seq_offset;
int pub_key_size = rsa_ctx->num_octets;
uint8_t *block = (uint8_t *)alloca(pub_key_size);
int seq_size = pre_adjust_with_size(
ASN1_SEQUENCE, &seq_offset, buf, offset);
buf[(*offset)++] = ASN1_INTEGER;
bi_export(rsa_ctx->bi_ctx, rsa_ctx->m, block, pub_key_size);
if (*block & 0x80) /* make integer positive */
{
set_gen_length(pub_key_size+1, buf, offset);
buf[(*offset)++] = 0;
}
else
set_gen_length(pub_key_size, buf, offset);
memcpy(&buf[*offset], block, pub_key_size);
*offset += pub_key_size;
memcpy(&buf[*offset], pub_key_seq, sizeof(pub_key_seq));
*offset += sizeof(pub_key_seq);
adjust_with_size(seq_size, seq_offset, buf, offset);
}
static void ICACHE_FLASH_ATTR gen_pub_key1(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset)
{
int seq_offset;
int seq_size = pre_adjust_with_size(
ASN1_BIT_STRING, &seq_offset, buf, offset);
buf[(*offset)++] = 0; /* bit string is multiple of 8 */
gen_pub_key2(rsa_ctx, buf, offset);
adjust_with_size(seq_size, seq_offset, buf, offset);
}
static void ICACHE_FLASH_ATTR gen_pub_key(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset)
{
/* OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1) */
static const uint8_t rsa_enc_oid[] =
{
ASN1_SEQUENCE, 0x0d, ASN1_OID, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
ASN1_NULL, 0x00
};
int seq_offset;
int seq_size = pre_adjust_with_size(
ASN1_SEQUENCE, &seq_offset, buf, offset);
memcpy(&buf[*offset], rsa_enc_oid, sizeof(rsa_enc_oid));
*offset += sizeof(rsa_enc_oid);
gen_pub_key1(rsa_ctx, buf, offset);
adjust_with_size(seq_size, seq_offset, buf, offset);
}
static void ICACHE_FLASH_ATTR gen_signature(const RSA_CTX *rsa_ctx, const uint8_t *sha_dgst,
uint8_t *buf, int *offset)
{
static const uint8_t asn1_sig[] =
{
ASN1_SEQUENCE, 0x21, ASN1_SEQUENCE, 0x09, ASN1_OID, 0x05,
0x2b, 0x0e, 0x03, 0x02, 0x1a, /* sha1 (1 3 14 3 2 26) */
ASN1_NULL, 0x00, ASN1_OCTET_STRING, 0x14
};
uint8_t *enc_block = (uint8_t *)alloca(rsa_ctx->num_octets);
uint8_t *block = (uint8_t *)alloca(sizeof(asn1_sig) + SHA1_SIZE);
int sig_size;
/* add the digest as an embedded asn.1 sequence */
memcpy(block, asn1_sig, sizeof(asn1_sig));
memcpy(&block[sizeof(asn1_sig)], sha_dgst, SHA1_SIZE);
sig_size = RSA_encrypt(rsa_ctx, block,
sizeof(asn1_sig) + SHA1_SIZE, enc_block, 1);
buf[(*offset)++] = ASN1_BIT_STRING;
set_gen_length(sig_size+1, buf, offset);
buf[(*offset)++] = 0; /* bit string is multiple of 8 */
memcpy(&buf[*offset], enc_block, sig_size);
*offset += sig_size;
}
static int ICACHE_FLASH_ATTR gen_tbs_cert(const char * dn[],
const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset,
uint8_t *sha_dgst)
{
int ret = X509_OK;
SHA1_CTX sha_ctx;
int seq_offset;
int begin_tbs = *offset;
int seq_size = pre_adjust_with_size(
ASN1_SEQUENCE, &seq_offset, buf, offset);
gen_serial_number(buf, offset);
gen_signature_alg(buf, offset);
/* CA certicate issuer */
if ((ret = gen_issuer(dn, buf, offset)))
goto error;
gen_utc_time(buf, offset);
/* certificate issuer */
if ((ret = gen_issuer(dn, buf, offset)))
goto error;
gen_pub_key(rsa_ctx, buf, offset);
adjust_with_size(seq_size, seq_offset, buf, offset);
SHA1_Init(&sha_ctx);
SHA1_Update(&sha_ctx, &buf[begin_tbs], *offset-begin_tbs);
SHA1_Final(sha_dgst, &sha_ctx);
error:
return ret;
}
/**
* Create a new certificate.
*/
EXP_FUNC int ICACHE_FLASH_ATTR STDCALL ssl_x509_create(SSL_CTX *ssl_ctx, uint32_t options, const char * dn[], uint8_t **cert_data)
{
int ret = X509_OK, offset = 0, seq_offset;
/* allocate enough space to load a new certificate */
uint8_t *buf = (uint8_t *)alloca(ssl_ctx->rsa_ctx->num_octets*2 + 512);
uint8_t sha_dgst[SHA1_SIZE];
int seq_size = pre_adjust_with_size(ASN1_SEQUENCE,
&seq_offset, buf, &offset);
if ((ret = gen_tbs_cert(dn, ssl_ctx->rsa_ctx, buf, &offset, sha_dgst)) < 0)
goto error;
gen_signature_alg(buf, &offset);
gen_signature(ssl_ctx->rsa_ctx, sha_dgst, buf, &offset);
adjust_with_size(seq_size, seq_offset, buf, &offset);
*cert_data = (uint8_t *)os_malloc(offset); /* create the exact memory for it */
memcpy(*cert_data, buf, offset);
error:
return ret < 0 ? ret : offset;
}
#endif
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Load certificates/keys into memory. These can be in many different formats.
* PEM support and other formats can be processed here.
*
* The PEM private keys may be optionally encrypted with AES128 or AES256.
* The encrypted PEM keys were generated with something like:
*
* openssl genrsa -aes128 -passout pass:abcd -out axTLS.key_aes128.pem 512
*/
//#include <stdlib.h>
//#include <string.h>
//#include <stdio.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_ssl.h"
static int do_obj(SSL_CTX *ssl_ctx, int obj_type,
SSLObjLoader *ssl_obj, const char *password);
#ifdef CONFIG_SSL_HAS_PEM
static int ssl_obj_PEM_load(SSL_CTX *ssl_ctx, int obj_type,
SSLObjLoader *ssl_obj, const char *password);
#endif
/*
* Load a file into memory that is in binary DER (or ascii PEM) format.
*/
EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_obj_load(SSL_CTX *ssl_ctx, int obj_type,
const char *filename, const char *password)
{
#ifndef CONFIG_SSL_SKELETON_MODE
static const char * const begin = "-----BEGIN";
int ret = SSL_OK;
SSLObjLoader *ssl_obj = NULL;
if (filename == NULL)
{
ret = SSL_ERROR_INVALID_KEY;
goto error;
}
ssl_obj = (SSLObjLoader *)os_zalloc(sizeof(SSLObjLoader));
ssl_obj->len = get_file(filename, &ssl_obj->buf);
if (ssl_obj->len <= 0)
{
ret = SSL_ERROR_INVALID_KEY;
goto error;
}
/* is the file a PEM file? */
if ((char *)os_strstr((const char *)ssl_obj->buf, begin) != NULL)
{
#ifdef CONFIG_SSL_HAS_PEM
ret = ssl_obj_PEM_load(ssl_ctx, obj_type, ssl_obj, password);
#else
ssl_printf(unsupported_str);
ret = SSL_ERROR_NOT_SUPPORTED;
#endif
}
else
ret = do_obj(ssl_ctx, obj_type, ssl_obj, password);
error:
ssl_obj_free(ssl_obj);
return ret;
#else
ssl_printf(unsupported_str);
return SSL_ERROR_NOT_SUPPORTED;
#endif /* CONFIG_SSL_SKELETON_MODE */
}
/*
* Transfer binary data into the object loader.
*/
EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_obj_memory_load(SSL_CTX *ssl_ctx, int mem_type,
const uint8_t *data, int len, const char *password)
{
int ret;
SSLObjLoader *ssl_obj;
ssl_obj = (SSLObjLoader *)os_zalloc(sizeof(SSLObjLoader));
ssl_obj->buf = (uint8_t *)os_malloc(len);
os_memcpy(ssl_obj->buf, data, len);
ssl_obj->len = len;
ret = do_obj(ssl_ctx, mem_type, ssl_obj, password);
ssl_obj_free(ssl_obj);
return ret;
}
/*
* Actually work out what we are doing
*/
static int ICACHE_FLASH_ATTR do_obj(SSL_CTX *ssl_ctx, int obj_type,
SSLObjLoader *ssl_obj, const char *password)
{
int ret = SSL_OK;
switch (obj_type)
{
case SSL_OBJ_RSA_KEY:
ret = add_private_key(ssl_ctx, ssl_obj);
break;
case SSL_OBJ_X509_CERT:
ret = add_cert(ssl_ctx, ssl_obj->buf, ssl_obj->len);
break;
#ifdef CONFIG_SSL_CERT_VERIFICATION
case SSL_OBJ_X509_CACERT:
add_cert_auth(ssl_ctx, ssl_obj->buf, ssl_obj->len);
break;
#endif
#ifdef CONFIG_SSL_USE_PKCS12
case SSL_OBJ_PKCS8:
ret = pkcs8_decode(ssl_ctx, ssl_obj, password);
break;
case SSL_OBJ_PKCS12:
ret = pkcs12_decode(ssl_ctx, ssl_obj, password);
break;
#endif
default:
ssl_printf(unsupported_str);
ret = SSL_ERROR_NOT_SUPPORTED;
break;
}
return ret;
}
/*
* Clean up our mess.
*/
void ICACHE_FLASH_ATTR ssl_obj_free(SSLObjLoader *ssl_obj)
{
if (ssl_obj)
{
os_free(ssl_obj->buf);
os_free(ssl_obj);
}
}
/*
* Support for PEM encoded keys/certificates.
*/
#ifdef CONFIG_SSL_HAS_PEM
#define NUM_PEM_TYPES 4
#define IV_SIZE 16
#define IS_RSA_PRIVATE_KEY 0
#define IS_ENCRYPTED_PRIVATE_KEY 1
#define IS_PRIVATE_KEY 2
#define IS_CERTIFICATE 3
static const char * const begins[NUM_PEM_TYPES] =
{
"-----BEGIN RSA PRIVATE KEY-----",
"-----BEGIN ENCRYPTED PRIVATE KEY-----",
"-----BEGIN PRIVATE KEY-----",
"-----BEGIN CERTIFICATE-----",
};
static const char * const ends[NUM_PEM_TYPES] =
{
"-----END RSA PRIVATE KEY-----",
"-----END ENCRYPTED PRIVATE KEY-----",
"-----END PRIVATE KEY-----",
"-----END CERTIFICATE-----",
};
static const char * const aes_str[2] =
{
"DEK-Info: AES-128-CBC,",
"DEK-Info: AES-256-CBC,"
};
/**
* Take a base64 blob of data and decrypt it (using AES) into its
* proper ASN.1 form.
*/
static int ICACHE_FLASH_ATTR pem_decrypt(const char *where, const char *end,
const char *password, SSLObjLoader *ssl_obj)
{
int ret = -1;
int is_aes_256 = 0;
char *start = NULL;
uint8_t iv[IV_SIZE];
int i, pem_size;
MD5_CTX md5_ctx;
AES_CTX aes_ctx;
uint8_t key[32]; /* AES256 size */
if (password == NULL || os_strlen(password) == 0)
{
#ifdef CONFIG_SSL_FULL_MODE
ssl_printf("Error: Need a password for this PEM file\n"); //TTY_FLUSH();
#endif
goto error;
}
if ((start = (char *)os_strstr((const char *)where, aes_str[0]))) /* AES128? */
{
start += os_strlen(aes_str[0]);
}
else if ((start = (char *)os_strstr((const char *)where, aes_str[1]))) /* AES256? */
{
is_aes_256 = 1;
start += os_strlen(aes_str[1]);
}
else
{
#ifdef CONFIG_SSL_FULL_MODE
ssl_printf("Error: Unsupported password cipher\n"); //TTY_FLUSH();
#endif
goto error;
}
/* convert from hex to binary - assumes uppercase hex */
for (i = 0; i < IV_SIZE; i++)
{
char c = *start++ - '0';
iv[i] = (c > 9 ? c + '0' - 'A' + 10 : c) << 4;
c = *start++ - '0';
iv[i] += (c > 9 ? c + '0' - 'A' + 10 : c);
}
while (*start == '\r' || *start == '\n')
start++;
/* turn base64 into binary */
pem_size = (int)(end-start);
if (base64_decode(start, pem_size, ssl_obj->buf, &ssl_obj->len) != 0)
goto error;
/* work out the key */
MD5_Init(&md5_ctx);
MD5_Update(&md5_ctx, (const uint8_t *)password, os_strlen(password));
MD5_Update(&md5_ctx, iv, SALT_SIZE);
MD5_Final(key, &md5_ctx);
if (is_aes_256)
{
MD5_Init(&md5_ctx);
MD5_Update(&md5_ctx, key, MD5_SIZE);
MD5_Update(&md5_ctx, (const uint8_t *)password, os_strlen(password));
MD5_Update(&md5_ctx, iv, SALT_SIZE);
MD5_Final(&key[MD5_SIZE], &md5_ctx);
}
/* decrypt using the key/iv */
AES_set_key(&aes_ctx, key, iv, is_aes_256 ? AES_MODE_256 : AES_MODE_128);
AES_convert_key(&aes_ctx);
AES_cbc_decrypt(&aes_ctx, ssl_obj->buf, ssl_obj->buf, ssl_obj->len);
ret = 0;
error:
return ret;
}
/**
* Take a base64 blob of data and turn it into its proper ASN.1 form.
*/
static int ICACHE_FLASH_ATTR new_pem_obj(SSL_CTX *ssl_ctx, int is_cacert, char *where,
int remain, const char *password)
{
int ret = SSL_ERROR_BAD_CERTIFICATE;
SSLObjLoader *ssl_obj = NULL;
while (remain > 0)
{
int i, pem_size, obj_type;
char *start = NULL, *end = NULL;
for (i = 0; i < NUM_PEM_TYPES; i++)
{
if ((start = (char *)os_strstr(where, begins[i])) &&
(end = (char *)os_strstr(where, ends[i])))
{
remain -= (int)(end-where);
start += os_strlen(begins[i]);
pem_size = (int)(end-start);
ssl_obj = (SSLObjLoader *)os_zalloc(sizeof(SSLObjLoader));
/* 4/3 bigger than what we need but so what */
ssl_obj->buf = (uint8_t *)os_zalloc(pem_size);
ssl_obj->len = pem_size;
if (i == IS_RSA_PRIVATE_KEY &&
os_strstr(start, "Proc-Type:") &&
os_strstr(start, "4,ENCRYPTED"))
{
/* check for encrypted PEM file */
if (pem_decrypt(start, end, password, ssl_obj) < 0)
{
ret = SSL_ERROR_BAD_CERTIFICATE;
goto error;
}
}
else
{
ssl_obj->len = pem_size;
if (base64_decode(start, pem_size,
ssl_obj->buf, &ssl_obj->len) != 0)
{
ret = SSL_ERROR_BAD_CERTIFICATE;
goto error;
}
}
switch (i)
{
case IS_RSA_PRIVATE_KEY:
obj_type = SSL_OBJ_RSA_KEY;
break;
case IS_ENCRYPTED_PRIVATE_KEY:
case IS_PRIVATE_KEY:
obj_type = SSL_OBJ_PKCS8;
break;
case IS_CERTIFICATE:
obj_type = is_cacert ?
SSL_OBJ_X509_CACERT : SSL_OBJ_X509_CERT;
break;
default:
ret = SSL_ERROR_BAD_CERTIFICATE;
goto error;
}
/* In a format we can now understand - so process it */
if ((ret = do_obj(ssl_ctx, obj_type, ssl_obj, password)))
goto error;
end += os_strlen(ends[i]);
remain -= os_strlen(ends[i]);
while (remain > 0 && (*end == '\r' || *end == '\n'))
{
end++;
remain--;
}
where = end;
break;
}
}
ssl_obj_free(ssl_obj);
ssl_obj = NULL;
if (start == NULL)
break;
}
error:
ssl_obj_free(ssl_obj);
return ret;
}
/*
* Load a file into memory that is in ASCII PEM format.
*/
static int ICACHE_FLASH_ATTR ssl_obj_PEM_load(SSL_CTX *ssl_ctx, int obj_type,
SSLObjLoader *ssl_obj, const char *password)
{
char *start;
/* add a null terminator */
ssl_obj->len++;
ssl_obj->buf = (uint8_t *)os_realloc(ssl_obj->buf, ssl_obj->len);
ssl_obj->buf[ssl_obj->len-1] = 0;
start = (char *)ssl_obj->buf;
return new_pem_obj(ssl_ctx, obj_type == SSL_OBJ_X509_CACERT,
start, ssl_obj->len, password);
}
#endif /* CONFIG_SSL_HAS_PEM */
/**
* Load the key/certificates in memory depending on compile-time and user
* options.
*/
int ICACHE_FLASH_ATTR load_key_certs(SSL_CTX *ssl_ctx)
{
int ret = SSL_OK;
uint32_t options = ssl_ctx->options;
#ifdef CONFIG_SSL_GENERATE_X509_CERT
uint8_t *cert_data = NULL;
int cert_size;
static const char *dn[] =
{
CONFIG_SSL_X509_COMMON_NAME,
CONFIG_SSL_X509_ORGANIZATION_NAME,
CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME
};
#endif
/* do the private key first */
if (os_strlen(CONFIG_SSL_PRIVATE_KEY_LOCATION) > 0)
{
if ((ret = ssl_obj_load(ssl_ctx, SSL_OBJ_RSA_KEY,
CONFIG_SSL_PRIVATE_KEY_LOCATION,
CONFIG_SSL_PRIVATE_KEY_PASSWORD)) < 0)
goto error;
}
else if (!(options & SSL_NO_DEFAULT_KEY))
{
#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE)
// static const /* saves a few more bytes */
//#include "private_key.h"
extern unsigned int default_private_key_len;
extern unsigned char default_private_key[];
ssl_obj_memory_load(ssl_ctx, SSL_OBJ_RSA_KEY, default_private_key,
default_private_key_len, NULL);
#endif
}
/* now load the certificate */
#ifdef CONFIG_SSL_GENERATE_X509_CERT
if ((cert_size = ssl_x509_create(ssl_ctx, 0, dn, &cert_data)) < 0)
{
ret = cert_size;
goto error;
}
ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT, cert_data, cert_size, NULL);
os_free(cert_data);
#else
if (os_strlen(CONFIG_SSL_X509_CERT_LOCATION))
{
if ((ret = ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT,
CONFIG_SSL_X509_CERT_LOCATION, NULL)) < 0)
goto error;
}
else if (!(options & SSL_NO_DEFAULT_KEY))
{
#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE)
// static const /* saves a few bytes and RAM */
//#include "cert.h"
extern unsigned char default_certificate[];
extern unsigned int default_certificate_len;
ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT,
default_certificate, default_certificate_len, NULL);
#endif
}
#endif
error:
#ifdef CONFIG_SSL_FULL_MODE
if (ret)
{
ssl_printf("Error: Certificate or key not loaded\n"); //TTY_FLUSH();
}
#endif
return ret;
}
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Enable a subset of openssl compatible functions. We don't aim to be 100%
* compatible - just to be able to do basic ports etc.
*
* Only really tested on mini_httpd, so I'm not too sure how extensive this
* port is.
*/
#include "ssl/ssl_config.h"
#ifdef CONFIG_OPENSSL_COMPATIBLE
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_ssl.h"
#define OPENSSL_CTX_ATTR ((OPENSSL_CTX *)ssl_ctx->bonus_attr)
static char *key_password = NULL;
void *SSLv23_server_method(void) { return NULL; }
void *SSLv3_server_method(void) { return NULL; }
void *TLSv1_server_method(void) { return NULL; }
void *SSLv23_client_method(void) { return NULL; }
void *SSLv3_client_method(void) { return NULL; }
void *TLSv1_client_method(void) { return NULL; }
typedef void * (*ssl_func_type_t)(void);
typedef void * (*bio_func_type_t)(void);
typedef struct
{
ssl_func_type_t ssl_func_type;
} OPENSSL_CTX;
SSL_CTX *ICACHE_FLASH_ATTR SSL_CTX_new(ssl_func_type_t meth)
{
SSL_CTX *ssl_ctx = ssl_ctx_new(0, 5);
ssl_ctx->bonus_attr = os_malloc(sizeof(OPENSSL_CTX));
OPENSSL_CTX_ATTR->ssl_func_type = meth;
return ssl_ctx;
}
void ICACHE_FLASH_ATTR SSL_CTX_free(SSL_CTX *ssl_ctx)
{
free(ssl_ctx->bonus_attr);
ssl_ctx_free(ssl_ctx);
}
SSL *ICACHE_FLASH_ATTR SSL_new(SSL_CTX *ssl_ctx)
{
SSL *ssl;
ssl_func_type_t ssl_func_type;
ssl = ssl_new(ssl_ctx, -1); /* fd is set later */
ssl_func_type = OPENSSL_CTX_ATTR->ssl_func_type;
#ifdef CONFIG_SSL_ENABLE_CLIENT
if (ssl_func_type == SSLv23_client_method ||
ssl_func_type == SSLv3_client_method ||
ssl_func_type == TLSv1_client_method)
{
SET_SSL_FLAG(SSL_IS_CLIENT);
}
else
#endif
{
ssl->next_state = HS_CLIENT_HELLO;
}
return ssl;
}
int ICACHE_FLASH_ATTR SSL_set_fd(SSL *s, int fd)
{
s->client_fd = fd;
return 1; /* always succeeds */
}
int ICACHE_FLASH_ATTR SSL_accept(SSL *ssl)
{
while (ssl_read(ssl, NULL) == SSL_OK)
{
if (ssl->next_state == HS_CLIENT_HELLO)
return 1; /* we're done */
}
return -1;
}
#ifdef CONFIG_SSL_ENABLE_CLIENT
int ICACHE_FLASH_ATTR SSL_connect(SSL *ssl)
{
return do_client_connect(ssl) == SSL_OK ? 1 : -1;
}
#endif
void ICACHE_FLASH_ATTR SSL_free(SSL *ssl)
{
ssl_free(ssl);
}
int ICACHE_FLASH_ATTR SSL_read(SSL *ssl, void *buf, int num)
{
uint8_t *read_buf;
int ret;
while ((ret = ssl_read(ssl, &read_buf)) == SSL_OK);
if (ret > SSL_OK)
{
os_memcpy(buf, read_buf, ret > num ? num : ret);
}
return ret;
}
int ICACHE_FLASH_ATTR SSL_write(SSL *ssl, const void *buf, int num)
{
return ssl_write(ssl, buf, num);
}
int ICACHE_FLASH_ATTR SSL_CTX_use_certificate_file(SSL_CTX *ssl_ctx, const char *file, int type)
{
return (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, file, NULL) == SSL_OK);
}
int ICACHE_FLASH_ATTR SSL_CTX_use_PrivateKey_file(SSL_CTX *ssl_ctx, const char *file, int type)
{
return (ssl_obj_load(ssl_ctx, SSL_OBJ_RSA_KEY, file, key_password) == SSL_OK);
}
int ICACHE_FLASH_ATTR SSL_CTX_use_certificate_ASN1(SSL_CTX *ssl_ctx, int len, const uint8_t *d)
{
return (ssl_obj_memory_load(ssl_ctx,
SSL_OBJ_X509_CERT, d, len, NULL) == SSL_OK);
}
int ICACHE_FLASH_ATTR SSL_CTX_set_session_id_context(SSL_CTX *ctx, const unsigned char *sid_ctx,
unsigned int sid_ctx_len)
{
return 1;
}
int ICACHE_FLASH_ATTR SSL_CTX_set_default_verify_paths(SSL_CTX *ctx)
{
return 1;
}
int ICACHE_FLASH_ATTR SSL_CTX_use_certificate_chain_file(SSL_CTX *ssl_ctx, const char *file)
{
return (ssl_obj_load(ssl_ctx,
SSL_OBJ_X509_CERT, file, NULL) == SSL_OK);
}
int ICACHE_FLASH_ATTR SSL_shutdown(SSL *ssl)
{
return 1;
}
/*** get/set session ***/
SSL_SESSION *ICACHE_FLASH_ATTR SSL_get1_session(SSL *ssl)
{
return (SSL_SESSION *)ssl_get_session_id(ssl); /* note: wrong cast */
}
int ICACHE_FLASH_ATTR SSL_set_session(SSL *ssl, SSL_SESSION *session)
{
os_memcpy(ssl->session_id, (uint8_t *)session, SSL_SESSION_ID_SIZE);
return 1;
}
void ICACHE_FLASH_ATTR SSL_SESSION_free(SSL_SESSION *session) { }
/*** end get/set session ***/
long ICACHE_FLASH_ATTR SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg)
{
return 0;
}
void ICACHE_FLASH_ATTR SSL_CTX_set_verify(SSL_CTX *ctx, int mode,
int (*verify_callback)(int, void *)) { }
void ICACHE_FLASH_ATTR SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth) { }
int ICACHE_FLASH_ATTR SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile,
const char *CApath)
{
return 1;
}
void *ICACHE_FLASH_ATTR SSL_load_client_CA_file(const char *file)
{
return (void *)file;
}
void ICACHE_FLASH_ATTR SSL_CTX_set_client_CA_list(SSL_CTX *ssl_ctx, void *file)
{
ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, (const char *)file, NULL);
}
void ICACHE_FLASH_ATTR SSLv23_method(void) { }
void ICACHE_FLASH_ATTR SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, void *cb) { }
void ICACHE_FLASH_ATTR SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *u)
{
key_password = (char *)u;
}
int ICACHE_FLASH_ATTR SSL_peek(SSL *ssl, void *buf, int num)
{
os_memcpy(buf, ssl->bm_data, num);
return num;
}
void ICACHE_FLASH_ATTR SSL_set_bio(SSL *ssl, void *rbio, void *wbio) { }
long ICACHE_FLASH_ATTR SSL_get_verify_result(const SSL *ssl)
{
return ssl_handshake_status(ssl);
}
int ICACHE_FLASH_ATTR SSL_state(SSL *ssl)
{
return 0x03; // ok state
}
/** end of could do better list */
void *ICACHE_FLASH_ATTR SSL_get_peer_certificate(const SSL *ssl)
{
return &ssl->ssl_ctx->certs[0];
}
int ICACHE_FLASH_ATTR SSL_clear(SSL *ssl)
{
return 1;
}
int ICACHE_FLASH_ATTR SSL_CTX_check_private_key(const SSL_CTX *ctx)
{
return 1;
}
int ICACHE_FLASH_ATTR SSL_CTX_set_cipher_list(SSL *s, const char *str)
{
return 1;
}
int ICACHE_FLASH_ATTR SSL_get_error(const SSL *ssl, int ret)
{
ssl_display_error(ret);
return 0; /* TODO: return proper return code */
}
void ICACHE_FLASH_ATTR SSL_CTX_set_options(SSL_CTX *ssl_ctx, int option) {}
int ICACHE_FLASH_ATTR SSL_library_init(void ) { return 1; }
void ICACHE_FLASH_ATTR SSL_load_error_strings(void ) {}
void ICACHE_FLASH_ATTR ERR_print_errors_fp(FILE *fp) {}
#ifndef CONFIG_SSL_SKELETON_MODE
long ICACHE_FLASH_ATTR SSL_CTX_get_timeout(const SSL_CTX *ssl_ctx) {
return CONFIG_SSL_EXPIRY_TIME*3600; }
long ICACHE_FLASH_ATTR SSL_CTX_set_timeout(SSL_CTX *ssl_ctx, long t) {
return SSL_CTX_get_timeout(ssl_ctx); }
#endif
void ICACHE_FLASH_ATTR BIO_printf(FILE *f, const char *format, ...)
{
va_list(ap);
va_start(ap, format);
vfprintf(f, format, ap);
va_end(ap);
}
void* ICACHE_FLASH_ATTR BIO_s_null(void) { return NULL; }
FILE *ICACHE_FLASH_ATTR BIO_new(bio_func_type_t func)
{
if (func == BIO_s_null)
return fopen("/dev/null", "r");
else
return NULL;
}
FILE *ICACHE_FLASH_ATTR BIO_new_fp(FILE *stream, int close_flag) { return stream; }
int ICACHE_FLASH_ATTR BIO_free(FILE *a) { if (a != stdout && a != stderr) fclose(a); return 1; }
#endif
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file os_port.c
*
* OS specific functions.
*/
//#include <time.h>
//#include <stdlib.h>
//#include <errno.h>
//#include <stdarg.h>
#include "ssl/ssl_os_port.h"
#ifdef WIN32
/**
* gettimeofday() not in Win32
*/
EXP_FUNC void STDCALL gettimeofday(struct timeval* t, void* timezone)
{
#if defined(_WIN32_WCE)
t->tv_sec = time(NULL);
t->tv_usec = 0; /* 1sec precision only */
#else
struct _timeb timebuffer;
_ftime(&timebuffer);
t->tv_sec = (long)timebuffer.time;
t->tv_usec = 1000 * timebuffer.millitm; /* 1ms precision */
#endif
}
/**
* strcasecmp() not in Win32
*/
EXP_FUNC int STDCALL strcasecmp(const char *s1, const char *s2)
{
while (tolower(*s1) == tolower(*s2++))
{
if (*s1++ == '\0')
{
return 0;
}
}
return *(unsigned char *)s1 - *(unsigned char *)(s2 - 1);
}
EXP_FUNC int STDCALL getdomainname(char *buf, int buf_size)
{
HKEY hKey;
unsigned long datatype;
unsigned long bufferlength = buf_size;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"),
0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)
return -1;
RegQueryValueEx(hKey, "Domain", NULL, &datatype, buf, &bufferlength);
RegCloseKey(hKey);
return 0;
}
#endif
#if 0
#undef malloc
#undef realloc
#undef calloc
static const char * out_of_mem_str = "out of memory";
static const char * file_open_str = "Could not open file \"%s\"";
/*
* Some functions that call display some error trace and then call abort().
* This just makes life much easier on embedded systems, since we're
* suffering major trauma...
*/
EXP_FUNC void * STDCALL ax_malloc(size_t s)
{
void *x;
if ((x = malloc(s)) == NULL)
exit_now(out_of_mem_str);
return x;
}
EXP_FUNC void * STDCALL ax_realloc(void *y, size_t s)
{
void *x;
if ((x = realloc(y, s)) == NULL)
exit_now(out_of_mem_str);
return x;
}
EXP_FUNC void * STDCALL ax_calloc(size_t n, size_t s)
{
void *x;
if ((x = calloc(n, s)) == NULL)
exit_now(out_of_mem_str);
return x;
}
EXP_FUNC int STDCALL ax_open(const char *pathname, int flags)
{
int x;
if ((x = open(pathname, flags)) < 0)
exit_now(file_open_str, pathname);
return x;
}
/**
* This is a call which will deliberately exit an application, but will
* display some information before dying.
*/
void exit_now(const char *format, ...)
{
va_list argp;
va_start(argp, format);
vfprintf(stderr, format, argp);
va_end(argp);
abort();
}
/**
* gettimeofday() not in Win32
*/
EXP_FUNC void STDCALL gettimeofday(struct timeval* t, void* timezone)
{
#if defined(_WIN32_WCE)
t->tv_sec = time(NULL);
t->tv_usec = 0; /* 1sec precision only */
#else
/* wujg : pass compile first */
t->tv_sec = 0;
t->tv_usec = 0; /* 1ms precision */
#endif
}
#endif
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Process PKCS#8/PKCS#12 keys.
*
* The decoding of a PKCS#12 key is fairly specific - this code was tested on a
* key generated with:
*
* openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem
* -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128
* -name "p12_withoutCA" -out axTLS.withoutCA.p12 -password pass:abcd
*
* or with a certificate chain:
*
* openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem
* -certfile axTLS.ca_x509.pem -keypbe PBE-SHA1-RC4-128 -certpbe
* PBE-SHA1-RC4-128 -name "p12_withCA" -out axTLS.withCA.p12 -password pass:abcd
*
* Note that the PBE has to be specified with PBE-SHA1-RC4-128. The
* private/public keys/certs have to use RSA encryption. Both the integrity
* and privacy passwords are the same.
*
* The PKCS#8 files were generated with something like:
*
* PEM format:
* openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -v1
* PBE-SHA1-RC4-128 -out axTLS.encrypted_pem.p8
*
* DER format:
* openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -outform DER
* -v1 PBE-SHA1-RC4-128 -out axTLS.encrypted.p8
*/
//#include <stdlib.h>
//#include <string.h>
//#include <stdio.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_ssl.h"
/* all commented out if not used */
#ifdef CONFIG_SSL_USE_PKCS12
#define BLOCK_SIZE 64
#define PKCS12_KEY_ID 1
#define PKCS12_IV_ID 2
#define PKCS12_MAC_ID 3
static char *make_uni_pass(const char *password, int *uni_pass_len);
static int p8_decrypt(const char *uni_pass, int uni_pass_len,
const uint8_t *salt, int iter,
uint8_t *priv_key, int priv_key_len, int id);
static int p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key);
static int get_pbe_params(uint8_t *buf, int *offset,
const uint8_t **salt, int *iterations);
/*
* Take a raw pkcs8 block and then decrypt it and turn it into a normal key.
*/
int ICACHE_FLASH_ATTR pkcs8_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password)
{
uint8_t *buf = ssl_obj->buf;
int len, offset = 0;
int iterations;
int ret = SSL_NOT_OK;
uint8_t *version = NULL;
const uint8_t *salt;
uint8_t *priv_key;
int uni_pass_len;
char *uni_pass = make_uni_pass(password, &uni_pass_len);
if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0)
{
#ifdef CONFIG_SSL_FULL_MODE
ssl_printf("Error: Invalid p8 ASN.1 file\n");
#endif
goto error;
}
/* unencrypted key? */
if (asn1_get_int(buf, &offset, &version) > 0 && *version == 0)
{
ret = p8_add_key(ssl_ctx, buf);
goto error;
}
if (get_pbe_params(buf, &offset, &salt, &iterations) < 0)
goto error;
if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0)
goto error;
priv_key = &buf[offset];
p8_decrypt(uni_pass, uni_pass_len, salt,
iterations, priv_key, len, PKCS12_KEY_ID);
ret = p8_add_key(ssl_ctx, priv_key);
error:
os_free(version);
os_free(uni_pass);
return ret;
}
/*
* Take the unencrypted pkcs8 and turn it into a private key
*/
static int ICACHE_FLASH_ATTR p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key)
{
uint8_t *buf = priv_key;
int len, offset = 0;
int ret = SSL_NOT_OK;
/* Skip the preamble and go straight to the private key.
We only support rsaEncryption (1.2.840.113549.1.1.1) */
if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 ||
asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
(len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0)
goto error;
ret = asn1_get_private_key(&buf[offset], len, &ssl_ctx->rsa_ctx);
error:
return ret;
}
/*
* Create the unicode password
*/
static char * ICACHE_FLASH_ATTR make_uni_pass(const char *password, int *uni_pass_len)
{
int pass_len = 0, i;
char *uni_pass;
if (password == NULL)
{
password = "";
}
uni_pass = (char *)os_malloc((os_strlen(password)+1)*2);
/* modify the password into a unicode version */
for (i = 0; i < (int)os_strlen(password); i++)
{
uni_pass[pass_len++] = 0;
uni_pass[pass_len++] = password[i];
}
uni_pass[pass_len++] = 0; /* null terminate */
uni_pass[pass_len++] = 0;
*uni_pass_len = pass_len;
return uni_pass;
}
/*
* Decrypt a pkcs8 block.
*/
static int ICACHE_FLASH_ATTR p8_decrypt(const char *uni_pass, int uni_pass_len,
const uint8_t *salt, int iter,
uint8_t *priv_key, int priv_key_len, int id)
{
uint8_t p[BLOCK_SIZE*2];
uint8_t d[BLOCK_SIZE];
uint8_t Ai[SHA1_SIZE];
SHA1_CTX sha_ctx;
RC4_CTX rc4_ctx;
int i;
for (i = 0; i < BLOCK_SIZE; i++)
{
p[i] = salt[i % SALT_SIZE];
p[BLOCK_SIZE+i] = uni_pass[i % uni_pass_len];
d[i] = id;
}
/* get the key - no IV since we are using RC4 */
SHA1_Init(&sha_ctx);
SHA1_Update(&sha_ctx, d, sizeof(d));
SHA1_Update(&sha_ctx, p, sizeof(p));
SHA1_Final(Ai, &sha_ctx);
for (i = 1; i < iter; i++)
{
SHA1_Init(&sha_ctx);
SHA1_Update(&sha_ctx, Ai, SHA1_SIZE);
SHA1_Final(Ai, &sha_ctx);
}
/* do the decryption */
if (id == PKCS12_KEY_ID)
{
RC4_setup(&rc4_ctx, Ai, 16);
RC4_crypt(&rc4_ctx, priv_key, priv_key, priv_key_len);
}
else /* MAC */
os_memcpy(priv_key, Ai, SHA1_SIZE);
return 0;
}
/*
* Take a raw pkcs12 block and the decrypt it and turn it into a certificate(s)
* and keys.
*/
int ICACHE_FLASH_ATTR pkcs12_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password)
{
uint8_t *buf = ssl_obj->buf;
int len, iterations, auth_safes_start,
auth_safes_end, auth_safes_len, key_offset, offset = 0;
int all_certs = 0;
uint8_t *version = NULL, *auth_safes = NULL, *cert, *orig_mac;
uint8_t key[SHA1_SIZE];
uint8_t mac[SHA1_SIZE];
const uint8_t *salt;
int uni_pass_len, ret = SSL_OK;
char *uni_pass = make_uni_pass(password, &uni_pass_len);
static const uint8_t pkcs_data[] = /* pkc7 data */
{ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01 };
static const uint8_t pkcs_encrypted[] = /* pkc7 encrypted */
{ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x06 };
static const uint8_t pkcs8_key_bag[] = /* 1.2.840.113549.1.12.10.1.2 */
{ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02 };
if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0)
{
#ifdef CONFIG_SSL_FULL_MODE
ssl_printf("Error: Invalid p12 ASN.1 file\n");
#endif
goto error;
}
if (asn1_get_int(buf, &offset, &version) < 0 || *version != 3)
{
ret = SSL_ERROR_INVALID_VERSION;
goto error;
}
/* remove all the boring pcks7 bits */
if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
(len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
len != sizeof(pkcs_data) ||
os_memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data)))
goto error;
offset += len;
if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 ||
asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0)
goto error;
/* work out the MAC start/end points (done on AuthSafes) */
auth_safes_start = offset;
auth_safes_end = offset;
if (asn1_skip_obj(buf, &auth_safes_end, ASN1_SEQUENCE) < 0)
goto error;
auth_safes_len = auth_safes_end - auth_safes_start;
auth_safes = os_malloc(auth_safes_len);
os_memcpy(auth_safes, &buf[auth_safes_start], auth_safes_len);
if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
(len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
(len != sizeof(pkcs_encrypted) ||
os_memcmp(&buf[offset], pkcs_encrypted, sizeof(pkcs_encrypted))))
goto error;
offset += len;
if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 ||
asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 ||
asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
(len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
len != sizeof(pkcs_data) ||
os_memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data)))
goto error;
offset += len;
/* work out the salt for the certificate */
if (get_pbe_params(buf, &offset, &salt, &iterations) < 0 ||
(len = asn1_next_obj(buf, &offset, ASN1_IMPLICIT_TAG)) < 0)
goto error;
/* decrypt the certificate */
cert = &buf[offset];
if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert,
len, PKCS12_KEY_ID)) < 0)
goto error;
offset += len;
/* load the certificate */
key_offset = 0;
all_certs = asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE);
/* keep going until all certs are loaded */
while (key_offset < all_certs)
{
int cert_offset = key_offset;
if (asn1_skip_obj(cert, &cert_offset, ASN1_SEQUENCE) < 0 ||
asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 ||
asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 ||
asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 ||
asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 ||
asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 ||
asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 ||
(len = asn1_next_obj(cert, &key_offset, ASN1_OCTET_STRING)) < 0)
goto error;
if ((ret = add_cert(ssl_ctx, &cert[key_offset], len)) < 0)
goto error;
key_offset = cert_offset;
}
if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
(len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
len != sizeof(pkcs_data) ||
os_memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data)))
goto error;
offset += len;
if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 ||
asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0 ||
asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
(len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
(len != sizeof(pkcs8_key_bag)) ||
os_memcmp(&buf[offset], pkcs8_key_bag, sizeof(pkcs8_key_bag)))
goto error;
offset += len;
/* work out the salt for the private key */
if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 ||
asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
get_pbe_params(buf, &offset, &salt, &iterations) < 0 ||
(len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0)
goto error;
/* decrypt the private key */
cert = &buf[offset];
if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert,
len, PKCS12_KEY_ID)) < 0)
goto error;
offset += len;
/* load the private key */
if ((ret = p8_add_key(ssl_ctx, cert)) < 0)
goto error;
/* miss out on friendly name, local key id etc */
if (asn1_skip_obj(buf, &offset, ASN1_SET) < 0)
goto error;
/* work out the MAC */
if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
(len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 ||
len != SHA1_SIZE)
goto error;
orig_mac = &buf[offset];
offset += len;
/* get the salt */
if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 || len != 8)
goto error;
salt = &buf[offset];
/* work out what the mac should be */
if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations,
key, SHA1_SIZE, PKCS12_MAC_ID)) < 0)
goto error;
ssl_hmac_sha1(auth_safes, auth_safes_len, key, SHA1_SIZE, mac);
if (os_memcmp(mac, orig_mac, SHA1_SIZE))
{
ret = SSL_ERROR_INVALID_HMAC;
goto error;
}
error:
os_free(version);
os_free(uni_pass);
os_free(auth_safes);
return ret;
}
/*
* Retrieve the salt/iteration details from a PBE block.
*/
static int ICACHE_FLASH_ATTR get_pbe_params(uint8_t *buf, int *offset,
const uint8_t **salt, int *iterations)
{
static const uint8_t pbeSH1RC4[] = /* pbeWithSHAAnd128BitRC4 */
{ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x01 };
int i, len;
uint8_t *iter = NULL;
int error_code = SSL_ERROR_NOT_SUPPORTED;
/* Get the PBE type */
if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 ||
(len = asn1_next_obj(buf, offset, ASN1_OID)) < 0)
goto error;
/* we expect pbeWithSHAAnd128BitRC4 (1.2.840.113549.1.12.1.1)
which is the only algorithm we support */
if (len != sizeof(pbeSH1RC4) ||
os_memcmp(&buf[*offset], pbeSH1RC4, sizeof(pbeSH1RC4)))
{
#ifdef CONFIG_SSL_FULL_MODE
ssl_printf("Error: pkcs8/pkcs12 must use \"PBE-SHA1-RC4-128\"\n");
#endif
goto error;
}
*offset += len;
if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 ||
(len = asn1_next_obj(buf, offset, ASN1_OCTET_STRING)) < 0 ||
len != 8)
goto error;
*salt = &buf[*offset];
*offset += len;
if ((len = asn1_get_int(buf, offset, &iter)) < 0)
goto error;
*iterations = 0;
for (i = 0; i < len; i++)
{
(*iterations) <<= 8;
(*iterations) += iter[i];
}
os_free(iter);
error_code = SSL_OK; /* got here - we are ok */
error:
return error_code;
}
#endif
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Common ssl/tlsv1 code to both the client and server implementations.
*/
//#include <string.h>
//#include <stdlib.h>
//#include <stdio.h>
//#include <stdarg.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_ssl.h"
#include "lwip/tcp.h"
#include "ssl/app/espconn_ssl.h"
extern struct pbuf* psslpbuf;
/* The session expiry time */
#define SSL_EXPIRY_TIME (CONFIG_SSL_EXPIRY_TIME*3600)
static const uint8_t g_hello_request[] = { HS_HELLO_REQUEST, 0, 0, 0 };
static const uint8_t g_chg_cipher_spec_pkt[] = { 1 };
static const char * server_finished = "server finished";
static const char * client_finished = "client finished";
static int do_handshake(SSL *ssl, uint8_t *buf, int read_len);
static int set_key_block(SSL *ssl, int is_write);
static int verify_digest(SSL *ssl, int mode, const uint8_t *buf, int read_len);
static void *crypt_new(SSL *ssl, uint8_t *key, uint8_t *iv, int is_decrypt);
static err_t send_raw_packet(SSL *ssl, uint8_t protocol);
/**
* The server will pick the cipher based on the order that the order that the
* ciphers are listed. This order is defined at compile time.
*/
#ifdef CONFIG_SSL_SKELETON_MODE
const uint8_t ssl_prot_prefs[NUM_PROTOCOLS] =
{ SSL_RC4_128_SHA };
#else
static void session_free(SSL_SESSION *ssl_sessions[], int sess_index);
const uint8_t ssl_prot_prefs[NUM_PROTOCOLS] =
#ifdef CONFIG_SSL_PROT_LOW /* low security, fast speed */
{ SSL_RC4_128_SHA, SSL_AES128_SHA, SSL_AES256_SHA, SSL_RC4_128_MD5 };
#elif CONFIG_SSL_PROT_MEDIUM /* medium security, medium speed */
{ SSL_AES128_SHA, SSL_AES256_SHA, SSL_RC4_128_SHA, SSL_RC4_128_MD5 };
#else /* CONFIG_SSL_PROT_HIGH */ /* high security, low speed */
{ SSL_AES256_SHA, SSL_AES128_SHA, SSL_RC4_128_SHA, SSL_RC4_128_MD5 };
#endif
#endif /* CONFIG_SSL_SKELETON_MODE */
/**
* The cipher map containing all the essentials for each cipher.
*/
#ifdef CONFIG_SSL_SKELETON_MODE
static const cipher_info_t cipher_info[NUM_PROTOCOLS] =
{
{ /* RC4-SHA */
SSL_RC4_128_SHA, /* RC4-SHA */
16, /* key size */
0, /* iv size */
2*(SHA1_SIZE+16), /* key block size */
0, /* no padding */
SHA1_SIZE, /* digest size */
ssl_hmac_sha1, /* hmac algorithm */
(crypt_func)RC4_crypt, /* encrypt */
(crypt_func)RC4_crypt /* decrypt */
},
};
#else
static const cipher_info_t cipher_info[NUM_PROTOCOLS] =
{
{ /* AES128-SHA */
SSL_AES128_SHA, /* AES128-SHA */
16, /* key size */
16, /* iv size */
2*(SHA1_SIZE+16+16), /* key block size */
16, /* block padding size */
SHA1_SIZE, /* digest size */
ssl_hmac_sha1, /* hmac algorithm */
(crypt_func)AES_cbc_encrypt, /* encrypt */
(crypt_func)AES_cbc_decrypt /* decrypt */
},
{ /* AES256-SHA */
SSL_AES256_SHA, /* AES256-SHA */
32, /* key size */
16, /* iv size */
2*(SHA1_SIZE+32+16), /* key block size */
16, /* block padding size */
SHA1_SIZE, /* digest size */
ssl_hmac_sha1, /* hmac algorithm */
(crypt_func)AES_cbc_encrypt, /* encrypt */
(crypt_func)AES_cbc_decrypt /* decrypt */
},
{ /* RC4-SHA */
SSL_RC4_128_SHA, /* RC4-SHA */
16, /* key size */
0, /* iv size */
2*(SHA1_SIZE+16), /* key block size */
0, /* no padding */
SHA1_SIZE, /* digest size */
ssl_hmac_sha1, /* hmac algorithm */
(crypt_func)RC4_crypt, /* encrypt */
(crypt_func)RC4_crypt /* decrypt */
},
/*
* This protocol is from SSLv2 days and is unlikely to be used - but was
* useful for testing different possible digest algorithms.
*/
{ /* RC4-MD5 */
SSL_RC4_128_MD5, /* RC4-MD5 */
16, /* key size */
0, /* iv size */
2*(MD5_SIZE+16), /* key block size */
0, /* no padding */
MD5_SIZE, /* digest size */
ssl_hmac_md5, /* hmac algorithm */
(crypt_func)RC4_crypt, /* encrypt */
(crypt_func)RC4_crypt /* decrypt */
},
};
#endif
static void prf(const uint8_t *sec, int sec_len, uint8_t *seed, int seed_len,
uint8_t *out, int olen);
static const cipher_info_t *get_cipher_info(uint8_t cipher);
static void increment_read_sequence(SSL *ssl);
static void increment_write_sequence(SSL *ssl);
static void add_hmac_digest(SSL *ssl, int snd, uint8_t *hmac_header,
const uint8_t *buf, int buf_len, uint8_t *hmac_buf);
/* win32 VC6.0 doesn't have variadic macros */
#if defined(WIN32) && !defined(CONFIG_SSL_FULL_MODE)
void DISPLAY_BYTES(SSL *ssl, const char *format,
const uint8_t *data, int size, ...) {}
#endif
/**
* Establish a new client/server context.
*/
EXP_FUNC SSL_CTX *STDCALL ICACHE_FLASH_ATTR ssl_ctx_new(uint32_t options, int num_sessions)
{
SSL_CTX *ssl_ctx = (SSL_CTX *)os_zalloc(sizeof (SSL_CTX));
ssl_ctx->options = options;
RNG_initialize();
if (load_key_certs(ssl_ctx) < 0)
{
os_free(ssl_ctx); /* can't load our key/certificate pair, so die */
return NULL;
}
#ifndef CONFIG_SSL_SKELETON_MODE
ssl_ctx->num_sessions = num_sessions;
#endif
SSL_CTX_MUTEX_INIT(ssl_ctx->mutex);
#ifndef CONFIG_SSL_SKELETON_MODE
if (num_sessions)
{
ssl_ctx->ssl_sessions = (SSL_SESSION **)
os_zalloc(num_sessions*sizeof(SSL_SESSION *));
}
#endif
return ssl_ctx;
}
/*
* Remove a client/server context.
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR ssl_ctx_free(SSL_CTX *ssl_ctx)
{
SSL *ssl;
int i;
if (ssl_ctx == NULL)
return;
ssl = ssl_ctx->head;
/* clear out all the ssl entries */
while (ssl)
{
SSL *next = ssl->next;
ssl_free(ssl);
ssl = next;
}
#ifndef CONFIG_SSL_SKELETON_MODE
/* clear out all the sessions */
for (i = 0; i < ssl_ctx->num_sessions; i++)
session_free(ssl_ctx->ssl_sessions, i);
os_free(ssl_ctx->ssl_sessions);
#endif
i = 0;
while (i < CONFIG_SSL_MAX_CERTS && ssl_ctx->certs[i].buf)
{
os_free(ssl_ctx->certs[i].buf);
ssl_ctx->certs[i++].buf = NULL;
}
#ifdef CONFIG_SSL_CERT_VERIFICATION
remove_ca_certs(ssl_ctx->ca_cert_ctx);
#endif
ssl_ctx->chain_length = 0;
SSL_CTX_MUTEX_DESTROY(ssl_ctx->mutex);
// ssl_printf("%s %p\n", __func__,ssl_ctx->rsa_ctx);
RSA_free(ssl_ctx->rsa_ctx);
RNG_terminate();
// ssl_printf("%s %p\n", __func__,ssl_ctx);
os_free(ssl_ctx);
}
/*
* Free any used resources used by this connection.
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR ssl_free(SSL *ssl)
{
SSL_CTX *ssl_ctx;
if (ssl == NULL) /* just ignore null pointers */
return;
/* only notify if we weren't notified first */
/* spec says we must notify when we are dying */
if (!IS_SET_SSL_FLAG(SSL_SENT_CLOSE_NOTIFY))
send_alert(ssl, SSL_ALERT_CLOSE_NOTIFY);
// ssl_printf("%s %d\n", __func__, __LINE__);
ssl_ctx = ssl->ssl_ctx;
SSL_CTX_LOCK(ssl_ctx->mutex);
/* adjust the server SSL list */
if (ssl->prev)
ssl->prev->next = ssl->next;
else
ssl_ctx->head = ssl->next;
if (ssl->next)
ssl->next->prev = ssl->prev;
else
ssl_ctx->tail = ssl->prev;
SSL_CTX_UNLOCK(ssl_ctx->mutex);
/* may already be free - but be sure */
os_free(ssl->encrypt_ctx);
os_free(ssl->decrypt_ctx);
disposable_free(ssl);
#ifdef CONFIG_SSL_CERT_VERIFICATION
x509_free(ssl->x509_ctx);
#endif
os_free(ssl);
}
/*
* Read the SSL connection and send any alerts for various errors.
*/
EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_read(SSL *ssl, uint8_t **in_data)
{
int ret = basic_read(ssl, in_data);
/* check for return code so we can send an alert */
if (ret < SSL_OK && ret != SSL_CLOSE_NOTIFY)
{
if (ret != SSL_ERROR_CONN_LOST)
{
send_alert(ssl, ret);
#ifndef CONFIG_SSL_SKELETON_MODE
/* something nasty happened, so get rid of this session */
kill_ssl_session(ssl->ssl_ctx->ssl_sessions, ssl);
#endif
}
}
return ret;
}
/*
* Write application data to the client
*/
EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_write(SSL *ssl, const uint8_t *out_data, int out_len)
{
int n = out_len, nw, i, tot = 0;
/* maximum size of a TLS packet is around 16kB, so fragment */
do
{
nw = n;
if (nw > RT_MAX_PLAIN_LENGTH) /* fragment if necessary */
nw = RT_MAX_PLAIN_LENGTH;
if ((i = send_packet(ssl, PT_APP_PROTOCOL_DATA,
&out_data[tot], nw)) <= 0)
{
out_len = i; /* an error */
break;
}
tot += i;
n -= i;
} while (n > 0);
return out_len;
}
/**
* Add a certificate to the certificate chain.
*/
int ICACHE_FLASH_ATTR add_cert(SSL_CTX *ssl_ctx, const uint8_t *buf, int len)
{
int ret = SSL_ERROR_NO_CERT_DEFINED, i = 0;
SSL_CERT *ssl_cert;
X509_CTX *cert = NULL;
int offset;
while (ssl_ctx->certs[i].buf && i < CONFIG_SSL_MAX_CERTS)
i++;
if (i == CONFIG_SSL_MAX_CERTS) /* too many certs */
{
#ifdef CONFIG_SSL_FULL_MODE
ssl_printf("Error: maximum number of certs added (%d) - change of "
"compile-time configuration required\n",
CONFIG_SSL_MAX_CERTS);
#endif
goto error;
}
if ((ret = x509_new(buf, &offset, &cert)))
goto error;
#if defined (CONFIG_SSL_FULL_MODE)
if (ssl_ctx->options & SSL_DISPLAY_CERTS)
x509_print(cert, NULL);
#endif
ssl_cert = &ssl_ctx->certs[i];
ssl_cert->size = len;
ssl_cert->buf = (uint8_t *)os_malloc(len);
os_memcpy(ssl_cert->buf, buf, len);
ssl_ctx->chain_length++;
len -= offset;
ret = SSL_OK; /* ok so far */
/* recurse? */
if (len > 0)
{
ret = add_cert(ssl_ctx, &buf[offset], len);
}
error:
x509_free(cert); /* don't need anymore */
return ret;
}
#ifdef CONFIG_SSL_CERT_VERIFICATION
/**
* Add a certificate authority.
*/
int ICACHE_FLASH_ATTR add_cert_auth(SSL_CTX *ssl_ctx, const uint8_t *buf, int len)
{
int ret = SSL_OK; /* ignore errors for now */
int i = 0;
CA_CERT_CTX *ca_cert_ctx;
if (ssl_ctx->ca_cert_ctx == NULL)
ssl_ctx->ca_cert_ctx = (CA_CERT_CTX *)os_zalloc(sizeof(CA_CERT_CTX));
ca_cert_ctx = ssl_ctx->ca_cert_ctx;
while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
i++;
while (len > 0)
{
int offset;
if (i >= CONFIG_X509_MAX_CA_CERTS)
{
#ifdef CONFIG_SSL_FULL_MODE
ssl_printf("Error: maximum number of CA certs added (%d) - change of "
"compile-time configuration required\n",
CONFIG_X509_MAX_CA_CERTS);
#endif
break;
}
/* ignore the return code */
if (x509_new(buf, &offset, &ca_cert_ctx->cert[i]) == X509_OK)
{
#if defined (CONFIG_SSL_FULL_MODE)
if (ssl_ctx->options & SSL_DISPLAY_CERTS)
x509_print(ca_cert_ctx->cert[i], NULL);
#endif
}
i++;
len -= offset;
}
return ret;
}
/*
* Retrieve an X.509 distinguished name component
*/
EXP_FUNC const char * STDCALL ICACHE_FLASH_ATTR ssl_get_cert_dn(const SSL *ssl, int component)
{
if (ssl->x509_ctx == NULL)
return NULL;
switch (component)
{
case SSL_X509_CERT_COMMON_NAME:
return ssl->x509_ctx->cert_dn[X509_COMMON_NAME];
case SSL_X509_CERT_ORGANIZATION:
return ssl->x509_ctx->cert_dn[X509_ORGANIZATION];
case SSL_X509_CERT_ORGANIZATIONAL_NAME:
return ssl->x509_ctx->cert_dn[X509_ORGANIZATIONAL_UNIT];
case SSL_X509_CA_CERT_COMMON_NAME:
return ssl->x509_ctx->ca_cert_dn[X509_COMMON_NAME];
case SSL_X509_CA_CERT_ORGANIZATION:
return ssl->x509_ctx->ca_cert_dn[X509_ORGANIZATION];
case SSL_X509_CA_CERT_ORGANIZATIONAL_NAME:
return ssl->x509_ctx->ca_cert_dn[X509_ORGANIZATIONAL_UNIT];
default:
return NULL;
}
}
/*
* Retrieve a "Subject Alternative Name" from a v3 certificate
*/
EXP_FUNC const char * STDCALL ICACHE_FLASH_ATTR ssl_get_cert_subject_alt_dnsname(const SSL *ssl,
int dnsindex)
{
int i;
if (ssl->x509_ctx == NULL || ssl->x509_ctx->subject_alt_dnsnames == NULL)
return NULL;
for (i = 0; i < dnsindex; ++i)
{
if (ssl->x509_ctx->subject_alt_dnsnames[i] == NULL)
return NULL;
}
return ssl->x509_ctx->subject_alt_dnsnames[dnsindex];
}
#endif /* CONFIG_SSL_CERT_VERIFICATION */
#if 0
/*
* Find an ssl object based on the client's file descriptor.
*/
EXP_FUNC SSL * STDCALL ICACHE_FLASH_ATTR ssl_find(SSL_CTX *ssl_ctx, int client_fd)
{
SSL *ssl;
SSL_CTX_LOCK(ssl_ctx->mutex);
ssl = ssl_ctx->head;
/* search through all the ssl entries */
while (ssl)
{
if (ssl->client_fd == client_fd)
{
SSL_CTX_UNLOCK(ssl_ctx->mutex);
return ssl;
}
ssl = ssl->next;
}
SSL_CTX_UNLOCK(ssl_ctx->mutex);
return NULL;
}
#endif
/*
* Force the client to perform its handshake again.
*/
EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_renegotiate(SSL *ssl)
{
int ret = SSL_OK;
disposable_new(ssl);
#ifdef CONFIG_SSL_ENABLE_CLIENT
if (IS_SET_SSL_FLAG(SSL_IS_CLIENT))
{
ret = do_client_connect(ssl);
}
else
#endif
{
send_packet(ssl, PT_HANDSHAKE_PROTOCOL,
g_hello_request, sizeof(g_hello_request));
SET_SSL_FLAG(SSL_NEED_RECORD);
}
return ret;
}
/**
* @brief Get what we need for key info.
* @param cipher [in] The cipher information we are after
* @param key_size [out] The key size for the cipher
* @param iv_size [out] The iv size for the cipher
* @return The amount of key information we need.
*/
static const cipher_info_t *ICACHE_FLASH_ATTR get_cipher_info(uint8_t cipher)
{
int i;
for (i = 0; i < NUM_PROTOCOLS; i++)
{
if (cipher_info[i].cipher == cipher)
{
return &cipher_info[i];
}
}
return NULL; /* error */
}
#if 0
/*
* Get a new ssl context for a new connection.
*/
SSL *ICACHE_FLASH_ATTR ssl_new(SSL_CTX *ssl_ctx, int client_fd)
{
SSL *ssl = (SSL *)os_zalloc(sizeof(SSL));
ssl->ssl_ctx = ssl_ctx;
ssl->need_bytes = SSL_RECORD_SIZE; /* need a record */
ssl->client_fd = client_fd;
ssl->flag = SSL_NEED_RECORD;
ssl->bm_data = ssl->bm_all_data+BM_RECORD_OFFSET; /* space at the start */
ssl->hs_status = SSL_NOT_OK; /* not connected */
#ifdef CONFIG_ENABLE_VERIFICATION
ssl->ca_cert_ctx = ssl_ctx->ca_cert_ctx;
#endif
disposable_new(ssl);
/* a bit hacky but saves a few bytes of memory */
ssl->flag |= ssl_ctx->options;
SSL_CTX_LOCK(ssl_ctx->mutex);
if (ssl_ctx->head == NULL)
{
ssl_ctx->head = ssl;
ssl_ctx->tail = ssl;
}
else
{
ssl->prev = ssl_ctx->tail;
ssl_ctx->tail->next = ssl;
ssl_ctx->tail = ssl;
}
SSL_CTX_UNLOCK(ssl_ctx->mutex);
return ssl;
}
#endif
/*
* Get a new ssl context for a new connection.(raw api)add by ives 12.12.2013
*/
SSL *ICACHE_FLASH_ATTR ssl_new_context(SSL_CTX *ssl_ctx, struct tcp_pcb *SslClient_pcb)
{
SSL *ssl = (SSL *)os_zalloc(sizeof(SSL));
ssl->ssl_ctx = ssl_ctx;
ssl->need_bytes = SSL_RECORD_SIZE; /* need a record */
//ssl->client_fd = client_fd;annotation by ives 12.12.2013
ssl->SslClient_pcb = SslClient_pcb;
ssl->ssl_pbuf = NULL;
ssl->flag = SSL_NEED_RECORD;
ssl->bm_data = ssl->bm_all_data + BM_RECORD_OFFSET; /* space at the start */
ssl->hs_status = SSL_NOT_OK; /* not connected */
#ifdef CONFIG_ENABLE_VERIFICATION
ssl->ca_cert_ctx = ssl_ctx->ca_cert_ctx;
#endif
disposable_new(ssl);
/* a bit hacky but saves a few bytes of memory */
ssl->flag |= ssl_ctx->options;
SSL_CTX_LOCK(ssl_ctx->mutex);
if (ssl_ctx->head == NULL) {
ssl_ctx->head = ssl;
ssl_ctx->tail = ssl;
} else {
ssl->prev = ssl_ctx->tail;
ssl_ctx->tail->next = ssl;
ssl_ctx->tail = ssl;
}
SSL_CTX_UNLOCK(ssl_ctx->mutex);
return ssl;
}
/*
* Add a private key to a context.
*/
int ICACHE_FLASH_ATTR add_private_key(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj)
{
int ret = SSL_OK;
/* get the private key details */
if (asn1_get_private_key(ssl_obj->buf, ssl_obj->len, &ssl_ctx->rsa_ctx))
{
ret = SSL_ERROR_INVALID_KEY;
goto error;
}
error:
return ret;
}
/**
* Increment the read sequence number (as a 64 bit endian indepenent #)
*/
static void ICACHE_FLASH_ATTR increment_read_sequence(SSL *ssl)
{
int i;
for (i = 7; i >= 0; i--)
{
if (++ssl->read_sequence[i])
break;
}
}
/**
* Increment the read sequence number (as a 64 bit endian indepenent #)
*/
static void ICACHE_FLASH_ATTR increment_write_sequence(SSL *ssl)
{
int i;
for (i = 7; i >= 0; i--)
{
if (++ssl->write_sequence[i])
break;
}
}
/**
* Work out the HMAC digest in a packet.
*/
static void ICACHE_FLASH_ATTR add_hmac_digest(SSL *ssl, int mode, uint8_t *hmac_header,
const uint8_t *buf, int buf_len, uint8_t *hmac_buf)
{
int hmac_len = buf_len + 8 + SSL_RECORD_SIZE;
uint8_t *t_buf = (uint8_t *)os_malloc(hmac_len+10);
os_memcpy(t_buf, (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_WRITE) ?
ssl->write_sequence : ssl->read_sequence, 8);
os_memcpy(&t_buf[8], hmac_header, SSL_RECORD_SIZE);
os_memcpy(&t_buf[8+SSL_RECORD_SIZE], buf, buf_len);
ssl->cipher_info->hmac(t_buf, hmac_len,
(mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_READ) ?
ssl->server_mac : ssl->client_mac,
ssl->cipher_info->digest_size, hmac_buf);
/* add by wujg */
os_free(t_buf);
#if 0
print_blob("record", hmac_header, SSL_RECORD_SIZE);
print_blob("buf", buf, buf_len);
if (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_WRITE)
{
print_blob("write seq", ssl->write_sequence, 8);
}
else
{
print_blob("read seq", ssl->read_sequence, 8);
}
if (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_READ)
{
print_blob("server mac",
ssl->server_mac, ssl->cipher_info->digest_size);
}
else
{
print_blob("client mac",
ssl->client_mac, ssl->cipher_info->digest_size);
}
print_blob("hmac", hmac_buf, SHA1_SIZE);
#endif
}
/**
* Verify that the digest of a packet is correct.
*/
static int ICACHE_FLASH_ATTR verify_digest(SSL *ssl, int mode, const uint8_t *buf, int read_len)
{
uint8_t hmac_buf[SHA1_SIZE];
int hmac_offset;
if (ssl->cipher_info->padding_size)
{
int last_blk_size = buf[read_len-1], i;
hmac_offset = read_len-last_blk_size-ssl->cipher_info->digest_size-1;
/* guard against a timing attack - make sure we do the digest */
if (hmac_offset < 0)
{
hmac_offset = 0;
}
else
{
/* already looked at last byte */
for (i = 1; i < last_blk_size; i++)
{
if (buf[read_len-i] != last_blk_size)
{
hmac_offset = 0;
break;
}
}
}
}
else /* stream cipher */
{
hmac_offset = read_len - ssl->cipher_info->digest_size;
if (hmac_offset < 0)
{
hmac_offset = 0;
}
}
/* sanity check the offset */
ssl->hmac_header[3] = hmac_offset >> 8; /* insert size */
ssl->hmac_header[4] = hmac_offset & 0xff;
add_hmac_digest(ssl, mode, ssl->hmac_header, buf, hmac_offset, hmac_buf);
if (memcmp(hmac_buf, &buf[hmac_offset], ssl->cipher_info->digest_size))
{
return SSL_ERROR_INVALID_HMAC;
}
return hmac_offset;
}
/**
* Add a packet to the end of our sent and received packets, so that we may use
* it to calculate the hash at the end.
*/
void ICACHE_FLASH_ATTR add_packet(SSL *ssl, const uint8_t *pkt, int len)
{
MD5_Update(&ssl->dc->md5_ctx, pkt, len);
SHA1_Update(&ssl->dc->sha1_ctx, pkt, len);
}
/**
* Work out the MD5 PRF.
*/
static void ICACHE_FLASH_ATTR p_hash_md5(const uint8_t *sec, int sec_len,
uint8_t *seed, int seed_len, uint8_t *out, int olen)
{
uint8_t a1[128];
/* A(1) */
ssl_hmac_md5(seed, seed_len, sec, sec_len, a1);
os_memcpy(&a1[MD5_SIZE], seed, seed_len);
ssl_hmac_md5(a1, MD5_SIZE+seed_len, sec, sec_len, out);
while (olen > MD5_SIZE)
{
uint8_t a2[MD5_SIZE];
out += MD5_SIZE;
olen -= MD5_SIZE;
/* A(N) */
ssl_hmac_md5(a1, MD5_SIZE, sec, sec_len, a2);
os_memcpy(a1, a2, MD5_SIZE);
/* work out the actual hash */
ssl_hmac_md5(a1, MD5_SIZE+seed_len, sec, sec_len, out);
}
}
/**
* Work out the SHA1 PRF.
*/
static void ICACHE_FLASH_ATTR p_hash_sha1(const uint8_t *sec, int sec_len,
uint8_t *seed, int seed_len, uint8_t *out, int olen)
{
uint8_t a1[128];
/* A(1) */
ssl_hmac_sha1(seed, seed_len, sec, sec_len, a1);
os_memcpy(&a1[SHA1_SIZE], seed, seed_len);
ssl_hmac_sha1(a1, SHA1_SIZE+seed_len, sec, sec_len, out);
while (olen > SHA1_SIZE)
{
uint8_t a2[SHA1_SIZE];
out += SHA1_SIZE;
olen -= SHA1_SIZE;
/* A(N) */
ssl_hmac_sha1(a1, SHA1_SIZE, sec, sec_len, a2);
os_memcpy(a1, a2, SHA1_SIZE);
/* work out the actual hash */
ssl_hmac_sha1(a1, SHA1_SIZE+seed_len, sec, sec_len, out);
}
}
/**
* Work out the PRF.
*/
static void ICACHE_FLASH_ATTR prf(const uint8_t *sec, int sec_len, uint8_t *seed, int seed_len,
uint8_t *out, int olen)
{
int len, i;
const uint8_t *S1, *S2;
uint8_t xbuf[256]; /* needs to be > the amount of key data */
uint8_t ybuf[256]; /* needs to be > the amount of key data */
len = sec_len/2;
S1 = sec;
S2 = &sec[len];
len += (sec_len & 1); /* add for odd, make longer */
p_hash_md5(S1, len, seed, seed_len, xbuf, olen);
p_hash_sha1(S2, len, seed, seed_len, ybuf, olen);
for (i = 0; i < olen; i++)
out[i] = xbuf[i] ^ ybuf[i];
}
/**
* Generate a master secret based on the client/server random data and the
* premaster secret.
*/
void ICACHE_FLASH_ATTR generate_master_secret(SSL *ssl, const uint8_t *premaster_secret)
{
uint8_t buf[128]; /* needs to be > 13+32+32 in size */
os_strcpy((char *)buf, "master secret");
os_memcpy(&buf[13], ssl->dc->client_random, SSL_RANDOM_SIZE);
os_memcpy(&buf[45], ssl->dc->server_random, SSL_RANDOM_SIZE);
prf(premaster_secret, SSL_SECRET_SIZE, buf, 77, ssl->dc->master_secret,
SSL_SECRET_SIZE);
}
/**
* Generate a 'random' blob of data used for the generation of keys.
*/
static void ICACHE_FLASH_ATTR generate_key_block(uint8_t *client_random, uint8_t *server_random,
uint8_t *master_secret, uint8_t *key_block, int key_block_size)
{
uint8_t buf[128];
os_strcpy((char *)buf, "key expansion");
os_memcpy(&buf[13], server_random, SSL_RANDOM_SIZE);
os_memcpy(&buf[45], client_random, SSL_RANDOM_SIZE);
prf(master_secret, SSL_SECRET_SIZE, buf, 77, key_block, key_block_size);
}
/**
* Calculate the digest used in the finished message. This function also
* doubles up as a certificate verify function.
*/
void ICACHE_FLASH_ATTR finished_digest(SSL *ssl, const char *label, uint8_t *digest)
{
uint8_t mac_buf[128];
uint8_t *q = mac_buf;
MD5_CTX md5_ctx = ssl->dc->md5_ctx;
SHA1_CTX sha1_ctx = ssl->dc->sha1_ctx;
if (label)
{
os_strcpy((char *)q, label);
q += os_strlen(label);
}
MD5_Final(q, &md5_ctx);
q += MD5_SIZE;
SHA1_Final(q, &sha1_ctx);
q += SHA1_SIZE;
if (label)
{
prf(ssl->dc->master_secret, SSL_SECRET_SIZE, mac_buf, (int)(q-mac_buf),
digest, SSL_FINISHED_HASH_SIZE);
}
else /* for use in a certificate verify */
{
os_memcpy(digest, mac_buf, MD5_SIZE + SHA1_SIZE);
}
#if 0
printf("label: %s\n", label);
print_blob("master secret", ssl->dc->master_secret, 48);
print_blob("mac_buf", mac_buf, q-mac_buf);
print_blob("finished digest", digest, SSL_FINISHED_HASH_SIZE);
#endif
}
/**
* Retrieve (and initialise) the context of a cipher.
*/
static void *ICACHE_FLASH_ATTR crypt_new(SSL *ssl, uint8_t *key, uint8_t *iv, int is_decrypt)
{
switch (ssl->cipher)
{
#ifndef CONFIG_SSL_SKELETON_MODE
case SSL_AES128_SHA:
{
AES_CTX *aes_ctx = (AES_CTX *)os_malloc(sizeof(AES_CTX));
AES_set_key(aes_ctx, key, iv, AES_MODE_128);
if (is_decrypt)
{
AES_convert_key(aes_ctx);
}
return (void *)aes_ctx;
}
case SSL_AES256_SHA:
{
AES_CTX *aes_ctx = (AES_CTX *)os_malloc(sizeof(AES_CTX));
AES_set_key(aes_ctx, key, iv, AES_MODE_256);
if (is_decrypt)
{
AES_convert_key(aes_ctx);
}
return (void *)aes_ctx;
}
case SSL_RC4_128_MD5:
#endif
case SSL_RC4_128_SHA:
{
RC4_CTX *rc4_ctx = (RC4_CTX *)os_malloc(sizeof(RC4_CTX));
RC4_setup(rc4_ctx, key, 16);
return (void *)rc4_ctx;
}
}
return NULL; /* its all gone wrong */
}
/**
* Send a packet over the socket.
*/
static err_t ICACHE_FLASH_ATTR send_raw_packet(SSL *ssl, uint8_t protocol)
{
uint8_t *rec_buf = ssl->bm_all_data;
int pkt_size = SSL_RECORD_SIZE + ssl->bm_index;
int Length = 0;
//int ret = SSL_OK;
err_t Err = ERR_OK;
rec_buf[0] = protocol;
rec_buf[1] = 0x03; /* version = 3.1 or higher */
rec_buf[2] = ssl->version & 0x0f;
rec_buf[3] = ssl->bm_index >> 8;
rec_buf[4] = ssl->bm_index & 0xff;
//DISPLAY_BYTES(ssl, "sending %d bytes", ssl->bm_all_data,
// pkt_size, pkt_size);
ssl_printf("send_raw_packet pkt_size %d\n", pkt_size);
if(tcp_sndbuf(ssl->SslClient_pcb) < pkt_size) {
Length = tcp_sndbuf(ssl->SslClient_pcb);
} else {
Length = pkt_size;
}
if(Length > 2 * ssl->SslClient_pcb->mss) {
Length = 2 * ssl->SslClient_pcb->mss;
}
do {
Err = tcp_write(ssl->SslClient_pcb, &ssl->bm_all_data[0], Length, 0);
if (Err == ERR_MEM) {
Length /= 2;
}
} while(Err == ERR_MEM && Length > 1);
ssl_printf("send_raw_packet Length %d\n", Length);
if (Err == ERR_OK) {
Err = tcp_output(ssl->SslClient_pcb);
}
SET_SSL_FLAG(SSL_NEED_RECORD); /* reset for next time */
//ssl->bm_index = 0;
if (protocol != PT_APP_PROTOCOL_DATA) {
/* always return SSL_OK during handshake */
ssl->bm_index = 0;
Err = SSL_OK;
}
return Err;
}
/**
* Send an encrypted packet with padding bytes if necessary.
*/
int ICACHE_FLASH_ATTR send_packet(SSL *ssl, uint8_t protocol, const uint8_t *in, int length)
{
int ret, msg_length = 0;
/* if our state is bad, don't bother */
if (ssl->hs_status == SSL_ERROR_DEAD)
return SSL_ERROR_CONN_LOST;
if (in) /* has the buffer already been initialised? */
{
os_memcpy(ssl->bm_data, in, length);
}
msg_length += length;
if (IS_SET_SSL_FLAG(SSL_TX_ENCRYPTED))
{
int mode = IS_SET_SSL_FLAG(SSL_IS_CLIENT) ?
SSL_CLIENT_WRITE : SSL_SERVER_WRITE;
uint8_t hmac_header[SSL_RECORD_SIZE] =
{
protocol,
0x03, /* version = 3.1 or higher */
ssl->version & 0x0f,
msg_length >> 8,
msg_length & 0xff
};
if (protocol == PT_HANDSHAKE_PROTOCOL)
{
//DISPLAY_STATE(ssl, 1, ssl->bm_data[0], 0);
if (ssl->bm_data[0] != HS_HELLO_REQUEST)
{
add_packet(ssl, ssl->bm_data, msg_length);
}
}
/* add the packet digest */
add_hmac_digest(ssl, mode, hmac_header, ssl->bm_data, msg_length,
&ssl->bm_data[msg_length]);
msg_length += ssl->cipher_info->digest_size;
/* add padding? */
if (ssl->cipher_info->padding_size)
{
int last_blk_size = msg_length%ssl->cipher_info->padding_size;
int pad_bytes = ssl->cipher_info->padding_size - last_blk_size;
/* ensure we always have at least 1 padding byte */
if (pad_bytes == 0)
pad_bytes += ssl->cipher_info->padding_size;
os_memset(&ssl->bm_data[msg_length], pad_bytes-1, pad_bytes);
msg_length += pad_bytes;
}
//DISPLAY_BYTES(ssl, "unencrypted write", ssl->bm_data, msg_length);
increment_write_sequence(ssl);
/* add the explicit IV for TLS1.1 */
if (ssl->version >= SSL_PROTOCOL_VERSION1_1 &&
ssl->cipher_info->iv_size)
{
uint8_t iv_size = ssl->cipher_info->iv_size;
uint8_t *t_buf = (uint8_t *)os_malloc(msg_length + iv_size);
os_memcpy(t_buf + iv_size, ssl->bm_data, msg_length);
get_random(iv_size, t_buf);
msg_length += iv_size;
os_memcpy(ssl->bm_data, t_buf, msg_length);
os_free(t_buf); /* add by wujg */
}
/* now encrypt the packet */
ssl->cipher_info->encrypt(ssl->encrypt_ctx, ssl->bm_data,
ssl->bm_data, msg_length);
}
else if (protocol == PT_HANDSHAKE_PROTOCOL)
{
//DISPLAY_STATE(ssl, 1, ssl->bm_data[0], 0);
if (ssl->bm_data[0] != HS_HELLO_REQUEST)
{
add_packet(ssl, ssl->bm_data, length);
}
}
ssl->bm_index = msg_length;
if ((ret = send_raw_packet(ssl, protocol)) <= 0)
return ret;
return length; /* just return what we wanted to send */
}
/**
* Work out the cipher keys we are going to use for this session based on the
* master secret.
*/
static int ICACHE_FLASH_ATTR set_key_block(SSL *ssl, int is_write)
{
const cipher_info_t *ciph_info = get_cipher_info(ssl->cipher);
uint8_t *q;
uint8_t client_key[32], server_key[32]; /* big enough for AES256 */
uint8_t client_iv[16], server_iv[16]; /* big enough for AES128/256 */
int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT);
if (ciph_info == NULL)
return -1;
/* only do once in a handshake */
if (ssl->dc->key_block == NULL)
{
ssl->dc->key_block = (uint8_t *)os_malloc(ciph_info->key_block_size);
#if 0
print_blob("client", ssl->dc->client_random, 32);
print_blob("server", ssl->dc->server_random, 32);
print_blob("master", ssl->dc->master_secret, SSL_SECRET_SIZE);
#endif
generate_key_block(ssl->dc->client_random, ssl->dc->server_random,
ssl->dc->master_secret, ssl->dc->key_block,
ciph_info->key_block_size);
#if 0
print_blob("keyblock", ssl->dc->key_block, ciph_info->key_block_size);
#endif
}
q = ssl->dc->key_block;
if ((is_client && is_write) || (!is_client && !is_write))
{
os_memcpy(ssl->client_mac, q, ciph_info->digest_size);
}
q += ciph_info->digest_size;
if ((!is_client && is_write) || (is_client && !is_write))
{
os_memcpy(ssl->server_mac, q, ciph_info->digest_size);
}
q += ciph_info->digest_size;
os_memcpy(client_key, q, ciph_info->key_size);
q += ciph_info->key_size;
os_memcpy(server_key, q, ciph_info->key_size);
q += ciph_info->key_size;
#ifndef CONFIG_SSL_SKELETON_MODE
if (ciph_info->iv_size) /* RC4 has no IV, AES does */
{
os_memcpy(client_iv, q, ciph_info->iv_size);
q += ciph_info->iv_size;
os_memcpy(server_iv, q, ciph_info->iv_size);
q += ciph_info->iv_size;
}
#endif
os_free(is_write ? ssl->encrypt_ctx : ssl->decrypt_ctx);
/* now initialise the ciphers */
if (is_client)
{
finished_digest(ssl, server_finished, ssl->dc->final_finish_mac);
if (is_write)
ssl->encrypt_ctx = crypt_new(ssl, client_key, client_iv, 0);
else
ssl->decrypt_ctx = crypt_new(ssl, server_key, server_iv, 1);
}
else
{
finished_digest(ssl, client_finished, ssl->dc->final_finish_mac);
if (is_write)
ssl->encrypt_ctx = crypt_new(ssl, server_key, server_iv, 0);
else
ssl->decrypt_ctx = crypt_new(ssl, client_key, client_iv, 1);
}
ssl->cipher_info = ciph_info;
return 0;
}
/**
* Read the SSL connection.
*/
int ICACHE_FLASH_ATTR basic_read(SSL *ssl, uint8_t **in_data)
{
int ret = SSL_OK;
int j,i = 0;
int read_len, is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT);
uint8_t *buf = ssl->bm_data;
uint8_t *read_buf = NULL;
uint8_t *pread_buf = NULL;
u16_t recvlength = 0;
read_buf =(uint8_t*)os_zalloc(ssl->ssl_pbuf->len + 1);
pread_buf = read_buf;
if (pread_buf != NULL){
recvlength = pbuf_copy_partial(ssl->ssl_pbuf, read_buf,ssl->ssl_pbuf->len,0);
}
if (recvlength != 0){
do{
// ssl_printf("basic_read ssl->bm_read_index %d\n", ssl->bm_read_index);
// ssl_printf("basic_read ssl->need_bytes %d\n", ssl->need_bytes);
// ssl_printf("basic_read ssl->got_bytes %d\n", ssl->got_bytes);
read_len = ssl->need_bytes - ssl->got_bytes;
if (read_len >= recvlength){
read_len = recvlength;
}
os_memcpy(&buf[ssl->bm_read_index],read_buf, read_len);
// ssl_printf("basic_read read_len %d\n", read_len);
// for (i = ssl->bm_read_index; i < (ssl->bm_read_index + read_len); i ++){
// ssl_printf("%2x ",buf[i]);
// if ((i + 1) % 16 == 0)
// ssl_printf("\n");
// }
// ssl_printf("\n");
read_buf += read_len;
recvlength -= read_len;
// ssl_printf("basic_read %d %d\n", __LINE__, recvlength);
/* connection has gone, so die */
if (read_len <= 0)
{
ret = SSL_ERROR_CONN_LOST;
ssl->hs_status = SSL_ERROR_DEAD; /* make sure it stays dead */
goto error;
}
//DISPLAY_BYTES(ssl, "received %d bytes",
// &ssl->bm_data[ssl->bm_read_index], read_len, read_len);
ssl->got_bytes += read_len;
ssl->bm_read_index += read_len;
/* haven't quite got what we want, so try again later */
if (ssl->got_bytes < ssl->need_bytes){
// ssl_printf("basic_read %d %p\n", __LINE__, pread_buf);
os_free(pread_buf);
pread_buf = NULL;
return SSL_OK;
}
read_len = ssl->got_bytes;
ssl->got_bytes = 0;
if (IS_SET_SSL_FLAG(SSL_NEED_RECORD))
{
/* check for sslv2 "client hello" */
if (buf[0] & 0x80 && buf[2] == 1)
{
#ifdef CONFIG_SSL_ENABLE_V23_HANDSHAKE
uint8_t version = (buf[3] << 4) + buf[4];
DISPLAY_BYTES(ssl, "ssl2 record", buf, 5);
/* should be v3.1 (TLSv1) or better */
ssl->version = ssl->client_version = version;
if (version > SSL_PROTOCOL_VERSION_MAX)
{
/* use client's version */
ssl->version = SSL_PROTOCOL_VERSION_MAX;
}
else if (version < SSL_PROTOCOL_MIN_VERSION)
{
ret = SSL_ERROR_INVALID_VERSION;
ssl_display_error(ret);
return ret;
}
add_packet(ssl, &buf[2], 3);
ret = process_sslv23_client_hello(ssl);
#else
ssl_printf("Error: no SSLv23 handshaking allowed\n"); //TTY_FLUSH();
ret = SSL_ERROR_NOT_SUPPORTED;
#endif
goto error; /* not an error - just get out of here */
}
ssl->need_bytes = (buf[3] << 8) + buf[4];
/* do we violate the spec with the message size? */
if (ssl->need_bytes > RT_MAX_PLAIN_LENGTH+RT_EXTRA-BM_RECORD_OFFSET)
{
ret = SSL_ERROR_INVALID_PROT_MSG;
recvlength = 0;
os_printf("we violate the spec with the message size\n");
goto error;
}
CLR_SSL_FLAG(SSL_NEED_RECORD);
os_memcpy(ssl->hmac_header, buf, 3); /* store for hmac */
ssl->record_type = buf[0];
goto error; /* no error, we're done */
}
/* for next time - just do it now in case of an error */
SET_SSL_FLAG(SSL_NEED_RECORD);
ssl->need_bytes = SSL_RECORD_SIZE;
/* decrypt if we need to */
if (IS_SET_SSL_FLAG(SSL_RX_ENCRYPTED))
{
ssl->cipher_info->decrypt(ssl->decrypt_ctx, buf, buf, read_len);
if (ssl->version >= SSL_PROTOCOL_VERSION1_1 &&
ssl->cipher_info->iv_size)
{
buf += ssl->cipher_info->iv_size;
read_len -= ssl->cipher_info->iv_size;
}
read_len = verify_digest(ssl,
is_client ? SSL_CLIENT_READ : SSL_SERVER_READ, buf, read_len);
/* does the hmac work? */
if (read_len < 0)
{
ret = read_len;
goto error;
}
//DISPLAY_BYTES(ssl, "decrypted", buf, read_len);
increment_read_sequence(ssl);
}
/* The main part of the SSL packet */
//ssl_printf("basic_read %d %x %p\n", __LINE__, ssl->record_type, in_data);
switch (ssl->record_type)
{
case PT_HANDSHAKE_PROTOCOL:
if (ssl->dc != NULL)
{
ssl->dc->bm_proc_index = 0;
ret = do_handshake(ssl, buf, read_len);
}
else /* no client renegotiation allowed */
{
ret = SSL_ERROR_NO_CLIENT_RENOG;
goto error;
}
break;
case PT_CHANGE_CIPHER_SPEC:
if (ssl->next_state != HS_FINISHED)
{
ret = SSL_ERROR_INVALID_HANDSHAKE;
goto error;
}
/* all encrypted from now on */
SET_SSL_FLAG(SSL_RX_ENCRYPTED);
if (set_key_block(ssl, 0) < 0)
{
ret = SSL_ERROR_INVALID_HANDSHAKE;
goto error;
}
os_memset(ssl->read_sequence, 0, 8);
break;
case PT_APP_PROTOCOL_DATA:
if (in_data)
{
*in_data = buf; /* point to the work buffer */
(*in_data)[read_len] = 0; /* null terminate just in case */
}
ret = read_len;
recvlength = 0;
break;
case PT_ALERT_PROTOCOL:
/* return the alert # with alert bit set */
if(buf[0] == SSL_ALERT_TYPE_WARNING &&
buf[1] == SSL_ALERT_CLOSE_NOTIFY)
{
ret = SSL_CLOSE_NOTIFY;
//send_alert(ssl, SSL_ALERT_CLOSE_NOTIFY);
SET_SSL_FLAG(SSL_SENT_CLOSE_NOTIFY);
}
else
{
ret = -buf[1];
//DISPLAY_ALERT(ssl, buf[1]);
}
break;
default:
ret = SSL_ERROR_INVALID_PROT_MSG;
break;
}
error:
ssl->bm_read_index = 0; /* reset to go again */
if (ret < SSL_OK && in_data)/* if all wrong, then clear this buffer ptr */
*in_data = NULL;
}while(recvlength != 0);
}else{
ssl_printf("%s %d %d\n", __func__, __LINE__,recvlength);
}
os_free(pread_buf);
pread_buf = NULL;
return ret;
}
/**
* Do some basic checking of data and then perform the appropriate handshaking.
*/
static int ICACHE_FLASH_ATTR do_handshake(SSL *ssl, uint8_t *buf, int read_len)
{
int hs_len = (buf[2]<<8) + buf[3];
uint8_t handshake_type = buf[0];
int ret = SSL_OK;
int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT);
/* some integrity checking on the handshake */
PARANOIA_CHECK(read_len-SSL_HS_HDR_SIZE, hs_len);
if (handshake_type != ssl->next_state)
{
/* handle a special case on the client */
if (!is_client || handshake_type != HS_CERT_REQ ||
ssl->next_state != HS_SERVER_HELLO_DONE)
{
ret = SSL_ERROR_INVALID_HANDSHAKE;
goto error;
}
}
hs_len += SSL_HS_HDR_SIZE; /* adjust for when adding packets */
ssl->bm_index = hs_len; /* store the size and check later */
//DISPLAY_STATE(ssl, 0, handshake_type, 0);
if (handshake_type != HS_CERT_VERIFY && handshake_type != HS_HELLO_REQUEST)
add_packet(ssl, buf, hs_len);
#if defined(CONFIG_SSL_ENABLE_CLIENT)
ret = is_client ?
do_clnt_handshake(ssl, handshake_type, buf, hs_len) :
do_svr_handshake(ssl, handshake_type, buf, hs_len);
#else
ret = do_svr_handshake(ssl, handshake_type, buf, hs_len);
#endif
/* just use recursion to get the rest */
if (hs_len < read_len && ret == SSL_OK)
ret = do_handshake(ssl, &buf[hs_len], read_len-hs_len);
error:
return ret;
}
/**
* Sends the change cipher spec message. We have just read a finished message
* from the client.
*/
int ICACHE_FLASH_ATTR send_change_cipher_spec(SSL *ssl)
{
int ret = send_packet(ssl, PT_CHANGE_CIPHER_SPEC,
g_chg_cipher_spec_pkt, sizeof(g_chg_cipher_spec_pkt));
SET_SSL_FLAG(SSL_TX_ENCRYPTED);
if (ret >= 0 && set_key_block(ssl, 1) < 0)
ret = SSL_ERROR_INVALID_HANDSHAKE;
os_memset(ssl->write_sequence, 0, 8);
return ret;
}
/**
* Send a "finished" message
*/
int ICACHE_FLASH_ATTR send_finished(SSL *ssl)
{
uint8_t buf[SSL_FINISHED_HASH_SIZE+4] = {
HS_FINISHED, 0, 0, SSL_FINISHED_HASH_SIZE };
/* now add the finished digest mac (12 bytes) */
finished_digest(ssl,
IS_SET_SSL_FLAG(SSL_IS_CLIENT) ?
client_finished : server_finished, &buf[4]);
#ifndef CONFIG_SSL_SKELETON_MODE
/* store in the session cache */
if (!IS_SET_SSL_FLAG(SSL_SESSION_RESUME) && ssl->ssl_ctx->num_sessions)
{
os_memcpy(ssl->session->master_secret,
ssl->dc->master_secret, SSL_SECRET_SIZE);
}
#endif
return send_packet(ssl, PT_HANDSHAKE_PROTOCOL,
buf, SSL_FINISHED_HASH_SIZE+4);
}
/**
* Send an alert message.
* Return 1 if the alert was an "error".
*/
int ICACHE_FLASH_ATTR send_alert(SSL *ssl, int error_code)
{
int alert_num = 0;
int is_warning = 0;
uint8_t buf[2];
/* Don't bother we're already dead */
if (ssl->hs_status == SSL_ERROR_DEAD)
{
return SSL_ERROR_CONN_LOST;
}
#ifdef CONFIG_SSL_FULL_MODE
//if (IS_SET_SSL_FLAG(SSL_DISPLAY_STATES))
//ssl_display_error(error_code);
#endif
switch (error_code)
{
case SSL_ALERT_CLOSE_NOTIFY:
is_warning = 1;
alert_num = SSL_ALERT_CLOSE_NOTIFY;
break;
case SSL_ERROR_CONN_LOST: /* don't send alert just yet */
is_warning = 1;
break;
case SSL_ERROR_INVALID_HANDSHAKE:
case SSL_ERROR_INVALID_PROT_MSG:
alert_num = SSL_ALERT_HANDSHAKE_FAILURE;
break;
case SSL_ERROR_INVALID_HMAC:
case SSL_ERROR_FINISHED_INVALID:
alert_num = SSL_ALERT_BAD_RECORD_MAC;
break;
case SSL_ERROR_INVALID_VERSION:
alert_num = SSL_ALERT_INVALID_VERSION;
break;
case SSL_ERROR_INVALID_SESSION:
case SSL_ERROR_NO_CIPHER:
case SSL_ERROR_INVALID_KEY:
alert_num = SSL_ALERT_ILLEGAL_PARAMETER;
break;
case SSL_ERROR_BAD_CERTIFICATE:
alert_num = SSL_ALERT_BAD_CERTIFICATE;
break;
case SSL_ERROR_NO_CLIENT_RENOG:
alert_num = SSL_ALERT_NO_RENEGOTIATION;
break;
default:
/* a catch-all for any badly verified certificates */
alert_num = (error_code <= SSL_X509_OFFSET) ?
SSL_ALERT_BAD_CERTIFICATE : SSL_ALERT_UNEXPECTED_MESSAGE;
break;
}
buf[0] = is_warning ? 1 : 2;
buf[1] = alert_num;
send_packet(ssl, PT_ALERT_PROTOCOL, buf, sizeof(buf));
//DISPLAY_ALERT(ssl, alert_num);
return is_warning ? 0 : 1;
}
/**
* Process a client finished message.
*/
int ICACHE_FLASH_ATTR process_finished(SSL *ssl, uint8_t *buf, int hs_len)
{
int ret = SSL_OK;
int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT);
int resume = IS_SET_SSL_FLAG(SSL_SESSION_RESUME);
PARANOIA_CHECK(ssl->bm_index, SSL_FINISHED_HASH_SIZE+4);
/* check that we all work before we continue */
if (os_memcmp(ssl->dc->final_finish_mac, &buf[4], SSL_FINISHED_HASH_SIZE))
return SSL_ERROR_FINISHED_INVALID;
if ((!is_client && !resume) || (is_client && resume))
{
if ((ret = send_change_cipher_spec(ssl)) == SSL_OK)
ret = send_finished(ssl);
}
/* if we ever renegotiate */
ssl->next_state = is_client ? HS_HELLO_REQUEST : HS_CLIENT_HELLO;
ssl->hs_status = ret; /* set the final handshake status */
error:
return ret;
}
/**
* Send a certificate.
*/
int ICACHE_FLASH_ATTR send_certificate(SSL *ssl)
{
int i = 0;
uint8_t *buf = ssl->bm_data;
int offset = 7;
int chain_length;
buf[0] = HS_CERTIFICATE;
buf[1] = 0;
buf[4] = 0;
while (i < ssl->ssl_ctx->chain_length)
{
SSL_CERT *cert = &ssl->ssl_ctx->certs[i];
buf[offset++] = 0;
buf[offset++] = cert->size >> 8; /* cert 1 length */
buf[offset++] = cert->size & 0xff;
os_memcpy(&buf[offset], cert->buf, cert->size);
offset += cert->size;
i++;
}
chain_length = offset - 7;
buf[5] = chain_length >> 8; /* cert chain length */
buf[6] = chain_length & 0xff;
chain_length += 3;
buf[2] = chain_length >> 8; /* handshake length */
buf[3] = chain_length & 0xff;
ssl->bm_index = offset;
return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset);
}
/**
* Create a blob of memory that we'll get rid of once the handshake is
* complete.
*/
void ICACHE_FLASH_ATTR disposable_new(SSL *ssl)
{
if (ssl->dc == NULL)
{
ssl->dc = (DISPOSABLE_CTX *)os_zalloc(sizeof(DISPOSABLE_CTX));
MD5_Init(&ssl->dc->md5_ctx);
SHA1_Init(&ssl->dc->sha1_ctx);
}
}
/**
* Remove the temporary blob of memory.
*/
void ICACHE_FLASH_ATTR disposable_free(SSL *ssl)
{
if (ssl->dc)
{
os_free(ssl->dc->key_block);
os_memset(ssl->dc, 0, sizeof(DISPOSABLE_CTX));
os_free(ssl->dc);
ssl->dc = NULL;
}
}
#ifndef CONFIG_SSL_SKELETON_MODE /* no session resumption in this mode */
/**
* Find if an existing session has the same session id. If so, use the
* master secret from this session for session resumption.
*/
SSL_SESSION *ICACHE_FLASH_ATTR ssl_session_update(int max_sessions, SSL_SESSION *ssl_sessions[],
SSL *ssl, const uint8_t *session_id)
{
time_t tm = 0; //time(NULL); wujg
time_t oldest_sess_time = tm;
SSL_SESSION *oldest_sess = NULL;
int i;
/* no sessions? Then bail */
if (max_sessions == 0)
return NULL;
SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
if (session_id)
{
for (i = 0; i < max_sessions; i++)
{
if (ssl_sessions[i])
{
/* kill off any expired sessions (including those in
the future) */
if ((tm > ssl_sessions[i]->conn_time + SSL_EXPIRY_TIME) ||
(tm < ssl_sessions[i]->conn_time))
{
session_free(ssl_sessions, i);
continue;
}
/* if the session id matches, it must still be less than
the expiry time */
if (os_memcmp(ssl_sessions[i]->session_id, session_id,
SSL_SESSION_ID_SIZE) == 0)
{
ssl->session_index = i;
os_memcpy(ssl->dc->master_secret,
ssl_sessions[i]->master_secret, SSL_SECRET_SIZE);
SET_SSL_FLAG(SSL_SESSION_RESUME);
SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
return ssl_sessions[i]; /* a session was found */
}
}
}
}
/* If we've got here, no matching session was found - so create one */
for (i = 0; i < max_sessions; i++)
{
if (ssl_sessions[i] == NULL)
{
/* perfect, this will do */
ssl_sessions[i] = (SSL_SESSION *)os_zalloc(sizeof(SSL_SESSION));
ssl_sessions[i]->conn_time = tm;
ssl->session_index = i;
SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
return ssl_sessions[i]; /* return the session object */
}
else if (ssl_sessions[i]->conn_time <= oldest_sess_time)
{
/* find the oldest session */
oldest_sess_time = ssl_sessions[i]->conn_time;
oldest_sess = ssl_sessions[i];
ssl->session_index = i;
}
}
/* ok, we've used up all of our sessions. So blow the oldest session away */
oldest_sess->conn_time = tm;
os_memset(oldest_sess->session_id, 0, sizeof(SSL_SESSION_ID_SIZE));
os_memset(oldest_sess->master_secret, 0, sizeof(SSL_SECRET_SIZE));
SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
return oldest_sess;
}
/**
* Free an existing session.
*/
static void ICACHE_FLASH_ATTR session_free(SSL_SESSION *ssl_sessions[], int sess_index)
{
if (ssl_sessions[sess_index])
{
os_free(ssl_sessions[sess_index]);
ssl_sessions[sess_index] = NULL;
}
}
/**
* This ssl object doesn't want this session anymore.
*/
void ICACHE_FLASH_ATTR kill_ssl_session(SSL_SESSION **ssl_sessions, SSL *ssl)
{
SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
if (ssl->ssl_ctx->num_sessions)
{
session_free(ssl_sessions, ssl->session_index);
ssl->session = NULL;
}
SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
}
#endif /* CONFIG_SSL_SKELETON_MODE */
/*
* Get the session id for a handshake. This will be a 32 byte sequence.
*/
EXP_FUNC const uint8_t * STDCALL ICACHE_FLASH_ATTR ssl_get_session_id(const SSL *ssl)
{
return ssl->session_id;
}
/*
* Get the session id size for a handshake.
*/
EXP_FUNC uint8_t STDCALL ICACHE_FLASH_ATTR ssl_get_session_id_size(const SSL *ssl)
{
return ssl->sess_id_size;
}
/*
* Return the cipher id (in the SSL form).
*/
EXP_FUNC uint8_t STDCALL ICACHE_FLASH_ATTR ssl_get_cipher_id(const SSL *ssl)
{
return ssl->cipher;
}
/*
* Return the status of the handshake.
*/
EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_handshake_status(const SSL *ssl)
{
return ssl->hs_status;
}
/*
* Retrieve various parameters about the SSL engine.
*/
EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_get_config(int offset)
{
switch (offset)
{
/* return the appropriate build mode */
case SSL_BUILD_MODE:
#if defined(CONFIG_SSL_FULL_MODE)
return SSL_BUILD_FULL_MODE;
#elif defined(CONFIG_SSL_ENABLE_CLIENT)
return SSL_BUILD_ENABLE_CLIENT;
#elif defined(CONFIG_ENABLE_VERIFICATION)
return SSL_BUILD_ENABLE_VERIFICATION;
#elif defined(CONFIG_SSL_SERVER_ONLY )
return SSL_BUILD_SERVER_ONLY;
#else
return SSL_BUILD_SKELETON_MODE;
#endif
case SSL_MAX_CERT_CFG_OFFSET:
return CONFIG_SSL_MAX_CERTS;
#ifdef CONFIG_SSL_CERT_VERIFICATION
case SSL_MAX_CA_CERT_CFG_OFFSET:
return CONFIG_X509_MAX_CA_CERTS;
#endif
#ifdef CONFIG_SSL_HAS_PEM
case SSL_HAS_PEM:
return 1;
#endif
default:
return 0;
}
}
#ifdef CONFIG_SSL_CERT_VERIFICATION
/**
* Authenticate a received certificate.
*/
EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_verify_cert(const SSL *ssl)
{
int ret;
SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
ret = x509_verify(ssl->ssl_ctx->ca_cert_ctx, ssl->x509_ctx);
SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
if (ret) /* modify into an SSL error type */
{
ret = SSL_X509_ERROR(ret);
}
return ret;
}
/**
* Process a certificate message.
*/
int ICACHE_FLASH_ATTR process_certificate(SSL *ssl, X509_CTX **x509_ctx)
{
int ret = SSL_OK;
uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index];
int pkt_size = ssl->bm_index;
int cert_size, offset = 5;
int total_cert_size = (buf[offset]<<8) + buf[offset+1];
int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT);
X509_CTX **chain = x509_ctx;
offset += 2;
PARANOIA_CHECK(total_cert_size, offset);
while (offset < total_cert_size)
{
offset++; /* skip empty char */
cert_size = (buf[offset]<<8) + buf[offset+1];
offset += 2;
if (x509_new(&buf[offset], NULL, chain))
{
ret = SSL_ERROR_BAD_CERTIFICATE;
goto error;
}
chain = &((*chain)->next);
offset += cert_size;
}
PARANOIA_CHECK(pkt_size, offset);
/* if we are client we can do the verify now or later */
if (is_client && !IS_SET_SSL_FLAG(SSL_SERVER_VERIFY_LATER))
{
ret = ssl_verify_cert(ssl);
}
ssl->next_state = is_client ? HS_SERVER_HELLO_DONE : HS_CLIENT_KEY_XCHG;
ssl->dc->bm_proc_index += offset;
error:
return ret;
}
#endif /* CONFIG_SSL_CERT_VERIFICATION */
/**
* Debugging routine to display SSL handshaking stuff.
*/
#ifdef CONFIG_SSL_FULL_MODE
/**
* Debugging routine to display SSL states.
*/
#if 0
void ICACHE_FLASH_ATTR DISPLAY_STATE(SSL *ssl, int is_send, uint8_t state, int not_ok)
{
const char *str;
if (!IS_SET_SSL_FLAG(SSL_DISPLAY_STATES))
return;
ssl_printf(not_ok ? "Error - invalid State:\t" : "State:\t");
ssl_printf(is_send ? "sending " : "receiving ");
switch (state)
{
case HS_HELLO_REQUEST:
str = "Hello Request (0)";
break;
case HS_CLIENT_HELLO:
str = "Client Hello (1)";
break;
case HS_SERVER_HELLO:
str = "Server Hello (2)";
break;
case HS_CERTIFICATE:
str = "Certificate (11)";
break;
case HS_SERVER_KEY_XCHG:
str = "Certificate Request (12)";
break;
case HS_CERT_REQ:
str = "Certificate Request (13)";
break;
case HS_SERVER_HELLO_DONE:
str = "Server Hello Done (14)";
break;
case HS_CERT_VERIFY:
str = "Certificate Verify (15)";
break;
case HS_CLIENT_KEY_XCHG:
str = "Client Key Exchange (16)";
break;
case HS_FINISHED:
str = "Finished (16)";
break;
default:
str = "Error (Unknown)";
break;
}
ssl_printf("%s\n", str);
//TTY_FLUSH();
}
/**
* Debugging routine to display RSA objects
*/
void ICACHE_FLASH_ATTR DISPLAY_RSA(SSL *ssl, const RSA_CTX *rsa_ctx)
{
if (!IS_SET_SSL_FLAG(SSL_DISPLAY_RSA))
return;
RSA_print(rsa_ctx);
//TTY_FLUSH();
}
/**
* Debugging routine to display SSL handshaking bytes.
*/
void ICACHE_FLASH_ATTR DISPLAY_BYTES(SSL *ssl, const char *format,
const uint8_t *data, int size, ...)
{
// wujg : pass compile first
// va_list(ap);
// if (!IS_SET_SSL_FLAG(SSL_DISPLAY_BYTES))
// return;
// va_start(ap, size);
// print_blob(format, data, size, va_arg(ap, char *));
// va_end(ap);
// TTY_FLUSH();
}
/**
* Debugging routine to display SSL handshaking errors.
*/
EXP_FUNC void STDCALL ICACHE_FLASH_ATTR ssl_display_error(int error_code)
{
if (error_code == SSL_OK)
return;
ssl_printf("Error: ");
/* X509 error? */
if (error_code < SSL_X509_OFFSET)
{
ssl_printf("%s\n", x509_display_error(error_code - SSL_X509_OFFSET));
return;
}
/* SSL alert error code */
if (error_code > SSL_ERROR_CONN_LOST)
{
ssl_printf("SSL error %d\n", -error_code);
return;
}
switch (error_code)
{
case SSL_ERROR_DEAD:
ssl_printf("connection dead");
break;
case SSL_ERROR_INVALID_HANDSHAKE:
ssl_printf("invalid handshake");
break;
case SSL_ERROR_INVALID_PROT_MSG:
ssl_printf("invalid protocol message");
break;
case SSL_ERROR_INVALID_HMAC:
ssl_printf("invalid mac");
break;
case SSL_ERROR_INVALID_VERSION:
ssl_printf("invalid version");
break;
case SSL_ERROR_INVALID_SESSION:
ssl_printf("invalid session");
break;
case SSL_ERROR_NO_CIPHER:
ssl_printf("no cipher");
break;
case SSL_ERROR_CONN_LOST:
ssl_printf("connection lost");
break;
case SSL_ERROR_BAD_CERTIFICATE:
ssl_printf("bad certificate");
break;
case SSL_ERROR_INVALID_KEY:
ssl_printf("invalid key");
break;
case SSL_ERROR_FINISHED_INVALID:
ssl_printf("finished invalid");
break;
case SSL_ERROR_NO_CERT_DEFINED:
ssl_printf("no certificate defined");
break;
case SSL_ERROR_NO_CLIENT_RENOG:
ssl_printf("client renegotiation not supported");
break;
case SSL_ERROR_NOT_SUPPORTED:
ssl_printf("Option not supported");
break;
default:
ssl_printf("undefined as yet - %d", error_code);
break;
}
ssl_printf("\n");
//TTY_FLUSH();
}
/**
* Debugging routine to display alerts.
*/
void ICACHE_FLASH_ATTR DISPLAY_ALERT(SSL *ssl, int alert)
{
if (!IS_SET_SSL_FLAG(SSL_DISPLAY_STATES))
return;
ssl_printf("Alert: ");
switch (alert)
{
case SSL_ALERT_CLOSE_NOTIFY:
ssl_printf("close notify");
break;
case SSL_ALERT_INVALID_VERSION:
ssl_printf("invalid version");
break;
case SSL_ALERT_BAD_CERTIFICATE:
ssl_printf("bad certificate");
break;
case SSL_ALERT_UNEXPECTED_MESSAGE:
ssl_printf("unexpected message");
break;
case SSL_ALERT_BAD_RECORD_MAC:
ssl_printf("bad record mac");
break;
case SSL_ALERT_HANDSHAKE_FAILURE:
ssl_printf("handshake failure");
break;
case SSL_ALERT_ILLEGAL_PARAMETER:
ssl_printf("illegal parameter");
break;
case SSL_ALERT_DECODE_ERROR:
ssl_printf("decode error");
break;
case SSL_ALERT_DECRYPT_ERROR:
ssl_printf("decrypt error");
break;
case SSL_ALERT_NO_RENEGOTIATION:
ssl_printf("no renegotiation");
break;
default:
ssl_printf("alert - (unknown %d)", alert);
break;
}
ssl_printf("\n");
//TTY_FLUSH();
}
#endif
#endif /* CONFIG_SSL_FULL_MODE */
/**
* Return the version of this library.
*/
EXP_FUNC const char * STDCALL ICACHE_FLASH_ATTR ssl_version()
{
static const char * axtls_version = AXTLS_VERSION;
return axtls_version;
}
/**
* Enable the various language bindings to work regardless of the
* configuration - they just return an error statement and a bad return code.
*/
#if !defined(CONFIG_SSL_FULL_MODE)
EXP_FUNC void STDCALL ssl_display_error(int error_code) {}
#endif
#ifdef CONFIG_BINDINGS
#if !defined(CONFIG_SSL_ENABLE_CLIENT)
EXP_FUNC SSL * STDCALL ICACHE_FLASH_ATTR ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const
uint8_t *session_id, uint8_t sess_id_size)
{
ssl_printf(unsupported_str);
return NULL;
}
#endif
#if !defined(CONFIG_SSL_CERT_VERIFICATION)
EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_verify_cert(const SSL *ssl)
{
ssl_printf(unsupported_str);
return -1;
}
EXP_FUNC const char * STDCALL ICACHE_FLASH_ATTR ssl_get_cert_dn(const SSL *ssl, int component)
{
ssl_printf(unsupported_str);
return NULL;
}
EXP_FUNC const char * STDCALL ICACHE_FLASH_ATTR ssl_get_cert_subject_alt_dnsname(const SSL *ssl, int index)
{
ssl_printf(unsupported_str);
return NULL;
}
#endif /* CONFIG_SSL_CERT_VERIFICATION */
#endif /* CONFIG_BINDINGS */
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//#include <stdlib.h>
//#include <string.h>
//#include <time.h>
//#include <stdio.h>
#include "ssl/ssl_os_port.h"
#include "ssl/ssl_ssl.h"
#include "lwip/tcp.h"
#include "ssl/app/espconn_ssl.h"
#ifdef CONFIG_SSL_ENABLE_CLIENT /* all commented out if no client */
static int send_client_hello(SSL *ssl);
static int process_server_hello(SSL *ssl);
static int process_server_hello_done(SSL *ssl);
static int send_client_key_xchg(SSL *ssl);
static int process_cert_req(SSL *ssl);
static int send_cert_verify(SSL *ssl);
#if 0
/*
* Establish a new SSL connection to an SSL server.
*/
EXP_FUNC SSL * STDCALL ICACHE_FLASH_ATTR ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const
uint8_t *session_id, uint8_t sess_id_size)
{
SSL *ssl = ssl_new(ssl_ctx, client_fd);
ssl->version = SSL_PROTOCOL_VERSION_MAX; /* try top version first */
if (session_id && ssl_ctx->num_sessions)
{
if (sess_id_size > SSL_SESSION_ID_SIZE) /* validity check */
{
ssl_free(ssl);
return NULL;
}
os_memcpy(ssl->session_id, session_id, sess_id_size);
ssl->sess_id_size = sess_id_size;
SET_SSL_FLAG(SSL_SESSION_RESUME); /* just flag for later */
}
SET_SSL_FLAG(SSL_IS_CLIENT);
do_client_connect(ssl);
return ssl;
}
#endif
/*
* Establish a new SSL connection to an SSL server.(raw api)add by ives 12.12.2013
*/
EXP_FUNC SSL *STDCALL ICACHE_FLASH_ATTR SSLClient_new(SSL_CTX *ssl_ctx, struct tcp_pcb *SslClient_pcb, const
uint8_t *session_id, uint8_t sess_id_size)
{
SSL *ssl = ssl_new_context(ssl_ctx, SslClient_pcb);
ssl->version = SSL_PROTOCOL_VERSION_MAX;
if (session_id && ssl_ctx->num_sessions) {
if (sess_id_size > SSL_SESSION_ID_SIZE) {
ssl_free(ssl);
return NULL;
}
os_memcpy(ssl->session_id, session_id, sess_id_size);
ssl->sess_id_size = sess_id_size;
SET_SSL_FLAG(SSL_SESSION_RESUME);
}
SET_SSL_FLAG(SSL_IS_CLIENT);
do_client_connect(ssl);
return ssl;
}
/*
* Process the handshake record.
*/
int ICACHE_FLASH_ATTR do_clnt_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len)
{
int ret;
/* To get here the state must be valid */
// ssl_printf("do_clnt_handshake: %d %d\n",__LINE__, handshake_type);
switch (handshake_type)
{
case HS_SERVER_HELLO:
ret = process_server_hello(ssl);
break;
case HS_CERTIFICATE:
ret = process_certificate(ssl, &ssl->x509_ctx);
break;
case HS_SERVER_HELLO_DONE:
if ((ret = process_server_hello_done(ssl)) == SSL_OK)
{
if (IS_SET_SSL_FLAG(SSL_HAS_CERT_REQ))
{
if ((ret = send_certificate(ssl)) == SSL_OK &&
(ret = send_client_key_xchg(ssl)) == SSL_OK)
{
send_cert_verify(ssl);
}
}
else
{
ret = send_client_key_xchg(ssl);
}
if (ret == SSL_OK &&
(ret = send_change_cipher_spec(ssl)) == SSL_OK)
{
ret = send_finished(ssl);
}
}
break;
case HS_CERT_REQ:
ret = process_cert_req(ssl);
break;
case HS_FINISHED:
ret = process_finished(ssl, buf, hs_len);
disposable_free(ssl); /* free up some memory */
/* note: client renegotiation is not allowed after this */
break;
case HS_HELLO_REQUEST:
disposable_new(ssl);
ret = do_client_connect(ssl);
break;
default:
ret = SSL_ERROR_INVALID_HANDSHAKE;
break;
}
return ret;
}
/*
* Do the handshaking from the beginning.
*/
int ICACHE_FLASH_ATTR do_client_connect(SSL *ssl)
{
int ret = SSL_OK;
send_client_hello(ssl); /* send the client hello */
ssl->bm_read_index = 0;
ssl->next_state = HS_SERVER_HELLO;
ssl->hs_status = SSL_NOT_OK; /* not connected */
#if 0
/* sit in a loop until it all looks good */
if (!IS_SET_SSL_FLAG(SSL_CONNECT_IN_PARTS))
{
while (ssl->hs_status != SSL_OK)
{
ret = ssl_read(ssl, NULL);
ssl_printf("%s %d %d\n", __func__, __LINE__,ret);
if (ret < SSL_OK)
break;
}
ssl->hs_status = ret; /* connected? */
}
#endif
return ret;
}
/*
* Send the initial client hello.
*/
static int ICACHE_FLASH_ATTR send_client_hello(SSL *ssl)
{
uint8_t *buf = ssl->bm_data;
time_t tm = 0; //time(NULL); wujg : pass compile first
uint8_t *tm_ptr = &buf[6]; /* time will go here */
int i, offset;
buf[0] = HS_CLIENT_HELLO;
buf[1] = 0;
buf[2] = 0;
/* byte 3 is calculated later */
buf[4] = 0x03;
buf[5] = ssl->version & 0x0f;
/* client random value - spec says that 1st 4 bytes are big endian time */
*tm_ptr++ = (uint8_t)(((long)tm & 0xff000000) >> 24);
*tm_ptr++ = (uint8_t)(((long)tm & 0x00ff0000) >> 16);
*tm_ptr++ = (uint8_t)(((long)tm & 0x0000ff00) >> 8);
*tm_ptr++ = (uint8_t)(((long)tm & 0x000000ff));
get_random(SSL_RANDOM_SIZE-4, &buf[10]);
os_memcpy(ssl->dc->client_random, &buf[6], SSL_RANDOM_SIZE);
offset = 6 + SSL_RANDOM_SIZE;
/* give session resumption a go */
if (IS_SET_SSL_FLAG(SSL_SESSION_RESUME)) /* set initially by user */
{
buf[offset++] = ssl->sess_id_size;
os_memcpy(&buf[offset], ssl->session_id, ssl->sess_id_size);
offset += ssl->sess_id_size;
CLR_SSL_FLAG(SSL_SESSION_RESUME); /* clear so we can set later */
}
else
{
/* no session id - because no session resumption just yet */
buf[offset++] = 0;
}
buf[offset++] = 0; /* number of ciphers */
buf[offset++] = NUM_PROTOCOLS*2;/* number of ciphers */
/* put all our supported protocols in our request */
for (i = 0; i < NUM_PROTOCOLS; i++)
{
buf[offset++] = 0; /* cipher we are using */
buf[offset++] = ssl_prot_prefs[i];
}
buf[offset++] = 1; /* no compression */
buf[offset++] = 0;
buf[3] = offset - 4; /* handshake size */
return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset);
}
/*
* Process the server hello.
*/
static int ICACHE_FLASH_ATTR process_server_hello(SSL *ssl)
{
uint8_t *buf = ssl->bm_data;
int pkt_size = ssl->bm_index;
int num_sessions = ssl->ssl_ctx->num_sessions;
uint8_t sess_id_size;
int offset, ret = SSL_OK;
/* check that we are talking to a TLSv1 server */
uint8_t version = (buf[4] << 4) + buf[5];
if (version > SSL_PROTOCOL_VERSION_MAX)
{
version = SSL_PROTOCOL_VERSION_MAX;
}
else if (ssl->version < SSL_PROTOCOL_MIN_VERSION)
{
ret = SSL_ERROR_INVALID_VERSION;
//ssl_display_error(ret);
goto error;
}
ssl->version = version;
/* get the server random value */
os_memcpy(ssl->dc->server_random, &buf[6], SSL_RANDOM_SIZE);
offset = 6 + SSL_RANDOM_SIZE; /* skip of session id size */
sess_id_size = buf[offset++];
if (sess_id_size > SSL_SESSION_ID_SIZE)
{
ret = SSL_ERROR_INVALID_SESSION;
goto error;
}
if (num_sessions)
{
ssl->session = ssl_session_update(num_sessions,
ssl->ssl_ctx->ssl_sessions, ssl, &buf[offset]);
os_memcpy(ssl->session->session_id, &buf[offset], sess_id_size);
/* pad the rest with 0's */
if (sess_id_size < SSL_SESSION_ID_SIZE)
{
os_memset(&ssl->session->session_id[sess_id_size], 0,
SSL_SESSION_ID_SIZE-sess_id_size);
}
}
os_memcpy(ssl->session_id, &buf[offset], sess_id_size);
ssl->sess_id_size = sess_id_size;
offset += sess_id_size;
/* get the real cipher we are using */
ssl->cipher = buf[++offset];
ssl->next_state = IS_SET_SSL_FLAG(SSL_SESSION_RESUME) ?
HS_FINISHED : HS_CERTIFICATE;
offset++; // skip the compr
PARANOIA_CHECK(pkt_size, offset);
ssl->dc->bm_proc_index = offset+1;
error:
return ret;
}
/**
* Process the server hello done message.
*/
static int ICACHE_FLASH_ATTR process_server_hello_done(SSL *ssl)
{
ssl->next_state = HS_FINISHED;
return SSL_OK;
}
/*
* Send a client key exchange message.
*/
static int ICACHE_FLASH_ATTR send_client_key_xchg(SSL *ssl)
{
uint8_t *buf = ssl->bm_data;
uint8_t premaster_secret[SSL_SECRET_SIZE];
int enc_secret_size = -1;
buf[0] = HS_CLIENT_KEY_XCHG;
buf[1] = 0;
premaster_secret[0] = 0x03; /* encode the version number */
premaster_secret[1] = SSL_PROTOCOL_MINOR_VERSION; /* must be TLS 1.1 */
get_random(SSL_SECRET_SIZE-2, &premaster_secret[2]);
//DISPLAY_RSA(ssl, ssl->x509_ctx->rsa_ctx);
/* rsa_ctx->bi_ctx is not thread-safe */
SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
enc_secret_size = RSA_encrypt(ssl->x509_ctx->rsa_ctx, premaster_secret,
SSL_SECRET_SIZE, &buf[6], 0);
SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
buf[2] = (enc_secret_size + 2) >> 8;
buf[3] = (enc_secret_size + 2) & 0xff;
buf[4] = enc_secret_size >> 8;
buf[5] = enc_secret_size & 0xff;
generate_master_secret(ssl, premaster_secret);
return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, enc_secret_size+6);
}
/*
* Process the certificate request.
*/
static int ICACHE_FLASH_ATTR process_cert_req(SSL *ssl)
{
uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index];
int ret = SSL_OK;
int offset = (buf[2] << 4) + buf[3];
int pkt_size = ssl->bm_index;
/* don't do any processing - we will send back an RSA certificate anyway */
ssl->next_state = HS_SERVER_HELLO_DONE;
SET_SSL_FLAG(SSL_HAS_CERT_REQ);
ssl->dc->bm_proc_index += offset;
PARANOIA_CHECK(pkt_size, offset);
error:
return ret;
}
/*
* Send a certificate verify message.
*/
static int ICACHE_FLASH_ATTR send_cert_verify(SSL *ssl)
{
uint8_t *buf = ssl->bm_data;
uint8_t dgst[MD5_SIZE+SHA1_SIZE];
RSA_CTX *rsa_ctx = ssl->ssl_ctx->rsa_ctx;
int n = 0, ret;
//DISPLAY_RSA(ssl, rsa_ctx);
buf[0] = HS_CERT_VERIFY;
buf[1] = 0;
finished_digest(ssl, NULL, dgst); /* calculate the digest */
/* rsa_ctx->bi_ctx is not thread-safe */
if (rsa_ctx)
{
SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
n = RSA_encrypt(rsa_ctx, dgst, sizeof(dgst), &buf[6], 1);
SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
if (n == 0)
{
ret = SSL_ERROR_INVALID_KEY;
goto error;
}
}
buf[4] = n >> 8; /* add the RSA size (not officially documented) */
buf[5] = n & 0xff;
n += 2;
buf[2] = n >> 8;
buf[3] = n & 0xff;
ret = send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, n+4);
error:
return ret;
}
#endif /* CONFIG_SSL_ENABLE_CLIENT */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment