Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update GcmHeader types #3572

Merged
merged 11 commits into from
Feb 18, 2022
2 changes: 2 additions & 0 deletions include/ccf/crypto/sha256_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include <array>
#include <span>
#define FMT_HEADER_ONLY
#include <fmt/format.h>

namespace crypto
{
Expand Down
148 changes: 34 additions & 114 deletions include/ccf/crypto/symmetric_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,149 +2,68 @@
// Licensed under the Apache 2.0 License.
#pragma once

#include "ccf/crypto/entropy.h"
#include "ccf/ds/buffer.h"
#include "ds/serialized.h"
#include "ds/thread_messaging.h"

#include <vector>

namespace crypto
{
constexpr size_t GCM_SIZE_KEY = 32;
constexpr size_t GCM_DEFAULT_KEY_SIZE = 32;

constexpr size_t GCM_SIZE_TAG = 16;
constexpr size_t GCM_SIZE_IV = 12;

template <size_t SIZE_IV = GCM_SIZE_IV>
struct GcmHeader
{
uint8_t tag[GCM_SIZE_TAG] = {};
uint8_t iv[SIZE_IV] = {};

// 12 bytes IV with 8 LSB are unique sequence number
// and 4 MSB are 4 LSB of term (with last bit indicating a snapshot)
constexpr static uint8_t IV_DELIMITER = 8;
constexpr static size_t RAW_DATA_SIZE = sizeof(tag) + sizeof(iv);

GcmHeader() = default;
GcmHeader(const uint8_t* data, size_t size)
{
if (size != RAW_DATA_SIZE)
{
throw std::logic_error("Incompatible IV size");
}

memcpy(tag, data, sizeof(tag));
memcpy(iv, data + sizeof(tag), sizeof(iv));
}

GcmHeader(const std::vector<uint8_t>& data) :
GcmHeader(data.data(), data.size())
{}

void set_iv_seq(uint64_t seq)
{
*reinterpret_cast<uint64_t*>(iv) = seq;
}

void set_iv_term(uint64_t term)
{
if (term > 0x7FFFFFFF)
{
throw std::logic_error(fmt::format(
"term should fit in 31 bits of IV. Value is: 0x{0:x}", term));
}

*reinterpret_cast<uint32_t*>(iv + IV_DELIMITER) =
static_cast<uint32_t>(term);
}

uint64_t get_term() const
{
return *reinterpret_cast<const uint32_t*>(iv + IV_DELIMITER);
}

void set_iv_snapshot(bool is_snapshot)
{
// Set very last bit in IV
iv[SIZE_IV - 1] |= (is_snapshot << ((sizeof(uint8_t) * 8) - 1));
}

void set_iv(uint8_t* iv_, size_t size)
{
if (size != SIZE_IV)
{
throw std::logic_error(
fmt::format("Specified IV is not of size {}", SIZE_IV));
}
// Size does not change after construction
std::vector<uint8_t> iv;

memcpy(iv, iv_, size);
}
GcmHeader(size_t iv_size);

CBuffer get_iv() const
{
return {iv, SIZE_IV};
}
void set_iv(const uint8_t* data, size_t size);
CBuffer get_iv() const;

uint64_t get_iv_int() const
{
return *reinterpret_cast<const uint64_t*>(iv);
}
size_t serialised_size() const;
std::vector<uint8_t> serialise();

std::vector<uint8_t> serialise()
{
auto space = RAW_DATA_SIZE;
std::vector<uint8_t> serial_hdr(space);
void deserialise(const std::vector<uint8_t>& ser);
void deserialise(const uint8_t*& data, size_t& size);
};

auto data_ = serial_hdr.data();
serialized::write(data_, space, tag, sizeof(tag));
serialized::write(data_, space, iv, sizeof(iv));
template <size_t IV_BYTES>
struct FixedSizeGcmHeader : public GcmHeader
{
static constexpr size_t IV_SIZE = IV_BYTES;

return serial_hdr;
}
FixedSizeGcmHeader() : GcmHeader(IV_SIZE) {}

void deserialise(const std::vector<uint8_t>& ser)
static size_t serialised_size()
{
auto data = ser.data();
auto size = ser.size();

deserialise(data, size);
return GCM_SIZE_TAG + IV_SIZE;
}

void deserialise(const uint8_t*& data, size_t& size)
void set_random_iv(EntropyPtr entropy = crypto::create_entropy())
{
memcpy(
tag, serialized::read(data, size, GCM_SIZE_TAG).data(), GCM_SIZE_TAG);
memcpy(iv, serialized::read(data, size, SIZE_IV).data(), SIZE_IV);
iv = entropy->random(IV_SIZE);
}
};

// GcmHeader with 12-byte (96-bit) IV
using StandardGcmHeader = FixedSizeGcmHeader<12>;

struct GcmCipher
{
GcmHeader<> hdr;
StandardGcmHeader hdr;
std::vector<uint8_t> cipher;

GcmCipher() {}
GcmCipher(size_t size) : cipher(size) {}

std::vector<uint8_t> serialise()
{
std::vector<uint8_t> serial;
auto space = GcmHeader<>::RAW_DATA_SIZE + cipher.size();
serial.resize(space);
GcmCipher();
GcmCipher(size_t size);

auto data_ = serial.data();
serialized::write(data_, space, hdr.tag, sizeof(hdr.tag));
serialized::write(data_, space, hdr.iv, sizeof(hdr.iv));
serialized::write(data_, space, cipher.data(), cipher.size());
std::vector<uint8_t> serialise();

return serial;
}

void deserialise(const std::vector<uint8_t>& serial)
{
auto size = serial.size();
auto data_ = serial.data();
hdr = serialized::read(data_, size, GcmHeader<>::RAW_DATA_SIZE);
cipher = serialized::read(data_, size, size);
}
void deserialise(const std::vector<uint8_t>& serial);
};

class KeyAesGcm
Expand All @@ -169,6 +88,7 @@ namespace crypto
CBuffer aad,
uint8_t* plain) const = 0;

// Key size in bits
virtual size_t key_size() const = 0;
};

