LCOV - code coverage report
Current view: top level - sgx/src/untrusted - io.c (source / functions) Hit Total Coverage
Test: powHSM firmware Lines: 86 108 79.6 %
Date: 2025-07-10 13:49:13 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /**
       2             :  * The MIT License (MIT)
       3             :  *
       4             :  * Copyright (c) 2021 RSK Labs Ltd
       5             :  *
       6             :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       7             :  * of this software and associated documentation files (the "Software"), to
       8             :  * deal in the Software without restriction, including without limitation the
       9             :  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
      10             :  * sell copies of the Software, and to permit persons to whom the Software is
      11             :  * furnished to do so, subject to the following conditions:
      12             :  *
      13             :  * The above copyright notice and this permission notice shall be included in
      14             :  * all copies or substantial portions of the Software.
      15             :  *
      16             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      17             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      18             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      19             :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      20             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      21             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
      22             :  * IN THE SOFTWARE.
      23             :  */
      24             : 
      25             : #include <stdbool.h>
      26             : #include <string.h>
      27             : #include <unistd.h>
      28             : #include <netdb.h>
      29             : #include <netinet/tcp.h>
      30             : #include <sys/ioctl.h>
      31             : 
      32             : #include "io.h"
      33             : #include "log.h"
      34             : 
      35             : /**
      36             :  * APDU buffer
      37             :  */
      38             : unsigned char io_apdu_buffer[APDU_BUFFER_SIZE];
      39             : 
      40             : /**
      41             :  * For the TCP server
      42             :  */
      43             : int serverfd;
      44             : int connfd;
      45             : struct sockaddr_in servaddr, cliaddr;
      46             : 
      47          28 : static void close_and_reset_fd(int *fd) {
      48          28 :     if (fd && (*fd != -1)) {
      49          28 :         close(*fd);
      50          28 :         *fd = -1;
      51             :     }
      52          28 : }
      53             : 
      54          13 : static int start_server(int port, const char *host) {
      55             :     int sockfd;
      56             :     struct hostent *hostinfo;
      57          13 :     hostinfo = gethostbyname(host);
      58             : 
      59          13 :     if (hostinfo == NULL) {
      60           0 :         LOG("Host not found.\n");
      61           0 :         return -1;
      62             :     }
      63             : 
      64             :     // socket create and verification
      65          13 :     sockfd = socket(AF_INET, SOCK_STREAM, 0);
      66          13 :     if (sockfd == -1) {
      67           0 :         LOG("Socket creation failed...\n");
      68           0 :         return -1;
      69             :     }
      70             : 
      71          13 :     explicit_bzero(&servaddr, sizeof(servaddr));
      72             : 
      73          13 :     if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) <
      74             :         0) {
      75           0 :         LOG("Socket option setting failed failed\n");
      76           0 :         return -1;
      77             :     }
      78             : 
      79          13 :     if (setsockopt(sockfd, SOL_TCP, TCP_NODELAY, &(int){1}, sizeof(int)) < 0) {
      80           0 :         LOG("Socket option setting failed failed\n");
      81           0 :         return -1;
      82             :     }
      83             : 
      84             :     // Set address and port
      85          13 :     servaddr.sin_family = AF_INET;
      86          13 :     memcpy(&servaddr.sin_addr, hostinfo->h_addr_list[0], hostinfo->h_length);
      87          13 :     servaddr.sin_port = htons(port);
      88             : 
      89             :     // Binding newly created socket to given IP and verification
      90          13 :     if ((bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) != 0) {
      91           0 :         LOG("Socket bind failed...\n");
      92           0 :         return -1;
      93             :     }
      94             : 
      95             :     // Now server is ready to listen and verification
      96          13 :     if ((listen(sockfd, 5)) != 0) {
      97           0 :         LOG("Listen failed...\n");
      98           0 :         return -1;
      99             :     }
     100             : 
     101          13 :     LOG("Server listening...\n");
     102          13 :     return sockfd;
     103             : }
     104             : 
     105          15 : static bool accept_connection() {
     106          15 :     socklen_t len = sizeof(cliaddr);
     107          15 :     connfd = accept(serverfd, (struct sockaddr *)&cliaddr, &len);
     108          15 :     if (connfd == -1) {
     109           0 :         LOG("Client connection failed...\n");
     110           0 :         return 0;
     111             :     }
     112             : 
     113          15 :     LOG("Client connected...\n");
     114          15 :     return connfd != -1;
     115             : }
     116             : 
     117          13 : bool io_init(int port, const char *host) {
     118          13 :     connfd = -1;
     119          13 :     serverfd = start_server(port, host);
     120          13 :     return (serverfd != -1);
     121             : }
     122             : 
     123          13 : void io_finalise() {
     124          13 :     close_and_reset_fd(&connfd);
     125          13 :     close_and_reset_fd(&serverfd);
     126          13 : }
     127             : 
     128             : #define CHECK_READ_STATUS(read_result, err_prefix)                            \
     129             :     {                                                                         \
     130             :         if ((read_result) == 0) {                                             \
     131             :             LOG("%s: connection closed by the client\n", err_prefix);         \
     132             :             close_and_reset_fd(&connfd);                                      \
     133             :             goto end_readloop;                                                \
     134             :         } else if ((read_result) == -1) {                                     \
     135             :             LOG("%s: error reading from socket. Disconnected\n", err_prefix); \
     136             :             close_and_reset_fd(&connfd);                                      \
     137             :             goto end_readloop;                                                \
     138             :         }                                                                     \
     139             :     }
     140             : 
     141          25 : unsigned short io_exchange(unsigned short tx) {
     142             :     enum {
     143             :         READ_LENGTH,
     144             :         READ_PLOAD,
     145             :         SKIP_PLOAD,
     146             :     } read_state;
     147             : 
     148           2 :     while (true) {
     149          27 :         if (connfd == -1) {
     150          15 :             if (!accept_connection()) {
     151           0 :                 LOG("Error accepting client connection\n");
     152          25 :                 return 0;
     153             :             }
     154          15 :             tx = 0;
     155             :         }
     156             : 
     157             :         // Write len (Compatibility with LedgerBlue commTCP.py)
     158          27 :         if (tx > 0) {
     159             :             // Write APDU length minus two bytes of the sw
     160             :             // (encoded in 4 bytes network byte-order)
     161             :             // (compatibility with LedgerBlue commTCP.py)
     162          11 :             uint32_t tx_net = tx - 2;
     163          11 :             tx_net = htonl(tx_net);
     164          11 :             if (send(connfd, &tx_net, sizeof(tx_net), MSG_NOSIGNAL) == -1) {
     165           0 :                 LOG("Connection closed by the client\n");
     166           0 :                 close_and_reset_fd(&connfd);
     167           0 :                 continue;
     168             :             }
     169             :             // Write APDU
     170          11 :             if (send(connfd, io_apdu_buffer, tx, MSG_NOSIGNAL) == -1) {
     171           0 :                 LOG("Connection closed by the client\n");
     172           0 :                 close_and_reset_fd(&connfd);
     173           0 :                 continue;
     174             :             }
     175          11 :             LOG_HEX("I/O =>", io_apdu_buffer, tx);
     176             :         }
     177             : 
     178             :         // Read from buffer until whole APDU is read or something happens
     179          27 :         read_state = READ_LENGTH;
     180             :         ssize_t readlen;
     181             :         uint32_t rx_net;
     182             :         uint8_t len_buf[sizeof(rx_net)];
     183             :         char discard_buffer[1024 * 1024];
     184             :         unsigned int rx;
     185          27 :         unsigned int offset = 0;
     186             :         while (true) {
     187          46 :             switch (read_state) {
     188          30 :             case READ_LENGTH:
     189             :                 readlen =
     190          30 :                     read(connfd, len_buf + offset, sizeof(len_buf) - offset);
     191          30 :                 CHECK_READ_STATUS(readlen, "Error while reading APDU length");
     192          29 :                 offset += readlen;
     193          29 :                 if (offset == sizeof(len_buf)) {
     194          26 :                     memcpy(&rx_net, len_buf, sizeof(rx_net));
     195          26 :                     rx = ntohl(rx_net);
     196             :                     // Empty packet?
     197          26 :                     if (rx == 0) {
     198          13 :                         LOG("I/O <= <EMPTY MESSAGE>\n");
     199          13 :                         return 0;
     200          13 :                     } else if (rx > sizeof(io_apdu_buffer) ||
     201          12 :                                rx != (unsigned short)rx) {
     202           1 :                         LOG("Client tried to send a message "
     203             :                             "that was too big (%u bytes). "
     204             :                             "Skipping payload.\n",
     205             :                             rx);
     206             :                         // Move onto skipping the APDU payload
     207           1 :                         read_state = SKIP_PLOAD;
     208           1 :                         offset = 0;
     209             :                     } else {
     210             :                         // Move onto reading the APDU payload
     211          12 :                         read_state = READ_PLOAD;
     212          12 :                         offset = 0;
     213             :                     }
     214             :                 }
     215          16 :                 break;
     216          15 :             case READ_PLOAD:
     217          15 :                 readlen = read(connfd, io_apdu_buffer + offset, rx - offset);
     218          15 :                 CHECK_READ_STATUS(readlen, "Error while reading APDU payload");
     219          14 :                 offset += readlen;
     220          14 :                 if (offset == rx) {
     221          11 :                     LOG("I/O <= (%u bytes)", rx);
     222          11 :                     LOG_HEX("", io_apdu_buffer, rx);
     223          11 :                     return (unsigned short)rx;
     224             :                 }
     225           3 :                 break;
     226           1 :             case SKIP_PLOAD:
     227           1 :                 readlen = read(connfd, discard_buffer, sizeof(discard_buffer));
     228           1 :                 CHECK_READ_STATUS(readlen, "Error while skipping APDU payload");
     229           1 :                 offset += readlen;
     230           1 :                 if (offset == rx) {
     231           1 :                     LOG("I/O <= (%u bytes) <SKIPPED PAYLOAD>\n", rx);
     232           1 :                     memset(io_apdu_buffer, 0, sizeof(io_apdu_buffer));
     233           1 :                     return rx != (unsigned short)rx ? (unsigned short)0xFFFFFFFF
     234           1 :                                                     : (unsigned short)rx;
     235             :                 }
     236           0 :                 break;
     237             :             }
     238             :         }
     239           2 :     end_readloop:;
     240             :     }
     241             : }

Generated by: LCOV version 1.16