blob: 4f31747c0fa538ce5fe793d2c7c9e2619cf39696 [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_CM_128_HMAC_SHA256_4,
{
from_hex("101112131415161718191a1b1c1d1e1f"),
from_hex("170023b51101e8cf3180"),
from_hex("1701aa0743f6fed8c056"),
from_hex("1702eae8243335f26dc9"),
from_hex("1affff0023b51101b0927605"),
from_hex("2affff01001981bb4f5d35ad0c"),
} },
{ CipherSuite::AES_CM_128_HMAC_SHA256_8,
{
from_hex("202122232425262728292a2b2c2d2e2f"),
from_hex("170022067e9270080090597dfadc"),
from_hex("1701d868b21f5e80434093d12eef"),
from_hex("170266de5b9332a80dea44a6407c"),
from_hex("1affff0022067e92500ce44901a10eef"),
from_hex("2affff01005ba58d1302a41630f1214e17"),
} },
{ CipherSuite::AES_GCM_128_SHA256,
{
from_hex("303132333435363738393a3b3c3d3e3f"),
from_hex("170048310f3b8c8a7297a92b3ed392938f9d0d087118"),
from_hex("170145c8c2cd5ef5773e38f23ee6236a623f8351cfce"),
from_hex("17021ea6e7b05246606050b44fe105f419dea85b4b7a"),
from_hex("1affff0048310f3b542c2bc859816a10ee5f83f4f840f6e5"),
from_hex("2affff0100f1f838df14b1e675fb0b0618291838e628fea346"),
} },
{ CipherSuite::AES_GCM_256_SHA512,
{
from_hex(
"404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"),
from_hex("1700b591faafe60c9c3a7d8dd1c18f91a72c510c8e63"),
from_hex("1701d555e665358a2486d99ac7272bedd503f53ec9d7"),
from_hex("170222e5fcd4709da8cc4d4a4e6e38a0b16afd0063fc"),
from_hex("1affff00b591faafc843b5831c7fc08b477d926f8c4c8f9b"),
from_hex("2affff01007b0e9ee905ab26c73927d7ece036a08c618610e4"),
} },
};
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_CM_128_HMAC_SHA256_4,
from_hex("101112131415161718191a1b1c1d1e1f") },
{ CipherSuite::AES_CM_128_HMAC_SHA256_8,
from_hex("202122232425262728292a2b2c2d2e2f") },
{ CipherSuite::AES_GCM_128_SHA256,
from_hex("303132333435363738393a3b3c3d3e3f") },
{ CipherSuite::AES_GCM_256_SHA512,
from_hex("404142434445464748494a4b4c4d4e4f"
"505152535455565758595a5b5c5d5e5f") },
};
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);
}
}
}