blob: ab4c46cc29d84c99f5c3c4070399a1acd8aa77a9 [file] [edit]
#include "header.h"
namespace sframe {
static size_t
uint_size(uint64_t val)
{
if (val < 0x08) {
// Fits in the config byte
return 0;
}
for (unsigned int i = 0; i < 8; i += 1) {
if ((val >> (8 * i)) == 0) {
return i;
}
}
return 8;
}
void
encode_uint(uint64_t val, output_bytes buffer)
{
size_t size = buffer.size();
for (size_t i = 0; i < size && i < 8; i++) {
buffer[size - i - 1] = uint8_t(val >> (8 * i));
}
}
static uint64_t
decode_uint(input_bytes data)
{
if (!data.empty() && data[0] == 0) {
throw invalid_parameter_error("Integer is not minimally encoded");
}
uint64_t val = 0;
for (size_t i = 0; i < data.size(); i++) {
val = (val << 8) + static_cast<uint64_t>(data[i]);
}
return val;
}
struct ValueOrLength
{
bool is_length = false;
uint8_t value_or_length = 0;
static ValueOrLength for_u64(uint64_t value)
{
if (value >= 0x08) {
return { true, static_cast<uint8_t>(uint_size(value) - 1) };
} else {
return { false, static_cast<uint8_t>(value) };
}
}
static ValueOrLength decode(uint8_t encoded)
{
const auto is_length = (encoded & 0x08) != 0;
const auto value_or_length = (encoded & 0x07);
return { is_length, static_cast<uint8_t>(value_or_length) };
}
uint8_t encode() const
{
return (((is_length) ? 1 : 0) << 3) | (value_or_length & 0x07);
}
size_t value_size() const
{
if (!is_length) {
return 0;
}
return value_or_length + 1;
}
std::tuple<uint64_t, input_bytes> read(input_bytes data) const
{
if (!is_length) {
// Nothing to read; value is already in config byte
return { value_or_length, data };
}
const auto size = value_size();
const auto value = decode_uint(data.subspan(0, size));
const auto remaining = data.subspan(size);
return { value, remaining };
}
private:
ValueOrLength(bool is_length_in, uint8_t value_or_length_in)
: is_length(is_length_in)
, value_or_length(value_or_length_in)
{
}
};
struct ConfigByte
{
ValueOrLength kid;
ValueOrLength ctr;
ConfigByte(uint64_t kid_in, uint64_t ctr_in)
: kid(ValueOrLength::for_u64(kid_in))
, ctr(ValueOrLength::for_u64(ctr_in))
{
}
explicit ConfigByte(uint8_t encoded)
: kid(ValueOrLength::decode(encoded >> 4))
, ctr(ValueOrLength::decode(encoded & 0x0f))
{
}
size_t encoded_size() const
{
return 1 + kid.value_size() + ctr.value_size();
}
uint8_t encode() const { return (kid.encode() << 4) | ctr.encode(); }
};
Header::Header(KeyID key_id_in, Counter counter_in)
: key_id(key_id_in)
, counter(counter_in)
{
const auto cfg = ConfigByte{ key_id, counter };
_encoded[0] = cfg.encode();
_encoded.resize(cfg.encoded_size());
const auto encoded = output_bytes(_encoded);
const auto after_cfg = encoded.subspan(1);
encode_uint(key_id, after_cfg.subspan(0, cfg.kid.value_size()));
const auto after_kid = after_cfg.subspan(cfg.kid.value_size());
encode_uint(counter, after_kid.subspan(0, cfg.ctr.value_size()));
}
Header
Header::parse(input_bytes buffer)
{
if (buffer.size() < Header::min_size) {
throw buffer_too_small_error("Ciphertext too small to decode header");
}
const auto cfg = ConfigByte{ buffer[0] };
const auto after_cfg = buffer.subspan(1);
const auto [key_id, after_kid] = cfg.kid.read(after_cfg);
const auto [counter, _] = cfg.ctr.read(after_kid);
const auto encoded = buffer.subspan(0, cfg.encoded_size());
return Header(key_id, counter, encoded);
}
Header::Header(KeyID key_id_in, Counter counter_in, input_bytes encoded_in)
: key_id(key_id_in)
, counter(counter_in)
, _encoded(encoded_in)
{
}
#if 0
std::tuple<Header, input_bytes>
decode(input_bytes buffer)
{
if (buffer.size() < Header::min_size) {
throw buffer_too_small_error("Ciphertext too small to decode header");
}
const auto cfg = ConfigByte{ buffer[0] };
const auto after_cfg = buffer.subspan(1);
const auto [kid, after_kid] = cfg.kid.read(after_cfg);
const auto [ctr, after_ctr] = cfg.ctr.read(after_kid);
const auto header = Header{ KeyID(kid), Counter(ctr) };
return { header, after_ctr };
}
size_t
Header::encode(output_bytes buffer) const
{
if (buffer.size() < size()) {
throw buffer_too_small_error("Buffer too small to encode header");
}
const auto cfg = ConfigByte{ key_id, counter };
buffer[0] = cfg.encode();
const auto after_cfg = buffer.subspan(1);
encode_uint(key_id, after_cfg.subspan(0, cfg.kid.size()));
const auto after_kid = after_cfg.subspan(cfg.kid.size());
encode_uint(counter, after_kid.subspan(0, cfg.ctr.size()));
return size();
}
#endif
} // namespace sframe