From 5e081b7b50efaef0a100c20c41d2991139deca5e Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Sat, 5 Dec 2020 04:27:14 +0100 Subject: Rework receiver as a c++ programm --- Makefile | 4 +- receiver.cpp | 303 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 receiver.cpp diff --git a/Makefile b/Makefile index 631717d..3864298 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,8 @@ all: sender receiver sender: sender.c $(CC) -o sender sender.c -lmbedtls -lmbedcrypto -receiver: receiver.c - $(CC) -o receiver receiver.c -lmbedtls -lmbedcrypto +receiver: receiver.cpp + $(CC) -std=c++17 -o receiver receiver.cpp -lmbedtls -lmbedcrypto -lc++ clean: 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 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/gcm.h" + +#include +#include +#include + +const unsigned short PORT = 58132; + +static const unsigned char privkey[] = +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIIEpAIBAAKCAQEAwWlNmLHOOzZpdrfp+EAANwqab0FhQwCyZ/u+ySBW5XxPf6mo\n" +"bySvtJrLdsWzdwnup/UfwZiEhJk/4wpD4Qf/2+syuJi3Rf7L+Jfh//Qf9uXAS80+\n" +"LYad7dW0c1r5nt+F9Can5fBn7futnd8n672T+y8QpHRwX9GtaILvYQe5GQac8cfq\n" +"2rGUd5iYj5KSdcaIZnJ4YgnjLHg2PMbtEJwqcV+2oAkcOPzTAJoNE7XjLZTwXmLl\n" +"FgL/2cN4uJZBDnwv3RZSAhpdYF4KOJmE2GFs2jdvRUrYO7WSl8fM16vRH4vz5MNN\n" +"caprg2MlXheVTPQa+WMdcz7dyQx8s9kRVPPUSwIDAQABAoIBAH1KD8A4flYRO2Ry\n" +"YxgzrW/6aGxlt/HFg8ykYcS8NE5Yps8WQkwtQb0HAYKhM06LmpQm0DmC6WVUOPSE\n" +"c9BUdEQsKiE2nJK1KcCR8w7xP7uavWTdQcgQCkJFS63mYwmt1oKAgAcOIuUhQiig\n" +"pKWrmy7+IBPIcftAQssO9q6uaBNy+ONu6KU/FYd4UAoEt07MzuIAl5rybROOWCrA\n" +"cjuOKK830Q2Mi2ESwwlO0+3Vyz9VhSha5wW2WwFv9zx8YQblaVXxfXC6O5XcXb0j\n" +"O2rTpgHMmOVik3Zrg3XoRXsNZCvFQqbIevwGhRNEFQTnzakh/5g0VDa54RAK/APC\n" +"t3ABEAECgYEA+rnqDqmXQc/xMCLOhJduxRFvZbT6EbUVJG5+l6XtATTa4LWzqx1B\n" +"QDXS/Dixxc9FA1vSH2rAW//6wbr8KXihSeI7YxgIfWyrSIxbS3Dd1qwddvniYIpx\n" +"ms9vYpQKAewv2p310nf7fyuURES8YikhpSuff3DhzXBEi7s3Y7gIIEsCgYEAxXrD\n" +"6xjgLSgbbdyqxKeNk8ADMifbj8ZoiNIHkJ6sShFaTEbyHweOJ4RY7OmTBUDhgzUY\n" +"1oynztilADFaq8KhsiMqzI3DgZ3/2OElGYReEAbXljvgudL3tmiBWc5j9S7XNLBe\n" +"u9f3WYAYDu7/BVqmQWa/QtD9JquoZ1xgV2D6nAECgYEA29w9t9/VSJvM9xX+nNyi\n" +"AON6GOjrRK3TPWA7WEXjH+S2bshHJi0ANAs+2Xfpw/kunnRdPLmCtuowfMO4LbGf\n" +"VcexpgLEJyAszvBteikeDwpcyCD19wxP9J4kIYCJiggQKpfLoWUfP/P6DydrPnSt\n" +"EUbAlaNqDpl9Mj7YonQVhCMCgYEAv0c9M5+hrDuX7d76zYaZvI4UymUO54E/yZ7e\n" +"UvdOXGPYed+SL/oKeD5aQAeyLzl79bHdgBs3g0QW9kvXzly0cC5eC0oZH5hhs7nI\n" +"TKII1i86bLtM3dD5vQYWnF0sNtWK/+8Bo6L5ZAiNxRE7lP0L4ndaNKbnPaixcoRo\n" +"kNpPhAECgYAg7jmNlw+7VVurzR36LKKE+d6veraF5ltpJiboDb3j38RGe3982LLq\n" +"WaBKm1gkHfXgBjkNzS4r2kyRijw0GQ9JgmWooR7BXFH30HkPNl4gFTSsrOG5zGLi\n" +"0aexkDpXQuKsgBzqU0Wn94GZMM+RhuOYec/56JFT+8U5Tcntb26wwA==\n" +"-----END RSA PRIVATE KEY-----\n"; + +static const unsigned char pp[] = "IJUHZGFDXTZKHJKHGFDHZLUÖDRTFGHHJGHH"; + +// Close files after 20 minutes +#define ACCESS_TIMEOUT (20*60) + +/* + 1 byte type: 0 allocate session, 1 log to session, rest: discard + 8 bytes session id + type 0: + 256 bytes rsa 2048 data yielding 32 byte AES session key + type 1: + 16 bytes iv + 16 bytes tag + rest cipher text + + File name format: 2020-10-13-23-42-05-SSSSSSSSSSSSSSSS-KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK.log + +*/ + +// Time helper +static time_t now() { + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC, &tp); + return tp.tv_sec; +} + +// Constants +enum { SESSION_ID_LENGTH = 8, AES_KEY_LENGTH = 16, GCM_IV_LENGTH = 16, GCM_TAG_LENGTH = 16, MIN_PACKET_SIZE = 40 }; +enum { FILENAME_LENGTH = 73, SIDOFFS = 20, KEYOFFS = 37 }; + +class Session { +public: + Session(uint64_t session_id, uint8_t key[AES_KEY_LENGTH], const std::string &filename) : + _session_id(session_id), _filename(filename) { + memcpy(_key, key, AES_KEY_LENGTH); + mbedtls_gcm_init(&_ctx); + mbedtls_gcm_setkey(&_ctx, MBEDTLS_CIPHER_ID_AES, _key, 8 * AES_KEY_LENGTH); + } + + Session(uint64_t session_id, uint8_t key[AES_KEY_LENGTH]) : _session_id(session_id) { + memcpy(_key, key, AES_KEY_LENGTH); + mbedtls_gcm_init(&_ctx); + mbedtls_gcm_setkey(&_ctx, MBEDTLS_CIPHER_ID_AES, _key, 8 * AES_KEY_LENGTH); + + // Create timestamp + char tprefix[32]; + time_t t = time(NULL); + struct tm * jetzt = localtime(&t); + size_t nlen = strftime(tprefix, sizeof(tprefix), "%F-%H-%M-%S", jetzt); + + // Dump key + char hexkey[2*AES_KEY_LENGTH + 1]; + for (int i=0; i(alloca(len)); + + // This should fail on invalid input sizes + switch (mbedtls_gcm_auth_decrypt(&_ctx, len, iv, GCM_IV_LENGTH, (uint8_t*)&_session_id, SESSION_ID_LENGTH, tag, GCM_TAG_LENGTH, payload, output)) + { + case 0: + write(_fd, output, len); + _last_access = now(); + break; + case MBEDTLS_ERR_GCM_BAD_INPUT: + std::cerr << "Error: Invalid log data" << std::endl; + break; + case MBEDTLS_ERR_GCM_AUTH_FAILED : + std::cerr << "Error: Can't decrypt" << std::endl; + break; + default: + std::cerr << "Error: Unknown gcm error" << std::endl; + } + } + + void check_timeout(time_t when) { + if (_last_access && _last_access < when && _fd != -1) { + ::close(_fd); + _fd = -1; + } + } + +private: + uint64_t _session_id; + uint8_t _key[AES_KEY_LENGTH]; + int _fd = -1; + time_t _last_access = 0; + std::string _filename; + mbedtls_gcm_context _ctx; +}; + +std::map> g_sessions; + +static uint8_t hex2nyble(char c) +{ + return (c>='0'&&c<='9') ? (uint8_t)(c-'0') + : (c>='a'&&c<='f') ? (uint8_t)(c-'a'+10) + : (c>='A'&&c<='F') ? (uint8_t)(c-'A'+10) + : 0; +} + +static void import_sessions(const char *dirname) { + DIR * dirp = opendir(dirname); + if (!dirp) + errx(-1, "Fatal: Can't open dir %s\n", dirname); + + regex_t regex; + if (regcomp(®ex, "^[[:digit:]]{4}-[[:digit:]][[:digit:]]-[[:digit:]][[:digit:]]-[[:digit:]][[:digit:]]-[[:digit:]][[:digit:]]-" + "[[:digit:]][[:digit:]]-[[:xdigit:]]{16}-[[:xdigit:]]{32}.log$", REG_EXTENDED)) + errx(-1, "Fatal: Can't compile re"); + + struct dirent * entry; + while ((entry = readdir(dirp)) != NULL) { + // We expect a very specific format + if (entry->d_type != DT_REG || entry->d_namlen != FILENAME_LENGTH) + continue; + + std::string filename(entry->d_name, entry->d_name + entry->d_namlen); + uint64_t session_id; + uint8_t aeskey[AES_KEY_LENGTH]; + + if (regexec(®ex, filename.c_str(), (size_t) 0, NULL, 0) || + sscanf(filename.c_str() + SIDOFFS, "%" SCNx64, &session_id) != 1) { + std::cerr << "Skipping non-re-matching file: " << filename << std::endl; + continue; + } + + const char * hexkey = filename.c_str() + KEYOFFS; + for (int i=0; i<16; ++i) + aeskey[i] = (hex2nyble(hexkey[2*i]) << 4 ) | hex2nyble(hexkey[2*i+1]); + g_sessions[session_id] = std::make_unique(session_id, aeskey, filename); + } + closedir(dirp); + regfree(®ex); +} + +int main() { + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_entropy_context entropy; + mbedtls_pk_context pk; + int ret = 0; + unsigned char result[256]; + unsigned char input[256]; + size_t inputlen = 0; + + chdir("sessions"); + + mbedtls_pk_init( &pk ); + mbedtls_entropy_init( &entropy ); + mbedtls_ctr_drbg_init( &ctr_drbg ); + mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, pp, sizeof(pp)); + + if ((ret = mbedtls_pk_parse_key(&pk, privkey, sizeof(privkey), NULL, 0) ) != 0 ) + errx(-1, "Fatal: mbedtls_pk_parse_key returned -0x%04x\n", -ret ); + + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + errx(-1, "Fatal: Can not make socket\n"); + + struct sockaddr_in servaddr, peer; + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = INADDR_ANY; + servaddr.sin_port = htons(PORT); + if (bind(sock, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) + errx(-1, "Fatal: Can't bind to port %d\n", PORT); + + import_sessions("."); + + time_t last_close_check = now(); + while(1) { + uint8_t packet[16*1024]; + uint8_t rsa_plain_text[AES_KEY_LENGTH]; + size_t olen; + socklen_t peer_len = sizeof(peer); + + ssize_t len = recvfrom(sock, packet, sizeof(packet), MSG_WAITALL, (struct sockaddr *) &peer, &peer_len); + if (len <= 0) + errx(-1, "Fatal: recvfrom yields %zd\n", len); + + if (len < MIN_PACKET_SIZE) { + std::cerr << "Skipping short packet" << std::endl; + continue; + } + + uint64_t session_id = *(uint64_t*)(packet + 1); + auto session = g_sessions.find(session_id); + + switch(packet[0]) { + case 0: // Session setup + ret = mbedtls_pk_decrypt(&pk, packet + 1 + SESSION_ID_LENGTH, len - 1 - SESSION_ID_LENGTH, + rsa_plain_text, &olen, sizeof(rsa_plain_text), mbedtls_ctr_drbg_random, &ctr_drbg); + if (ret < 0) { + std::cerr << "Error: Failed to decrypt key (error " << -ret << ") for session " << std::hex << session_id; + break; + } + + if (session == g_sessions.end()) + g_sessions[session_id] = std::make_unique(session_id, rsa_plain_text); + break; + case 1: + if (session != g_sessions.end()) + session->second->write_log(packet + 1 + SESSION_ID_LENGTH, len - 1 - SESSION_ID_LENGTH); + else + std::cerr << "Error: Can't log to unknown session " << std::hex << session_id << std::endl; + break; + default: + break; + } + + time_t jetzt = now(); + if (jetzt > last_close_check + 60) { + for (auto &&session: g_sessions) + session.second->check_timeout(jetzt - ACCESS_TIMEOUT); + last_close_check = jetzt; + } + } +} -- cgit v1.2.3