mirror of
https://github.com/itzmeanjan/ml-kem.git
synced 2026-01-09 15:47:55 -05:00
Trigger encapsulation failure by providing with malformed (i.e. non-reduced) public key
Signed-off-by: Anjan Roy <hello@itzmeanjan.in>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
#pragma once
|
||||
#include "ml_kem/internals/math/field.hpp"
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <charconv>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
// Given a hex encoded string of length 2*L, this routine can be used for parsing it as a byte array of length L.
|
||||
@@ -30,3 +32,25 @@ from_hex(std::string_view bytes)
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Given a valid ML-KEM-{512, 768, 1024} public key, this function mutates the last coefficient
|
||||
// of serialized polynomial vector s.t. it produces a malformed (i.e. non-reduced) polynomial vector.
|
||||
template<size_t pubkey_byte_len>
|
||||
static inline constexpr void
|
||||
make_malformed_pubkey(std::span<uint8_t, pubkey_byte_len> pubkey)
|
||||
{
|
||||
constexpr auto last_coeff_ends_at = pubkey_byte_len - 32;
|
||||
constexpr auto last_coeff_begins_at = last_coeff_ends_at - 2;
|
||||
|
||||
// < 16 -bit word >
|
||||
// (MSB) ---- | ---- | ---- | ---- (LSB)
|
||||
// | 12 -bits of last coeff, to be mutated | Most significant 4 -bits of second last coeff |
|
||||
const uint16_t last_coeff = (static_cast<uint16_t>(pubkey[last_coeff_begins_at + 1]) << 8) | static_cast<uint16_t>(pubkey[last_coeff_begins_at + 0]);
|
||||
|
||||
constexpr uint16_t hi = ml_kem_field::Q << 4; // Q (=3329) is not a valid element of Zq. Any value >= Q && < 2^12, would work.
|
||||
const uint16_t lo = last_coeff & 0xfu; // Don't touch most significant 4 -bits of second last coefficient
|
||||
const uint16_t updated_last_coeff = hi ^ lo; // 16 -bit word s.t. last coefficient is not reduced modulo prime Q
|
||||
|
||||
pubkey[last_coeff_begins_at + 0] = static_cast<uint8_t>(updated_last_coeff >> 0);
|
||||
pubkey[last_coeff_begins_at + 1] = static_cast<uint8_t>(updated_last_coeff >> 8);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ml_kem/ml_kem_1024.hpp"
|
||||
#include "test_helper.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// For ML-KEM-1024
|
||||
@@ -36,3 +37,33 @@ TEST(ML_KEM, ML_KEM_1024_KeygenEncapsDecaps)
|
||||
EXPECT_TRUE(is_encapsulated);
|
||||
EXPECT_EQ(shared_secret_sender, shared_secret_receiver);
|
||||
}
|
||||
|
||||
// For ML-KEM-1024
|
||||
//
|
||||
// - Generate a valid keypair.
|
||||
// - Malform public key s.t. last coefficient of polynomial vector is not properly reduced.
|
||||
// - Attempt to encapsulate using malformed public key. It must fail.
|
||||
TEST(ML_KEM, ML_KEM_1024_EncapsFailureDueToNonReducedPubKey)
|
||||
{
|
||||
std::array<uint8_t, ml_kem_1024::SEED_D_BYTE_LEN> seed_d{};
|
||||
std::array<uint8_t, ml_kem_1024::SEED_Z_BYTE_LEN> seed_z{};
|
||||
std::array<uint8_t, ml_kem_1024::SEED_M_BYTE_LEN> seed_m{};
|
||||
|
||||
std::array<uint8_t, ml_kem_1024::PKEY_BYTE_LEN> pubkey{};
|
||||
std::array<uint8_t, ml_kem_1024::SKEY_BYTE_LEN> seckey{};
|
||||
std::array<uint8_t, ml_kem_1024::CIPHER_TEXT_BYTE_LEN> cipher{};
|
||||
|
||||
std::array<uint8_t, ml_kem_1024::SHARED_SECRET_BYTE_LEN> shared_secret{};
|
||||
|
||||
ml_kem_prng::prng_t<256> prng{};
|
||||
prng.read(seed_d);
|
||||
prng.read(seed_z);
|
||||
prng.read(seed_m);
|
||||
|
||||
ml_kem_1024::keygen(seed_d, seed_z, pubkey, seckey);
|
||||
|
||||
make_malformed_pubkey<pubkey.size()>(pubkey);
|
||||
const auto is_encapsulated = ml_kem_1024::encapsulate(seed_m, pubkey, cipher, shared_secret);
|
||||
|
||||
EXPECT_FALSE(is_encapsulated);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ml_kem/ml_kem_512.hpp"
|
||||
#include "test_helper.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// For ML-KEM-512
|
||||
@@ -36,3 +37,33 @@ TEST(ML_KEM, ML_KEM_512_KeygenEncapsDecaps)
|
||||
EXPECT_TRUE(is_encapsulated);
|
||||
EXPECT_EQ(shared_secret_sender, shared_secret_receiver);
|
||||
}
|
||||
|
||||
// For ML-KEM-512
|
||||
//
|
||||
// - Generate a valid keypair.
|
||||
// - Malform public key s.t. last coefficient of polynomial vector is not properly reduced.
|
||||
// - Attempt to encapsulate using malformed public key. It must fail.
|
||||
TEST(ML_KEM, ML_KEM_512_EncapsFailureDueToNonReducedPubKey)
|
||||
{
|
||||
std::array<uint8_t, ml_kem_512::SEED_D_BYTE_LEN> seed_d{};
|
||||
std::array<uint8_t, ml_kem_512::SEED_Z_BYTE_LEN> seed_z{};
|
||||
std::array<uint8_t, ml_kem_512::SEED_M_BYTE_LEN> seed_m{};
|
||||
|
||||
std::array<uint8_t, ml_kem_512::PKEY_BYTE_LEN> pubkey{};
|
||||
std::array<uint8_t, ml_kem_512::SKEY_BYTE_LEN> seckey{};
|
||||
std::array<uint8_t, ml_kem_512::CIPHER_TEXT_BYTE_LEN> cipher{};
|
||||
|
||||
std::array<uint8_t, ml_kem_512::SHARED_SECRET_BYTE_LEN> shared_secret{};
|
||||
|
||||
ml_kem_prng::prng_t<128> prng{};
|
||||
prng.read(seed_d);
|
||||
prng.read(seed_z);
|
||||
prng.read(seed_m);
|
||||
|
||||
ml_kem_512::keygen(seed_d, seed_z, pubkey, seckey);
|
||||
|
||||
make_malformed_pubkey<pubkey.size()>(pubkey);
|
||||
const auto is_encapsulated = ml_kem_512::encapsulate(seed_m, pubkey, cipher, shared_secret);
|
||||
|
||||
EXPECT_FALSE(is_encapsulated);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ml_kem/ml_kem_768.hpp"
|
||||
#include "test_helper.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// For ML-KEM-768
|
||||
@@ -36,3 +37,33 @@ TEST(ML_KEM, ML_KEM_768_KeygenEncapsDecaps)
|
||||
EXPECT_TRUE(is_encapsulated);
|
||||
EXPECT_EQ(shared_secret_sender, shared_secret_receiver);
|
||||
}
|
||||
|
||||
// For ML-KEM-768
|
||||
//
|
||||
// - Generate a valid keypair.
|
||||
// - Malform public key s.t. last coefficient of polynomial vector is not properly reduced.
|
||||
// - Attempt to encapsulate using malformed public key. It must fail.
|
||||
TEST(ML_KEM, ML_KEM_768_EncapsFailureDueToNonReducedPubKey)
|
||||
{
|
||||
std::array<uint8_t, ml_kem_768::SEED_D_BYTE_LEN> seed_d{};
|
||||
std::array<uint8_t, ml_kem_768::SEED_Z_BYTE_LEN> seed_z{};
|
||||
std::array<uint8_t, ml_kem_768::SEED_M_BYTE_LEN> seed_m{};
|
||||
|
||||
std::array<uint8_t, ml_kem_768::PKEY_BYTE_LEN> pubkey{};
|
||||
std::array<uint8_t, ml_kem_768::SKEY_BYTE_LEN> seckey{};
|
||||
std::array<uint8_t, ml_kem_768::CIPHER_TEXT_BYTE_LEN> cipher{};
|
||||
|
||||
std::array<uint8_t, ml_kem_768::SHARED_SECRET_BYTE_LEN> shared_secret{};
|
||||
|
||||
ml_kem_prng::prng_t<192> prng{};
|
||||
prng.read(seed_d);
|
||||
prng.read(seed_z);
|
||||
prng.read(seed_m);
|
||||
|
||||
ml_kem_768::keygen(seed_d, seed_z, pubkey, seckey);
|
||||
|
||||
make_malformed_pubkey<pubkey.size()>(pubkey);
|
||||
const auto is_encapsulated = ml_kem_768::encapsulate(seed_m, pubkey, cipher, shared_secret);
|
||||
|
||||
EXPECT_FALSE(is_encapsulated);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user