Expand All @@ -180,7 +100,7 @@ namespace crypto
inline void check_supported_aes_key_size(size_t num_bits)
{
if (num_bits != 128 && num_bits != 192 && num_bits != 256)
throw std::runtime_error("unsupported key size");
throw std::runtime_error("Unsupported key size");
}

/** Default initialization vector for AES-GCM (12 zeroes) */
Expand Down
86 changes: 86 additions & 0 deletions src/crypto/symmetric_key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,95 @@

#include "ccf/crypto/rsa_key_pair.h"
#include "ccf/crypto/symmetric_key.h"
#include "ds/serialized.h"

#define FMT_HEADER_ONLY
#include <fmt/format.h>

namespace crypto
{
/// GcmHeader implementation
GcmHeader::GcmHeader(size_t iv_size)
{
iv.resize(iv_size);
}

void GcmHeader::set_iv(const uint8_t* data, size_t size)
{
if (size != iv.size())
{
throw std::logic_error(
fmt::format("Specified IV is not of size {}", iv.size()));
}

memcpy(iv.data(), data, size);
}

CBuffer GcmHeader::get_iv() const
{
return {iv.data(), iv.size()};
}

size_t GcmHeader::serialised_size() const
{
return sizeof(tag) + iv.size();
}

std::vector<uint8_t> GcmHeader::serialise()
{
auto space = serialised_size();
std::vector<uint8_t> serial_hdr(space);

auto data_ = serial_hdr.data();
serialized::write(data_, space, tag, sizeof(tag));
serialized::write(data_, space, iv.data(), iv.size());

return serial_hdr;
}

void GcmHeader::deserialise(const std::vector<uint8_t>& ser)
{
auto data = ser.data();
auto size = ser.size();

deserialise(data, size);
}

void GcmHeader::deserialise(const uint8_t*& data, size_t& size)
{
memcpy(
tag, serialized::read(data, size, GCM_SIZE_TAG).data(), GCM_SIZE_TAG);
iv = serialized::read(data, size, iv.size());
}

/// GcmCipher implementation
GcmCipher::GcmCipher() = default;

GcmCipher::GcmCipher(size_t size) : cipher(size) {}

std::vector<uint8_t> GcmCipher::serialise()
{
std::vector<uint8_t> serial;
auto space = hdr.serialised_size() + cipher.size();
serial.resize(space);

auto data_ = serial.data();
serialized::write(data_, space, hdr.tag, sizeof(hdr.tag));
serialized::write(data_, space, hdr.iv.data(), hdr.iv.size());
serialized::write(data_, space, cipher.data(), cipher.size());

return serial;
}

void GcmCipher::deserialise(const std::vector<uint8_t>& serial)
{
auto data = serial.data();
auto size = serial.size();
hdr.deserialise(data, size);
cipher = serialized::read(data, size, size);
}

/// Free function implementation
std::unique_ptr<KeyAesGcm> make_key_aes_gcm(CBuffer rawKey)
{
return std::make_unique<KeyAesGcm_OpenSSL>(rawKey);
Expand Down
3 changes: 2 additions & 1 deletion src/crypto/test/bench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ template <MDType M, size_t NContents>
static void benchmark_hmac(picobench::state& s)
{
const auto contents = make_contents<NContents>();
const auto key = crypto::create_entropy()->random(crypto::GCM_SIZE_KEY);
const auto key =
crypto::create_entropy()->random(crypto::GCM_DEFAULT_KEY_SIZE);

s.start_timer();
for (auto _ : s)
Expand Down
25 changes: 17 additions & 8 deletions src/crypto/test/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,31 +425,40 @@ TEST_CASE("Create sign and verify certificates")
} while (corrupt_csr);
}

static const vector<uint8_t>& getRawKey()
static const vector<uint8_t>& get_raw_key()
{
static const vector<uint8_t> v(16, '$');
return v;
}

TEST_CASE("ExtendedIv0")
{
auto k = crypto::make_key_aes_gcm(getRawKey());
auto k = crypto::make_key_aes_gcm(get_raw_key());
// setup plain text
unsigned char rawP[100];
memset(rawP, 'x', sizeof(rawP));
Buffer p{rawP, sizeof(rawP)};

// test large IV
GcmHeader<1234> h;
using LargeIVGcmHeader = FixedSizeGcmHeader<1234>;
LargeIVGcmHeader h;

SUBCASE("Null IV") {}

SUBCASE("Random IV")
{
h.set_random_iv();
}

k->encrypt(h.get_iv(), p, nullb, p.p, h.tag);

auto k2 = crypto::make_key_aes_gcm(getRawKey());
auto k2 = crypto::make_key_aes_gcm(get_raw_key());
REQUIRE(k2->decrypt(h.get_iv(), h.tag, p, nullb, p.p));
}

TEST_CASE("AES Key wrap with padding")
{
auto key = getRawKey();
GcmHeader<1234> h;
auto key = get_raw_key();
std::vector<uint8_t> aad(123, 'y');

std::vector<uint8_t> key_to_wrap = create_entropy()->random(997);
Expand All @@ -465,7 +474,7 @@ TEST_CASE("AES Key wrap with padding")

TEST_CASE("CKM_RSA_PKCS_OAEP")
{
auto key = getRawKey();
auto key = get_raw_key();

auto rsa_kp = make_rsa_key_pair();
auto rsa_pk = make_rsa_public_key(rsa_kp->public_key_pem());
Expand Down Expand Up @@ -499,7 +508,7 @@ TEST_CASE("CKM_RSA_AES_KEY_WRAP")
TEST_CASE("AES-GCM convenience functions")
{
EntropyPtr entropy = create_entropy();
std::vector<uint8_t> key = entropy->random(GCM_SIZE_KEY);
std::vector<uint8_t> key = entropy->random(GCM_DEFAULT_KEY_SIZE);
auto encrypted = aes_gcm_encrypt(key, contents);
auto decrypted = aes_gcm_decrypt(key, encrypted);
REQUIRE(decrypted == contents);
Expand Down
7 changes: 3 additions & 4 deletions src/indexing/enclave_lfs_access.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ namespace ccf::indexing
crypto::GcmCipher gcm(contents.size());

// Use a random IV for each call
auto iv = entropy_src->random(crypto::GCM_SIZE_IV);
gcm.hdr.set_iv(iv.data(), iv.size());
gcm.hdr.set_random_iv();

encryption_key->encrypt(
gcm.hdr.get_iv(), contents, nullb, gcm.cipher.data(), gcm.hdr.tag);
Expand All @@ -127,8 +126,8 @@ namespace ccf::indexing
{
// Generate a fresh random key. Only this specific instance, in this
// enclave, can read these files!
encryption_key =
crypto::make_key_aes_gcm(entropy_src->random(crypto::GCM_SIZE_KEY));
encryption_key = crypto::make_key_aes_gcm(
entropy_src->random(crypto::GCM_DEFAULT_KEY_SIZE));
}

void register_message_handlers(
Expand Down
Loading