Skip to content

Commit

Permalink
implement ::check_key() methods
Browse files Browse the repository at this point in the history
  • Loading branch information
reneme committed Sep 20, 2024
1 parent 80816a3 commit 8a46ea6
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 5 deletions.
33 changes: 32 additions & 1 deletion src/lib/pubkey/kyber/kyber_common/kyber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <botan/assert.h>
#include <botan/mem_ops.h>
#include <botan/pubkey.h>
#include <botan/rng.h>
#include <botan/secmem.h>

Expand Down Expand Up @@ -209,7 +210,16 @@ size_t Kyber_PublicKey::key_length() const {
}

bool Kyber_PublicKey::check_key(RandomNumberGenerator&, bool) const {
return true; // ??
// The length checks described in FIPS 203, Section 7.2 are already performed
// while decoding the public key. See constructor of Kyber_PublicKeyInternal.
// The decoding function KyberAlgos::byte_decode() also checks the range of
// the decoded values. The check below is added for completeness.

std::vector<uint8_t> test(m_public->mode().polynomial_vector_bytes());
Kyber_Algos::encode_polynomial_vector(test, m_public->t());

const auto& serialized_pubkey = m_public->public_key_bits_raw();
return test.size() < serialized_pubkey.size() && std::equal(test.begin(), test.end(), serialized_pubkey.begin());
}

std::unique_ptr<Private_Key> Kyber_PublicKey::generate_another(RandomNumberGenerator& rng) const {
Expand Down Expand Up @@ -252,6 +262,27 @@ secure_vector<uint8_t> Kyber_PrivateKey::private_key_bits() const {
return m_private->mode().keypair_codec().encode_keypair({m_public, m_private});
}

bool Kyber_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const {
// As we do not support loading a private key in extended format but rather
// always extract it from a 64-byte seed, these checks (as described in
// FIPS 203, Section 7.1) should never fail. Particularly, the length checks
// and the hash consistency check described in Section 7.2 and 7.3 are
// trivial when the private key is always extracted from a seed. The encaps/
// decaps roundtrip test is added for completeness.

if(!Kyber_PublicKey::check_key(rng, strong)) {
return false;
}

PK_KEM_Encryptor enc(*this, "Raw");
PK_KEM_Decryptor dec(*this, rng, "Raw");

const auto [c, K] = KEM_Encapsulation::destructure(enc.encrypt(rng));
const auto K_prime = dec.decrypt(c);

return K == K_prime;
}

std::unique_ptr<PK_Ops::KEM_Encryption> Kyber_PublicKey::create_kem_encryption_op(std::string_view params,
std::string_view provider) const {
if(provider.empty() || provider == "base") {
Expand Down
4 changes: 3 additions & 1 deletion src/lib/pubkey/kyber/kyber_common/kyber.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class BOTAN_PUBLIC_API(3, 0) Kyber_PublicKey : public virtual Public_Key {

std::vector<uint8_t> public_key_bits() const override;

bool check_key(RandomNumberGenerator&, bool) const override;
bool check_key(RandomNumberGenerator& rng, bool strong) const override;

std::unique_ptr<Private_Key> generate_another(RandomNumberGenerator& rng) const final;

Expand Down Expand Up @@ -152,6 +152,8 @@ class BOTAN_PUBLIC_API(3, 0) Kyber_PrivateKey final : public virtual Kyber_Publi

secure_vector<uint8_t> raw_private_key_bits() const override;

bool check_key(RandomNumberGenerator& rng, bool strong) const override;

std::unique_ptr<PK_Ops::KEM_Decryption> create_kem_decryption_op(RandomNumberGenerator& rng,
std::string_view params,
std::string_view provider) const override;
Expand Down
1 change: 1 addition & 0 deletions src/lib/pubkey/kyber/kyber_common/kyber_algos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ std::pair<KyberPolyVec, KyberPoly> decompress_ciphertext(StrongSpan<const KyberC
const size_t pvb = mode.polynomial_vector_compressed_bytes();
const size_t pcb = mode.polynomial_compressed_bytes();

// FIPS 203, Section 7.3 check 1 "Ciphertext type check"
if(ct.size() != pvb + pcb) {
throw Decoding_Error("Kyber: unexpected ciphertext length");
}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/pubkey/kyber/ml_kem/ml_kem_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ void ML_KEM_Encryptor::encapsulate(StrongSpan<KyberCompressedCiphertext> out_enc
* NIST FIPS 203, Algorithm 18 (ML-KEM.Decaps_internal) and 21 (ML-KEM.Decaps)
*
* The public and private keys are readily available as member variables and
* don't need to be decoded.
* don't need to be decoded. The checks stated in FIPS 203, Section 7.3 are
* performed before decoding the keys and the ciphertext.
*/
void ML_KEM_Decryptor::decapsulate(StrongSpan<KyberSharedSecret> out_shared_key,
StrongSpan<const KyberCompressedCiphertext> c) {
Expand Down
13 changes: 12 additions & 1 deletion src/tests/test_kyber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,22 @@ class Kyber_Keygen_Tests final : public PK_Key_Generation_Test {
#endif
#if defined(BOTAN_HAS_KYBER)
"Kyber-512-r3", "Kyber-768-r3", "Kyber-1024-r3",
#endif
#if defined(BOTAN_HAS_ML_KEM)
"ML-KEM-512", "ML-KEM-768", "ML-KEM-1024",
#endif
};
}

std::string algo_name() const override { return "Kyber"; }
std::string algo_name(std::string_view param) const override {
if(param.starts_with("Kyber-")) {
return "Kyber";
} else {
return "ML-KEM";
}
}

std::string algo_name() const override { throw Test_Error("No default algo name set for Kyber"); }

std::unique_ptr<Botan::Public_Key> public_key_from_raw(std::string_view keygen_params,
std::string_view /* provider */,
Expand Down
2 changes: 1 addition & 1 deletion src/tests/test_pubkey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ std::vector<Test::Result> PK_Key_Generation_Test::run() {
oid.value().to_string(),
key.object_identifier().to_string());
} else {
const bool exception = name == "Kyber" || name == "FrodoKEM" || name == "SPHINCS+";
const bool exception = name == "Kyber" || name == "ML-KEM" || name == "FrodoKEM" || name == "SPHINCS+";

if(!exception) {
result.test_failure("Keys name " + name + " does not map to an OID");
Expand Down

0 comments on commit 8a46ea6

Please sign in to comment.