blob: cc9955ce239f89ca3d68c6facb925d05346e34c8 [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:
return "AES_GCM_128";
case CipherSuite::AES_GCM_256:
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,
{
from_hex("101112131415161718191a1b1c1d1e1f"),
from_hex("1700111266b003b9cd7225d9d4cb6e5c8143bb80bf98"),
from_hex("1701c7845beffa61a9d13b2a85bd8e181e6e637aedcd"),
from_hex("1702475146c7d0d0aac1c97cf2d5c7610bac15ba9515"),
from_hex("1affff00111266b0a5fc4fc173aafd0c5c56956dc6bc87c7"),
from_hex("2affff0100f15d230ba637848d89c9910dde14f9f23372e610"),
} },
{ CipherSuite::AES_GCM_256,
{
from_hex("202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c"
"3d3e3f"),
from_hex("1700e32fe35a4af2c10a224d72d44f9bd0cb037cdb81"),
from_hex("17010bea6abc8a7b049b74f09e9eaecb1c5d58291ccb"),
from_hex("1702385144f2c67d0ee316f05ce00bb9ae10f83e0e24"),
from_hex("1affff00e32fe35a8b7f2445746251610df8600801b02829"),
from_hex("2affff01008e9c505cb18ec35ea0bdbaa8727f50921a52e902"),
} },
};
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, from_hex("101112131415161718191a1b1c1d1e1f") },
{ CipherSuite::AES_GCM_256,
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);
}
}
}