From 630c27806c693c6dba8233a28939fe69c31fe808 Mon Sep 17 00:00:00 2001 From: kwasniow Date: Wed, 23 Sep 2026 08:46:56 +0200 Subject: [PATCH] feat: add AES-256-CTR-HMAC-SHA512 cipher suites from draft-barnes-sframe-iana-256 --- fuzz/fuzz_unprotect.cpp | 2 +- include/sframe/sframe.h | 6 ++- src/crypto.cpp | 20 ++++++++++ src/crypto.h | 2 +- src/crypto_boringssl.cpp | 24 +++++++++--- src/crypto_openssl11.cpp | 29 +++++++++++---- src/crypto_openssl3.cpp | 24 +++++++++--- src/sframe.cpp | 6 ++- test/sframe.cpp | 79 +++++++++++++++++++++++++++++++++++++++- test/test-vectors.json | 48 ++++++++++++++++++++++++ 10 files changed, 215 insertions(+), 25 deletions(-) diff --git a/fuzz/fuzz_unprotect.cpp b/fuzz/fuzz_unprotect.cpp index 67ccb11..ec6ea75 100644 --- a/fuzz/fuzz_unprotect.cpp +++ b/fuzz/fuzz_unprotect.cpp @@ -21,7 +21,7 @@ LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) } // Use the first byte to select a cipher suite, remaining bytes as ciphertext. - auto suite = static_cast((data[0] % 5) + 1); + auto suite = static_cast((data[0] % 8) + 1); auto ciphertext = input_bytes(data + 1, size - 1); auto ctx = Context(suite); diff --git a/include/sframe/sframe.h b/include/sframe/sframe.h index c380a06..b6f624f 100644 --- a/include/sframe/sframe.h +++ b/include/sframe/sframe.h @@ -81,6 +81,9 @@ enum class CipherSuite : uint16_t AES_128_CTR_HMAC_SHA256_32 = 3, AES_GCM_128_SHA256 = 4, AES_GCM_256_SHA512 = 5, + AES_256_CTR_HMAC_SHA512_80 = 6, + AES_256_CTR_HMAC_SHA512_64 = 7, + AES_256_CTR_HMAC_SHA512_32 = 8, }; using input_bytes = span; @@ -106,7 +109,8 @@ struct KeyRecord KeyUsage usage, input_bytes base_key); - static constexpr size_t max_key_size = 48; + // 32-byte AES-256 key + 64-byte HMAC-SHA512 key + static constexpr size_t max_key_size = 96; static constexpr size_t max_salt_size = 12; owned_bytes key; diff --git a/src/crypto.cpp b/src/crypto.cpp index 8b8a8df..2ac8262 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -18,6 +18,9 @@ cipher_digest_size(CipherSuite suite) return 32; case CipherSuite::AES_GCM_256_SHA512: + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: return 64; default: @@ -35,6 +38,12 @@ cipher_key_size(CipherSuite suite) // 16-byte AES key + 32-byte HMAC key return 48; + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: + // 32-byte AES key + 64-byte HMAC key + return 96; + case CipherSuite::AES_GCM_128_SHA256: return 16; @@ -55,6 +64,11 @@ cipher_enc_key_size(CipherSuite suite) case CipherSuite::AES_128_CTR_HMAC_SHA256_32: return 16; + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: + return 32; + default: return SFrameErrorType::unsupported_ciphersuite_error; } @@ -69,6 +83,9 @@ cipher_nonce_size(CipherSuite suite) case CipherSuite::AES_128_CTR_HMAC_SHA256_32: case CipherSuite::AES_GCM_128_SHA256: case CipherSuite::AES_GCM_256_SHA512: + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: return 12; default: @@ -81,12 +98,15 @@ cipher_overhead(CipherSuite suite) { switch (suite) { case CipherSuite::AES_128_CTR_HMAC_SHA256_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: return 10; // 80-bit tag case CipherSuite::AES_128_CTR_HMAC_SHA256_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: return 8; // 64-bit tag case CipherSuite::AES_128_CTR_HMAC_SHA256_32: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: return 4; // 32-bit tag case CipherSuite::AES_GCM_128_SHA256: diff --git a/src/crypto.h b/src/crypto.h index 9f1a10e..6070420 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -27,7 +27,7 @@ clear_openssl_errors(); /// static constexpr size_t max_hkdf_extract_size = 64; -static constexpr size_t max_hkdf_expand_size = 64; +static constexpr size_t max_hkdf_expand_size = 96; Result> hkdf_extract(CipherSuite suite, input_bytes salt, input_bytes ikm); diff --git a/src/crypto_boringssl.cpp b/src/crypto_boringssl.cpp index 7a168eb..69f7bf3 100644 --- a/src/crypto_boringssl.cpp +++ b/src/crypto_boringssl.cpp @@ -39,6 +39,9 @@ openssl_digest_type(CipherSuite suite) return EVP_sha256(); case CipherSuite::AES_GCM_256_SHA512: + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: return EVP_sha512(); default: @@ -55,6 +58,11 @@ openssl_cipher(CipherSuite suite) case CipherSuite::AES_128_CTR_HMAC_SHA256_32: return EVP_aes_128_ctr(); + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: + return EVP_aes_256_ctr(); + case CipherSuite::AES_GCM_128_SHA256: return EVP_aes_128_gcm(); @@ -70,12 +78,12 @@ openssl_cipher(CipherSuite suite) /// HKDF /// -Result> +Result> hkdf_extract(CipherSuite suite, input_bytes salt, input_bytes ikm) { clear_openssl_errors(); SFRAME_VALUE_OR_RETURN(md, openssl_digest_type(suite)); - auto out = owned_bytes(EVP_MD_size(md)); + auto out = owned_bytes(EVP_MD_size(md)); auto out_len = size_t(out.size()); if (1 != HKDF_extract(out.data(), &out_len, @@ -90,7 +98,7 @@ hkdf_extract(CipherSuite suite, input_bytes salt, input_bytes ikm) return out; } -Result> +Result> hkdf_expand(CipherSuite suite, input_bytes prk, input_bytes info, size_t size) { clear_openssl_errors(); @@ -318,7 +326,10 @@ seal(CipherSuite suite, switch (suite) { case CipherSuite::AES_128_CTR_HMAC_SHA256_80: case CipherSuite::AES_128_CTR_HMAC_SHA256_64: - case CipherSuite::AES_128_CTR_HMAC_SHA256_32: { + case CipherSuite::AES_128_CTR_HMAC_SHA256_32: + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: { return seal_ctr(suite, key, nonce, ct, aad, pt); } @@ -444,7 +455,10 @@ open(CipherSuite suite, switch (suite) { case CipherSuite::AES_128_CTR_HMAC_SHA256_80: case CipherSuite::AES_128_CTR_HMAC_SHA256_64: - case CipherSuite::AES_128_CTR_HMAC_SHA256_32: { + case CipherSuite::AES_128_CTR_HMAC_SHA256_32: + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: { return open_ctr(suite, key, nonce, pt, aad, ct); } diff --git a/src/crypto_openssl11.cpp b/src/crypto_openssl11.cpp index 962f9e8..c925484 100644 --- a/src/crypto_openssl11.cpp +++ b/src/crypto_openssl11.cpp @@ -45,6 +45,9 @@ openssl_digest_type(CipherSuite suite) return EVP_sha256(); case CipherSuite::AES_GCM_256_SHA512: + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: return EVP_sha512(); default: @@ -61,6 +64,11 @@ openssl_cipher(CipherSuite suite) case CipherSuite::AES_128_CTR_HMAC_SHA256_32: return EVP_aes_128_ctr(); + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: + return EVP_aes_256_ctr(); + case CipherSuite::AES_GCM_128_SHA256: return EVP_aes_128_gcm(); @@ -146,30 +154,29 @@ struct HMAC /// HKDF /// -Result> +Result> hkdf_extract(CipherSuite suite, input_bytes salt, input_bytes ikm) { clear_openssl_errors(); SFRAME_VALUE_OR_RETURN(h, HMAC::create(suite, salt)); SFRAME_VOID_OR_RETURN(h.write(ikm)); - auto out = owned_bytes(); + auto out = owned_bytes(); SFRAME_VALUE_OR_RETURN(md, h.digest(out)); out.resize(md.size()); return out; } -Result> +Result> hkdf_expand(CipherSuite suite, input_bytes prk, input_bytes info, size_t size) { clear_openssl_errors(); - // Ensure that we need only one hash invocation - if (size > max_hkdf_extract_size) { + if (size > max_hkdf_expand_size) { return SFrameError(SFrameErrorType::invalid_parameter_error, "Size too big for hkdf_expand"); } - auto out = owned_bytes(0); + auto out = owned_bytes(0); auto block = owned_bytes(0); SFRAME_VALUE_OR_RETURN(block_size, cipher_digest_size(suite)); @@ -370,7 +377,10 @@ seal(CipherSuite suite, switch (suite) { case CipherSuite::AES_128_CTR_HMAC_SHA256_80: case CipherSuite::AES_128_CTR_HMAC_SHA256_64: - case CipherSuite::AES_128_CTR_HMAC_SHA256_32: { + case CipherSuite::AES_128_CTR_HMAC_SHA256_32: + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: { return seal_ctr(suite, key, nonce, ct, aad, pt); } @@ -496,7 +506,10 @@ open(CipherSuite suite, switch (suite) { case CipherSuite::AES_128_CTR_HMAC_SHA256_80: case CipherSuite::AES_128_CTR_HMAC_SHA256_64: - case CipherSuite::AES_128_CTR_HMAC_SHA256_32: { + case CipherSuite::AES_128_CTR_HMAC_SHA256_32: + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: { return open_ctr(suite, key, nonce, pt, aad, ct); } diff --git a/src/crypto_openssl3.cpp b/src/crypto_openssl3.cpp index 5587624..f76fff5 100644 --- a/src/crypto_openssl3.cpp +++ b/src/crypto_openssl3.cpp @@ -38,6 +38,11 @@ openssl_cipher(CipherSuite suite) case CipherSuite::AES_128_CTR_HMAC_SHA256_32: return EVP_aes_128_ctr(); + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: + return EVP_aes_256_ctr(); + case CipherSuite::AES_GCM_128_SHA256: return EVP_aes_128_gcm(); @@ -60,6 +65,9 @@ openssl_digest_name(CipherSuite suite) return std::string(OSSL_DIGEST_NAME_SHA2_256); case CipherSuite::AES_GCM_256_SHA512: + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: return std::string(OSSL_DIGEST_NAME_SHA2_512); default: @@ -75,7 +83,7 @@ using scoped_evp_kdf = std::unique_ptr; using scoped_evp_kdf_ctx = std::unique_ptr; -Result> +Result> hkdf_extract(CipherSuite suite, input_bytes salt, input_bytes ikm) { clear_openssl_errors(); @@ -104,7 +112,7 @@ hkdf_extract(CipherSuite suite, input_bytes salt, input_bytes ikm) } const auto digest_size = EVP_KDF_CTX_get_kdf_size(ctx.get()); - auto out = owned_bytes(digest_size); + auto out = owned_bytes(digest_size); if (1 != EVP_KDF_derive(ctx.get(), out.data(), out.size(), nullptr)) { return SFrameErrorType::crypto_error; } @@ -112,7 +120,7 @@ hkdf_extract(CipherSuite suite, input_bytes salt, input_bytes ikm) return out; } -Result> +Result> hkdf_expand(CipherSuite suite, input_bytes prk, input_bytes info, size_t size) { clear_openssl_errors(); @@ -359,7 +367,10 @@ seal(CipherSuite suite, switch (suite) { case CipherSuite::AES_128_CTR_HMAC_SHA256_80: case CipherSuite::AES_128_CTR_HMAC_SHA256_64: - case CipherSuite::AES_128_CTR_HMAC_SHA256_32: { + case CipherSuite::AES_128_CTR_HMAC_SHA256_32: + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: { return seal_ctr(suite, key, nonce, ct, aad, pt); } @@ -485,7 +496,10 @@ open(CipherSuite suite, switch (suite) { case CipherSuite::AES_128_CTR_HMAC_SHA256_80: case CipherSuite::AES_128_CTR_HMAC_SHA256_64: - case CipherSuite::AES_128_CTR_HMAC_SHA256_32: { + case CipherSuite::AES_128_CTR_HMAC_SHA256_32: + case CipherSuite::AES_256_CTR_HMAC_SHA512_80: + case CipherSuite::AES_256_CTR_HMAC_SHA512_64: + case CipherSuite::AES_256_CTR_HMAC_SHA512_32: { return open_ctr(suite, key, nonce, pt, aad, ct); } diff --git a/src/sframe.cpp b/src/sframe.cpp index eccafd8..d6cf90c 100644 --- a/src/sframe.cpp +++ b/src/sframe.cpp @@ -359,8 +359,10 @@ MLSContext::EpochKeys::base_key(CipherSuite ciphersuite, auto enc_sender_id = owned_bytes<8>(); encode_uint(sender_id, enc_sender_id); - return hkdf_expand( - ciphersuite, sframe_epoch_secret, enc_sender_id, hash_size); + SFRAME_VALUE_OR_RETURN( + expanded, + hkdf_expand(ciphersuite, sframe_epoch_secret, enc_sender_id, hash_size)); + return owned_bytes(expanded); } void diff --git a/test/sframe.cpp b/test/sframe.cpp index 1dc488b..979a3a7 100644 --- a/test/sframe.cpp +++ b/test/sframe.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "common.h" #include @@ -23,15 +25,21 @@ TEST_CASE("SFrame Round-Trip") const std::map keys{ { CipherSuite::AES_128_CTR_HMAC_SHA256_80, from_hex("000102030405060708090a0b0c0d0e0f") }, - { CipherSuite::AES_128_CTR_HMAC_SHA256_80, + { CipherSuite::AES_128_CTR_HMAC_SHA256_64, from_hex("101112131415161718191a1b1c1d1e1f") }, - { CipherSuite::AES_128_CTR_HMAC_SHA256_80, + { CipherSuite::AES_128_CTR_HMAC_SHA256_32, from_hex("202122232425262728292a2b2c2d2e2f") }, { CipherSuite::AES_GCM_128_SHA256, from_hex("303132333435363738393a3b3c3d3e3f") }, { CipherSuite::AES_GCM_256_SHA512, from_hex("404142434445464748494a4b4c4d4e4f" "505152535455565758595a5b5c5d5e5f") }, + { CipherSuite::AES_256_CTR_HMAC_SHA512_80, + from_hex("606162636465666768696a6b6c6d6e6f") }, + { CipherSuite::AES_256_CTR_HMAC_SHA512_64, + from_hex("707172737475767778797a7b7c7d7e7f") }, + { CipherSuite::AES_256_CTR_HMAC_SHA512_32, + from_hex("808182838485868788898a8b8c8d8e8f") }, }; auto pt_out = bytes(plaintext.size()); @@ -73,6 +81,9 @@ TEST_CASE("MLS Round-Trip") CipherSuite::AES_128_CTR_HMAC_SHA256_32, CipherSuite::AES_GCM_128_SHA256, CipherSuite::AES_GCM_256_SHA512, + CipherSuite::AES_256_CTR_HMAC_SHA512_80, + CipherSuite::AES_256_CTR_HMAC_SHA512_64, + CipherSuite::AES_256_CTR_HMAC_SHA512_32, }; auto pt_out = bytes(plaintext.size()); @@ -126,6 +137,9 @@ TEST_CASE("MLS Round-Trip with context") CipherSuite::AES_128_CTR_HMAC_SHA256_32, CipherSuite::AES_GCM_128_SHA256, CipherSuite::AES_GCM_256_SHA512, + CipherSuite::AES_256_CTR_HMAC_SHA512_80, + CipherSuite::AES_256_CTR_HMAC_SHA512_64, + CipherSuite::AES_256_CTR_HMAC_SHA512_32, }; auto pt_out = bytes(plaintext.size()); @@ -414,3 +428,64 @@ TEST_CASE("MLS Remove Epoch") dec = to_bytes(member_b.unprotect(pt_out, enc, metadata).unwrap()); CHECK(plaintext == dec); } + +TEST_CASE("SFrame Tamper Detection") +{ + const auto kid = KeyID(0x42); + const auto base_key = from_hex("000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f"); + const auto plaintext = from_hex("00010203"); + const auto metadata = from_hex("0405060708"); + const std::vector suites{ + CipherSuite::AES_128_CTR_HMAC_SHA256_80, + CipherSuite::AES_128_CTR_HMAC_SHA256_64, + CipherSuite::AES_128_CTR_HMAC_SHA256_32, + CipherSuite::AES_GCM_128_SHA256, + CipherSuite::AES_GCM_256_SHA512, + CipherSuite::AES_256_CTR_HMAC_SHA512_80, + CipherSuite::AES_256_CTR_HMAC_SHA512_64, + CipherSuite::AES_256_CTR_HMAC_SHA512_32, + }; + + auto pt_out = bytes(plaintext.size()); + auto ct_out = bytes(plaintext.size() + Context::max_overhead); + + for (const auto& suite : suites) { + auto send = Context(suite); + send.add_key(kid, KeyUsage::protect, base_key).unwrap(); + + auto recv = Context(suite); + recv.add_key(kid, KeyUsage::unprotect, base_key).unwrap(); + + const auto encrypted = + to_bytes(send.protect(kid, ct_out, plaintext, metadata).unwrap()); + CHECK(to_bytes(recv.unprotect(pt_out, encrypted, metadata).unwrap()) == + plaintext); + + const auto header_size = Header::parse(encrypted).unwrap().size(); + + // A bit flipped in the authentication tag is detected. This is the case + // that a round-trip test cannot catch if the tag is truncated to the wrong + // length, since both sides would truncate identically. + auto bad_tag = encrypted; + bad_tag.back() ^= 0x01; + CHECK(recv.unprotect(pt_out, bad_tag, metadata).error().type() == + SFrameErrorType::authentication_error); + + auto bad_body = encrypted; + bad_body.at(header_size) ^= 0x01; + CHECK(recv.unprotect(pt_out, bad_body, metadata).error().type() == + SFrameErrorType::authentication_error); + + // Metadata is authenticated as part of the AAD, but not transmitted + auto bad_metadata = metadata; + bad_metadata.back() ^= 0x01; + CHECK(recv.unprotect(pt_out, encrypted, bad_metadata).error().type() == + SFrameErrorType::authentication_error); + + auto truncated = encrypted; + truncated.pop_back(); + CHECK(recv.unprotect(pt_out, truncated, metadata).error().type() == + SFrameErrorType::authentication_error); + } +} \ No newline at end of file diff --git a/test/test-vectors.json b/test/test-vectors.json index 34c1948..edd4760 100644 --- a/test/test-vectors.json +++ b/test/test-vectors.json @@ -1558,6 +1558,54 @@ "aad": "99012345674945544620534672616d65205747", "pt": "64726166742d696574662d736672616d652d656e63", "ct": "990123456794f509d36e9beacb0e261d99c7d1e972f1fed787d4049f17ca21353c1cc24d56ceabced279" + }, + { + "cipher_suite": 6, + "kid": 291, + "ctr": 17767, + "base_key": "000102030405060708090a0b0c0d0e0f", + "sframe_key_label": "534672616d6520312e3020536563726574206b65792000000000000001230006", + "sframe_salt_label": "534672616d6520312e30205365637265742073616c742000000000000001230006", + "sframe_secret": "0fc3ea6de6aac97a35f194cf9bed94d4b5230f1cb45a785c9fe5dce9c188938ab6ba005bc4c0a19181599e9d1bcf7b74aca48b60bf5e254e546d809313e083a3", + "sframe_key": "3c343886ec1c79278836863e00fe934c8894460cfa367ebdc4856b0a9268a4f4fb99437876819394ef90b10ee12602d023f7128ee50f2314c2cc3cff4c56616d2fe03ad2a254cc2ed29b2a4d3f2534c0dda9e7c391ad1917ea07aa221dd4b224", + "sframe_salt": "e082f7ce012ad30c87c49e3f", + "metadata": "4945544620534672616d65205747", + "nonce": "e082f7ce012ad30c87c4db58", + "aad": "99012345674945544620534672616d65205747", + "pt": "64726166742d696574662d736672616d652d656e63", + "ct": "9901234567b369e03ec6467ad505ddc84914115069280c5c797555be6e32cde6ac25bc9e" + }, + { + "cipher_suite": 7, + "kid": 291, + "ctr": 17767, + "base_key": "000102030405060708090a0b0c0d0e0f", + "sframe_key_label": "534672616d6520312e3020536563726574206b65792000000000000001230007", + "sframe_salt_label": "534672616d6520312e30205365637265742073616c742000000000000001230007", + "sframe_secret": "0fc3ea6de6aac97a35f194cf9bed94d4b5230f1cb45a785c9fe5dce9c188938ab6ba005bc4c0a19181599e9d1bcf7b74aca48b60bf5e254e546d809313e083a3", + "sframe_key": "7271d6c6cbccd2e2343d480ebea65718a7bb379eefcf3f8d107c1e2a76e755293a497fd9e4e8291b965161987ef4ef24983eabb06cb0a392defaab18654780a39c106ffa4a47d4183a6e593cd0c1bcab2b9c6dcf049215845bfb7580c4dea80e", + "sframe_salt": "46b4367993a314910d4d9f3d", + "metadata": "4945544620534672616d65205747", + "nonce": "46b4367993a314910d4dda5a", + "aad": "99012345674945544620534672616d65205747", + "pt": "64726166742d696574662d736672616d652d656e63", + "ct": "990123456797cb5644d8831ff8bdc080249990b24b569144cab2a87be22c20d97976" + }, + { + "cipher_suite": 8, + "kid": 291, + "ctr": 17767, + "base_key": "000102030405060708090a0b0c0d0e0f", + "sframe_key_label": "534672616d6520312e3020536563726574206b65792000000000000001230008", + "sframe_salt_label": "534672616d6520312e30205365637265742073616c742000000000000001230008", + "sframe_secret": "0fc3ea6de6aac97a35f194cf9bed94d4b5230f1cb45a785c9fe5dce9c188938ab6ba005bc4c0a19181599e9d1bcf7b74aca48b60bf5e254e546d809313e083a3", + "sframe_key": "afe92c81e0df8c00fab619e0559fe5aeefce1ef77789d4c728af1b1c1f2e3552c405d274415a5291ec075c2d9954c450fbd36682a4e978494808b703ce78b409f9fec29b91e6e703a75c4131377c80c9d51b8906088092452e2593eb142eea2d", + "sframe_salt": "f6de647bac1263524cfb6533", + "metadata": "4945544620534672616d65205747", + "nonce": "f6de647bac1263524cfb2054", + "aad": "99012345674945544620534672616d65205747", + "pt": "64726166742d696574662d736672616d652d656e63", + "ct": "9901234567112a94a288b85b49ffef1d279f2830165c39d76cac8884011c" } ] }