diff options
| author | Dirk Engling <erdgeist@erdgeist.org> | 2020-12-05 04:27:14 +0100 | 
|---|---|---|
| committer | Dirk Engling <erdgeist@erdgeist.org> | 2020-12-05 04:27:14 +0100 | 
| commit | 5e081b7b50efaef0a100c20c41d2991139deca5e (patch) | |
| tree | f129d75c0db8f7f3d711ffa4a343b5585af7868c | |
| parent | 0730917115f2f2df04ef7d9c075fcef907d29708 (diff) | |
Rework receiver as a c++ programm
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | receiver.cpp | 303 | 
2 files changed, 305 insertions, 2 deletions
| @@ -3,8 +3,8 @@ all: sender receiver | |||
| 3 | sender: sender.c | 3 | sender: sender.c | 
| 4 | $(CC) -o sender sender.c -lmbedtls -lmbedcrypto | 4 | $(CC) -o sender sender.c -lmbedtls -lmbedcrypto | 
| 5 | 5 | ||
| 6 | receiver: receiver.c | 6 | receiver: receiver.cpp | 
| 7 | $(CC) -o receiver receiver.c -lmbedtls -lmbedcrypto | 7 | $(CC) -std=c++17 -o receiver receiver.cpp -lmbedtls -lmbedcrypto -lc++ | 
| 8 | 8 | ||
| 9 | clean: | 9 | clean: | 
| 10 | rm -f sender receiver | 10 | rm -f sender receiver | 
| diff --git a/receiver.cpp b/receiver.cpp new file mode 100644 index 0000000..fdeee05 --- /dev/null +++ b/receiver.cpp | |||
| @@ -0,0 +1,303 @@ | |||
| 1 | #include <regex.h> | ||
| 2 | #include <string.h> | ||
| 3 | #include <dirent.h> | ||
| 4 | #include <err.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include <inttypes.h> | ||
| 7 | #include <fcntl.h> | ||
| 8 | #include <unistd.h> | ||
| 9 | #include <sys/socket.h> | ||
| 10 | #include <netinet/in.h> | ||
| 11 | |||
| 12 | #include <mbedtls/pk.h> | ||
| 13 | #include <mbedtls/entropy.h> | ||
| 14 | #include "mbedtls/ctr_drbg.h" | ||
| 15 | #include "mbedtls/gcm.h" | ||
| 16 | |||
| 17 | #include <string> | ||
| 18 | #include <map> | ||
| 19 | #include <iostream> | ||
| 20 | |||
| 21 | const unsigned short PORT = 58132; | ||
| 22 | |||
| 23 | static const unsigned char privkey[] = | ||
| 24 | "-----BEGIN RSA PRIVATE KEY-----\n" | ||
| 25 | "MIIEpAIBAAKCAQEAwWlNmLHOOzZpdrfp+EAANwqab0FhQwCyZ/u+ySBW5XxPf6mo\n" | ||
| 26 | "bySvtJrLdsWzdwnup/UfwZiEhJk/4wpD4Qf/2+syuJi3Rf7L+Jfh//Qf9uXAS80+\n" | ||
| 27 | "LYad7dW0c1r5nt+F9Can5fBn7futnd8n672T+y8QpHRwX9GtaILvYQe5GQac8cfq\n" | ||
| 28 | "2rGUd5iYj5KSdcaIZnJ4YgnjLHg2PMbtEJwqcV+2oAkcOPzTAJoNE7XjLZTwXmLl\n" | ||
| 29 | "FgL/2cN4uJZBDnwv3RZSAhpdYF4KOJmE2GFs2jdvRUrYO7WSl8fM16vRH4vz5MNN\n" | ||
| 30 | "caprg2MlXheVTPQa+WMdcz7dyQx8s9kRVPPUSwIDAQABAoIBAH1KD8A4flYRO2Ry\n" | ||
| 31 | "YxgzrW/6aGxlt/HFg8ykYcS8NE5Yps8WQkwtQb0HAYKhM06LmpQm0DmC6WVUOPSE\n" | ||
| 32 | "c9BUdEQsKiE2nJK1KcCR8w7xP7uavWTdQcgQCkJFS63mYwmt1oKAgAcOIuUhQiig\n" | ||
| 33 | "pKWrmy7+IBPIcftAQssO9q6uaBNy+ONu6KU/FYd4UAoEt07MzuIAl5rybROOWCrA\n" | ||
| 34 | "cjuOKK830Q2Mi2ESwwlO0+3Vyz9VhSha5wW2WwFv9zx8YQblaVXxfXC6O5XcXb0j\n" | ||
| 35 | "O2rTpgHMmOVik3Zrg3XoRXsNZCvFQqbIevwGhRNEFQTnzakh/5g0VDa54RAK/APC\n" | ||
| 36 | "t3ABEAECgYEA+rnqDqmXQc/xMCLOhJduxRFvZbT6EbUVJG5+l6XtATTa4LWzqx1B\n" | ||
| 37 | "QDXS/Dixxc9FA1vSH2rAW//6wbr8KXihSeI7YxgIfWyrSIxbS3Dd1qwddvniYIpx\n" | ||
| 38 | "ms9vYpQKAewv2p310nf7fyuURES8YikhpSuff3DhzXBEi7s3Y7gIIEsCgYEAxXrD\n" | ||
| 39 | "6xjgLSgbbdyqxKeNk8ADMifbj8ZoiNIHkJ6sShFaTEbyHweOJ4RY7OmTBUDhgzUY\n" | ||
| 40 | "1oynztilADFaq8KhsiMqzI3DgZ3/2OElGYReEAbXljvgudL3tmiBWc5j9S7XNLBe\n" | ||
| 41 | "u9f3WYAYDu7/BVqmQWa/QtD9JquoZ1xgV2D6nAECgYEA29w9t9/VSJvM9xX+nNyi\n" | ||
| 42 | "AON6GOjrRK3TPWA7WEXjH+S2bshHJi0ANAs+2Xfpw/kunnRdPLmCtuowfMO4LbGf\n" | ||
| 43 | "VcexpgLEJyAszvBteikeDwpcyCD19wxP9J4kIYCJiggQKpfLoWUfP/P6DydrPnSt\n" | ||
| 44 | "EUbAlaNqDpl9Mj7YonQVhCMCgYEAv0c9M5+hrDuX7d76zYaZvI4UymUO54E/yZ7e\n" | ||
| 45 | "UvdOXGPYed+SL/oKeD5aQAeyLzl79bHdgBs3g0QW9kvXzly0cC5eC0oZH5hhs7nI\n" | ||
| 46 | "TKII1i86bLtM3dD5vQYWnF0sNtWK/+8Bo6L5ZAiNxRE7lP0L4ndaNKbnPaixcoRo\n" | ||
| 47 | "kNpPhAECgYAg7jmNlw+7VVurzR36LKKE+d6veraF5ltpJiboDb3j38RGe3982LLq\n" | ||
| 48 | "WaBKm1gkHfXgBjkNzS4r2kyRijw0GQ9JgmWooR7BXFH30HkPNl4gFTSsrOG5zGLi\n" | ||
| 49 | "0aexkDpXQuKsgBzqU0Wn94GZMM+RhuOYec/56JFT+8U5Tcntb26wwA==\n" | ||
| 50 | "-----END RSA PRIVATE KEY-----\n"; | ||
| 51 | |||
| 52 | static const unsigned char pp[] = "IJUHZGFDXTZKHJKHGFDHZLUÖDRTFGHHJGHH"; | ||
| 53 | |||
| 54 | // Close files after 20 minutes | ||
| 55 | #define ACCESS_TIMEOUT (20*60) | ||
| 56 | |||
| 57 | /* | ||
| 58 | 1 byte type: 0 allocate session, 1 log to session, rest: discard | ||
| 59 | 8 bytes session id | ||
| 60 | type 0: | ||
| 61 | 256 bytes rsa 2048 data yielding 32 byte AES session key | ||
| 62 | type 1: | ||
| 63 | 16 bytes iv | ||
| 64 | 16 bytes tag | ||
| 65 | rest cipher text | ||
| 66 | |||
| 67 | File name format: 2020-10-13-23-42-05-SSSSSSSSSSSSSSSS-KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK.log | ||
| 68 | |||
| 69 | */ | ||
| 70 | |||
| 71 | // Time helper | ||
| 72 | static time_t now() { | ||
| 73 | struct timespec tp; | ||
| 74 | clock_gettime(CLOCK_MONOTONIC, &tp); | ||
| 75 | return tp.tv_sec; | ||
| 76 | } | ||
| 77 | |||
| 78 | // Constants | ||
| 79 | enum { SESSION_ID_LENGTH = 8, AES_KEY_LENGTH = 16, GCM_IV_LENGTH = 16, GCM_TAG_LENGTH = 16, MIN_PACKET_SIZE = 40 }; | ||
| 80 | enum { FILENAME_LENGTH = 73, SIDOFFS = 20, KEYOFFS = 37 }; | ||
| 81 | |||
| 82 | class Session { | ||
| 83 | public: | ||
| 84 | Session(uint64_t session_id, uint8_t key[AES_KEY_LENGTH], const std::string &filename) : | ||
| 85 | _session_id(session_id), _filename(filename) { | ||
| 86 | memcpy(_key, key, AES_KEY_LENGTH); | ||
| 87 | mbedtls_gcm_init(&_ctx); | ||
| 88 | mbedtls_gcm_setkey(&_ctx, MBEDTLS_CIPHER_ID_AES, _key, 8 * AES_KEY_LENGTH); | ||
| 89 | } | ||
| 90 | |||
| 91 | Session(uint64_t session_id, uint8_t key[AES_KEY_LENGTH]) : _session_id(session_id) { | ||
| 92 | memcpy(_key, key, AES_KEY_LENGTH); | ||
| 93 | mbedtls_gcm_init(&_ctx); | ||
| 94 | mbedtls_gcm_setkey(&_ctx, MBEDTLS_CIPHER_ID_AES, _key, 8 * AES_KEY_LENGTH); | ||
| 95 | |||
| 96 | // Create timestamp | ||
| 97 | char tprefix[32]; | ||
| 98 | time_t t = time(NULL); | ||
| 99 | struct tm * jetzt = localtime(&t); | ||
| 100 | size_t nlen = strftime(tprefix, sizeof(tprefix), "%F-%H-%M-%S", jetzt); | ||
| 101 | |||
| 102 | // Dump key | ||
| 103 | char hexkey[2*AES_KEY_LENGTH + 1]; | ||
| 104 | for (int i=0; i<AES_KEY_LENGTH; ++i) | ||
| 105 | sprintf(hexkey + 2 * i, "%02x", _key[i]); | ||
| 106 | |||
| 107 | // Glue together serialisation | ||
| 108 | char filename[FILENAME_LENGTH + 1]; | ||
| 109 | snprintf(filename, sizeof(filename), "%s-%016" PRIx64 "-%s.log", tprefix, _session_id, hexkey); | ||
| 110 | |||
| 111 | // Touch file to save session_id and key | ||
| 112 | close(open(filename, O_WRONLY|O_CREAT, 0755)); | ||
| 113 | |||
| 114 | _filename = std::string(filename, filename + FILENAME_LENGTH); | ||
| 115 | } | ||
| 116 | |||
| 117 | ~Session() { | ||
| 118 | mbedtls_gcm_free(&_ctx); | ||
| 119 | } | ||
| 120 | |||
| 121 | void write_log(const uint8_t *packet, size_t len) { | ||
| 122 | // First check if the packet holds enough space for session id, iv and at least one gcm block | ||
| 123 | if (len < GCM_IV_LENGTH + GCM_TAG_LENGTH) { | ||
| 124 | std::cerr << "Error: Short packet, size " << len << std::endl; | ||
| 125 | return; | ||
| 126 | } | ||
| 127 | |||
| 128 | const uint8_t *iv = packet; | ||
| 129 | const uint8_t *tag = packet + GCM_IV_LENGTH; | ||
| 130 | const uint8_t *payload = packet + GCM_IV_LENGTH + GCM_TAG_LENGTH; | ||
| 131 | len -= GCM_IV_LENGTH + GCM_TAG_LENGTH; | ||
| 132 | |||
| 133 | // Create output file if it doesn't exist | ||
| 134 | if (_fd < 0) | ||
| 135 | _fd = ::open(_filename.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0755); | ||
| 136 | if (_fd < 0) { | ||
| 137 | std::cerr << "Error: Can't create file " << _filename << " for session " << std::hex << _session_id; | ||
| 138 | return; | ||
| 139 | } | ||
| 140 | |||
| 141 | // Prepare output scratch space | ||
| 142 | uint8_t *output = static_cast<uint8_t*>(alloca(len)); | ||
| 143 | |||
| 144 | // This should fail on invalid input sizes | ||
| 145 | switch (mbedtls_gcm_auth_decrypt(&_ctx, len, iv, GCM_IV_LENGTH, (uint8_t*)&_session_id, SESSION_ID_LENGTH, tag, GCM_TAG_LENGTH, payload, output)) | ||
| 146 | { | ||
| 147 | case 0: | ||
| 148 | write(_fd, output, len); | ||
| 149 | _last_access = now(); | ||
| 150 | break; | ||
| 151 | case MBEDTLS_ERR_GCM_BAD_INPUT: | ||
| 152 | std::cerr << "Error: Invalid log data" << std::endl; | ||
| 153 | break; | ||
| 154 | case MBEDTLS_ERR_GCM_AUTH_FAILED : | ||
| 155 | std::cerr << "Error: Can't decrypt" << std::endl; | ||
| 156 | break; | ||
| 157 | default: | ||
| 158 | std::cerr << "Error: Unknown gcm error" << std::endl; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | void check_timeout(time_t when) { | ||
| 163 | if (_last_access && _last_access < when && _fd != -1) { | ||
| 164 | ::close(_fd); | ||
| 165 | _fd = -1; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | private: | ||
| 170 | uint64_t _session_id; | ||
| 171 | uint8_t _key[AES_KEY_LENGTH]; | ||
| 172 | int _fd = -1; | ||
| 173 | time_t _last_access = 0; | ||
| 174 | std::string _filename; | ||
| 175 | mbedtls_gcm_context _ctx; | ||
| 176 | }; | ||
| 177 | |||
| 178 | std::map<uint64_t, std::unique_ptr<Session>> g_sessions; | ||
| 179 | |||
| 180 | static uint8_t hex2nyble(char c) | ||
| 181 | { | ||
| 182 | return (c>='0'&&c<='9') ? (uint8_t)(c-'0') | ||
| 183 | : (c>='a'&&c<='f') ? (uint8_t)(c-'a'+10) | ||
| 184 | : (c>='A'&&c<='F') ? (uint8_t)(c-'A'+10) | ||
| 185 | : 0; | ||
| 186 | } | ||
| 187 | |||
| 188 | static void import_sessions(const char *dirname) { | ||
| 189 | DIR * dirp = opendir(dirname); | ||
| 190 | if (!dirp) | ||
| 191 | errx(-1, "Fatal: Can't open dir %s\n", dirname); | ||
| 192 | |||
| 193 | regex_t regex; | ||
| 194 | if (regcomp(®ex, "^[[:digit:]]{4}-[[:digit:]][[:digit:]]-[[:digit:]][[:digit:]]-[[:digit:]][[:digit:]]-[[:digit:]][[:digit:]]-" | ||
| 195 | "[[:digit:]][[:digit:]]-[[:xdigit:]]{16}-[[:xdigit:]]{32}.log$", REG_EXTENDED)) | ||
| 196 | errx(-1, "Fatal: Can't compile re"); | ||
| 197 | |||
| 198 | struct dirent * entry; | ||
| 199 | while ((entry = readdir(dirp)) != NULL) { | ||
| 200 | // We expect a very specific format | ||
| 201 | if (entry->d_type != DT_REG || entry->d_namlen != FILENAME_LENGTH) | ||
| 202 | continue; | ||
| 203 | |||
| 204 | std::string filename(entry->d_name, entry->d_name + entry->d_namlen); | ||
| 205 | uint64_t session_id; | ||
| 206 | uint8_t aeskey[AES_KEY_LENGTH]; | ||
| 207 | |||
| 208 | if (regexec(®ex, filename.c_str(), (size_t) 0, NULL, 0) || | ||
| 209 | sscanf(filename.c_str() + SIDOFFS, "%" SCNx64, &session_id) != 1) { | ||
| 210 | std::cerr << "Skipping non-re-matching file: " << filename << std::endl; | ||
| 211 | continue; | ||
| 212 | } | ||
| 213 | |||
| 214 | const char * hexkey = filename.c_str() + KEYOFFS; | ||
| 215 | for (int i=0; i<16; ++i) | ||
| 216 | aeskey[i] = (hex2nyble(hexkey[2*i]) << 4 ) | hex2nyble(hexkey[2*i+1]); | ||
| 217 | g_sessions[session_id] = std::make_unique<Session>(session_id, aeskey, filename); | ||
| 218 | } | ||
| 219 | closedir(dirp); | ||
| 220 | regfree(®ex); | ||
| 221 | } | ||
| 222 | |||
| 223 | int main() { | ||
| 224 | mbedtls_ctr_drbg_context ctr_drbg; | ||
| 225 | mbedtls_entropy_context entropy; | ||
| 226 | mbedtls_pk_context pk; | ||
| 227 | int ret = 0; | ||
| 228 | unsigned char result[256]; | ||
| 229 | unsigned char input[256]; | ||
| 230 | size_t inputlen = 0; | ||
| 231 | |||
| 232 | chdir("sessions"); | ||
| 233 | |||
| 234 | mbedtls_pk_init( &pk ); | ||
| 235 | mbedtls_entropy_init( &entropy ); | ||
| 236 | mbedtls_ctr_drbg_init( &ctr_drbg ); | ||
| 237 | mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, pp, sizeof(pp)); | ||
| 238 | |||
| 239 | if ((ret = mbedtls_pk_parse_key(&pk, privkey, sizeof(privkey), NULL, 0) ) != 0 ) | ||
| 240 | errx(-1, "Fatal: mbedtls_pk_parse_key returned -0x%04x\n", -ret ); | ||
| 241 | |||
| 242 | int sock = socket(AF_INET, SOCK_DGRAM, 0); | ||
| 243 | if (sock < 0) | ||
| 244 | errx(-1, "Fatal: Can not make socket\n"); | ||
| 245 | |||
| 246 | struct sockaddr_in servaddr, peer; | ||
| 247 | servaddr.sin_family = AF_INET; | ||
| 248 | servaddr.sin_addr.s_addr = INADDR_ANY; | ||
| 249 | servaddr.sin_port = htons(PORT); | ||
| 250 | if (bind(sock, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) | ||
| 251 | errx(-1, "Fatal: Can't bind to port %d\n", PORT); | ||
| 252 | |||
| 253 | import_sessions("."); | ||
| 254 | |||
| 255 | time_t last_close_check = now(); | ||
| 256 | while(1) { | ||
| 257 | uint8_t packet[16*1024]; | ||
| 258 | uint8_t rsa_plain_text[AES_KEY_LENGTH]; | ||
| 259 | size_t olen; | ||
| 260 | socklen_t peer_len = sizeof(peer); | ||
| 261 | |||
| 262 | ssize_t len = recvfrom(sock, packet, sizeof(packet), MSG_WAITALL, (struct sockaddr *) &peer, &peer_len); | ||
| 263 | if (len <= 0) | ||
| 264 | errx(-1, "Fatal: recvfrom yields %zd\n", len); | ||
| 265 | |||
| 266 | if (len < MIN_PACKET_SIZE) { | ||
| 267 | std::cerr << "Skipping short packet" << std::endl; | ||
| 268 | continue; | ||
| 269 | } | ||
| 270 | |||
| 271 | uint64_t session_id = *(uint64_t*)(packet + 1); | ||
| 272 | auto session = g_sessions.find(session_id); | ||
| 273 | |||
| 274 | switch(packet[0]) { | ||
| 275 | case 0: // Session setup | ||
| 276 | ret = mbedtls_pk_decrypt(&pk, packet + 1 + SESSION_ID_LENGTH, len - 1 - SESSION_ID_LENGTH, | ||
| 277 | rsa_plain_text, &olen, sizeof(rsa_plain_text), mbedtls_ctr_drbg_random, &ctr_drbg); | ||
| 278 | if (ret < 0) { | ||
| 279 | std::cerr << "Error: Failed to decrypt key (error " << -ret << ") for session " << std::hex << session_id; | ||
| 280 | break; | ||
| 281 | } | ||
| 282 | |||
| 283 | if (session == g_sessions.end()) | ||
| 284 | g_sessions[session_id] = std::make_unique<Session>(session_id, rsa_plain_text); | ||
| 285 | break; | ||
| 286 | case 1: | ||
| 287 | if (session != g_sessions.end()) | ||
| 288 | session->second->write_log(packet + 1 + SESSION_ID_LENGTH, len - 1 - SESSION_ID_LENGTH); | ||
| 289 | else | ||
| 290 | std::cerr << "Error: Can't log to unknown session " << std::hex << session_id << std::endl; | ||
| 291 | break; | ||
| 292 | default: | ||
| 293 | break; | ||
| 294 | } | ||
| 295 | |||
| 296 | time_t jetzt = now(); | ||
| 297 | if (jetzt > last_close_check + 60) { | ||
| 298 | for (auto &&session: g_sessions) | ||
| 299 | session.second->check_timeout(jetzt - ACCESS_TIMEOUT); | ||
| 300 | last_close_check = jetzt; | ||
| 301 | } | ||
| 302 | } | ||
| 303 | } | ||
