13#include <botan/mceliece.h>
15#include <botan/ber_dec.h>
16#include <botan/der_enc.h>
18#include <botan/internal/bit_ops.h>
19#include <botan/internal/buffer_stuffer.h>
20#include <botan/internal/code_based_util.h>
21#include <botan/internal/loadstor.h>
22#include <botan/internal/mce_internal.h>
23#include <botan/internal/pk_ops_impl.h>
24#include <botan/internal/polyn_gf2m.h>
33enum class McEliece_Key_Source : uint8_t {
Raw, Encoded };
35constexpr std::array<std::pair<size_t, size_t>, 6> MCE_SUPPORTED_PARAMS = {
36 {{1632, 33}, {2480, 45}, {2960, 57}, {3408, 67}, {4624, 95}, {6624, 115}}};
38bool mceliece_params_are_supported(
size_t code_length,
size_t t) {
39 for(
const auto& [supported_n, supported_t] : MCE_SUPPORTED_PARAMS) {
40 if(code_length == supported_n && t == supported_t) {
47[[noreturn]]
void throw_mceliece_validation_error(McEliece_Key_Source source,
const char* msg) {
48 if(source == McEliece_Key_Source::Encoded) {
55McEliece_Params mceliece_validate_params(
size_t code_length,
size_t t, McEliece_Key_Source source) {
56 if(!mceliece_params_are_supported(code_length, t)) {
57 throw_mceliece_validation_error(source,
"Unsupported McEliece parameters");
60 const size_t ext_deg =
ceil_log2(code_length);
61 if(ext_deg < 2 || ext_deg > 15) {
62 throw_mceliece_validation_error(source,
"McEliece code length out of supported range");
65 const size_t codimension = ext_deg * t;
66 if(codimension >= code_length) {
67 throw_mceliece_validation_error(source,
"McEliece parameters are inconsistent");
70 const size_t dimension = code_length - codimension;
72 const size_t public_matrix_bytes = dimension * words_per_matrix_row *
sizeof(uint32_t);
74 return McEliece_Params{code_length, t, ext_deg, codimension, dimension, words_per_matrix_row, public_matrix_bytes};
77uint32_t padding_mask(
size_t bit_count) {
78 const size_t used_bits = bit_count % 32;
82 return ~((
static_cast<uint32_t
>(1) << used_bits) - 1);
85void validate_public_matrix(
const std::vector<uint8_t>& public_matrix,
87 McEliece_Key_Source source) {
88 if(public_matrix.size() != params.public_matrix_bytes) {
89 throw_mceliece_validation_error(source,
"McEliece public matrix size does not match parameters");
92 const uint32_t unused_bits_mask = padding_mask(params.codimension);
93 if(unused_bits_mask == 0) {
97 const size_t row_bytes = params.words_per_matrix_row *
sizeof(uint32_t);
98 const size_t final_word_offset = (params.words_per_matrix_row - 1) *
sizeof(uint32_t);
99 for(
size_t row = 0; row != params.dimension; ++row) {
100 const uint8_t* row_ptr = public_matrix.data() + row * row_bytes;
102 if((final_word & unused_bits_mask) != 0) {
103 throw_mceliece_validation_error(source,
"McEliece public matrix contains non-zero padding bits");
108void validate_polynomial(
const polyn_gf2m& polyn,
110 size_t min_coeff_count,
112 McEliece_Key_Source source) {
113 const std::shared_ptr<GF2m_Field> field = polyn.get_sp_field();
114 if(!field || field->get_extension_degree() != params.ext_deg) {
115 throw_mceliece_validation_error(source,
"McEliece polynomial uses an inconsistent field");
118 if(polyn.get_coeff_count() < min_coeff_count) {
119 throw_mceliece_validation_error(source,
"McEliece polynomial has too few coefficients");
122 const int degree = polyn.get_degree();
123 if(degree >= 0 &&
static_cast<size_t>(degree) > max_degree) {
124 throw_mceliece_validation_error(source,
"McEliece polynomial degree is too large");
127 const size_t field_cardinality =
static_cast<size_t>(1) << params.ext_deg;
128 for(
size_t i = 0; i != polyn.get_coeff_count(); ++i) {
129 if(polyn.get_coef(i) >= field_cardinality) {
130 throw_mceliece_validation_error(source,
"McEliece polynomial coefficient is out of range");
135void validate_support_inverse(
const std::vector<gf2m>& inverse_support,
137 McEliece_Key_Source source) {
138 if(inverse_support.size() != params.code_length) {
139 throw_mceliece_validation_error(source,
"McEliece support size does not match code length");
142 std::vector<uint8_t> seen(params.code_length);
143 for(
const gf2m support_elem : inverse_support) {
144 if(support_elem >= params.code_length) {
145 throw_mceliece_validation_error(source,
"McEliece support element is out of range");
147 if(seen[support_elem] != 0) {
148 throw_mceliece_validation_error(source,
"McEliece support is not a permutation");
150 seen[support_elem] = 1;
154void validate_parity_check_matrix(
const std::vector<uint32_t>& parity_check_matrix_coeffs,
156 McEliece_Key_Source source) {
157 if(parity_check_matrix_coeffs.size() != params.words_per_matrix_row * params.code_length) {
158 throw_mceliece_validation_error(source,
"McEliece parity check matrix has wrong length");
161 const uint32_t unused_bits_mask = padding_mask(params.codimension);
162 if(unused_bits_mask == 0) {
166 for(
size_t row = 0; row != params.code_length; ++row) {
167 const uint32_t final_word =
168 parity_check_matrix_coeffs[row * params.words_per_matrix_row + params.words_per_matrix_row - 1];
169 if((final_word & unused_bits_mask) != 0) {
170 throw_mceliece_validation_error(source,
"McEliece parity check matrix contains non-zero padding bits");
175void validate_private_components(
const polyn_gf2m& goppa_polyn,
176 const std::vector<uint32_t>& parity_check_matrix_coeffs,
177 const std::vector<polyn_gf2m>& square_root_matrix,
178 const std::vector<gf2m>& inverse_support,
179 const std::vector<uint8_t>& public_matrix,
181 McEliece_Key_Source source) {
182 validate_public_matrix(public_matrix, params, source);
184 if(goppa_polyn.get_degree() !=
static_cast<int>(params.t)) {
185 throw_mceliece_validation_error(source,
"degree of decoded Goppa polynomial is incorrect");
187 validate_polynomial(goppa_polyn, params, params.t + 1, params.t, source);
188 if(goppa_polyn.get_lead_coef() != 1) {
189 throw_mceliece_validation_error(source,
"McEliece Goppa polynomial is not monic");
192 if(square_root_matrix.size() != params.t / 2) {
193 throw_mceliece_validation_error(source,
"McEliece square root matrix has wrong length");
195 for(
const auto& sqrt_polyn : square_root_matrix) {
196 validate_polynomial(sqrt_polyn, params, params.t, params.t - 1, source);
199 validate_support_inverse(inverse_support, params, source);
200 validate_parity_check_matrix(parity_check_matrix_coeffs, params, source);
206 return mceliece_validate_params(code_length, t, McEliece_Key_Source::Raw);
210 return mceliece_validate_params(code_length, t, McEliece_Key_Source::Encoded);
215McEliece_PrivateKey& McEliece_PrivateKey::operator=(const McEliece_PrivateKey&) = default;
216McEliece_PrivateKey& McEliece_PrivateKey::operator=(McEliece_PrivateKey&&) noexcept = default;
217McEliece_PrivateKey::~McEliece_PrivateKey() = default;
220 const std::vector<uint32_t>& parity_check_matrix_coeffs,
221 const std::vector<
polyn_gf2m>& square_root_matrix,
222 const std::vector<
gf2m>& inverse_support,
223 const std::vector<uint8_t>& public_matrix) {
224 const int goppa_degree = goppa_polyn.get_degree();
225 if(goppa_degree <= 0) {
230 validate_private_components(goppa_polyn,
231 parity_check_matrix_coeffs,
236 McEliece_Key_Source::Raw);
238 m_public = std::make_shared<const McEliece_PublicKeyInternal>(public_matrix, params.
t, params.
code_length);
239 m_private = std::make_shared<const McEliece_PrivateKeyInternal>(std::vector<polyn_gf2m>{goppa_polyn},
242 parity_check_matrix_coeffs,
254 const size_t codimension =
ceil_log2(m_code_length) * m_t;
255 return m_code_length - codimension;
262 rng.
randomize(plaintext.data(), plaintext.size());
265 if(
const uint32_t used = bits % 8) {
266 const uint8_t mask = (1 << used) - 1;
267 plaintext[plaintext.size() - 1] &= mask;
275 validate_public_matrix(pub_matrix, params, McEliece_Key_Source::Raw);
276 m_public = std::make_shared<const McEliece_PublicKeyInternal>(pub_matrix, t, the_code_length);
292 return m_public->message_word_bit_length();
296 return m_public->random_plaintext_element(rng);
306 validate_public_matrix(
m_public->public_matrix(), params, McEliece_Key_Source::Raw);
314 return m_private->goppa_polyn();
318 return m_private->H_coeffs();
322 return m_private->Linv();
326 return m_private->sqrtmod();
330 return m_private->dimension();
334 return m_private->codimension();
346 std::vector<uint8_t> output;
373 throw Decoding_Error(
"Unexpected parameters for McEliece public key");
379 std::vector<uint8_t> public_matrix;
390 validate_public_matrix(public_matrix, params, McEliece_Key_Source::Encoded);
392 m_public = std::make_shared<const McEliece_PublicKeyInternal>(std::move(public_matrix), t, n);
405 for(
const auto& x : m_private->sqrtmod()) {
411 for(
const uint16_t Linv : m_private->Linv()) {
417 for(
const uint32_t coef : m_private->H_coeffs()) {
439 if(errors != errors_out || plaintext != plaintext_out) {
453 throw Decoding_Error(
"Unexpected parameters for McEliece private key");
458 std::vector<uint8_t> public_matrix;
466 validate_public_matrix(public_matrix, params, McEliece_Key_Source::Encoded);
468 auto sp_field = std::make_shared<GF2m_Field>(params.
ext_deg);
469 std::vector<polyn_gf2m> g = {
polyn_gf2m(enc_g, sp_field)};
470 std::vector<polyn_gf2m> sqrtmod;
472 for(uint32_t i = 0; i < t / 2; i++) {
475 while(sqrt_enc.size() < (t * 2)) {
477 sqrt_enc.push_back(0);
478 sqrt_enc.push_back(0);
480 if(sqrt_enc.size() != t * 2) {
481 throw Decoding_Error(
"length of square root polynomial entry is too large");
488 if(enc_support.size() % 2 != 0) {
491 if(enc_support.size() / 2 != n) {
492 throw Decoding_Error(
"encoded support has length different from code length");
494 std::vector<gf2m> Linv;
495 for(uint32_t i = 0; i < n * 2; i += 2) {
496 const gf2m el = (enc_support[i] << 8) | enc_support[i + 1];
501 if(enc_H.size() % 4 != 0) {
502 throw Decoding_Error(
"encoded parity check matrix has length which is not a multiple of four");
505 throw Decoding_Error(
"encoded parity check matrix has wrong length");
508 std::vector<uint32_t> coeffs;
509 for(uint32_t i = 0; i < enc_H.size(); i += 4) {
510 coeffs.push_back(
make_uint32(enc_H[i], enc_H[i + 1], enc_H[i + 2], enc_H[i + 3]));
513 validate_private_components(g[0], coeffs, sqrtmod, Linv, public_matrix, params, McEliece_Key_Source::Encoded);
515 m_public = std::make_shared<const McEliece_PublicKeyInternal>(std::move(public_matrix), t, n);
516 m_private = std::make_shared<const McEliece_PrivateKeyInternal>(
517 std::move(g), std::move(sqrtmod), std::move(Linv), std::move(coeffs), params.
codimension, params.
dimension);
524 if(m_private->goppa_polyn_vec() != other.m_private->goppa_polyn_vec()) {
528 if(m_private->sqrtmod() != other.m_private->sqrtmod()) {
531 if(m_private->Linv() != other.m_private->Linv()) {
534 if(m_private->H_coeffs() != other.m_private->H_coeffs()) {
538 if(m_private->codimension() != other.m_private->codimension() ||
539 m_private->dimension() != other.m_private->dimension()) {
567 MCE_KEM_Encryptor(std::shared_ptr<const McEliece_PublicKeyInternal> key, std::string_view kdf) :
568 KEM_Encryption_with_KDF(kdf), m_key(std::move(key)) {}
571 size_t raw_kem_shared_key_length()
const override {
572 const size_t err_sz = (m_key->code_length() + 7) / 8;
573 const size_t ptext_sz = (m_key->message_word_bit_length() + 7) / 8;
574 return ptext_sz + err_sz;
577 size_t encapsulated_key_length()
const override {
return (m_key->code_length() + 7) / 8; }
579 void raw_kem_encrypt(std::span<uint8_t> out_encapsulated_key,
580 std::span<uint8_t> raw_shared_key,
581 RandomNumberGenerator& rng)
override {
590 std::copy(ciphertext.begin(), ciphertext.end(), out_encapsulated_key.begin());
593 BufferStuffer bs(raw_shared_key);
594 bs.append(plaintext);
595 bs.append(error_mask);
598 std::shared_ptr<const McEliece_PublicKeyInternal> m_key;
603 MCE_KEM_Decryptor(std::shared_ptr<const McEliece_PrivateKeyInternal> key, std::string_view kdf) :
604 KEM_Decryption_with_KDF(kdf), m_key(std::move(key)) {}
607 size_t raw_kem_shared_key_length()
const override {
608 const size_t err_sz = (m_key->code_length() + 7) / 8;
609 const size_t ptext_sz = (m_key->message_word_bit_length() + 7) / 8;
610 return ptext_sz + err_sz;
613 size_t encapsulated_key_length()
const override {
return (m_key->code_length() + 7) / 8; }
615 void raw_kem_decrypt(std::span<uint8_t> out_shared_key, std::span<const uint8_t> encapsulated_key)
override {
618 mceliece_decrypt(plaintext, error_mask, encapsulated_key.data(), encapsulated_key.size(), *m_key);
622 BufferStuffer bs(out_shared_key);
623 bs.append(plaintext);
624 bs.append(error_mask);
627 std::shared_ptr<const McEliece_PrivateKeyInternal> m_key;
637 std::string_view provider)
const {
638 if(provider ==
"base" || provider.empty()) {
639 return std::make_unique<MCE_KEM_Encryptor>(
m_public, params);
645 std::string_view params,
646 std::string_view provider)
const {
647 if(provider ==
"base" || provider.empty()) {
648 return std::make_unique<MCE_KEM_Decryptor>(m_private, params);
#define BOTAN_ASSERT_NOMSG(expr)
bool parameters_are_empty() const
virtual OID object_identifier() const
void push_back(const BER_Object &obj)
BER_Decoder & decode(bool &out)
BER_Decoder & verify_end()
BER_Decoder start_sequence()
secure_vector< uint8_t > get_contents()
DER_Encoder & start_sequence()
DER_Encoder & encode(bool b)
secure_vector< uint8_t > private_key_bits() const override
const std::vector< polyn_gf2m > & get_sqrtmod() const
McEliece_PrivateKey(RandomNumberGenerator &rng, size_t code_length, size_t t)
std::unique_ptr< Public_Key > public_key() const override
size_t get_codimension() const
size_t get_dimension() const
std::unique_ptr< PK_Ops::KEM_Decryption > create_kem_decryption_op(RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
const polyn_gf2m & get_goppa_polyn() const
const std::vector< gf2m > & get_Linv() const
bool operator==(const McEliece_PrivateKey &other) const
const std::vector< uint32_t > & get_H_coeffs() const
bool check_key(RandomNumberGenerator &rng, bool strong) const override
secure_vector< uint8_t > random_plaintext_element(RandomNumberGenerator &rng) const
size_t message_word_bit_length() const
secure_vector< uint8_t > random_plaintext_element(RandomNumberGenerator &rng) const
size_t get_message_word_bit_length() const
std::shared_ptr< const McEliece_PublicKeyInternal > m_public
std::vector< uint8_t > raw_public_key_bits() const override
std::unique_ptr< PK_Ops::KEM_Encryption > create_kem_encryption_op(std::string_view params, std::string_view provider) const override
std::string algo_name() const override
std::vector< uint8_t > public_key_bits() const override
std::unique_ptr< Private_Key > generate_another(RandomNumberGenerator &rng) const final
McEliece_PublicKey()=default
McEliece_PublicKey(const AlgorithmIdentifier &alg_id, std::span< const uint8_t > key_bits)
const std::vector< uint8_t > & get_public_matrix() const
bool check_key(RandomNumberGenerator &rng, bool strong) const override
size_t estimated_strength() const override
size_t get_code_length() const
bool operator==(const McEliece_PublicKey &other) const
AlgorithmIdentifier algorithm_identifier() const override
size_t key_length() const override
void randomize(std::span< uint8_t > output)
constexpr uint8_t get_byte(T input)
void mceliece_decrypt(secure_vector< uint8_t > &plaintext_out, secure_vector< uint8_t > &error_mask_out, const secure_vector< uint8_t > &ciphertext, const McEliece_PrivateKeyInternal &key)
constexpr uint32_t make_uint32(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3)
constexpr uint8_t ceil_log2(T x)
McEliece_Params mceliece_validate_key_encoding_params(size_t code_length, size_t t)
McEliece_PrivateKey generate_mceliece_key(RandomNumberGenerator &rng, size_t ext_deg, size_t code_length, size_t t)
size_t mceliece_work_factor(size_t n, size_t t)
constexpr auto load_le(ParamTs &&... params)
void mceliece_encrypt(secure_vector< uint8_t > &ciphertext_out, secure_vector< uint8_t > &error_mask_out, const secure_vector< uint8_t > &plaintext, const McEliece_PublicKeyInternal &key, RandomNumberGenerator &rng)
std::vector< T, secure_allocator< T > > secure_vector
size_t bit_size_to_32bit_size(size_t bit_size)
McEliece_Params mceliece_validate_keygen_params(size_t code_length, size_t t)
size_t words_per_matrix_row