Skip to content

Commit

Permalink
Update GcmHeader types (#3572)
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyashton authored Feb 18, 2022
1 parent 2e09384 commit f1ac915
Show file tree
Hide file tree
Showing 12 changed files with 205 additions and 145 deletions.
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

0 comments on commit f1ac915

Please sign in to comment.