blob: c302f51e3939f981398d0f6d3d0657d05a199a87 [file] [edit]
#include <sframe/sframe.h>
#include "crypto.h"
#include "header.h"
#include <array>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <tuple>
namespace sframe {
std::ostream&
operator<<(std::ostream& str, const input_bytes data)
{
str.flags(std::ios::hex);
for (const auto& byte : data) {
str << std::setw(2) << std::setfill('0') << int(byte);
}
return str;
}
Context::Context(CipherSuite suite_in)
: suite(suite_in)
{}
static const bytes sframe_label{
0x53, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x31, 0x30 // "SFrame10"
};
static const bytes sframe_key_label{ 0x6b, 0x65, 0x79 }; // "key"
static const bytes sframe_salt_label{ 0x73, 0x61, 0x6c, 0x74 }; // "salt"
static const bytes sframe_ctr_label{
// "SFrame10 AES CM AEAD"
0x53, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x31, 0x30, 0x20, 0x41,
0x45, 0x53, 0x20, 0x43, 0x4d, 0x20, 0x41, 0x45, 0x41, 0x44,
};
static const bytes sframe_enc_label{ 0x65, 0x6e, 0x63 }; // "enc"
static const bytes sframe_auth_label{ 0x61, 0x75, 0x74, 0x68 }; // "auth"
void
Context::add_key(KeyID key_id, const bytes& base_key)
{
auto key_size = cipher_key_size(suite);
auto nonce_size = cipher_nonce_size(suite);
auto hash_size = cipher_digest_size(suite);
auto secret = hkdf_extract(suite, sframe_label, base_key);
auto key = hkdf_expand(suite, secret, sframe_key_label, key_size);
auto salt = hkdf_expand(suite, secret, sframe_salt_label, nonce_size);
// If using CTR+HMAC, set key = enc_key || auth_key
if (suite == CipherSuite::AES_CM_128_HMAC_SHA256_4 ||
suite == CipherSuite::AES_CM_128_HMAC_SHA256_8) {
secret = hkdf_extract(suite, sframe_ctr_label, key);
auto main_key = key;
auto enc_key = hkdf_expand(suite, secret, sframe_enc_label, key_size);
auto auth_key = hkdf_expand(suite, secret, sframe_auth_label, hash_size);
key = enc_key;
key.insert(key.end(), auth_key.begin(), auth_key.end());
}
state.insert_or_assign(key_id,
KeyState{ std::move(key), std::move(salt), 0 });
}
static bytes
form_nonce(Counter ctr, const bytes& salt)
{
auto nonce = salt;
for (size_t i = 0; i < sizeof(ctr); i++) {
nonce[nonce.size() - i - 1] ^= uint8_t(ctr >> (8 * i));
}
return nonce;
}
output_bytes
Context::protect(KeyID key_id, output_bytes ciphertext, input_bytes plaintext)
{
auto it = state.find(key_id);
if (it == state.end()) {
throw std::runtime_error("Unknown key");
}
auto& st = it->second;
const auto ctr = st.counter;
st.counter += 1;
auto hdr_size = Header{ key_id, ctr }.encode(ciphertext);
auto header = ciphertext.subspan(0, hdr_size);
auto inner_ciphertext = ciphertext.subspan(hdr_size);
const auto nonce = form_nonce(ctr, st.salt);
auto final_ciphertext =
seal(suite, st.key, nonce, inner_ciphertext, header, plaintext);
return ciphertext.subspan(0, hdr_size + final_ciphertext.size());
}
output_bytes
Context::unprotect(output_bytes plaintext, input_bytes ciphertext)
{
auto [header, aad] = Header::decode(ciphertext);
auto inner_ciphertext = ciphertext.subspan(aad.size());
auto it = state.find(header.key_id);
if (it == state.end()) {
throw std::runtime_error("Unknown key");
}
const auto& st = it->second;
const auto nonce = form_nonce(header.counter, st.salt);
return open(suite, st.key, nonce, plaintext, aad, inner_ciphertext);
}
} // namespace sframe