Add EVP_PKEY_check and EVP_PKEY_public_check for KEMs#2709
Add EVP_PKEY_check and EVP_PKEY_public_check for KEMs#2709
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2709 +/- ##
==========================================
+ Coverage 78.95% 78.96% +0.01%
==========================================
Files 671 671
Lines 114801 114998 +197
Branches 16153 16179 +26
==========================================
+ Hits 90643 90811 +168
- Misses 23372 23399 +27
- Partials 786 788 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
crypto/fipsmodule/kem/kem.c
Outdated
| // Check that at least the public key exists | ||
| if (key->public_key == NULL) { | ||
| OPENSSL_PUT_ERROR(EVP, EVP_R_NO_KEY_SET); | ||
| return 0; | ||
| } |
There was a problem hiding this comment.
NP: It's currently possible (although I wish it weren't) for the secret_key to be set but not the public_key. For those, this would always return an error.
There was a problem hiding this comment.
Currently intended behaviour, my idea is to merge the PRs first that ensure we can populate both. Then we fail this check if pub_key isn't set when secrect_key is? How does that sound?
There was a problem hiding this comment.
Once caveat is that EVP_PKEY_public_check also calls the same function e.g. EC_KEY_check_key that just handles it. See here. Little bit strange, but if we follow the pattern, we'd have to make KEM_CHECK ok with pub only. Or we could implement two functions, the second KEM_CHECK_PUBLIC_KEY and call the it from EVP_PKEY_public_check: drafted that idea out here 0616873, but I don't think I like the anti-pattern. Looking at EC check for inspo, I will implement similar to how they do it, If only public key: validates public key only, if secret key is present: requires public key and validates public key, secret key, and PCT.
There was a problem hiding this comment.
I've landed on f30bfd4 that preserves functionality with other EVP_PKEY_public_check/EVP_PKEY_check functions. As documented, it
// KEM_check_key: validates the key based on available key material
// - If only public key: validates public key only
// - If secret key is present: requires public key and validates public key, secret key, and PCT
I added negative testing for a bunch of cases, including the one you mention:
// ---- 7. Test with secret key only (no public key) ----
// Create EVP_PKEY with only secret key (no public key)
|
Upstream work discussed with Hanno on exposing the |
| corrupted_pk[1] = 0xFF; | ||
| if (pk_len > 2) { | ||
| corrupted_pk[2] = 0xFF; | ||
| } |
There was a problem hiding this comment.
isn't it enough to corrupt a single bit? why do we change two more bytes?
|
|
||
| // Create a corrupted public key | ||
| std::vector<uint8_t> corrupted_pk = pk_copy; | ||
| corrupted_pk[0] = 0xFF; |
There was a problem hiding this comment.
this will produce a flaky test because with some probability the valid keys will have the bytes equal to 0xFF :)
to be sure that you are corrupting the keys you can just XOR with 1 (^ 1), that always changes the least significant bit.
| // Create a corrupted secret key | ||
| std::vector<uint8_t> corrupted_sk = sk_copy; | ||
|
|
||
| // Corrupt the last 64 bytes of secret key (in ML-KEM this is the hash) |
There was a problem hiding this comment.
shouldn't corrupting any single bit in the secret key be sufficient?
| // Public key check will fail PCT since secret key is present, and corrupted | ||
| EXPECT_FALSE(EVP_PKEY_public_check(corrupted_sk_ctx.get())); | ||
|
|
||
| // ---- 4. Test mismatched keypair (PCT failure) ---- |
There was a problem hiding this comment.
is there a differentiation between mismatched keypair and corrupted key pare cases above? is a mismatched key pair going to fail differently than a corrupted key pair?
| // Call appropriate ML-KEM check functions based on KEM NID | ||
| switch (key->kem->nid) { | ||
| case NID_MLKEM512: | ||
| case NID_KYBER512_R3: |
There was a problem hiding this comment.
I don't think we should mix-up Kyber with ML-KEM, technically they are not the same algorithms. I'd drop Kyber NIDs from here.
| // These functions perform FIPS 203 compliant PCT by performing encapsulation | ||
| // and decapsulation operations and comparing the resulting shared secrets | ||
|
|
||
| int ml_kem_512_check_pct(const uint8_t *public_key, const uint8_t *secret_key) { |
There was a problem hiding this comment.
mlkem-native doesn't have pct already implemented?
|
@jakemas is this ready for review or still a draft? |
Issues:
N/A - ML-KEM support.
Description of changes:
mlkem-native provides
pkandskcheck functions crypto_kem_check_pk and crypto_kem_check_sk that are namespaced toml_kem_{512/768/1024}_check_{pk/sk}. This PR hooks them up toEVP_PKEY_checkandEVP_PKEY_public_checkso we can call them onPKEYof typeKEM.This PR also implements a Pairwise Consistency Test (PCT) between the provided public and secret key -- this means the the function
KEM_check_keyenforces pub key is present when secret key is present, this is intended behaviour -- we need both to be able to check consistency, otherwise the function serves little functionality.Call-outs:
Due to the way the MLKEM multi-level build is compiled, we can't call
mlkem{512/768/1024}_check_skdirectly, so have to have those little wrapper functions incrypto/fipsmodule/ml_kem/ml_kem.c, as with other functions in that file.Testing:
KEM tests are modified to:
Also added test suite
KEMCheckKeyNegativeTestsfor testingKEM_check_keyvia.EVP_PKEY_checkandEVP_PKEY_public_check. These checks include:By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.