diff --git a/TESTS/netsocket/tls/main.cpp b/TESTS/netsocket/tls/main.cpp index bd1fff47755..e17eb53bfcb 100644 --- a/TESTS/netsocket/tls/main.cpp +++ b/TESTS/netsocket/tls/main.cpp @@ -84,15 +84,15 @@ nsapi_error_t tlssocket_connect_to_srv(TLSSocket &sock, uint16_t port) printf("MBED: Server '%s', port %d\n", tls_addr.get_ip_address(), tls_addr.get_port()); - nsapi_error_t err = sock.set_root_ca_cert(tls_global::cert); + nsapi_error_t err = sock.open(NetworkInterface::get_default_instance()); if (err != NSAPI_ERROR_OK) { - printf("Error from sock.set_root_ca_cert: %d\n", err); + printf("Error from sock.open: %d\n", err); return err; } - err = sock.open(NetworkInterface::get_default_instance()); + err = sock.set_root_ca_cert(tls_global::cert); if (err != NSAPI_ERROR_OK) { - printf("Error from sock.open: %d\n", err); + printf("Error from sock.set_root_ca_cert: %d\n", err); return err; } diff --git a/TESTS/netsocket/tls/tlssocket_endpoint_close.cpp b/TESTS/netsocket/tls/tlssocket_endpoint_close.cpp index 186f03d5cf7..93abc1368db 100644 --- a/TESTS/netsocket/tls/tlssocket_endpoint_close.cpp +++ b/TESTS/netsocket/tls/tlssocket_endpoint_close.cpp @@ -48,6 +48,10 @@ static nsapi_error_t _tlssocket_connect_to_daytime_srv(TLSSocket &sock) return err; } + TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.set_root_ca_cert(tls_global::cert)); + + sock.set_timeout(10000); // Set timeout for case TLSSocket does not support peer closed indication + return sock.connect(tls_addr); } @@ -62,7 +66,6 @@ void TLSSOCKET_ENDPOINT_CLOSE() tc_exec_time.start(); TLSSocket sock; - TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.set_root_ca_cert(tls_global::cert)); if (_tlssocket_connect_to_daytime_srv(sock) != NSAPI_ERROR_OK) { TEST_FAIL(); return; diff --git a/TESTS/netsocket/tls/tlssocket_handshake_invalid.cpp b/TESTS/netsocket/tls/tlssocket_handshake_invalid.cpp index 15f0fa1bbb6..0c3b00ed75c 100644 --- a/TESTS/netsocket/tls/tlssocket_handshake_invalid.cpp +++ b/TESTS/netsocket/tls/tlssocket_handshake_invalid.cpp @@ -28,12 +28,18 @@ using namespace utest::v1; void TLSSOCKET_HANDSHAKE_INVALID() { + const int https_port = 443; SKIP_IF_TCP_UNSUPPORTED(); TLSSocket sock; TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance())); TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.set_root_ca_cert(tls_global::cert)); - TEST_ASSERT_EQUAL(NSAPI_ERROR_AUTH_FAILURE, - sock.connect("google.com", 443)); // 443 is https port. + TEST_ASSERT_EQUAL(NSAPI_ERROR_AUTH_FAILURE, sock.connect("expired.badssl.com", https_port)); + TEST_ASSERT_EQUAL(NSAPI_ERROR_AUTH_FAILURE, sock.connect("wrong.host.badssl.com", https_port)); + TEST_ASSERT_EQUAL(NSAPI_ERROR_AUTH_FAILURE, sock.connect("self-signed.badssl.com", https_port)); + TEST_ASSERT_EQUAL(NSAPI_ERROR_AUTH_FAILURE, sock.connect("untrusted-root.badssl.com", https_port)); + TEST_ASSERT_EQUAL(NSAPI_ERROR_AUTH_FAILURE, sock.connect("revoked.badssl.com", https_port)); + TEST_ASSERT_EQUAL(NSAPI_ERROR_AUTH_FAILURE, sock.connect("pinning-test.badssl.com", https_port)); + TEST_ASSERT_EQUAL(NSAPI_ERROR_AUTH_FAILURE, sock.connect("sha1-intermediate.badssl.com", https_port)); TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close()); } diff --git a/UNITTESTS/features/cellular/framework/AT/at_cellularstack/at_cellularstacktest.cpp b/UNITTESTS/features/cellular/framework/AT/at_cellularstack/at_cellularstacktest.cpp index 57ad8519f52..3615cb2c850 100644 --- a/UNITTESTS/features/cellular/framework/AT/at_cellularstack/at_cellularstacktest.cpp +++ b/UNITTESTS/features/cellular/framework/AT/at_cellularstack/at_cellularstacktest.cpp @@ -244,7 +244,7 @@ TEST_F(TestAT_CellularStack, test_AT_CellularStack_socket_bind) MyStack st(at, 0, IPV6_STACK); SocketAddress addr; ATHandler_stub::nsapi_error_value = NSAPI_ERROR_ALREADY; - EXPECT_EQ(st.socket_bind(NULL, addr), NSAPI_ERROR_DEVICE_ERROR); + EXPECT_EQ(st.socket_bind(NULL, addr), NSAPI_ERROR_NO_SOCKET); EXPECT_EQ(st.socket_bind(&st.socket, addr), NSAPI_ERROR_ALREADY); } @@ -267,7 +267,7 @@ TEST_F(TestAT_CellularStack, test_AT_CellularStack_socket_connect) MyStack st(at, 0, IPV6_STACK); SocketAddress addr; - EXPECT_EQ(st.socket_connect(NULL, addr), NSAPI_ERROR_DEVICE_ERROR); + EXPECT_EQ(st.socket_connect(NULL, addr), NSAPI_ERROR_NO_SOCKET); EXPECT_EQ(st.socket_connect(&st.socket, addr), NSAPI_ERROR_OK); } @@ -290,9 +290,9 @@ TEST_F(TestAT_CellularStack, test_AT_CellularStack_socket_send) ATHandler at(&fh1, que, 0, ","); MyStack st(at, 0, IPV6_STACK); - EXPECT_EQ(st.socket_send(NULL, "addr", 4), NSAPI_ERROR_DEVICE_ERROR); + EXPECT_EQ(st.socket_send(NULL, "addr", 4), NSAPI_ERROR_NO_SOCKET); - EXPECT_EQ(st.socket_send(&st.socket, "addr", 4), NSAPI_ERROR_DEVICE_ERROR); + EXPECT_EQ(st.socket_send(&st.socket, "addr", 4), NSAPI_ERROR_NO_CONNECTION); SocketAddress addr; st.max_sock_value = 1; @@ -312,7 +312,7 @@ TEST_F(TestAT_CellularStack, test_AT_CellularStack_socket_sendto) MyStack st(at, 0, IPV6_STACK); SocketAddress addr; - EXPECT_EQ(st.socket_sendto(NULL, addr, "addr", 4), NSAPI_ERROR_DEVICE_ERROR); + EXPECT_EQ(st.socket_sendto(NULL, addr, "addr", 4), NSAPI_ERROR_NO_SOCKET); st.max_sock_value = 1; st.bool_value = true; @@ -334,7 +334,7 @@ TEST_F(TestAT_CellularStack, test_AT_CellularStack_socket_recv) MyStack st(at, 0, IPV6_STACK); char table[4]; - EXPECT_EQ(st.socket_recv(NULL, table, 4), NSAPI_ERROR_DEVICE_ERROR); + EXPECT_EQ(st.socket_recv(NULL, table, 4), NSAPI_ERROR_NO_SOCKET); } TEST_F(TestAT_CellularStack, test_AT_CellularStack_socket_recvfrom) @@ -345,7 +345,7 @@ TEST_F(TestAT_CellularStack, test_AT_CellularStack_socket_recvfrom) MyStack st(at, 0, IPV6_STACK); char table[4]; - EXPECT_EQ(st.socket_recvfrom(NULL, NULL, table, 4), NSAPI_ERROR_DEVICE_ERROR); + EXPECT_EQ(st.socket_recvfrom(NULL, NULL, table, 4), NSAPI_ERROR_NO_SOCKET); SocketAddress addr; st.max_sock_value = 1; diff --git a/features/cellular/framework/AT/AT_CellularStack.cpp b/features/cellular/framework/AT/AT_CellularStack.cpp index 621431eabe0..c90411cd8f9 100644 --- a/features/cellular/framework/AT/AT_CellularStack.cpp +++ b/features/cellular/framework/AT/AT_CellularStack.cpp @@ -156,7 +156,7 @@ nsapi_error_t AT_CellularStack::socket_close(nsapi_socket_t handle) struct CellularSocket *socket = (struct CellularSocket *)handle; if (!socket) { - return err; + return NSAPI_ERROR_NO_SOCKET; } int sock_id = socket->id; @@ -192,7 +192,7 @@ nsapi_error_t AT_CellularStack::socket_bind(nsapi_socket_t handle, const SocketA { struct CellularSocket *socket = (CellularSocket *)handle; if (!socket) { - return NSAPI_ERROR_DEVICE_ERROR; + return NSAPI_ERROR_NO_SOCKET; } if (addr) { @@ -220,14 +220,14 @@ nsapi_error_t AT_CellularStack::socket_bind(nsapi_socket_t handle, const SocketA nsapi_error_t AT_CellularStack::socket_listen(nsapi_socket_t handle, int backlog) { - return NSAPI_ERROR_UNSUPPORTED;; + return NSAPI_ERROR_UNSUPPORTED; } nsapi_error_t AT_CellularStack::socket_connect(nsapi_socket_t handle, const SocketAddress &addr) { CellularSocket *socket = (CellularSocket *)handle; if (!socket) { - return NSAPI_ERROR_DEVICE_ERROR; + return NSAPI_ERROR_NO_SOCKET; } socket->remoteAddress = addr; socket->connected = true; @@ -237,14 +237,17 @@ nsapi_error_t AT_CellularStack::socket_connect(nsapi_socket_t handle, const Sock nsapi_error_t AT_CellularStack::socket_accept(void *server, void **socket, SocketAddress *addr) { - return NSAPI_ERROR_UNSUPPORTED;; + return NSAPI_ERROR_UNSUPPORTED; } nsapi_size_or_error_t AT_CellularStack::socket_send(nsapi_socket_t handle, const void *data, unsigned size) { CellularSocket *socket = (CellularSocket *)handle; - if (!socket || !socket->connected) { - return NSAPI_ERROR_DEVICE_ERROR; + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + if (!socket->connected) { + return NSAPI_ERROR_NO_CONNECTION; } return socket_sendto(handle, socket->remoteAddress, data, size); } @@ -253,7 +256,7 @@ nsapi_size_or_error_t AT_CellularStack::socket_sendto(nsapi_socket_t handle, con { CellularSocket *socket = (CellularSocket *)handle; if (!socket) { - return NSAPI_ERROR_DEVICE_ERROR; + return NSAPI_ERROR_NO_SOCKET; } if (socket->closed && !socket->rx_avail) { @@ -319,7 +322,7 @@ nsapi_size_or_error_t AT_CellularStack::socket_recvfrom(nsapi_socket_t handle, S { CellularSocket *socket = (CellularSocket *)handle; if (!socket) { - return NSAPI_ERROR_DEVICE_ERROR; + return NSAPI_ERROR_NO_SOCKET; } if (socket->closed) { diff --git a/features/cellular/framework/AT/AT_CellularStack.h b/features/cellular/framework/AT/AT_CellularStack.h index 5015aeee48b..f5825202bd5 100644 --- a/features/cellular/framework/AT/AT_CellularStack.h +++ b/features/cellular/framework/AT/AT_CellularStack.h @@ -102,6 +102,7 @@ class AT_CellularStack : public NetworkStack, public AT_CellularBase { started(false), tx_ready(false), rx_avail(false), + tls_socket(false), pending_bytes(0) { } @@ -119,6 +120,7 @@ class AT_CellularStack : public NetworkStack, public AT_CellularBase { bool started; // socket has been opened on modem stack bool tx_ready; // socket is ready for sending on modem stack bool rx_avail; // socket has data for reading on modem stack + bool tls_socket; // socket uses modem's internal TLS socket functionality nsapi_size_t pending_bytes; // The number of received bytes pending }; diff --git a/features/cellular/framework/targets/QUECTEL/BG96/QUECTEL_BG96_CellularStack.cpp b/features/cellular/framework/targets/QUECTEL/BG96/QUECTEL_BG96_CellularStack.cpp index ee9652e4f64..b5a452e613a 100644 --- a/features/cellular/framework/targets/QUECTEL/BG96/QUECTEL_BG96_CellularStack.cpp +++ b/features/cellular/framework/targets/QUECTEL/BG96/QUECTEL_BG96_CellularStack.cpp @@ -18,6 +18,15 @@ #include #include "QUECTEL/BG96/QUECTEL_BG96_CellularStack.h" #include "CellularLog.h" +#include "netsocket/TLSSocket.h" + +// Ref: Quectel_BG96_SSL_AT_Commands_Manual, ch 2.1.1 AT+QSSLCFG +static const int BG96_SUPPORTED_SSL_VERSION = 4; // All +static const char BG96_SUPPORTED_CIPHER_SUITE[] = "0xFFFF"; // Support all + +// TODO: At the moment we support only one active SSL context +// Later can be expanded to support multiple contexts. Modem supports IDs 0-5. +static const int sslctxID = 0; using namespace mbed; @@ -25,12 +34,27 @@ QUECTEL_BG96_CellularStack::QUECTEL_BG96_CellularStack(ATHandler &atHandler, int #ifdef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES , _dns_callback(NULL), _dns_version(NSAPI_UNSPEC) #endif + , _tls_sec_level(0) { _at.set_urc_handler("+QIURC: \"recv", mbed::Callback(this, &QUECTEL_BG96_CellularStack::urc_qiurc_recv)); _at.set_urc_handler("+QIURC: \"close", mbed::Callback(this, &QUECTEL_BG96_CellularStack::urc_qiurc_closed)); #ifdef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES _at.set_urc_handler("+QIURC: \"dnsgip\",", mbed::Callback(this, &QUECTEL_BG96_CellularStack::urc_qiurc_dnsgip)); #endif + + _at.set_urc_handler("+QSSLURC: \"recv", mbed::Callback(this, &QUECTEL_BG96_CellularStack::urc_qiurc_recv)); + _at.set_urc_handler("+QSSLURC: \"close", mbed::Callback(this, &QUECTEL_BG96_CellularStack::urc_qiurc_closed)); + + // TODO: this needs to be handled properly, but now making just a quick hack + // Close all SSL sockets if open. This can happen for example if application processor + // was reset but modem not. Old sockets are still up and running and it prevents + // new SSL configurations and creating new sockets. + for (int i = 0; i < 12; i++) { + _at.clear_error(); + tr_debug("Closing SSL socket %d...", i); + _at.at_cmd_discard("+QSSLCLOSE", "=", "%d", i); + } + _at.clear_error(); } QUECTEL_BG96_CellularStack::~QUECTEL_BG96_CellularStack() @@ -52,7 +76,7 @@ nsapi_error_t QUECTEL_BG96_CellularStack::socket_connect(nsapi_socket_t handle, CellularSocket *socket = (CellularSocket *)handle; int modem_connect_id = -1; - int err = -1; + int err = NSAPI_ERROR_NO_CONNECTION; int request_connect_id = find_socket_index(socket); // assert here as its a programming error if the socket container doesn't contain @@ -61,25 +85,49 @@ nsapi_error_t QUECTEL_BG96_CellularStack::socket_connect(nsapi_socket_t handle, _at.lock(); if (socket->proto == NSAPI_TCP) { - char ipdot[NSAPI_IP_SIZE]; - ip2dot(address, ipdot); - _at.at_cmd_discard("+QIOPEN", "=", "%d%d%s%s%d%d%d", _cid, request_connect_id, "TCP", - ipdot, address.get_port(), socket->localAddress.get_port(), 0); - - handle_open_socket_response(modem_connect_id, err); - - if ((_at.get_last_error() == NSAPI_ERROR_OK) && err) { - if (err == BG96_SOCKET_BIND_FAIL) { - socket->id = -1; + if (socket->tls_socket) { + if (_tls_sec_level == 0) { _at.unlock(); - return NSAPI_ERROR_PARAMETER; + return NSAPI_ERROR_AUTH_FAILURE; } - _at.at_cmd_discard("+QICLOSE", "=", "%d", modem_connect_id); + _at.at_cmd_discard("+QSSLOPEN", "=", "%d%d%d%s%d%d", _cid, sslctxID, request_connect_id, + address.get_ip_address(), address.get_port(), 0); + handle_open_socket_response(modem_connect_id, err, true); + + if ((_at.get_last_error() == NSAPI_ERROR_OK) && err) { + if (err == BG96_SOCKET_BIND_FAIL) { + socket->id = -1; + _at.unlock(); + return NSAPI_ERROR_PARAMETER; + } + socket_close_impl(modem_connect_id); + + _at.at_cmd_discard("+QSSLOPEN", "=", "%d%d%d%s%d%d", _cid, sslctxID, request_connect_id, + address.get_ip_address(), address.get_port(), 0); + handle_open_socket_response(modem_connect_id, err, true); + } + } else { + char ipdot[NSAPI_IP_SIZE]; + ip2dot(address, ipdot); _at.at_cmd_discard("+QIOPEN", "=", "%d%d%s%s%d%d%d", _cid, request_connect_id, "TCP", ipdot, address.get_port(), socket->localAddress.get_port(), 0); - handle_open_socket_response(modem_connect_id, err); + handle_open_socket_response(modem_connect_id, err, false); + + if ((_at.get_last_error() == NSAPI_ERROR_OK) && err) { + if (err == BG96_SOCKET_BIND_FAIL) { + socket->id = -1; + _at.unlock(); + return NSAPI_ERROR_PARAMETER; + } + socket_close_impl(modem_connect_id); + + _at.at_cmd_discard("+QIOPEN", "=", "%d%d%s%s%d%d%d", _cid, request_connect_id, "TCP", + ipdot, address.get_port(), socket->localAddress.get_port(), 0); + + handle_open_socket_response(modem_connect_id, err, false); + } } } @@ -91,14 +139,14 @@ nsapi_error_t QUECTEL_BG96_CellularStack::socket_connect(nsapi_socket_t handle, nsapi_error_t ret_val = _at.get_last_error(); _at.unlock(); - if ((ret_val == NSAPI_ERROR_OK) && (modem_connect_id == request_connect_id)) { + if ((!err) && (ret_val == NSAPI_ERROR_OK) && (modem_connect_id == request_connect_id)) { socket->id = request_connect_id; socket->remoteAddress = address; socket->connected = true; return NSAPI_ERROR_OK; } - return NSAPI_ERROR_NO_CONNECTION; + return err; } void QUECTEL_BG96_CellularStack::urc_qiurc_recv() @@ -182,21 +230,41 @@ bool QUECTEL_BG96_CellularStack::is_protocol_supported(nsapi_protocol_t protocol nsapi_error_t QUECTEL_BG96_CellularStack::socket_close_impl(int sock_id) { _at.set_at_timeout(BG96_CLOSE_SOCKET_TIMEOUT); - nsapi_error_t err = _at.at_cmd_discard("+QICLOSE", "=", "%d", sock_id); + nsapi_error_t err; + CellularSocket *socket = find_socket(sock_id); + if (socket && socket->tls_socket) { + err = _at.at_cmd_discard("+QSSLCLOSE", "=", "%d", sock_id); + if (err == NSAPI_ERROR_OK) { + // Disable TLSSocket settings to prevent reuse on next socket without setting the values + _tls_sec_level = 0; + err = _at.at_cmd_discard("+QSSLCFG", "=\"seclevel\",", "%d%d", sslctxID, _tls_sec_level); + } + } else { + err = _at.at_cmd_discard("+QICLOSE", "=", "%d", sock_id); + } _at.restore_at_timeout(); return err; } -void QUECTEL_BG96_CellularStack::handle_open_socket_response(int &modem_connect_id, int &err) +void QUECTEL_BG96_CellularStack::handle_open_socket_response(int &modem_connect_id, int &err, bool tlssocket) { // OK // QIOPEN -> should be handled as URC? _at.set_at_timeout(BG96_CREATE_SOCKET_TIMEOUT); - _at.resp_start("+QIOPEN:"); + + if (tlssocket) { + _at.resp_start("+QSSLOPEN:"); + } else { + _at.resp_start("+QIOPEN:"); + } + _at.restore_at_timeout(); modem_connect_id = _at.read_int(); err = _at.read_int(); + if (tlssocket && err != 0) { + err = NSAPI_ERROR_AUTH_FAILURE; + } } nsapi_error_t QUECTEL_BG96_CellularStack::create_socket_impl(CellularSocket *socket) @@ -215,7 +283,7 @@ nsapi_error_t QUECTEL_BG96_CellularStack::create_socket_impl(CellularSocket *soc (_ip_ver_sendto == NSAPI_IPv4) ? "127.0.0.1" : "0:0:0:0:0:0:0:1", remote_port, socket->localAddress.get_port(), 0); - handle_open_socket_response(modem_connect_id, err); + handle_open_socket_response(modem_connect_id, err, false); if ((_at.get_last_error() == NSAPI_ERROR_OK) && err) { if (err == BG96_SOCKET_BIND_FAIL) { @@ -228,7 +296,7 @@ nsapi_error_t QUECTEL_BG96_CellularStack::create_socket_impl(CellularSocket *soc (_ip_ver_sendto == NSAPI_IPv4) ? "127.0.0.1" : "0:0:0:0:0:0:0:1", remote_port, socket->localAddress.get_port(), 0); - handle_open_socket_response(modem_connect_id, err); + handle_open_socket_response(modem_connect_id, err, false); } } else if (socket->proto == NSAPI_UDP && socket->connected) { char ipdot[NSAPI_IP_SIZE]; @@ -236,7 +304,7 @@ nsapi_error_t QUECTEL_BG96_CellularStack::create_socket_impl(CellularSocket *soc _at.at_cmd_discard("+QIOPEN", "=", "%d%d%s%s%d", _cid, request_connect_id, "UDP", ipdot, socket->remoteAddress.get_port()); - handle_open_socket_response(modem_connect_id, err); + handle_open_socket_response(modem_connect_id, err, false); if ((_at.get_last_error() == NSAPI_ERROR_OK) && err) { if (err == BG96_SOCKET_BIND_FAIL) { @@ -248,7 +316,7 @@ nsapi_error_t QUECTEL_BG96_CellularStack::create_socket_impl(CellularSocket *soc _at.at_cmd_discard("+QIOPEN", "=", "%d%d%s%s%d", _cid, request_connect_id, "UDP", ipdot, socket->remoteAddress.get_port()); - handle_open_socket_response(modem_connect_id, err); + handle_open_socket_response(modem_connect_id, err, false); } } @@ -283,8 +351,12 @@ nsapi_size_or_error_t QUECTEL_BG96_CellularStack::socket_sendto_impl(CellularSoc int sent_len_before = 0; int sent_len_after = 0; - // Get the sent count before sending - _at.at_cmd_int("+QISEND", "=", sent_len_before, "%d%d", socket->id, 0); + if (socket->tls_socket) { + sent_len_after = size; + } else { + // Get the sent count before sending + _at.at_cmd_int("+QISEND", "=", sent_len_before, "%d%d", socket->id, 0); + } // Send if (socket->proto == NSAPI_UDP) { @@ -293,7 +365,11 @@ nsapi_size_or_error_t QUECTEL_BG96_CellularStack::socket_sendto_impl(CellularSoc _at.cmd_start_stop("+QISEND", "=", "%d%d%s%d", socket->id, size, ipdot, address.get_port()); } else { - _at.cmd_start_stop("+QISEND", "=", "%d%d", socket->id, size); + if (socket->tls_socket) { + _at.cmd_start_stop("+QSSLSEND", "=", "%d%d", socket->id, size); + } else { + _at.cmd_start_stop("+QISEND", "=", "%d%d", socket->id, size); + } } _at.resp_start(">"); @@ -310,7 +386,12 @@ nsapi_size_or_error_t QUECTEL_BG96_CellularStack::socket_sendto_impl(CellularSoc } // Get the sent count after sending - nsapi_size_or_error_t err = _at.at_cmd_int("+QISEND", "=", sent_len_after, "%d%d", socket->id, 0); + nsapi_size_or_error_t err = NSAPI_ERROR_OK; + + if (!socket->tls_socket) { + err = _at.at_cmd_int("+QISEND", "=", sent_len_after, "%d%d", socket->id, 0); + } + if (err == NSAPI_ERROR_OK) { sent_len = sent_len_after - sent_len_before; return sent_len; @@ -323,22 +404,34 @@ nsapi_size_or_error_t QUECTEL_BG96_CellularStack::socket_recvfrom_impl(CellularS void *buffer, nsapi_size_t size) { nsapi_size_or_error_t recv_len = 0; - int port; + int port = -1; char ip_address[NSAPI_IP_SIZE + 1]; if (socket->proto == NSAPI_TCP) { // do not read more than max size size = size > BG96_MAX_RECV_SIZE ? BG96_MAX_RECV_SIZE : size; - _at.cmd_start_stop("+QIRD", "=", "%d%d", socket->id, size); + if (socket->tls_socket) { + _at.cmd_start_stop("+QSSLRECV", "=", "%d%d", socket->id, size); + } else { + _at.cmd_start_stop("+QIRD", "=", "%d%d", socket->id, size); + } } else { _at.cmd_start_stop("+QIRD", "=", "%d", socket->id); } - _at.resp_start("+QIRD:"); + if (socket->tls_socket) { + _at.resp_start("+QSSLRECV:"); + } else { + _at.resp_start("+QIRD:"); + } + recv_len = _at.read_int(); - _at.read_string(ip_address, sizeof(ip_address)); - port = _at.read_int(); if (recv_len > 0) { + // UDP has remote_IP and remote_port parameters + if (socket->proto == NSAPI_UDP) { + _at.read_string(ip_address, sizeof(ip_address)); + port = _at.read_int(); + } // do not read more than buffer size recv_len = recv_len > (nsapi_size_or_error_t)size ? size : recv_len; _at.read_bytes((uint8_t *)buffer, recv_len); @@ -436,3 +529,120 @@ void QUECTEL_BG96_CellularStack::ip2dot(const SocketAddress &ip, char *dot) *dot = '\0'; } } + +nsapi_error_t QUECTEL_BG96_CellularStack::set_to_modem_impl(const char *filename, const char *config, const char *data, size_t size) +{ + // Delete old file from the modem. + _at.at_cmd_discard("+QFDEL", "=", "%s", filename); + _at.clear_error(); // Ignore error if file didn't exist + + // Upload new file to modem + _at.cmd_start_stop("+QFUPL", "=", "%s%d", filename, size); + _at.resp_start("CONNECT"); + _at.write_bytes((uint8_t *)data, size); + _at.resp_start("+QFUPL:"); + size_t upload_size = _at.read_int(); + _at.resp_stop(); + if (upload_size != size) { + tr_error("Upload error! orig = %d, uploaded = %d", size, upload_size); + return NSAPI_ERROR_DEVICE_ERROR; + } + + // Configure into use + _at.at_cmd_discard("+QSSLCFG", "=", "%s%d%s", config, sslctxID, filename); + + return _at.get_last_error(); +} + + +nsapi_error_t QUECTEL_BG96_CellularStack::setsockopt(nsapi_socket_t handle, int level, + int optname, const void *optval, unsigned optlen) +{ + CellularSocket *socket = (CellularSocket *)handle; + nsapi_error_t ret = NSAPI_ERROR_OK; + + if (level == NSAPI_TLSSOCKET_LEVEL) { + if (optval) { + _at.lock(); + switch (optname) { + case NSAPI_TLSSOCKET_ENABLE: { + MBED_ASSERT(optlen == sizeof(bool)); + bool *enabled = (bool *)optval; + if (socket->proto == NSAPI_TCP) { + socket->tls_socket = enabled; + + if (enabled) { + _at.at_cmd_discard("+QSSLCFG", "=\"seclevel\",", "%d%d", sslctxID, _tls_sec_level); + + _at.at_cmd_discard("+QSSLCFG", "=\"sslversion\",", "%d%d", sslctxID, BG96_SUPPORTED_SSL_VERSION); + + _at.cmd_start("AT+QSSLCFG=\"ciphersuite\","); + _at.write_int(sslctxID); + _at.write_string(BG96_SUPPORTED_CIPHER_SUITE, false); + _at.cmd_stop_read_resp(); + + ret = _at.get_last_error(); + } + } else { + tr_error("Trying to set non-TCPSocket as TLSSocket"); + ret = NSAPI_ERROR_PARAMETER; + } + } + break; + + case NSAPI_TLSSOCKET_SET_HOSTNAME: { + const char *hostname = (const char *)optval; + _at.at_cmd_discard("+QSSLCFG", "=\"checkhost\",", "%d%s", sslctxID, hostname); + ret = _at.get_last_error(); + } + break; + + case NSAPI_TLSSOCKET_SET_CACERT: { + const char *cacert = (const char *)optval; + ret = set_to_modem_impl("cacert.pem", "cacert", cacert, optlen); + + // Set sec level to "Manage server authentication" if only cacert is in use + if (ret == NSAPI_ERROR_OK && _tls_sec_level == 0) { + _tls_sec_level = 1; + } + } + break; + + case NSAPI_TLSSOCKET_SET_CLCERT: { + const char *clcert = (const char *)optval; + ret = set_to_modem_impl("clcert.pem", "clientcert", clcert, optlen); + + // Set sec level to "Manage server and client authentication if requested by the remote server" + if (ret == NSAPI_ERROR_OK) { + _tls_sec_level = 2; + } + } + break; + + case NSAPI_TLSSOCKET_SET_CLKEY: { + const char *clkey = (const char *)optval; + ret = set_to_modem_impl("client.key", "clientkey", clkey, optlen); + + // Set sec level to "Manage server and client authentication if requested by the remote server" + if (ret == NSAPI_ERROR_OK) { + _tls_sec_level = 2; + } + } + break; + + default: + tr_error("Unsupported sockopt (%d)", optname); + ret = NSAPI_ERROR_UNSUPPORTED; + } + _at.unlock(); + } else { + tr_error("No optval!"); + ret = NSAPI_ERROR_PARAMETER; + } + } else { + tr_warning("Unsupported level (%d)", level); + ret = NSAPI_ERROR_UNSUPPORTED; + } + + return ret; +} diff --git a/features/cellular/framework/targets/QUECTEL/BG96/QUECTEL_BG96_CellularStack.h b/features/cellular/framework/targets/QUECTEL/BG96/QUECTEL_BG96_CellularStack.h index 0552f811f5e..e056344b73b 100644 --- a/features/cellular/framework/targets/QUECTEL/BG96/QUECTEL_BG96_CellularStack.h +++ b/features/cellular/framework/targets/QUECTEL/BG96/QUECTEL_BG96_CellularStack.h @@ -55,6 +55,9 @@ class QUECTEL_BG96_CellularStack : public AT_CellularStack { virtual nsapi_error_t gethostbyname_async_cancel(int id); #endif + virtual nsapi_error_t setsockopt(nsapi_socket_t handle, int level, + int optname, const void *optval, unsigned optlen); + protected: // AT_CellularStack virtual int get_max_socket_count(); @@ -79,7 +82,9 @@ class QUECTEL_BG96_CellularStack : public AT_CellularStack { // URC handler for socket being closed void urc_qiurc_closed(); - void handle_open_socket_response(int &modem_connect_id, int &err); + void handle_open_socket_response(int &modem_connect_id, int &err, bool tlssocket); + + nsapi_error_t set_to_modem_impl(const char *filename, const char *config, const char *data, size_t size); #ifdef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES // URC handler for DNS query @@ -89,6 +94,9 @@ class QUECTEL_BG96_CellularStack : public AT_CellularStack { hostbyname_cb_t _dns_callback; nsapi_version_t _dns_version; #endif + + uint8_t _tls_sec_level; + /** Convert IP address to dotted string representation * * BG96 requires consecutive zeros so can't use get_ip_address or ip6tos directly. diff --git a/features/netsocket/TLSSocket.cpp b/features/netsocket/TLSSocket.cpp index bb0c7eb9e4e..359613a98b3 100644 --- a/features/netsocket/TLSSocket.cpp +++ b/features/netsocket/TLSSocket.cpp @@ -20,6 +20,8 @@ #define TRACE_GROUP "TLSS" #include "mbed-trace/mbed_trace.h" +#if !defined(MBED_CONF_NSAPI_OFFLOAD_TLSSOCKET) || !(MBED_CONF_NSAPI_OFFLOAD_TLSSOCKET) + // This class requires Mbed TLS SSL/TLS client code #if defined(MBEDTLS_SSL_CLI_C) @@ -46,5 +48,71 @@ TLSSocket::~TLSSocket() */ close(); } - #endif // MBEDTLS_SSL_CLI_C + +#else // MBED_CONF_NSAPI_OFFLOAD_TLSSOCKET + +TLSSocket::TLSSocket() +{ +} + +TLSSocket::~TLSSocket() +{ +} + + +nsapi_error_t TLSSocket::set_hostname(const char *hostname) +{ + return setsockopt(NSAPI_TLSSOCKET_LEVEL, NSAPI_TLSSOCKET_SET_HOSTNAME, hostname, strlen(hostname)); +} + +nsapi_error_t TLSSocket::set_root_ca_cert(const void *root_ca, size_t len) +{ + return setsockopt(NSAPI_TLSSOCKET_LEVEL, NSAPI_TLSSOCKET_SET_CACERT, root_ca, len); +} + +nsapi_error_t TLSSocket::set_root_ca_cert(const char *root_ca_pem) +{ + return set_root_ca_cert(root_ca_pem, strlen(root_ca_pem)); +} + +nsapi_error_t TLSSocket::set_client_cert_key(const void *client_cert, size_t client_cert_len, + const void *client_private_key_pem, size_t client_private_key_len) +{ + nsapi_error_t ret = setsockopt(NSAPI_TLSSOCKET_LEVEL, NSAPI_TLSSOCKET_SET_CLCERT, client_cert, client_cert_len); + if (ret == NSAPI_ERROR_OK) { + ret = setsockopt(NSAPI_TLSSOCKET_LEVEL, NSAPI_TLSSOCKET_SET_CLKEY, client_private_key_pem, client_private_key_len); + } + return ret; +} + +nsapi_error_t TLSSocket::set_client_cert_key(const char *client_cert_pem, const char *client_private_key_pem) +{ + return set_client_cert_key(client_cert_pem, strlen(client_cert_pem), client_private_key_pem, strlen(client_private_key_pem)); +} + +nsapi_error_t TLSSocket::connect(const char *host, uint16_t port) +{ + nsapi_error_t ret = enable_tlssocket(); + if (ret == NSAPI_ERROR_OK) { + ret = TCPSocket::connect(host, port); + } + return ret; +} + +nsapi_error_t TLSSocket::connect(const SocketAddress &address) +{ + nsapi_error_t ret = enable_tlssocket(); + if (ret == NSAPI_ERROR_OK) { + ret = TCPSocket::connect(address); + } + return ret; +} + +nsapi_error_t TLSSocket::enable_tlssocket() +{ + bool enabled = true; + return setsockopt(NSAPI_TLSSOCKET_LEVEL, NSAPI_TLSSOCKET_ENABLE, &enabled, sizeof(enabled)); +} + +#endif // MBED_CONF_NSAPI_OFFLOAD_TLSSOCKET diff --git a/features/netsocket/TLSSocket.h b/features/netsocket/TLSSocket.h index 1ce1e7d3d38..98b4347f70f 100644 --- a/features/netsocket/TLSSocket.h +++ b/features/netsocket/TLSSocket.h @@ -23,7 +23,6 @@ #define _MBED_HTTPS_TLS_TCP_SOCKET_H_ #include "netsocket/TCPSocket.h" -#include "TLSSocketWrapper.h" #include "mbedtls/platform.h" #include "mbedtls/ssl.h" @@ -31,9 +30,13 @@ #include "mbedtls/ctr_drbg.h" #include "mbedtls/error.h" +#if !defined(MBED_CONF_NSAPI_OFFLOAD_TLSSOCKET) || !(MBED_CONF_NSAPI_OFFLOAD_TLSSOCKET) + // This class requires Mbed TLS SSL/TLS client code #if defined(MBEDTLS_SSL_CLI_C) || defined(DOXYGEN_ONLY) +#include "TLSSocketWrapper.h" + /** * \brief TLSSocket is a wrapper around TCPSocket for interacting with TLS servers. * @@ -62,7 +65,7 @@ class TLSSocket : public TLSSocketWrapper { * clear internal TLS memory structures. * * @param stack Network stack as target for socket. - * @return 0 on success, negative error code on failure. + * @return NSAPI_ERROR_OK on success, negative error code on failure. */ virtual nsapi_error_t open(NetworkStack *stack) { @@ -87,15 +90,80 @@ class TLSSocket : public TLSSocketWrapper { * * @param host Hostname of the remote host. * @param port Port of the remote host. - * @return 0 on success, negative error code on failure. + * @return NSAPI_ERROR_OK on success, negative error code on failure. */ nsapi_error_t connect(const char *host, uint16_t port); private: TCPSocket tcp_socket; }; - #endif // MBEDTLS_SSL_CLI_C + +#else // MBED_CONF_NSAPI_OFFLOAD_TLSSOCKET + +class TLSSocket : public TCPSocket { +public: + TLSSocket(); + virtual ~TLSSocket(); + + /** Set hostname. + * + * TLSSocket requires hostname used to verify the certificate. + * If hostname is not given in constructor, this function must be used before + * starting the TLS handshake. + * + * @param hostname Hostname of the remote host, used for certificate checking. + */ + nsapi_error_t set_hostname(const char *hostname); + + /** Sets the certification of Root CA. + * + * @note Must be called after open() before calling connect() + * + * @param root_ca Root CA Certificate in any Mbed TLS-supported format. + * @param len Length of certificate (including terminating 0 for PEM). + * @return NSAPI_ERROR_OK on success, negative error code on failure. + */ + virtual nsapi_error_t set_root_ca_cert(const void *root_ca, size_t len); + + /** Sets the certification of Root CA. + * + * @note Must be called after open() before calling connect() + * + * @param root_ca_pem Root CA Certificate in PEM format. + */ + virtual nsapi_error_t set_root_ca_cert(const char *root_ca_pem); + + + /** Sets client certificate, and client private key. + * + * @param client_cert Client certification in PEM or DER format. + * @param client_cert_len Certificate size including the terminating null byte for PEM data. + * @param client_private_key_pem Client private key in PEM or DER format. + * @param client_private_key_len Key size including the terminating null byte for PEM data + * @return NSAPI_ERROR_OK on success, negative error code on failure. + */ + virtual nsapi_error_t set_client_cert_key(const void *client_cert, size_t client_cert_len, + const void *client_private_key_pem, size_t client_private_key_len); + + /** Sets client certificate, and client private key. + * + * @param client_cert_pem Client certification in PEM format. + * @param client_private_key_pem Client private key in PEM format. + * @return NSAPI_ERROR_OK on success, negative error code on failure. + */ + virtual nsapi_error_t set_client_cert_key(const char *client_cert_pem, const char *client_private_key_pem); + + // From TCPSocket + virtual nsapi_error_t connect(const char *host, uint16_t port); + virtual nsapi_error_t connect(const SocketAddress &address); + +protected: + virtual nsapi_error_t enable_tlssocket(); +}; + +#endif // MBED_CONF_NSAPI_OFFLOAD_TLSSOCKET + #endif // _MBED_HTTPS_TLS_TCP_SOCKET_H_ /** @} */ diff --git a/features/netsocket/mbed_lib.json b/features/netsocket/mbed_lib.json index 002c5badaa3..352fa88a74d 100644 --- a/features/netsocket/mbed_lib.json +++ b/features/netsocket/mbed_lib.json @@ -65,6 +65,10 @@ "socket-stats-max-count": { "help": "Maximum number of socket statistics cached", "value": 10 + }, + "offload-tlssocket" : { + "help": "Use external TLSSocket implementation. Used network stack must support external TLSSocket setsockopt values (see nsapi_types.h)", + "value": null } }, "target_overrides": { diff --git a/features/netsocket/nsapi_types.h b/features/netsocket/nsapi_types.h index 00e07588d05..c640aa7dcd4 100644 --- a/features/netsocket/nsapi_types.h +++ b/features/netsocket/nsapi_types.h @@ -269,6 +269,18 @@ typedef enum nsapi_socket_option { NSAPI_BIND_TO_DEVICE, /*!< Bind socket network interface name*/ } nsapi_socket_option_t; +typedef enum nsapi_tlssocket_level { + NSAPI_TLSSOCKET_LEVEL = 7099, /*!< TLSSocket option level - see nsapi_tlssocket_option_t for options*/ +} nsapi_tlssocket_level_t; + +typedef enum nsapi_tlssocket_option { + NSAPI_TLSSOCKET_SET_HOSTNAME, /*!< Set host name */ + NSAPI_TLSSOCKET_SET_CACERT, /*!< Set server CA certificate */ + NSAPI_TLSSOCKET_SET_CLCERT, /*!< Set client certificate */ + NSAPI_TLSSOCKET_SET_CLKEY, /*!< Set client key */ + NSAPI_TLSSOCKET_ENABLE /*!< Enable TLSSocket */ +} nsapi_tlssocket_option_t; + /** Supported IP protocol versions of IP stack * * @enum nsapi_ip_stack