Skip to content

Commit

Permalink
Merge pull request #1718
Browse files Browse the repository at this point in the history
crypto_wrap: replace aes wrap with openssl aes wrap algorithm
  • Loading branch information
BareosBot committed Mar 8, 2024
2 parents f719acf + b2a0a4e commit df3bc2b
Show file tree
Hide file tree
Showing 9 changed files with 489 additions and 162 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -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]
Expand Down Expand Up @@ -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
11 changes: 8 additions & 3 deletions core/src/dird/ua_label.cc
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
229 changes: 91 additions & 138 deletions core/src/lib/crypto_wrap.cc
Expand Up @@ -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
Expand All @@ -19,160 +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 <jkmaline@cc.hut.fi>
*
* 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 <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/rand.h>

# ifdef HAVE_OPENSSL
# include <openssl/aes.h>
# endif
#include <memory>

/*
* @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)
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(uint8_t* kek, int n, 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<EVP_CIPHER_CTX, evp_ctx_free>;
} // 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<std::string> 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;
}

/*
Expand All @@ -181,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<std::string> 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 */
29 changes: 26 additions & 3 deletions core/src/lib/crypto_wrap.h
Expand Up @@ -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
Expand All @@ -22,7 +22,30 @@
#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 <cinttypes>
#include <string>
#include <optional>

/*
* @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<std::string> 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<std::string> AesUnwrap(const uint8_t* kek,
int n,
const uint8_t* cipher,
uint8_t* plain);

#endif // BAREOS_LIB_CRYPTO_WRAP_H_
15 changes: 7 additions & 8 deletions core/src/plugins/stored/scsicrypto/scsicrypto-sd.cc
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
}
Expand Down

0 comments on commit df3bc2b

Please sign in to comment.