From fbb4472d23151b252eac286ec52981f3dda17bfa Mon Sep 17 00:00:00 2001 From: Sebastian Sura Date: Wed, 21 Feb 2024 08:25:44 +0100 Subject: [PATCH 1/8] crypto-wrap: add const to const parameters --- core/src/lib/crypto_wrap.cc | 24 +++++++++--------------- core/src/lib/crypto_wrap.h | 8 +++++--- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/core/src/lib/crypto_wrap.cc b/core/src/lib/crypto_wrap.cc index 059506cf598..830f2609056 100644 --- a/core/src/lib/crypto_wrap.cc +++ b/core/src/lib/crypto_wrap.cc @@ -2,7 +2,7 @@ BAREOS® - Backup Archiving REcovery Open Sourced Copyright (C) 2011-2012 Planets Communications B.V. - Copyright (C) 2013-2022 Bareos GmbH & Co. KG + Copyright (C) 2013-2024 Bareos GmbH & Co. KG This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public @@ -55,7 +55,7 @@ * @plain: plaintext key to be wrapped, n * 64 bit * @cipher: wrapped key, (n + 1) * 64 bit */ -void AesWrap(uint8_t* kek, int n, uint8_t* plain, uint8_t* cipher) +void AesWrap(const uint8_t* kek, int n, const uint8_t* plain, uint8_t* cipher) { uint8_t *a, *r, b[16]; int i, j; @@ -74,14 +74,12 @@ void AesWrap(uint8_t* kek, int n, uint8_t* plain, uint8_t* cipher) ALLOW_DEPRECATED(AES_set_encrypt_key(kek, 128, &key)); # endif - /* - * 2) Calculate intermediate values. + /* 2) Calculate intermediate values. * For j = 0 to 5 * For i=1 to n * B = AES(K, A | R[i]) * A = MSB(64, B) ^ t where t = (n*j)+i - * R[i] = LSB(64, B) - */ + * R[i] = LSB(64, B) */ for (j = 0; j <= 5; j++) { r = cipher + 8; for (i = 1; i <= n; i++) { @@ -110,7 +108,7 @@ void AesWrap(uint8_t* kek, int n, uint8_t* plain, uint8_t* cipher) * @cipher: wrapped key to be unwrapped, (n + 1) * 64 bit * @plain: plaintext key, n * 64 bit */ -int AesUnwrap(uint8_t* kek, int n, uint8_t* cipher, uint8_t* plain) +int AesUnwrap(const uint8_t* kek, int n, const uint8_t* cipher, uint8_t* plain) { uint8_t a[8], *r, b[16]; int i, j; @@ -127,14 +125,12 @@ int AesUnwrap(uint8_t* kek, int n, uint8_t* cipher, uint8_t* plain) ALLOW_DEPRECATED(AES_set_decrypt_key(kek, 128, &key)); # endif - /* - * 2) Compute intermediate values. + /* 2) Compute intermediate values. * For j = 5 to 0 * For i = n to 1 * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i * A = MSB(64, B) - * R[i] = LSB(64, B) - */ + * R[i] = LSB(64, B) */ for (j = 5; j >= 0; j--) { r = plain + (n - 1) * 8; for (i = n; i >= 1; i--) { @@ -151,12 +147,10 @@ int AesUnwrap(uint8_t* kek, int n, uint8_t* cipher, uint8_t* plain) } } - /* - * 3) Output results. + /* 3) Output results. * * These are already in @plain due to the location of temporary - * variables. Just verify that the IV matches with the expected value. - */ + * variables. Just verify that the IV matches with the expected value. */ for (i = 0; i < 8; i++) { if (a[i] != 0xa6) { return -1; } } diff --git a/core/src/lib/crypto_wrap.h b/core/src/lib/crypto_wrap.h index 6028a72ed0a..f9ff4d2cd9d 100644 --- a/core/src/lib/crypto_wrap.h +++ b/core/src/lib/crypto_wrap.h @@ -2,7 +2,7 @@ /* BAREOS® - Backup Archiving REcovery Open Sourced - Copyright (C) 2018-2018 Bareos GmbH & Co. KG + Copyright (C) 2018-2024 Bareos GmbH & Co. KG This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public @@ -22,7 +22,9 @@ #ifndef BAREOS_LIB_CRYPTO_WRAP_H_ #define BAREOS_LIB_CRYPTO_WRAP_H_ -void AesWrap(uint8_t* kek, int n, uint8_t* plain, uint8_t* cipher); -int AesUnwrap(uint8_t* kek, int n, uint8_t* cipher, uint8_t* plain); +#include + +void AesWrap(const uint8_t* kek, int n, const uint8_t* plain, uint8_t* cipher); +int AesUnwrap(const uint8_t* kek, int n, const uint8_t* cipher, uint8_t* plain); #endif // BAREOS_LIB_CRYPTO_WRAP_H_ From 0260a1cba5a144f4de5dbd816a226a2b8e87b760 Mon Sep 17 00:00:00 2001 From: Sebastian Sura Date: Wed, 21 Feb 2024 08:26:04 +0100 Subject: [PATCH 2/8] unit-tests: add test comparing openssl wrap with ours --- core/src/tests/CMakeLists.txt | 4 + core/src/tests/wrap.cc | 212 ++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 core/src/tests/wrap.cc diff --git a/core/src/tests/CMakeLists.txt b/core/src/tests/CMakeLists.txt index 45a97698577..24d24d48c9f 100644 --- a/core/src/tests/CMakeLists.txt +++ b/core/src/tests/CMakeLists.txt @@ -515,6 +515,10 @@ bareos_add_test( channel LINK_LIBRARIES bareos Threads::Threads GTest::gtest_main ) +bareos_add_test( + wrap LINK_LIBRARIES bareos GTest::gtest_main ${OPENSSL_LIBRARIES} +) + if(NOT HAVE_WIN32) bareos_add_test(fvec LINK_LIBRARIES GTest::gtest_main) endif() diff --git a/core/src/tests/wrap.cc b/core/src/tests/wrap.cc new file mode 100644 index 00000000000..df1e5784860 --- /dev/null +++ b/core/src/tests/wrap.cc @@ -0,0 +1,212 @@ +/* + BAREOS® - Backup Archiving REcovery Open Sourced + + Copyright (C) 2024-2024 Bareos GmbH & Co. KG + + This program is Free Software; you can redistribute it and/or + modify it under the terms of version three of the GNU Affero General Public + License as published by the Free Software Foundation and included + in the file LICENSE. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#if defined(HAVE_MINGW) +# include "include/bareos.h" +# include "gtest/gtest.h" +#else +# include "gtest/gtest.h" +# include "include/bareos.h" +#endif + +#include +#include +#include +#include "lib/crypto_wrap.h" + +void OpenSSLPostErrors(const char* errstring) +{ + char buf[512]; + unsigned long sslerr; + + /* Pop errors off of the per-thread queue */ + while ((sslerr = ERR_get_error()) != 0) { + /* Acquire the human readable string */ + ERR_error_string_n(sslerr, buf, sizeof(buf)); + std::fprintf(stderr, "%s: ERR=%s\n", errstring, buf); + } +} + +constexpr int cipher_size = 256; + +/* A 64 bit IV */ +unsigned char iv[] = {0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6}; + +bool OpenSSLAesWrap(const uint8_t* kek, + int n, + const uint8_t* plain, + uint8_t* cipher) +{ + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + + if (ctx == nullptr) { + OpenSSLPostErrors("EVP_CIPHER_CTX_new()"); + return false; + } + + if (EVP_EncryptInit_ex(ctx, EVP_aes_128_wrap(), NULL, kek, iv) != 1) { + OpenSSLPostErrors("EVP_EncryptInit_ex()"); + return false; + } + + int total_len = 0; + int len; + if (EVP_EncryptUpdate(ctx, cipher, &len, plain, n * 8) != 1) { + OpenSSLPostErrors("EVP_EncryptUpdate()"); + return false; + } + total_len += len; + + if (EVP_EncryptFinal(ctx, cipher + len, &len) != 1) { + OpenSSLPostErrors("EVP_EncryptFinal()"); + return false; + } + total_len += len; + + if (total_len >= cipher_size) { + fprintf(stderr, + "Written to much data to cipher (%d written vs %d capacity)\n", + total_len, cipher_size); + return false; + } + + EVP_CIPHER_CTX_free(ctx); + + return true; +} + +bool OpenSSLAesUnwrap(const uint8_t* kek, + int n, + const uint8_t* cipher, + uint8_t* plain) +{ + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + + if (ctx == nullptr) { + OpenSSLPostErrors("EVP_CIPHER_CTX_new()"); + return false; + } + + if (EVP_DecryptInit_ex(ctx, EVP_aes_128_wrap(), NULL, kek, iv) != 1) { + OpenSSLPostErrors("EVP_EncryptInit_ex()"); + return false; + } + + int total_len = 0; + int len; + if (EVP_DecryptUpdate(ctx, plain, &len, cipher, (n + 1) * 8) != 1) { + OpenSSLPostErrors("EVP_EncryptUpdate()"); + return false; + } + total_len += len; + + if (EVP_DecryptFinal_ex(ctx, plain + len, &len) != 1) { + OpenSSLPostErrors("EVP_EncryptFinal()"); + return false; + } + total_len += len; + + if (total_len >= cipher_size) { + fprintf(stderr, + "Written to much data to cipher (%d written vs %d capacity)\n", + total_len, cipher_size); + return false; + } + + EVP_CIPHER_CTX_free(ctx); + + return true; +} + +/* A 256 bit key */ +uint8_t key[] + = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31}; + +/* Message to be encrypted */ +const uint8_t plaintext[] = "The quick brown fox jumps over the lazy"; + +static_assert((sizeof(plaintext) / 8) * 8 == sizeof(plaintext)); +static_assert(sizeof(plaintext) + 8 <= cipher_size); + +TEST(Aes, our) +{ + uint8_t our_cipher[cipher_size] = {}; + uint8_t our_plain[cipher_size] = {}; + AesWrap(key, sizeof(plaintext) / 8, plaintext, our_cipher); + AesUnwrap(key, sizeof(plaintext) / 8, our_cipher, our_plain); + + for (int i = 0; i < (int)sizeof(plaintext); ++i) { + EXPECT_EQ((unsigned)our_plain[i], (unsigned)plaintext[i]) + << "at index " << i << "."; + } +} + +TEST(Aes, openssl) +{ + uint8_t openssl_cipher[cipher_size] = {}; + uint8_t openssl_plain[cipher_size] = {}; + ASSERT_TRUE( + OpenSSLAesWrap(key, sizeof(plaintext) / 8, plaintext, openssl_cipher)); + ASSERT_TRUE(OpenSSLAesUnwrap(key, sizeof(plaintext) / 8, openssl_cipher, + openssl_plain)); + + for (int i = 0; i < (int)sizeof(plaintext); ++i) { + EXPECT_EQ((unsigned)openssl_plain[i], (unsigned)plaintext[i]) + << "at index " << i << "."; + } +} + +TEST(Aes, wrap) +{ + uint8_t openssl_cipher[cipher_size] = {}; + uint8_t our_cipher[cipher_size] = {}; + + ASSERT_TRUE( + OpenSSLAesWrap(key, sizeof(plaintext) / 8, plaintext, openssl_cipher)); + AesWrap(key, sizeof(plaintext) / 8, plaintext, our_cipher); + + for (int i = 0; i < cipher_size; ++i) { + EXPECT_EQ((unsigned)openssl_cipher[i], (unsigned)our_cipher[i]) + << "at index " << i << "."; + } +} + +TEST(Aes, unwrap) +{ + uint8_t cipher[cipher_size] = {}; + unsigned char openssl_plain[128] = {}; + unsigned char our_plain[128] = {}; + + static_assert(sizeof(our_plain) >= sizeof(plaintext)); + + ASSERT_TRUE(OpenSSLAesWrap(key, sizeof(plaintext) / 8, plaintext, cipher)); + + ASSERT_TRUE( + OpenSSLAesUnwrap(key, sizeof(plaintext) / 8, cipher, openssl_plain)); + ASSERT_EQ(AesUnwrap(key, sizeof(plaintext) / 8, cipher, our_plain), 0); + + for (int i = 0; i < 128; ++i) { + EXPECT_EQ((unsigned)openssl_plain[i], (unsigned)our_plain[i]) + << "at index " << i << "."; + } +} From f9d2f92645c2d2cd5f25a5689fe10f609ed4dfe5 Mon Sep 17 00:00:00 2001 From: Sebastian Sura Date: Wed, 21 Feb 2024 08:58:35 +0100 Subject: [PATCH 3/8] wrap-test: add randomization Now the plaintext that is getting wrapped/unwrapped is not static but instead is randomised. --- core/src/tests/wrap.cc | 143 ++++++++++++++++++++++++++++------------- 1 file changed, 98 insertions(+), 45 deletions(-) diff --git a/core/src/tests/wrap.cc b/core/src/tests/wrap.cc index df1e5784860..4706c704185 100644 --- a/core/src/tests/wrap.cc +++ b/core/src/tests/wrap.cc @@ -27,9 +27,12 @@ # include "include/bareos.h" #endif +#include +#include +#include #include #include -#include +#include #include "lib/crypto_wrap.h" void OpenSSLPostErrors(const char* errstring) @@ -45,8 +48,6 @@ void OpenSSLPostErrors(const char* errstring) } } -constexpr int cipher_size = 256; - /* A 64 bit IV */ unsigned char iv[] = {0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6}; @@ -81,10 +82,10 @@ bool OpenSSLAesWrap(const uint8_t* kek, } total_len += len; - if (total_len >= cipher_size) { + if (total_len > (n + 1) * 8) { fprintf(stderr, "Written to much data to cipher (%d written vs %d capacity)\n", - total_len, cipher_size); + total_len, (n + 1) * 8); return false; } @@ -124,10 +125,10 @@ bool OpenSSLAesUnwrap(const uint8_t* kek, } total_len += len; - if (total_len >= cipher_size) { + if (total_len > n * 8) { fprintf(stderr, - "Written to much data to cipher (%d written vs %d capacity)\n", - total_len, cipher_size); + "Written to much data to plain (%d written vs %d capacity)\n", + total_len, n * 8); return false; } @@ -136,56 +137,104 @@ bool OpenSSLAesUnwrap(const uint8_t* kek, return true; } +std::vector MakePayload() +{ + std::vector payload; + + // needs to be divisible by 8 + payload.resize(64); + + if (RAND_bytes(payload.data(), payload.size()) != 1) { + OpenSSLPostErrors("RAND_bytes()"); + return {}; + } + + printf("--- BEGIN PAYLOAD ---\n"); + for (std::size_t i = 0; i < payload.size(); i += 8) { + printf("%02x%02x%02x%02x%02x%02x%02x%02x\n", payload[i + 0], payload[i + 1], + payload[i + 2], payload[i + 3], payload[i + 4], payload[i + 5], + payload[i + 6], payload[i + 7]); + } + printf("--- END PAYLOAD ---\n"); + + return payload; +} + +std::vector MakeWrappedPayload(uint8_t* key) +{ + std::vector payload = MakePayload(); + + if (payload.size() == 0 || payload.size() % 8 != 0) { return {}; } + + std::vector wrapped; + wrapped.resize(payload.size() + 8); + + if (!OpenSSLAesWrap(key, payload.size() / 8, payload.data(), + wrapped.data())) { + return {}; + } + + return wrapped; +} + /* A 256 bit key */ uint8_t key[] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31}; -/* Message to be encrypted */ -const uint8_t plaintext[] = "The quick brown fox jumps over the lazy"; - -static_assert((sizeof(plaintext) / 8) * 8 == sizeof(plaintext)); -static_assert(sizeof(plaintext) + 8 <= cipher_size); - TEST(Aes, our) { - uint8_t our_cipher[cipher_size] = {}; - uint8_t our_plain[cipher_size] = {}; - AesWrap(key, sizeof(plaintext) / 8, plaintext, our_cipher); - AesUnwrap(key, sizeof(plaintext) / 8, our_cipher, our_plain); + auto payload = MakePayload(); + + ASSERT_NE(payload.size(), 0); + ASSERT_EQ(payload.size() % 8, 0); - for (int i = 0; i < (int)sizeof(plaintext); ++i) { - EXPECT_EQ((unsigned)our_plain[i], (unsigned)plaintext[i]) + auto our_cipher = std::make_unique(payload.size() + 8); + auto our_decipher = std::make_unique(payload.size()); + AesWrap(key, payload.size() / 8, payload.data(), our_cipher.get()); + AesUnwrap(key, payload.size() / 8, our_cipher.get(), our_decipher.get()); + + for (std::size_t i = 0; i < payload.size(); ++i) { + EXPECT_EQ((unsigned)our_decipher[i], (unsigned)payload[i]) << "at index " << i << "."; } } TEST(Aes, openssl) { - uint8_t openssl_cipher[cipher_size] = {}; - uint8_t openssl_plain[cipher_size] = {}; - ASSERT_TRUE( - OpenSSLAesWrap(key, sizeof(plaintext) / 8, plaintext, openssl_cipher)); - ASSERT_TRUE(OpenSSLAesUnwrap(key, sizeof(plaintext) / 8, openssl_cipher, - openssl_plain)); - - for (int i = 0; i < (int)sizeof(plaintext); ++i) { - EXPECT_EQ((unsigned)openssl_plain[i], (unsigned)plaintext[i]) + auto payload = MakePayload(); + + ASSERT_NE(payload.size(), 0); + ASSERT_EQ(payload.size() % 8, 0); + + auto openssl_cipher = std::make_unique(payload.size() + 8); + auto openssl_decipher = std::make_unique(payload.size()); + AesWrap(key, payload.size() / 8, payload.data(), openssl_cipher.get()); + AesUnwrap(key, payload.size() / 8, openssl_cipher.get(), + openssl_decipher.get()); + + for (std::size_t i = 0; i < payload.size(); ++i) { + EXPECT_EQ((unsigned)openssl_decipher[i], (unsigned)payload[i]) << "at index " << i << "."; } } TEST(Aes, wrap) { - uint8_t openssl_cipher[cipher_size] = {}; - uint8_t our_cipher[cipher_size] = {}; + auto payload = MakePayload(); - ASSERT_TRUE( - OpenSSLAesWrap(key, sizeof(plaintext) / 8, plaintext, openssl_cipher)); - AesWrap(key, sizeof(plaintext) / 8, plaintext, our_cipher); + ASSERT_NE(payload.size(), 0); + ASSERT_EQ(payload.size() % 8, 0); - for (int i = 0; i < cipher_size; ++i) { + auto openssl_cipher = std::make_unique(payload.size() + 8); + auto our_cipher = std::make_unique(payload.size() + 8); + + ASSERT_TRUE(OpenSSLAesWrap(key, payload.size() / 8, payload.data(), + openssl_cipher.get())); + AesWrap(key, payload.size() / 8, payload.data(), our_cipher.get()); + + for (std::size_t i = 0; i < payload.size() + 8; ++i) { EXPECT_EQ((unsigned)openssl_cipher[i], (unsigned)our_cipher[i]) << "at index " << i << "."; } @@ -193,20 +242,24 @@ TEST(Aes, wrap) TEST(Aes, unwrap) { - uint8_t cipher[cipher_size] = {}; - unsigned char openssl_plain[128] = {}; - unsigned char our_plain[128] = {}; + auto wrapped = MakeWrappedPayload(key); + + ASSERT_NE(wrapped.size(), 0); + ASSERT_EQ(wrapped.size() % 8, 0); + + auto payload_size = wrapped.size() - 8; - static_assert(sizeof(our_plain) >= sizeof(plaintext)); + auto openssl_decipher = std::make_unique(payload_size); + auto our_decipher = std::make_unique(payload_size); - ASSERT_TRUE(OpenSSLAesWrap(key, sizeof(plaintext) / 8, plaintext, cipher)); - ASSERT_TRUE( - OpenSSLAesUnwrap(key, sizeof(plaintext) / 8, cipher, openssl_plain)); - ASSERT_EQ(AesUnwrap(key, sizeof(plaintext) / 8, cipher, our_plain), 0); + ASSERT_TRUE(OpenSSLAesUnwrap(key, payload_size / 8, wrapped.data(), + openssl_decipher.get())); + ASSERT_EQ( + AesUnwrap(key, payload_size / 8, wrapped.data(), our_decipher.get()), 0); - for (int i = 0; i < 128; ++i) { - EXPECT_EQ((unsigned)openssl_plain[i], (unsigned)our_plain[i]) + for (std::size_t i = 0; i < payload_size; ++i) { + EXPECT_EQ((unsigned)openssl_decipher[i], (unsigned)our_decipher[i]) << "at index " << i << "."; } } From 214702f14de3878bd5a5dbf48be78eb737d97edf Mon Sep 17 00:00:00 2001 From: Sebastian Sura Date: Fri, 23 Feb 2024 09:22:42 +0100 Subject: [PATCH 4/8] crypto_wrap: switch to opessl wrap The test now has the old implementation instead. --- core/src/dird/ua_label.cc | 11 +- core/src/lib/crypto_wrap.cc | 221 +++++++---------- core/src/lib/crypto_wrap.h | 25 +- .../stored/scsicrypto/scsicrypto-sd.cc | 15 +- core/src/tests/wrap.cc | 225 ++++++++++-------- core/src/tools/bscrypto.cc | 23 +- 6 files changed, 264 insertions(+), 256 deletions(-) diff --git a/core/src/dird/ua_label.cc b/core/src/dird/ua_label.cc index e70cac16019..5857f746bd6 100644 --- a/core/src/dird/ua_label.cc +++ b/core/src/dird/ua_label.cc @@ -3,7 +3,7 @@ Copyright (C) 2003-2012 Free Software Foundation Europe e.V. Copyright (C) 2011-2016 Planets Communications B.V. - Copyright (C) 2013-2023 Bareos GmbH & Co. KG + Copyright (C) 2013-2024 Bareos GmbH & Co. KG This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public @@ -206,8 +206,13 @@ static bool GenerateNewEncryptionKey(UaContext* ua, MediaDbRecord* mr) length = DEFAULT_PASSPHRASE_LENGTH + 8; wrapped_passphrase = (char*)malloc(length); memset(wrapped_passphrase, 0, length); - AesWrap((unsigned char*)me->keyencrkey.value, DEFAULT_PASSPHRASE_LENGTH / 8, - (unsigned char*)passphrase, (unsigned char*)wrapped_passphrase); + if (auto error = AesWrap( + (unsigned char*)me->keyencrkey.value, DEFAULT_PASSPHRASE_LENGTH / 8, + (unsigned char*)passphrase, (unsigned char*)wrapped_passphrase)) { + ua->ErrorMsg(T_("Failed to wrap passphrase ERR=%s.\n"), error->c_str()); + free(passphrase); + return false; + } free(passphrase); passphrase = wrapped_passphrase; diff --git a/core/src/lib/crypto_wrap.cc b/core/src/lib/crypto_wrap.cc index 830f2609056..0a1c783e85e 100644 --- a/core/src/lib/crypto_wrap.cc +++ b/core/src/lib/crypto_wrap.cc @@ -19,154 +19,88 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* - * crypto_wrap.c Encryption key wrapping support functions - * - * crypto_wrap.c was based on sample code used in multiple - * other projects and has the following copyright: - * - * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * - * Copyright (c) 2003-2004, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * Adapted to BAREOS by Marco van Wieringen, March 2012 - */ #include "include/bareos.h" #include "lib/crypto_wrap.h" #include "include/allow_deprecated.h" -#if defined(HAVE_OPENSSL) +#include +#include +#include -# ifdef HAVE_OPENSSL -# include -# endif +#include -/* - * @kek: key encryption key (KEK) - * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes - * @plain: plaintext key to be wrapped, n * 64 bit - * @cipher: wrapped key, (n + 1) * 64 bit - */ -void AesWrap(const uint8_t* kek, int n, const uint8_t* plain, uint8_t* cipher) +namespace { +/* A 64 bit IV, chosen according to spec: + * https://datatracker.ietf.org/doc/html/rfc3394.html#section-2.2.3.1 */ +constexpr const unsigned char iv[] + = {0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6}; + +std::string OpenSSLErrors(const char* errstring) { - uint8_t *a, *r, b[16]; - int i, j; -# ifdef HAVE_OPENSSL - AES_KEY key; -# endif - - a = cipher; - r = cipher + 8; - - // 1) Initialize variables. - memset(a, 0xa6, 8); - memcpy(r, plain, 8 * n); - -# ifdef HAVE_OPENSSL - ALLOW_DEPRECATED(AES_set_encrypt_key(kek, 128, &key)); -# endif - - /* 2) Calculate intermediate values. - * For j = 0 to 5 - * For i=1 to n - * B = AES(K, A | R[i]) - * A = MSB(64, B) ^ t where t = (n*j)+i - * R[i] = LSB(64, B) */ - for (j = 0; j <= 5; j++) { - r = cipher + 8; - for (i = 1; i <= n; i++) { - memcpy(b, a, 8); - memcpy(b + 8, r, 8); -# ifdef HAVE_OPENSSL - ALLOW_DEPRECATED(AES_encrypt(b, b, &key)); -# endif - memcpy(a, b, 8); - a[7] ^= n * j + i; - memcpy(r, b + 8, 8); - r += 8; + std::string res{errstring}; + res += ": "; + unsigned long sslerr; + char buf[512]; + + bool first = true; + /* Pop errors off of the per-thread queue */ + while ((sslerr = ERR_get_error()) != 0) { + /* Acquire the human readable string */ + ERR_error_string_n(sslerr, buf, sizeof(buf)); + res += buf; + if (first) { + first = false; + } else { + res += ", "; } } - - /* 3) Output the results. - * - * These are already in @cipher due to the location of temporary - * variables. - */ + return res; } -/* - * @kek: key encryption key (KEK) - * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes - * @cipher: wrapped key to be unwrapped, (n + 1) * 64 bit - * @plain: plaintext key, n * 64 bit - */ -int AesUnwrap(const uint8_t* kek, int n, const uint8_t* cipher, uint8_t* plain) -{ - uint8_t a[8], *r, b[16]; - int i, j; -# ifdef HAVE_OPENSSL - AES_KEY key; -# endif - - // 1) Initialize variables. - memcpy(a, cipher, 8); - r = plain; - memcpy(r, cipher + 8, 8 * n); - -# ifdef HAVE_OPENSSL - ALLOW_DEPRECATED(AES_set_decrypt_key(kek, 128, &key)); -# endif - - /* 2) Compute intermediate values. - * For j = 5 to 0 - * For i = n to 1 - * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i - * A = MSB(64, B) - * R[i] = LSB(64, B) */ - for (j = 5; j >= 0; j--) { - r = plain + (n - 1) * 8; - for (i = n; i >= 1; i--) { - memcpy(b, a, 8); - b[7] ^= n * j + i; - - memcpy(b + 8, r, 8); -# ifdef HAVE_OPENSSL - ALLOW_DEPRECATED(AES_decrypt(b, b, &key)); -# endif - memcpy(a, b, 8); - memcpy(r, b + 8, 8); - r -= 8; - } - } +struct evp_ctx_free { + void operator()(EVP_CIPHER_CTX* ctx) const { EVP_CIPHER_CTX_free(ctx); } +}; - /* 3) Output results. - * - * These are already in @plain due to the location of temporary - * variables. Just verify that the IV matches with the expected value. */ - for (i = 0; i < 8; i++) { - if (a[i] != 0xa6) { return -1; } - } +using evp_ptr = std::unique_ptr; +} // namespace - return 0; -} -#else /* * @kek: key encryption key (KEK) * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes * @plain: plaintext key to be wrapped, n * 64 bit * @cipher: wrapped key, (n + 1) * 64 bit */ -void AesWrap(uint8_t* kek, int n, uint8_t* plain, uint8_t* cipher) +std::optional AesWrap(const uint8_t* kek, + int n, + const uint8_t* plain, + uint8_t* cipher) { - memcpy(cipher, plain, n * 8); + evp_ptr ctx{EVP_CIPHER_CTX_new()}; + + if (!ctx) { return OpenSSLErrors("EVP_CIPHER_CTX_new()"); } + + EVP_CIPHER_CTX_set_flags(ctx.get(), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + + if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_wrap(), NULL, kek, iv) != 1) { + return OpenSSLErrors("EVP_EncryptInit_ex()"); + } + + int total_len = 0; + int len; + if (EVP_EncryptUpdate(ctx.get(), cipher, &len, plain, n * 8) != 1) { + return OpenSSLErrors("EVP_EncryptUpdate()"); + } + total_len += len; + + if (EVP_EncryptFinal(ctx.get(), cipher + len, &len) != 1) { + return OpenSSLErrors("EVP_EncryptFinal()"); + } + total_len += len; + + ASSERT(total_len <= (n + 1) * 8); + + return std::nullopt; } /* @@ -175,9 +109,34 @@ void AesWrap(uint8_t* kek, int n, uint8_t* plain, uint8_t* cipher) * @cipher: wrapped key to be unwrapped, (n + 1) * 64 bit * @plain: plaintext key, n * 64 bit */ -int AesUnwrap(uint8_t* kek, int n, uint8_t* cipher, uint8_t* plain) +std::optional AesUnwrap(const uint8_t* kek, + int n, + const uint8_t* cipher, + uint8_t* plain) { - memcpy(cipher, plain, n * 8); - return 0; + evp_ptr ctx{EVP_CIPHER_CTX_new()}; + + if (!ctx) { return OpenSSLErrors("EVP_CIPHER_CTX_new()"); } + + EVP_CIPHER_CTX_set_flags(ctx.get(), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + + if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_wrap(), NULL, kek, iv) != 1) { + return OpenSSLErrors("EVP_EncryptInit_ex()"); + } + + int total_len = 0; + int len; + if (EVP_DecryptUpdate(ctx.get(), plain, &len, cipher, (n + 1) * 8) != 1) { + return OpenSSLErrors("EVP_EncryptUpdate()"); + } + total_len += len; + + if (EVP_DecryptFinal_ex(ctx.get(), plain + len, &len) != 1) { + return OpenSSLErrors("EVP_EncryptFinal()"); + } + total_len += len; + + ASSERT(total_len <= (n * 8)); + + return std::nullopt; } -#endif /* HAVE_OPENSSL */ diff --git a/core/src/lib/crypto_wrap.h b/core/src/lib/crypto_wrap.h index f9ff4d2cd9d..9224c80ec51 100644 --- a/core/src/lib/crypto_wrap.h +++ b/core/src/lib/crypto_wrap.h @@ -23,8 +23,29 @@ #define BAREOS_LIB_CRYPTO_WRAP_H_ #include +#include +#include -void AesWrap(const uint8_t* kek, int n, const uint8_t* plain, uint8_t* cipher); -int AesUnwrap(const uint8_t* kek, int n, const uint8_t* cipher, uint8_t* plain); +/* + * @kek: key encryption key (KEK) + * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes + * @plain: plaintext key to be wrapped, n * 64 bit + * @cipher: wrapped key, (n + 1) * 64 bit + */ +std::optional AesWrap(const uint8_t* kek, + int n, + const uint8_t* plain, + uint8_t* cipher); + +/* + * @kek: key encryption key (KEK) + * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes + * @cipher: wrapped key to be unwrapped, (n + 1) * 64 bit + * @plain: plaintext key, n * 64 bit + */ +std::optional AesUnwrap(const uint8_t* kek, + int n, + const uint8_t* cipher, + uint8_t* plain); #endif // BAREOS_LIB_CRYPTO_WRAP_H_ diff --git a/core/src/plugins/stored/scsicrypto/scsicrypto-sd.cc b/core/src/plugins/stored/scsicrypto/scsicrypto-sd.cc index 9c2e46e1d06..4b0fb4807d1 100644 --- a/core/src/plugins/stored/scsicrypto/scsicrypto-sd.cc +++ b/core/src/plugins/stored/scsicrypto/scsicrypto-sd.cc @@ -2,7 +2,7 @@ BAREOS® - Backup Archiving REcovery Open Sourced Copyright (C) 2012 Planets Communications B.V. - Copyright (C) 2013-2023 Bareos GmbH & Co. KG + Copyright (C) 2013-2024 Bareos GmbH & Co. KG This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public @@ -342,15 +342,14 @@ static bRC do_set_scsi_encryption_key(void* value) memcpy(WrappedVolEncrKey, VolEncrKey, MAX_NAME_LENGTH); memset(VolEncrKey, 0, MAX_NAME_LENGTH); - if (AesUnwrap((unsigned char*)director->keyencrkey.value, - DEFAULT_PASSPHRASE_LENGTH / 8, - (unsigned char*)WrappedVolEncrKey, - (unsigned char*)VolEncrKey) - != 0) { + if (auto error = AesUnwrap((unsigned char*)director->keyencrkey.value, + DEFAULT_PASSPHRASE_LENGTH / 8, + (unsigned char*)WrappedVolEncrKey, + (unsigned char*)VolEncrKey)) { Emsg1(M_ERROR, 0, "scsicrypto-sd: Failed to unwrap encryption key using %s, " - "probably wrong KeyEncryptionKey in config\n", - director->keyencrkey.value); + "probably wrong KeyEncryptionKey in config (ERR=%s)\n", + director->keyencrkey.value, error->c_str()); return bRC_Error; } } diff --git a/core/src/tests/wrap.cc b/core/src/tests/wrap.cc index 4706c704185..fca830a2592 100644 --- a/core/src/tests/wrap.cc +++ b/core/src/tests/wrap.cc @@ -33,108 +33,124 @@ #include #include #include +#include #include "lib/crypto_wrap.h" +#include "include/allow_deprecated.h" -void OpenSSLPostErrors(const char* errstring) +/* + * @kek: key encryption key (KEK) + * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes + * @plain: plaintext key to be wrapped, n * 64 bit + * @cipher: wrapped key, (n + 1) * 64 bit + */ +void OldAesWrap(const uint8_t* kek, + int n, + const uint8_t* plain, + uint8_t* cipher) { - char buf[512]; - unsigned long sslerr; - - /* Pop errors off of the per-thread queue */ - while ((sslerr = ERR_get_error()) != 0) { - /* Acquire the human readable string */ - ERR_error_string_n(sslerr, buf, sizeof(buf)); - std::fprintf(stderr, "%s: ERR=%s\n", errstring, buf); + uint8_t *a, *r, b[16]; + int i, j; + AES_KEY key; + + a = cipher; + r = cipher + 8; + + // 1) Initialize variables. + memset(a, 0xa6, 8); + memcpy(r, plain, 8 * n); + + ALLOW_DEPRECATED(AES_set_encrypt_key(kek, 128, &key)); + + /* 2) Calculate intermediate values. + * For j = 0 to 5 + * For i=1 to n + * B = AES(K, A | R[i]) + * A = MSB(64, B) ^ t where t = (n*j)+i + * R[i] = LSB(64, B) */ + for (j = 0; j <= 5; j++) { + r = cipher + 8; + for (i = 1; i <= n; i++) { + memcpy(b, a, 8); + memcpy(b + 8, r, 8); + ALLOW_DEPRECATED(AES_encrypt(b, b, &key)); + memcpy(a, b, 8); + a[7] ^= n * j + i; + memcpy(r, b + 8, 8); + r += 8; + } } -} -/* A 64 bit IV */ -unsigned char iv[] = {0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6}; + /* 3) Output the results. + * + * These are already in @cipher due to the location of temporary + * variables. + */ +} -bool OpenSSLAesWrap(const uint8_t* kek, - int n, - const uint8_t* plain, - uint8_t* cipher) +/* + * @kek: key encryption key (KEK) + * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes + * @cipher: wrapped key to be unwrapped, (n + 1) * 64 bit + * @plain: plaintext key, n * 64 bit + */ +int OldAesUnwrap(const uint8_t* kek, + int n, + const uint8_t* cipher, + uint8_t* plain) { - EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); - - if (ctx == nullptr) { - OpenSSLPostErrors("EVP_CIPHER_CTX_new()"); - return false; - } - - if (EVP_EncryptInit_ex(ctx, EVP_aes_128_wrap(), NULL, kek, iv) != 1) { - OpenSSLPostErrors("EVP_EncryptInit_ex()"); - return false; + uint8_t a[8], *r, b[16]; + int i, j; + AES_KEY key; + + // 1) Initialize variables. + memcpy(a, cipher, 8); + r = plain; + memcpy(r, cipher + 8, 8 * n); + + ALLOW_DEPRECATED(AES_set_decrypt_key(kek, 128, &key)); + + /* 2) Compute intermediate values. + * For j = 5 to 0 + * For i = n to 1 + * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i + * A = MSB(64, B) + * R[i] = LSB(64, B) */ + for (j = 5; j >= 0; j--) { + r = plain + (n - 1) * 8; + for (i = n; i >= 1; i--) { + memcpy(b, a, 8); + b[7] ^= n * j + i; + + memcpy(b + 8, r, 8); + ALLOW_DEPRECATED(AES_decrypt(b, b, &key)); + memcpy(a, b, 8); + memcpy(r, b + 8, 8); + r -= 8; + } } - int total_len = 0; - int len; - if (EVP_EncryptUpdate(ctx, cipher, &len, plain, n * 8) != 1) { - OpenSSLPostErrors("EVP_EncryptUpdate()"); - return false; + /* 3) Output results. + * + * These are already in @plain due to the location of temporary + * variables. Just verify that the IV matches with the expected value. */ + for (i = 0; i < 8; i++) { + if (a[i] != 0xa6) { return -1; } } - total_len += len; - - if (EVP_EncryptFinal(ctx, cipher + len, &len) != 1) { - OpenSSLPostErrors("EVP_EncryptFinal()"); - return false; - } - total_len += len; - - if (total_len > (n + 1) * 8) { - fprintf(stderr, - "Written to much data to cipher (%d written vs %d capacity)\n", - total_len, (n + 1) * 8); - return false; - } - - EVP_CIPHER_CTX_free(ctx); - return true; + return 0; } -bool OpenSSLAesUnwrap(const uint8_t* kek, - int n, - const uint8_t* cipher, - uint8_t* plain) +void OpenSSLPostErrors(const char* errstring) { - EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); - - if (ctx == nullptr) { - OpenSSLPostErrors("EVP_CIPHER_CTX_new()"); - return false; - } - - if (EVP_DecryptInit_ex(ctx, EVP_aes_128_wrap(), NULL, kek, iv) != 1) { - OpenSSLPostErrors("EVP_EncryptInit_ex()"); - return false; - } - - int total_len = 0; - int len; - if (EVP_DecryptUpdate(ctx, plain, &len, cipher, (n + 1) * 8) != 1) { - OpenSSLPostErrors("EVP_EncryptUpdate()"); - return false; - } - total_len += len; - - if (EVP_DecryptFinal_ex(ctx, plain + len, &len) != 1) { - OpenSSLPostErrors("EVP_EncryptFinal()"); - return false; - } - total_len += len; + char buf[512]; + unsigned long sslerr; - if (total_len > n * 8) { - fprintf(stderr, - "Written to much data to plain (%d written vs %d capacity)\n", - total_len, n * 8); - return false; + /* Pop errors off of the per-thread queue */ + while ((sslerr = ERR_get_error()) != 0) { + /* Acquire the human readable string */ + ERR_error_string_n(sslerr, buf, sizeof(buf)); + std::fprintf(stderr, "%s: ERR=%s\n", errstring, buf); } - - EVP_CIPHER_CTX_free(ctx); - - return true; } std::vector MakePayload() @@ -149,13 +165,13 @@ std::vector MakePayload() return {}; } - printf("--- BEGIN PAYLOAD ---\n"); + std::printf("--- BEGIN PAYLOAD ---\n"); for (std::size_t i = 0; i < payload.size(); i += 8) { - printf("%02x%02x%02x%02x%02x%02x%02x%02x\n", payload[i + 0], payload[i + 1], - payload[i + 2], payload[i + 3], payload[i + 4], payload[i + 5], - payload[i + 6], payload[i + 7]); + std::printf("%02x%02x%02x%02x%02x%02x%02x%02x\n", payload[i + 0], + payload[i + 1], payload[i + 2], payload[i + 3], payload[i + 4], + payload[i + 5], payload[i + 6], payload[i + 7]); } - printf("--- END PAYLOAD ---\n"); + std::printf("--- END PAYLOAD ---\n"); return payload; } @@ -169,8 +185,9 @@ std::vector MakeWrappedPayload(uint8_t* key) std::vector wrapped; wrapped.resize(payload.size() + 8); - if (!OpenSSLAesWrap(key, payload.size() / 8, payload.data(), - wrapped.data())) { + if (auto error + = AesWrap(key, payload.size() / 8, payload.data(), wrapped.data())) { + std::fprintf(stderr, "MakeWrappedPayload error: %s\n", error->c_str()); return {}; } @@ -183,7 +200,7 @@ uint8_t key[] 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31}; -TEST(Aes, our) +TEST(Aes, old) { auto payload = MakePayload(); @@ -192,8 +209,8 @@ TEST(Aes, our) auto our_cipher = std::make_unique(payload.size() + 8); auto our_decipher = std::make_unique(payload.size()); - AesWrap(key, payload.size() / 8, payload.data(), our_cipher.get()); - AesUnwrap(key, payload.size() / 8, our_cipher.get(), our_decipher.get()); + OldAesWrap(key, payload.size() / 8, payload.data(), our_cipher.get()); + OldAesUnwrap(key, payload.size() / 8, our_cipher.get(), our_decipher.get()); for (std::size_t i = 0; i < payload.size(); ++i) { EXPECT_EQ((unsigned)our_decipher[i], (unsigned)payload[i]) @@ -230,9 +247,10 @@ TEST(Aes, wrap) auto openssl_cipher = std::make_unique(payload.size() + 8); auto our_cipher = std::make_unique(payload.size() + 8); - ASSERT_TRUE(OpenSSLAesWrap(key, payload.size() / 8, payload.data(), - openssl_cipher.get())); - AesWrap(key, payload.size() / 8, payload.data(), our_cipher.get()); + ASSERT_EQ( + AesWrap(key, payload.size() / 8, payload.data(), openssl_cipher.get()), + std::nullopt); + OldAesWrap(key, payload.size() / 8, payload.data(), our_cipher.get()); for (std::size_t i = 0; i < payload.size() + 8; ++i) { EXPECT_EQ((unsigned)openssl_cipher[i], (unsigned)our_cipher[i]) @@ -252,11 +270,12 @@ TEST(Aes, unwrap) auto openssl_decipher = std::make_unique(payload_size); auto our_decipher = std::make_unique(payload_size); - - ASSERT_TRUE(OpenSSLAesUnwrap(key, payload_size / 8, wrapped.data(), - openssl_decipher.get())); ASSERT_EQ( - AesUnwrap(key, payload_size / 8, wrapped.data(), our_decipher.get()), 0); + AesUnwrap(key, payload_size / 8, wrapped.data(), openssl_decipher.get()), + std::nullopt); + ASSERT_EQ( + OldAesUnwrap(key, payload_size / 8, wrapped.data(), our_decipher.get()), + 0); for (std::size_t i = 0; i < payload_size; ++i) { EXPECT_EQ((unsigned)openssl_decipher[i], (unsigned)our_decipher[i]) diff --git a/core/src/tools/bscrypto.cc b/core/src/tools/bscrypto.cc index a7fa709650d..b207d1102d3 100644 --- a/core/src/tools/bscrypto.cc +++ b/core/src/tools/bscrypto.cc @@ -3,7 +3,7 @@ Copyright (C) 2012-2012 Free Software Foundation Europe e.V. Copyright (C) 2011-2012 Planets Communications B.V. - Copyright (C) 2013-2023 Bareos GmbH & Co. KG + Copyright (C) 2013-2024 Bareos GmbH & Co. KG This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public @@ -312,8 +312,14 @@ int main(int argc, char* const* argv) length = DEFAULT_PASSPHRASE_LENGTH + 8; wrapped_passphrase = (char*)malloc(length); memset(wrapped_passphrase, 0, length); - AesWrap((unsigned char*)wrapdata, DEFAULT_PASSPHRASE_LENGTH / 8, - (unsigned char*)passphrase, (unsigned char*)wrapped_passphrase); + if (auto error = AesWrap( + (unsigned char*)wrapdata, DEFAULT_PASSPHRASE_LENGTH / 8, + (unsigned char*)passphrase, (unsigned char*)wrapped_passphrase)) { + fprintf(stderr, T_("Cannot wrap passphrase ERR=%s\n"), error->c_str()); + free(passphrase); + retval = 1; + goto bail_out; + } free(passphrase); passphrase = wrapped_passphrase; @@ -414,14 +420,13 @@ int main(int argc, char* const* argv) passphrase = (char*)malloc(length); memset(passphrase, 0, length); - if (AesUnwrap((unsigned char*)wrapdata, length / 8, - (unsigned char*)wrapped_passphrase, - (unsigned char*)passphrase) - == -1) { + if (auto error = AesUnwrap((unsigned char*)wrapdata, length / 8, + (unsigned char*)wrapped_passphrase, + (unsigned char*)passphrase)) { fprintf(stderr, T_("Failed to aes unwrap the keydata read from %s using the " - "wrap data from %s, aborting...\n"), - keyfile, wrap_keyfile); + "wrap data from %s ERR=%s, aborting...\n"), + keyfile, wrap_keyfile, error->c_str()); free(wrapped_passphrase); goto bail_out; } From 4c07e921d66041e75e15c432fb1f2008a16b6178 Mon Sep 17 00:00:00 2001 From: Sebastian Sura Date: Wed, 21 Feb 2024 11:26:48 +0100 Subject: [PATCH 5/8] askdir: fix leaking poll mode to caller dev->poll disables AutoLabeling, as such we should not leak it into the caller context. --- core/src/stored/askdir.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/stored/askdir.cc b/core/src/stored/askdir.cc index 462c9f38a33..74a0725a82b 100644 --- a/core/src/stored/askdir.cc +++ b/core/src/stored/askdir.cc @@ -3,7 +3,7 @@ Copyright (C) 2000-2012 Free Software Foundation Europe e.V. Copyright (C) 2011-2012 Planets Communications B.V. - Copyright (C) 2013-2023 Bareos GmbH & Co. KG + Copyright (C) 2013-2024 Bareos GmbH & Co. KG This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public @@ -451,6 +451,7 @@ bool StorageDaemonDeviceControlRecord::DirUpdateFileAttributes( */ bool StorageDaemonDeviceControlRecord::DirAskSysopToCreateAppendableVolume() { + int poll_mode = dev->poll; int status = W_TIMEOUT; bool got_vol = false; @@ -517,6 +518,8 @@ bool StorageDaemonDeviceControlRecord::DirAskSysopToCreateAppendableVolume() } get_out: + dev->poll = poll_mode; // we do not want to change the poll + // mode in this function jcr->sendJobStatus(JS_Running); Dmsg0(debuglevel, "leave dir_ask_sysop_to_mount_create_appendable_volume\n"); From fb00363044896acfed65c2ae2a8fd4c2510541d1 Mon Sep 17 00:00:00 2001 From: Sebastian Sura Date: Tue, 27 Feb 2024 15:18:14 +0100 Subject: [PATCH 6/8] wrap-test: add additional assertions --- core/src/tests/wrap.cc | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/core/src/tests/wrap.cc b/core/src/tests/wrap.cc index fca830a2592..67c3b9a6d6d 100644 --- a/core/src/tests/wrap.cc +++ b/core/src/tests/wrap.cc @@ -210,7 +210,9 @@ TEST(Aes, old) auto our_cipher = std::make_unique(payload.size() + 8); auto our_decipher = std::make_unique(payload.size()); OldAesWrap(key, payload.size() / 8, payload.data(), our_cipher.get()); - OldAesUnwrap(key, payload.size() / 8, our_cipher.get(), our_decipher.get()); + ASSERT_NE(OldAesUnwrap(key, payload.size() / 8, our_cipher.get(), + our_decipher.get()), + -1); for (std::size_t i = 0; i < payload.size(); ++i) { EXPECT_EQ((unsigned)our_decipher[i], (unsigned)payload[i]) @@ -227,9 +229,16 @@ TEST(Aes, openssl) auto openssl_cipher = std::make_unique(payload.size() + 8); auto openssl_decipher = std::make_unique(payload.size()); - AesWrap(key, payload.size() / 8, payload.data(), openssl_cipher.get()); - AesUnwrap(key, payload.size() / 8, openssl_cipher.get(), - openssl_decipher.get()); + { + auto result = AesWrap(key, payload.size() / 8, payload.data(), + openssl_cipher.get()); + ASSERT_FALSE(!!result) << result->c_str(); + } + { + auto result = AesUnwrap(key, payload.size() / 8, openssl_cipher.get(), + openssl_decipher.get()); + ASSERT_FALSE(!!result) << result->c_str(); + } for (std::size_t i = 0; i < payload.size(); ++i) { EXPECT_EQ((unsigned)openssl_decipher[i], (unsigned)payload[i]) @@ -247,9 +256,11 @@ TEST(Aes, wrap) auto openssl_cipher = std::make_unique(payload.size() + 8); auto our_cipher = std::make_unique(payload.size() + 8); - ASSERT_EQ( - AesWrap(key, payload.size() / 8, payload.data(), openssl_cipher.get()), - std::nullopt); + { + auto result = AesWrap(key, payload.size() / 8, payload.data(), + openssl_cipher.get()); + ASSERT_FALSE(!!result) << result->c_str(); + } OldAesWrap(key, payload.size() / 8, payload.data(), our_cipher.get()); for (std::size_t i = 0; i < payload.size() + 8; ++i) { @@ -270,9 +281,11 @@ TEST(Aes, unwrap) auto openssl_decipher = std::make_unique(payload_size); auto our_decipher = std::make_unique(payload_size); - ASSERT_EQ( - AesUnwrap(key, payload_size / 8, wrapped.data(), openssl_decipher.get()), - std::nullopt); + { + auto result = AesUnwrap(key, payload_size / 8, wrapped.data(), + openssl_decipher.get()); + ASSERT_FALSE(!!result) << result->c_str(); + } ASSERT_EQ( OldAesUnwrap(key, payload_size / 8, wrapped.data(), our_decipher.get()), 0); From efed11bd4536f626346d9ac1b109a0e655b63fa2 Mon Sep 17 00:00:00 2001 From: Sebastian Sura Date: Tue, 5 Mar 2024 08:14:51 +0100 Subject: [PATCH 7/8] wrap: add official test data See: https://datatracker.ietf.org/doc/html/rfc3394.html#section-4.1 --- core/src/tests/wrap.cc | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/core/src/tests/wrap.cc b/core/src/tests/wrap.cc index 67c3b9a6d6d..2f3b9096fa8 100644 --- a/core/src/tests/wrap.cc +++ b/core/src/tests/wrap.cc @@ -295,3 +295,39 @@ TEST(Aes, unwrap) << "at index " << i << "."; } } + +TEST(Aes, test_val) +{ + uint8_t kek[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + }; + + uint8_t key[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + }; + + uint8_t wrapped[] = { + 0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47, 0xAE, 0xF3, 0x4B, 0xD8, + 0xFB, 0x5A, 0x7B, 0x82, 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5, + }; + + uint8_t out[sizeof(wrapped)]; + uint8_t out2[sizeof(key)]; + + auto payload_size = sizeof(key); + + OldAesWrap(kek, payload_size / 8, key, out); + + for (std::size_t i = 0; i < (payload_size + 8); ++i) { + EXPECT_EQ((unsigned)wrapped[i], (unsigned)out[i]) + << "at index " << i << "."; + } + + OldAesUnwrap(kek, payload_size / 8, out, out2); + + for (std::size_t i = 0; i < payload_size; ++i) { + EXPECT_EQ((unsigned)key[i], (unsigned)out2[i]) << "at index " << i << "."; + } +} From b2a0a4e43a95019a5796fbb3901c3436f89e0d0e Mon Sep 17 00:00:00 2001 From: Bareos Bot Date: Fri, 8 Mar 2024 10:11:14 +0000 Subject: [PATCH 8/8] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc37ffba0ea..1221511ea23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - jcr: fix some compiler warnings [PR #1648] - build: Fix debugsource RPM package generation [PR #1713] - Bugfix: Clean up error handling in LDAP plugin, fix dependencies [PR #1717] +- crypto_wrap: replace aes wrap with openssl aes wrap algorithm [PR #1718] ### Removed - plugins: remove old deprecated postgres plugin [PR #1606] @@ -107,4 +108,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [PR #1708]: https://github.com/bareos/bareos/pull/1708 [PR #1713]: https://github.com/bareos/bareos/pull/1713 [PR #1717]: https://github.com/bareos/bareos/pull/1717 +[PR #1718]: https://github.com/bareos/bareos/pull/1718 [unreleased]: https://github.com/bareos/bareos/tree/master