tls_client.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. /*
  2. * Copyright (C) 2014 Ole Reinhardt <ole.reinhardt@embedded-it.de>
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * 3. Neither the name of the copyright holders nor the names of
  16. * contributors may be used to endorse or promote products derived
  17. * from this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  22. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  23. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  24. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  25. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  26. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  27. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  29. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30. * SUCH DAMAGE.
  31. *
  32. * For additional information see http://www.ethernut.de/
  33. */
  34. /*!
  35. * \example tls_client/tls_client.c
  36. *
  37. * Requests an URL from the Internet using a SSL/TLS connection
  38. * and transfers the HTML source code to the serial device.
  39. *
  40. * Your local Ethernet network must provide Internet access.
  41. * Connect the RS232 port of the Ethernut with a free COM
  42. * port of your PC and run a terminal emulator at 115200 Baud.
  43. *
  44. * If your local network does not support DHCP, it may be
  45. * required to modify the MY_IP, MY_MASK and MY_GATE below.
  46. *
  47. * This sample demonstrates the tls library in client mode usage.
  48. * It creates a TLS connection and prints out the server certificate,
  49. * the used cypher suite and session parameters.
  50. *
  51. * To use this example, you need to enable the TLS and crypto library
  52. * in your configuration.
  53. * You should enable the following settings in your my_board.conf
  54. * file and re-configure your build tree:
  55. *
  56. * CRYPTO_BIGINT_BARRETT = ""
  57. * CRYPTO_BIGINT_CRT = ""
  58. * CRYPTO_BIGINT_SLIDING_WINDOW = ""
  59. * CRYPTO_BIGINT_SQUARE = ""
  60. * TLS_SSL_ENABLE_CLIENT = ""
  61. * TLS_SSL_PROT_MEDIUM = ""
  62. * TLS_SSL_ENABLE_V23_HANDSHAKE = ""
  63. */
  64. #include <toolchain.h>
  65. #include <cfg/arch.h>
  66. #include <sys/thread.h>
  67. #include <sys/heap.h>
  68. #include <sys/time.h>
  69. #include <sys/timer.h>
  70. #include <sys/socket.h>
  71. #include <sys/confnet.h>
  72. #include <sys/heap.h>
  73. #include <sys/version.h>
  74. #include <sys/nutdebug.h>
  75. #include <arpa/inet.h>
  76. #include <net/route.h>
  77. #include <netdb.h>
  78. #include <pro/dhcp.h>
  79. #include <pro/sntp.h>
  80. #include <dev/board.h>
  81. #include <dev/debug.h>
  82. #include <errno.h>
  83. #include <gorp/edline.h>
  84. #include <stdio.h>
  85. #include <string.h>
  86. #include <stdlib.h>
  87. #include <io.h>
  88. #include <tls/ssl.h>
  89. #include "cert.h"
  90. #include "private_key.h"
  91. #define DBG_BAUDRATE 115200
  92. #define DNSSERVERIP "192.168.1.254"
  93. #define MY_MAC {0x02,0x06,0x98,0x20,0x00,0x00}
  94. #define MY_IP "192.168.1.10"
  95. #define MY_MASK "255.255.255.0"
  96. #define MY_GATE "192.168.1.254"
  97. #define MYTZ -1 /* This is CET */
  98. #define MYTIMED "130.149.17.21"
  99. typedef struct {
  100. char *schm_uri;
  101. char *schm_user;
  102. char *schm_pass;
  103. char *schm_host;
  104. char *schm_port;
  105. char *schm_path;
  106. uint16_t schm_portnum;
  107. } HTTP_SCHEME;
  108. /**
  109. * Display what cipher we are using
  110. */
  111. static void display_cipher(SSL *ssl)
  112. {
  113. printf("CIPHER is ");
  114. switch (ssl_get_cipher_id(ssl))
  115. {
  116. case SSL_AES128_SHA:
  117. printf("AES128-SHA");
  118. break;
  119. case SSL_AES256_SHA:
  120. printf("AES256-SHA");
  121. break;
  122. case SSL_RC4_128_SHA:
  123. printf("RC4-SHA");
  124. break;
  125. case SSL_RC4_128_MD5:
  126. printf("RC4-MD5");
  127. break;
  128. default:
  129. printf("Unknown - %d", ssl_get_cipher_id(ssl));
  130. break;
  131. }
  132. printf("\n");
  133. }
  134. /**
  135. * Display what session id we have.
  136. */
  137. static void display_session_id(SSL *ssl)
  138. {
  139. int i;
  140. const uint8_t *session_id = ssl_get_session_id(ssl);
  141. int sess_id_size = ssl_get_session_id_size(ssl);
  142. if (sess_id_size > 0)
  143. {
  144. printf("\n-----BEGIN SSL SESSION PARAMETERS-----\n");
  145. for (i = 0; i < sess_id_size; i++)
  146. {
  147. printf("%02x", session_id[i]);
  148. }
  149. printf("\n-----END SSL SESSION PARAMETERS-----\n\n");
  150. }
  151. }
  152. /*!
  153. * \brief Release a HTTP scheme structure.
  154. *
  155. * \param schm Pointer to a \ref HttpSchemeParse "previously allocated"
  156. * HTTP scheme structure.
  157. */
  158. void HttpSchemeRelease(HTTP_SCHEME *schm)
  159. {
  160. if (schm) {
  161. if (schm->schm_uri) {
  162. free(schm->schm_uri);
  163. }
  164. free(schm);
  165. }
  166. }
  167. /*!
  168. * \brief Create a HTTP scheme structure from a URI.
  169. *
  170. * \param uri URI to parse. The expected format is
  171. * \code
  172. * [<user>[:<password>]@]<host>[:<port>][/<path>]
  173. * \endcode
  174. *
  175. * \return Pointer to an allocated \ref HTTP_SCHEME "HTTP scheme"
  176. * structure. If this structure is no longer used, the caller
  177. * must call HttpSchemeRelease(). In case of an error, NULL is
  178. * returned.
  179. */
  180. HTTP_SCHEME *HttpSchemeParse(CONST char *uri)
  181. {
  182. HTTP_SCHEME *schm = NULL;
  183. char *cp;
  184. /* Create a blank scheme structure. */
  185. if (*uri && (schm = malloc(sizeof(HTTP_SCHEME))) != NULL) {
  186. memset(schm, 0, sizeof(HTTP_SCHEME));
  187. /* Create a local copy of the URI string. */
  188. if ((schm->schm_uri = strdup(uri)) != NULL) {
  189. /* Split the local copy. */
  190. schm->schm_host = schm->schm_uri;
  191. for (cp = schm->schm_uri; *cp; cp++) {
  192. if (*cp == ':') {
  193. *cp = '\0';
  194. schm->schm_port = cp + 1;
  195. }
  196. else if (*cp == '/') {
  197. *cp = 0;
  198. schm->schm_path = cp + 1;
  199. break;
  200. }
  201. else if (*cp == '@') {
  202. *cp = 0;
  203. schm->schm_user = schm->schm_host;
  204. schm->schm_pass = schm->schm_port;
  205. schm->schm_host = cp + 1;
  206. schm->schm_port = NULL;
  207. }
  208. }
  209. if (schm->schm_port) {
  210. schm->schm_portnum = (uint16_t)atoi(schm->schm_port);
  211. }
  212. else {
  213. schm->schm_portnum = 80;
  214. }
  215. return schm;
  216. }
  217. }
  218. HttpSchemeRelease(schm);
  219. return NULL;
  220. }
  221. /**
  222. * Implement the SSL/TLS client logic.
  223. */
  224. static int tls_client(char *uri)
  225. {
  226. SSL_CTX *ssl_ctx;
  227. SSL *ssl = NULL;
  228. TCPSOCKET *sock;
  229. int client_fd;
  230. uint32_t remote_ip;
  231. uint32_t port = 443;
  232. uint32_t options = SSL_SERVER_VERIFY_LATER /*| SSL_DISPLAY_CERTS | SSL_DISPLAY_STATES | SSL_NO_DEFAULT_KEY */;
  233. uint32_t timeout;
  234. int rc = 0;
  235. HTTP_SCHEME *http_sch;
  236. http_sch = HttpSchemeParse(uri);
  237. remote_ip = NutDnsGetHostByName((const uint8_t*)http_sch->schm_host);
  238. if (http_sch->schm_port) {
  239. port = atoi (http_sch->schm_port);
  240. }
  241. if ((ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_CLNT_SESS)) == NULL) {
  242. NUTPANIC("Error: Client context is invalid\n");
  243. }
  244. ssl_obj_memory_load(ssl_ctx, SSL_OBJ_RSA_KEY, my_private_key,
  245. my_private_key_len, NULL);
  246. ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT, my_certificate,
  247. my_certificate_len, NULL);
  248. /* If you have enough RAM, you could load the CA bundle. The server
  249. certiciate will be validated then.
  250. if (ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CACERT, ca_bundle_crt, sizeof(ca_bundle_crt), NULL))
  251. {
  252. printf("Error loading ca-bundle\n");
  253. }
  254. */
  255. if ((sock = NutTcpCreateSocket()) != 0) {
  256. /* Define a connect timeout of 5ms */
  257. timeout = 1000;
  258. NutTcpSetSockOpt(sock, SO_SNDTIMEO, &timeout, sizeof(timeout));
  259. if ((rc = NutTcpConnect(sock, remote_ip, port)) == 0) {
  260. /* Reset the write timeout to infinite */
  261. timeout = 1000;
  262. NutTcpSetSockOpt(sock, SO_SNDTIMEO, &timeout, sizeof(timeout));
  263. timeout = 100;
  264. NutTcpSetSockOpt(sock, SO_RCVTIMEO, &timeout, sizeof(timeout));
  265. client_fd = (int)sock;
  266. ssl = ssl_client_new(ssl_ctx, client_fd, NULL, 0);
  267. /* check the return status */
  268. if ((rc = ssl_handshake_status(ssl)) != SSL_OK) {
  269. ssl_display_error(rc);
  270. ssl_free(ssl);
  271. ssl_ctx_free(ssl_ctx);
  272. NutTcpCloseSocket(sock);
  273. HttpSchemeRelease(http_sch);
  274. return -1;
  275. }
  276. printf("SSL session established: RAM: %d\n", NutHeapAvailable()); fflush(stdout);
  277. printf("Server cert detailes:\n");
  278. const char *common_name = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME);
  279. printf("Common Name (CN) : %s\n", common_name);
  280. const char *organisation = ssl_get_cert_dn(ssl, SSL_X509_CERT_ORGANIZATION);
  281. printf("Organisation (O) : %s\n", organisation);
  282. const char *organisational_unit = ssl_get_cert_dn(ssl, SSL_X509_CERT_ORGANIZATIONAL_NAME);
  283. printf("Organisational Unit (OU) : %s\n", organisational_unit);
  284. common_name = ssl_get_cert_dn(ssl, SSL_X509_CA_CERT_COMMON_NAME);
  285. printf("CA-Common Name (CN) : %s\n", common_name);
  286. organisation = ssl_get_cert_dn(ssl, SSL_X509_CA_CERT_ORGANIZATION);
  287. printf("CA-Organisation (O) : %s\n", organisation);
  288. organisational_unit = ssl_get_cert_dn(ssl, SSL_X509_CA_CERT_ORGANIZATIONAL_NAME);
  289. printf("CA-Organisational Unit (OU): %s\n", organisational_unit);
  290. display_session_id(ssl);
  291. display_cipher(ssl);
  292. printf("Verify server certificate: ");
  293. switch(ssl_verify_cert(ssl)) {
  294. case SSL_X509_ERROR(X509_OK): printf("OK\n"); break;
  295. case SSL_X509_ERROR(X509_NOT_OK): printf("NOT OK\n"); break;
  296. case SSL_X509_ERROR(X509_VFY_ERROR_NO_TRUSTED_CERT): printf("Not trusted\n"); break;
  297. case SSL_X509_ERROR(X509_VFY_ERROR_BAD_SIGNATURE): printf("Bad signature\n"); break;
  298. case SSL_X509_ERROR(X509_VFY_ERROR_NOT_YET_VALID): printf("Not yet valid\n"); break;
  299. case SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED): printf("Expired\n"); break;
  300. case SSL_X509_ERROR(X509_VFY_ERROR_SELF_SIGNED): printf("Self signed\n"); break;
  301. case SSL_X509_ERROR(X509_VFY_ERROR_UNSUPPORTED_DIGEST): printf("Unsupported digest\n"); break;
  302. }
  303. char *buf = malloc(1024);
  304. memset(buf, 0, 1024);
  305. sprintf(buf, "GET /%s HTTP/1.1\r\n"
  306. "User-Agent: Ethernut [en] (NutOS)\r\n"
  307. "Host: %s\r\n"
  308. "\r\n", http_sch->schm_path != NULL ? http_sch->schm_path : "", http_sch->schm_host);
  309. rc = ssl_write(ssl, (uint8_t*)buf, strlen(buf));
  310. printf("Available memory after session establishment: %d\n", NutHeapAvailable());
  311. printf("-----------------------------------------------------------\n");
  312. printf("Request Header:\n%s", buf);
  313. printf("-----------------------------------------------------------\n");
  314. free(buf);
  315. if (rc < 0) {
  316. printf("Error on ssl_write\n");
  317. } else
  318. do {
  319. char *inbuf;
  320. rc = ssl_read(ssl, (uint8_t **)&inbuf);
  321. if (rc == 0) {
  322. /* Timeout on read... continue */
  323. continue;
  324. } else
  325. if (rc > 0) {
  326. puts(inbuf);
  327. fflush(stdout);
  328. } else {
  329. if ((rc == SSL_ERROR_CONN_LOST) || (rc == SSL_CLOSE_NOTIFY)) {
  330. printf("-----------------------------------------------------------\n");
  331. printf("Connection closed by foreign host\n");
  332. } else {
  333. printf("-----------------------------------------------------------\n");
  334. printf("Error on SSL read: %d\n", rc);
  335. }
  336. }
  337. } while (rc >= 0);
  338. ssl_free(ssl);
  339. } else {
  340. if (NutTcpError(sock) == ETIMEDOUT) {
  341. printf("Timeout\n"); fflush(stdout);
  342. } else {
  343. printf("Connection refused\n"); fflush(stdout);
  344. }
  345. }
  346. NutTcpCloseSocket(sock);
  347. }
  348. ssl_ctx_free(ssl_ctx);
  349. HttpSchemeRelease(http_sch);
  350. printf("\nEverything cleaned up: RAM: %d\n", NutHeapAvailable()); fflush(stdout);
  351. return 0;
  352. }
  353. /*!
  354. * \brief This thread handles the TLS connection and implements a basic TLS client
  355. *
  356. */
  357. THREAD(TlsClient, arg)
  358. {
  359. char input[64] = "";
  360. char *url = NULL;
  361. EDLINE *ed;
  362. for(;;) {
  363. puts ("-----------------------------------------------------------\n");
  364. puts ("Please enter URL: ");
  365. strcpy(input, "https://");
  366. ed = EdLineOpen(EDIT_MODE_ECHO);
  367. EdLineRead(ed, input, sizeof(input));
  368. EdLineClose(ed);
  369. printf("\n");
  370. if (strncasecmp(input, "https://", 8) == 0) {
  371. url = &input[8];
  372. } else
  373. if (strncasecmp(input, "http://", 7) == 0) {
  374. url = &input[7];
  375. } else{
  376. url = input;
  377. }
  378. tls_client(url);
  379. }
  380. }
  381. /*!
  382. * \brief Main application routine.
  383. *
  384. */
  385. int main(void)
  386. {
  387. FILE *uart;
  388. uint32_t baud = 115200;
  389. uint32_t ip_addr;
  390. uint32_t timeserver = inet_addr(MYTIMED);
  391. time_t now;
  392. static uint8_t my_mac[] = MY_MAC;
  393. NutSleep(200);
  394. NutRegisterDevice(&DEV_CONSOLE, 0, 0);
  395. uart = fopen(DEV_CONSOLE.dev_name, "r+");
  396. _ioctl(_fileno(uart), UART_SETSPEED, &baud);
  397. freopen(DEV_CONSOLE.dev_name, "w", stdout);
  398. freopen(DEV_CONSOLE.dev_name, "w", stderr);
  399. freopen(DEV_CONSOLE.dev_name, "r", stdin);
  400. printf ("TLS Demo - Nut/OS (%s)\n", NutVersionString());
  401. if (NutHeapAvailable() < 50000) {
  402. printf("We do not have enough RAM for the TLS demo...\n");
  403. printf("STOP!\n");
  404. while(1) NutSleep(1000);
  405. }
  406. printf("Configure %s...", DEV_ETHER_NAME);
  407. if (NutRegisterDevice(&DEV_ETHER, 0, 0)) {
  408. NUTPANIC("failed\n");
  409. } else {
  410. if (NutDhcpIfConfig(DEV_ETHER_NAME, my_mac, 20000)) {
  411. ip_addr = inet_addr(MY_IP);
  412. NutNetIfConfig("eth0", my_mac, ip_addr, inet_addr(MY_MASK));
  413. NutIpRouteAdd(0, 0, inet_addr(MY_GATE), &DEV_ETHER);
  414. NutDnsConfig2(0, 0, inet_addr(DNSSERVERIP), 0);
  415. }
  416. printf("%s ready\n\n", inet_ntoa(confnet.cdn_ip_addr));
  417. _timezone = 1 * 60L * 60L;
  418. if (NutSNTPGetTime(&timeserver, &now) == 0) {
  419. stime(&now);
  420. printf("Time: %s\n", ctime(&now));
  421. }
  422. }
  423. NutThreadCreate("tls", TlsClient, NULL, 3096);
  424. for (;;) {
  425. NutSleep(10000);
  426. }
  427. return 0;
  428. }