From 9472f7930734bb466c79566087cbbcb81560c80b Mon Sep 17 00:00:00 2001 From: Jukka Laitinen Date: Fri, 4 Jun 2021 16:22:48 +0300 Subject: [PATCH] Take crypto interfaces into use in logger, to encrypt ulog data - Generate a symmetric encryption key and a nonce value - Use a public key in keystore to encrypt the symmteric key - Write the encrypted key and the nonce value to disk into .ulgk, name matching with the encrypted log file - use quick stream encryption to crypt the .ulg file contents Signed-off-by: Jukka Laitinen --- src/modules/logger/log_writer_file.cpp | 164 ++++++++++++++++++++++++- src/modules/logger/log_writer_file.h | 8 +- src/modules/logger/logger.cpp | 12 +- 3 files changed, 180 insertions(+), 4 deletions(-) diff --git a/src/modules/logger/log_writer_file.cpp b/src/modules/logger/log_writer_file.cpp index 484df104ba..5a013cda74 100644 --- a/src/modules/logger/log_writer_file.cpp +++ b/src/modules/logger/log_writer_file.cpp @@ -40,10 +40,15 @@ #include #include +#include #ifdef __PX4_NUTTX #include #endif /* __PX4_NUTTX */ +// TODO: how to define the key slots/product? +// this depends on the keystore as well +#define LOGGER_CRYPTO_KEY_IDX 2 + using namespace time_literals; @@ -81,6 +86,116 @@ LogWriterFile::~LogWriterFile() pthread_cond_destroy(&_cv); } +#if defined(PX4_CRYPTO) +bool LogWriterFile::init_logfile_encryption(const char *filename) +{ + // open a crypto session // TODO: use a parameter for algorithm + if (!_crypto.open(CRYPTO_XCHACHA20)) { + return false; + } + + // Get the minimum block size for which the encryption can be performed + _min_blocksize = _crypto.get_min_blocksize(_key_idx); + + // Generate a crypto key and store it to the keystore. + + // TODO: use parameter for alogrithm? Or product specific macro? + // TODO: how to define key index to the keystore? Product macro? parameter? + if (!_crypto.generate_key(LOGGER_CRYPTO_KEY_IDX, false)) { + PX4_ERR("Can't generate crypto key"); + return false; + } + + // Get the generated key, encrypted with RSA_OAEP. Open another temporary session for this. + PX4Crypto rsa_crypto; + + if (!rsa_crypto.open(CRYPTO_RSA_OAEP)) { + return false; + } + + // TODO: define the public key used for key exchange. Now hardcoded to 1 + + /* Get the size of an encrypted key and nonce */ + + size_t key_size; + size_t nonce_size; + + if (!rsa_crypto.get_encrypted_key(LOGGER_CRYPTO_KEY_IDX, + NULL, + &key_size, + 1) || + !_crypto.get_nonce(NULL, &nonce_size) || + key_size == 0) { + rsa_crypto.close(); + return false; + } + + /* Allocate space and get key + nonce */ + uint8_t *key = (uint8_t *)malloc(key_size + nonce_size); + + if (!key || + !rsa_crypto.get_encrypted_key( + LOGGER_CRYPTO_KEY_IDX, + key, + &key_size, + 1) || + !_crypto.get_nonce( + key + key_size, &nonce_size)) { + PX4_ERR("Can't get & encrypt the key"); + free(key); + rsa_crypto.close(); + return false; + } + + rsa_crypto.close(); + + // Write the encrypted key to the disk + + // Allocate a buffer for filename + size_t fnlen = strlen(filename); + char *tmp_buf = (char *)malloc(fnlen + 1); + + if (!tmp_buf) { + PX4_ERR("out of memory"); + free(key); + return false; + } + + // Copy the original logfile name, and append 'k' to the filename + + memcpy(tmp_buf, filename, fnlen + 1); + tmp_buf[fnlen - 1] = 'k'; + tmp_buf[fnlen] = 0; + + int key_fd = ::open((const char *)tmp_buf, O_CREAT | O_WRONLY, PX4_O_MODE_666); + + // The file name is no longer needed, free it + free(tmp_buf); + tmp_buf = nullptr; + + if (key_fd < 0) { + PX4_ERR("Can't open key file, errno: %d", errno); + free(key); + return false; + } + + size_t written = ::write(key_fd, key, key_size + nonce_size); + + // Free temporary memory allocations + free(key); + ::close(key_fd); + + // Check that writing to the disk succeeded + if (written != key_size + nonce_size) { + PX4_ERR("Writing the encryption key to disk fail"); + return false; + } + + return true; +} +#endif // PX4_CRYPTO + + void LogWriterFile::start_log(LogType type, const char *filename) { // At this point we don't expect the file to be open, but it can happen for very fast consecutive stop & start @@ -107,6 +222,18 @@ void LogWriterFile::start_log(LogType type, const char *filename) } } +#if PX4_CRYPTO + // TODO: use parameter to check if logfile crypto is enabled + bool enc_init = init_logfile_encryption(filename); + + if (!enc_init) { + PX4_ERR("Failed to start encrypted logging"); + _crypto.close(); + return; + } + +#endif + if (_buffers[(int)type].start_log(filename)) { PX4_INFO("Opened %s log file: %s", log_type_str(type), filename); notify(); @@ -183,7 +310,6 @@ void LogWriterFile::thread_stop() if (ret) { PX4_WARN("join failed: %d", ret); } - } void *LogWriterFile::run_helper(void *context) @@ -247,10 +373,39 @@ void LogWriterFile::run() LogFileBuffer &buffer = _buffers[i]; size_t available = buffer.get_read_ptr(&read_ptr, &is_part); +#if defined(PX4_CRYPTO) + // Split into min blocksize chunks, so it is good for encrypting in pieces + available = (available / _min_blocksize) * _min_blocksize; +#endif + /* if sufficient data available or partial read or terminating, write data */ if (available >= min_available[i] || is_part || (!buffer._should_run && available > 0)) { pthread_mutex_unlock(&_mtx); +#if defined(PX4_CRYPTO) + /* This makes the following assumptions: + * - the chipher size is always the + same as the input size + * - the encryption can be done in + place. This is always taken care + by the px4 crypto interfaces + */ + + // TODO: use parameter to check if logfile crypto is enabled + size_t out = available; + _crypto.encrypt_data( + LOGGER_CRYPTO_KEY_IDX, // key + (uint8_t *)read_ptr, + available, + (uint8_t *)read_ptr, + &out); + + if (out != available) { + PX4_ERR("Encryption output size mismatch, logfile corrupted"); + } + +#endif + written = buffer.write_to_file(read_ptr, available, call_fsync); /* buffer.mark_read() requires _mtx to be locked */ @@ -289,6 +444,12 @@ void LogWriterFile::run() if (_buffers[0].fd() < 0 && _buffers[1].fd() < 0) { // stop when both files are closed +#if defined(PX4_CRYPTO) + /* close the crypto session */ + + _crypto.close(); +#endif + break; } @@ -424,6 +585,7 @@ void LogWriterFile::LogFileBuffer::write_no_check(void *ptr, size_t size) // now: n = bytes already written size_t p = size - n; // number of bytes to write + memcpy(&(_buffer[_head]), &(buffer_c[n]), p); _head = (_head + p) % _buffer_size; _count += size; diff --git a/src/modules/logger/log_writer_file.h b/src/modules/logger/log_writer_file.h index b3649b053d..a042e639cf 100644 --- a/src/modules/logger/log_writer_file.h +++ b/src/modules/logger/log_writer_file.h @@ -38,6 +38,7 @@ #include #include #include +#include namespace px4 { @@ -182,7 +183,6 @@ private: size_t count() const { return _count; } bool _should_run = false; - private: const size_t _buffer_size; int _fd = -1; @@ -201,6 +201,12 @@ private: pthread_mutex_t _mtx; pthread_cond_t _cv; pthread_t _thread = 0; +#if defined(PX4_CRYPTO) + bool init_logfile_encryption(const char *filename); + PX4Crypto _crypto; + int _min_blocksize; +#endif + }; } diff --git a/src/modules/logger/logger.cpp b/src/modules/logger/logger.cpp index 93ee837591..ff46364a06 100644 --- a/src/modules/logger/logger.cpp +++ b/src/modules/logger/logger.cpp @@ -1269,6 +1269,12 @@ int Logger::get_log_file_name(LogType type, char *file_name, size_t file_name_si replay_suffix = "_replayed"; } + const char *crypto_suffix = ""; +#if defined(PX4_CRYPTO) + // TODO: use parameter to check if logfile crypto is enabled + crypto_suffix = "c"; +#endif + char *log_file_name = _file_name[(int)type].log_file_name; if (time_ok) { @@ -1280,7 +1286,8 @@ int Logger::get_log_file_name(LogType type, char *file_name, size_t file_name_si char log_file_name_time[16] = ""; strftime(log_file_name_time, sizeof(log_file_name_time), "%H_%M_%S", &tt); - snprintf(log_file_name, sizeof(LogFileName::log_file_name), "%s%s.ulg", log_file_name_time, replay_suffix); + snprintf(log_file_name, sizeof(LogFileName::log_file_name), "%s%s.ulg%s", log_file_name_time, replay_suffix, + crypto_suffix); snprintf(file_name + n, file_name_size - n, "/%s", log_file_name); if (notify) { @@ -1310,7 +1317,8 @@ int Logger::get_log_file_name(LogType type, char *file_name, size_t file_name_si /* look for the next file that does not exist */ while (file_number <= MAX_NO_LOGFILE) { /* format log file path: e.g. /fs/microsd/log/sess001/log001.ulg */ - snprintf(log_file_name, sizeof(LogFileName::log_file_name), "log%03" PRIu16 "%s.ulg", file_number, replay_suffix); + snprintf(log_file_name, sizeof(LogFileName::log_file_name), "log%03" PRIu16 "%s.ulg%s", file_number, replay_suffix, + crypto_suffix); snprintf(file_name + n, file_name_size - n, "/%s", log_file_name); if (!util::file_exist(file_name)) {