blob: cf9fdf7b551a31499b59462ff8501ed71fa13f5e [file] [edit]
#include <doctest/doctest.h>
#include <sframe/sframe.h>
#include <iostream> // for string, operator<<
#include <map> // for map
#include <stdexcept> // for invalid_argument
#include <string> // for basic_string, operator==
using namespace sframe;
bytes
from_hex(const std::string& hex)
{
if (hex.length() % 2 == 1) {
throw std::invalid_argument("Odd-length hex string");
}
auto len = int(hex.length() / 2);
auto out = bytes(len);
for (int i = 0; i < len; i += 1) {
auto byte = hex.substr(2 * i, 2);
out[i] = static_cast<uint8_t>(strtol(byte.c_str(), nullptr, 16));
}
return out;
}
std::string
ciphersuite_name(CipherSuite suite)
{
switch (suite) {
case CipherSuite::AES_GCM_128_SHA256:
return "AES_GCM_128";
case CipherSuite::AES_GCM_256_SHA512:
return "AES_GCM_256";
default:
return "Unknown ciphersuite";
}
}
TEST_CASE("SFrame Known-Answer")
{
struct KnownAnswerTest
{
bytes key;
bytes short_kid_ctr0;
bytes short_kid_ctr1;
bytes short_kid_ctr2;
bytes long_kid_short_ctr;
bytes long_kid_long_ctr;
};
const auto short_kid = KeyID(0x07);
const auto long_kid = KeyID(0xffff);
const auto long_ctr = KeyID(0x0100);
const auto plaintext = from_hex("00010203");
const std::map<CipherSuite, KnownAnswerTest> cases{
{ CipherSuite::AES_GCM_128_SHA256,
{
from_hex("101112131415161718191a1b1c1d1e1f"),
from_hex("170003fcfa7edc96c2588a057f97da438fd964805f2e"),
from_hex("17014999d3b99fc9f0aaf67bdaafbe2e063f62670e83"),
from_hex("1702ba35e14d380bb8783ac7ce0df0956b87e59ccfa5"),
from_hex("1affff0003fcfa7e05ae25c2e08687b08e6aa1b9e62eaf7e"),
from_hex("2affff0100ab7fed4b60f2a2bce541613a50fafc5adb518287"),
} },
{ CipherSuite::AES_GCM_256_SHA512,
{
from_hex(
"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"),
from_hex("1700eb166e453dac4de40e2bc7a2f07aaea11bbbb36c"),
from_hex("1701a817dcfd4e8aa98981459086566f3cd30701af9c"),
from_hex("1702294c87f5af03eb0602aed3a4beb2104ecc11653e"),
from_hex("1affff00eb166e45641c5c46e602d8f429d2a0de241d007a"),
from_hex("2affff010069f14831479e0ebe67276c36006aa5d1c584f817"),
} },
};
for (auto& pair : cases) {
auto& suite = pair.first;
auto& tc = pair.second;
auto ctx = Context(suite);
ctx.add_key(short_kid, tc.key);
ctx.add_key(long_kid, tc.key);
// KID=0x07, CTR=0, 1, 2
auto ct0 = ctx.protect(short_kid, plaintext);
auto ct1 = ctx.protect(short_kid, plaintext);
auto ct2 = ctx.protect(short_kid, plaintext);
CHECK(ct0 == tc.short_kid_ctr0);
CHECK(ct1 == tc.short_kid_ctr1);
CHECK(ct2 == tc.short_kid_ctr2);
CHECK(plaintext == ctx.unprotect(ct0));
CHECK(plaintext == ctx.unprotect(ct1));
CHECK(plaintext == ctx.unprotect(ct2));
// KID=0xffff, CTR=0
auto ctLS = ctx.protect(long_kid, plaintext);
for (Counter ctr = 1; ctr < long_ctr; ctr++) {
ctx.protect(long_kid, plaintext);
}
auto ctLL = ctx.protect(long_kid, plaintext);
CHECK(ctLS == tc.long_kid_short_ctr);
CHECK(ctLL == tc.long_kid_long_ctr);
CHECK(plaintext == ctx.unprotect(ct0));
CHECK(plaintext == ctx.unprotect(ct1));
CHECK(plaintext == ctx.unprotect(ct2));
}
}
TEST_CASE("SFrame Round-Trip")
{
const auto rounds = 1 << 9;
const auto kid = KeyID(0x42);
const auto plaintext = from_hex("00010203");
const std::map<CipherSuite, bytes> keys{
{ CipherSuite::AES_GCM_128_SHA256,
from_hex("101112131415161718191a1b1c1d1e1f") },
{ CipherSuite::AES_GCM_256_SHA512,
from_hex("202122232425262728292a2b2c2d2e2f"
"303132333435363738393a3b3c3d3e3f") },
};
for (auto& pair : keys) {
auto& suite = pair.first;
auto& key = pair.second;
auto send = Context(suite);
send.add_key(kid, key);
auto recv = Context(suite);
recv.add_key(kid, key);
for (int i = 0; i < rounds; i++) {
auto encrypted = send.protect(kid, plaintext);
auto decrypted = recv.unprotect(encrypted);
CHECK(decrypted == plaintext);
}
}
}