Skip to content

Commit

Permalink
connected deterministic ml-kem modes to experimental EVP API
Browse files Browse the repository at this point in the history
  • Loading branch information
jakemas committed May 7, 2024
1 parent 33637ff commit 91ca50f
Show file tree
Hide file tree
Showing 11 changed files with 388 additions and 2 deletions.
105 changes: 105 additions & 0 deletions crypto/evp_extra/evp_extra_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <openssl/err.h>
#include <openssl/pkcs8.h>
#include <openssl/rsa.h>
#include <openssl/experimental/ml-kem.h>

#include "../rand_extra/pq_custom_randombytes.h"
#include "../test/file_test.h"
Expand Down Expand Up @@ -2674,3 +2675,107 @@ TEST_P(PerKEMTest, KAT) {
EXPECT_EQ(Bytes(ss_expected), Bytes(ss));
});
}

//TEST FUNCTIONS FOR DETERMINISTIC APIS
// these will be condensed with the non-deterministic KEM test functions once a full
// migration from kyberR3 and the current pq_custom_randombytes_init_for_testing
// mechanisms have been moved. For now, we test functionality of the deterministic
// APIs separately.

static bssl::UniquePtr<EVP_PKEY_CTX> setup_ctx_and_generate_key_deterministic(int kem_nid, const uint8_t * seed) {

// Create context of KEM type.
bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_KEM, nullptr));
EXPECT_TRUE(ctx);

// Set up the context with specific KEM parameters.
EXPECT_TRUE(EVP_PKEY_CTX_kem_set_params(ctx.get(), kem_nid));

// Generate a key pair.
EVP_PKEY *raw = nullptr;
EXPECT_TRUE(EVP_PKEY_keygen_init(ctx.get()));
EXPECT_TRUE(EVP_PKEY_keygen_deterministic(ctx.get(), &raw, seed));
EXPECT_TRUE(raw);

// Create PKEY from the generated raw key and a new context with it.
bssl::UniquePtr<EVP_PKEY> pkey(raw);
ctx.reset(EVP_PKEY_CTX_new(pkey.get(), nullptr));
EXPECT_TRUE(ctx);

return ctx;
}

static const struct KnownKEM kKEMs_deterministic[] = {
//{"MLKEM512IPD", NID_MLKEM512IPD, 800, 1632, 768, 32, "ml_kem/kat/mlkem512ipd.txt"}, //these can be re-added once KATs are re-generated
//{"Kyber512r3", NID_KYBER512_R3, 800, 1632, 768, 32, "ml_kem/kat/mlkem512ipd.txt"},
//{"Kyber512r3", NID_KYBER512_R3, 800, 1632, 768, 32, "kyber/kat/kyber512r3.txt"},
{"MLKEM512IPD", NID_MLKEM512IPD, 800, 1632, 768, 32, "ml_kem/kat/mlkem512ipd.txt"},
};

class PerKEMTest_deterministic : public testing::TestWithParam<KnownKEM> {};

INSTANTIATE_TEST_SUITE_P(All, PerKEMTest_deterministic, testing::ValuesIn(kKEMs_deterministic),
[](const testing::TestParamInfo<KnownKEM> &params)
-> std::string { return params.param.name; });

TEST_P(PerKEMTest_deterministic, KAT) {
std::string kat_filepath = "crypto/";
kat_filepath += GetParam().kat_filename;

FileTestGTest(kat_filepath.c_str(), [&](FileTest *t) {
std::string count;
std::vector<uint8_t> seed, pk_expected, sk_expected, ct_expected, ss_expected;

ASSERT_TRUE(t->GetAttribute(&count, "count"));
ASSERT_TRUE(t->GetBytes(&seed, "seed"));
ASSERT_TRUE(t->GetBytes(&pk_expected, "pk"));
ASSERT_TRUE(t->GetBytes(&sk_expected, "sk"));
ASSERT_TRUE(t->GetBytes(&ct_expected, "ct"));
ASSERT_TRUE(t->GetBytes(&ss_expected, "ss"));

size_t pk_len = GetParam().public_key_len;
size_t sk_len = GetParam().secret_key_len;
size_t ct_len = GetParam().ciphertext_len;
size_t ss_len = GetParam().shared_secret_len;

// Set randomness generation in deterministic mode.
// This will be removed in a subsequent PR, the first stage is removing this
// from inside the kyber code to here. The second phase is to re-do the
// KATs to output coins not seeds, we can then finally remove
// pq_custom_randombytes completely.

pq_custom_randombytes_use_deterministic_for_testing();
pq_custom_randombytes_init_for_testing(seed.data());

// get random bytes from the PRNG (this is potentially KEM specific - TBD)
uint8_t buf[2*32];
pq_custom_randombytes(buf, 2*32);

// ---- 1. Setup the context and generate the key ----
bssl::UniquePtr<EVP_PKEY_CTX> ctx;
ctx = setup_ctx_and_generate_key_deterministic(GetParam().nid, buf);
ASSERT_TRUE(ctx);

EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx.get());
ASSERT_TRUE(pkey);
CMP_VEC_AND_PKEY_PUBLIC(pk_expected, pkey, pk_len);
CMP_VEC_AND_PKEY_SECRET(sk_expected, pkey, sk_len);

// ---- 2. Encapsulation ----
std::vector<uint8_t> ct(ct_len);
std::vector<uint8_t> ss(ss_len);

// get random bytes from the PRNG (this is potentially KEM specific - TBD)
uint8_t buf2[32];
pq_custom_randombytes(buf2, 32);

ASSERT_TRUE(EVP_PKEY_encapsulate_deterministic(ctx.get(), ct.data(), &ct_len, ss.data(), &ss_len, buf2));
EXPECT_EQ(Bytes(ct_expected), Bytes(ct));
EXPECT_EQ(Bytes(ss_expected), Bytes(ss));

// ---- 3. Decapsulation ----
ASSERT_TRUE(EVP_PKEY_decapsulate(ctx.get(), ss.data(), &ss_len, ct.data(), ct_len));
EXPECT_EQ(Bytes(ss_expected), Bytes(ss));
});
}

2 changes: 2 additions & 0 deletions crypto/evp_extra/p_ed25519.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ const EVP_PKEY_METHOD ed25519_pkey_meth = {
NULL /* derive */,
NULL /* paramgen */,
NULL /* ctrl */,
NULL /* keygen deterministic */,
NULL /* encapsulate deterministic */,
NULL /* encapsulate */,
NULL /* decapsulate */,
};
91 changes: 91 additions & 0 deletions crypto/evp_extra/p_kem.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,31 @@ static void pkey_kem_cleanup(EVP_PKEY_CTX *ctx) {
OPENSSL_free(ctx->data);
}

static int pkey_kem_keygen_deterministic(EVP_PKEY_CTX *ctx,
EVP_PKEY *pkey,
const uint8_t *seed) {
KEM_PKEY_CTX *dctx = ctx->data;
const KEM *kem = dctx->kem;
if (kem == NULL) {
if (ctx->pkey == NULL) {
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET);
return 0;
}
kem = KEM_KEY_get0_kem(ctx->pkey->pkey.kem_key);
}

KEM_KEY *key = KEM_KEY_new();
if (key == NULL ||
!KEM_KEY_init(key, kem) ||
!kem->method->keygen_deterministic(key->public_key, key->secret_key, seed) ||
!EVP_PKEY_assign(pkey, EVP_PKEY_KEM, key)) {
KEM_KEY_free(key);
return 0;
}

return 1;
}

static int pkey_kem_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
KEM_PKEY_CTX *dctx = ctx->data;
const KEM *kem = dctx->kem;
Expand All @@ -55,6 +80,70 @@ static int pkey_kem_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
return 1;
}

static int pkey_kem_encapsulate_deterministic(EVP_PKEY_CTX *ctx,
uint8_t *ciphertext,
size_t *ciphertext_len,
uint8_t *shared_secret,
size_t *shared_secret_len,
const uint8_t *seed) {
KEM_PKEY_CTX *dctx = ctx->data;
const KEM *kem = dctx->kem;
if (kem == NULL) {
if (ctx->pkey == NULL) {
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET);
return 0;
}
kem = KEM_KEY_get0_kem(ctx->pkey->pkey.kem_key);
}

// Caller is getting parameter values.
if (ciphertext == NULL && shared_secret == NULL) {
*ciphertext_len = kem->ciphertext_len;
*shared_secret_len = kem->shared_secret_len;
return 1;
}

// If not getting parameter values, then both
// output buffers need to be valid (non-NULL)
if (ciphertext == NULL || shared_secret == NULL) {
OPENSSL_PUT_ERROR(EVP, EVP_R_MISSING_PARAMETERS);
return 0;
}

// The output buffers need to be large enough.
if (*ciphertext_len < kem->ciphertext_len ||
*shared_secret_len < kem->shared_secret_len) {
OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
return 0;
}

// Check that the context is properly configured.
if (ctx->pkey == NULL ||
ctx->pkey->pkey.kem_key == NULL ||
ctx->pkey->type != EVP_PKEY_KEM) {
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATON_NOT_INITIALIZED);
return 0;
}

// Check that the key has a public key set.
KEM_KEY *key = ctx->pkey->pkey.kem_key;
if (key->public_key == NULL) {
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_KEY_SET);
return 0;
}

if (!kem->method->encaps_deterministic(ciphertext, shared_secret, key->public_key, seed)) {
return 0;
}

// The size of the ciphertext and the shared secret
// that has been writen to the output buffers.
*ciphertext_len = kem->ciphertext_len;
*shared_secret_len = kem->shared_secret_len;

return 1;
}

static int pkey_kem_encapsulate(EVP_PKEY_CTX *ctx,
uint8_t *ciphertext,
size_t *ciphertext_len,
Expand Down Expand Up @@ -189,6 +278,8 @@ const EVP_PKEY_METHOD kem_pkey_meth = {
NULL,
NULL,
NULL,
pkey_kem_keygen_deterministic,
pkey_kem_encapsulate_deterministic,
pkey_kem_encapsulate,
pkey_kem_decapsulate,
};
Expand Down
2 changes: 2 additions & 0 deletions crypto/evp_extra/p_x25519.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ const EVP_PKEY_METHOD x25519_pkey_meth = {
pkey_x25519_derive,
NULL /* paramgen */,
pkey_x25519_ctrl,
NULL /* keygen deterministic */,
NULL /* encapsulate deterministic */,
NULL /* encapsulate */,
NULL /* decapsulate */,
};
59 changes: 59 additions & 0 deletions crypto/fipsmodule/evp/evp_ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
* [including the GNU Public Licence.] */

#include <openssl/evp.h>
#include <openssl/experimental/ml-kem.h>

#include <string.h>

Expand Down Expand Up @@ -438,6 +439,49 @@ int EVP_PKEY_keygen_init(EVP_PKEY_CTX *ctx) {
return 1;
}

int EVP_PKEY_keygen_deterministic(EVP_PKEY_CTX *ctx,
EVP_PKEY **out_pkey,
const uint8_t *seed) {
// We have to avoid potential underlying services updating the indicator state,
// so we lock the state here.
FIPS_service_indicator_lock_state();
int ret = 0;
if (!ctx || !ctx->pmeth || (!ctx->pmeth->keygen && !ctx->pmeth->keygen_deterministic)) {
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
goto end;
}
if (ctx->operation != EVP_PKEY_OP_KEYGEN) {
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATON_NOT_INITIALIZED);
goto end;
}

if (!out_pkey) {
goto end;
}

if (!*out_pkey) {
*out_pkey = EVP_PKEY_new();
if (!*out_pkey) {
OPENSSL_PUT_ERROR(EVP, ERR_LIB_EVP);
goto end;
}
}

if (!ctx->pmeth->keygen_deterministic(ctx, *out_pkey, seed)) {
EVP_PKEY_free(*out_pkey);
*out_pkey = NULL;
goto end;
}

ret = 1;
end:
FIPS_service_indicator_unlock_state();
if(ret) {
EVP_PKEY_keygen_verify_service_indicator(*out_pkey);
}
return ret;
}

int EVP_PKEY_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY **out_pkey) {
// We have to avoid potential underlying services updating the indicator state,
// so we lock the state here.
Expand Down Expand Up @@ -518,6 +562,21 @@ int EVP_PKEY_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY **out_pkey) {
return 1;
}

int EVP_PKEY_encapsulate_deterministic(EVP_PKEY_CTX *ctx,
uint8_t *ciphertext,
size_t *ciphertext_len,
uint8_t *shared_secret,
size_t *shared_secret_len,
const uint8_t *seed) {
if (ctx == NULL || ctx->pmeth == NULL || ctx->pmeth->encapsulate_deterministic== NULL) {
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
return 0;
}

return ctx->pmeth->encapsulate_deterministic(ctx, ciphertext, ciphertext_len,
shared_secret, shared_secret_len, seed);
}

int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx,
uint8_t *ciphertext, size_t *ciphertext_len,
uint8_t *shared_secret, size_t *shared_secret_len) {
Expand Down
15 changes: 13 additions & 2 deletions crypto/fipsmodule/evp/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,19 @@ struct evp_pkey_method_st {

int (*ctrl)(EVP_PKEY_CTX *ctx, int type, int p1, void *p2);

// Encapsulate and decapsulate are operations defined for a
// Key Encapsulation Mechanism (KEM).
// Encapsulate, encapsulate_deterministic, keygen_deterministic, and
// decapsulate are operations defined for a Key Encapsulation Mechanism (KEM).
int (*keygen_deterministic)(EVP_PKEY_CTX *ctx,
EVP_PKEY *pkey,
const uint8_t *seed);

int (*encapsulate_deterministic)(EVP_PKEY_CTX *ctx,
uint8_t *ciphertext,
size_t *ciphertext_len,
uint8_t *shared_secret,
size_t *shared_secret_len,
const uint8_t *seed);

int (*encapsulate)(EVP_PKEY_CTX *ctx,
uint8_t *ciphertext, size_t *ciphertext_len,
uint8_t *shared_secret, size_t *shared_secret_len);
Expand Down
9 changes: 9 additions & 0 deletions crypto/kem/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,18 @@ extern "C" {

// KEM_METHOD structure and helper functions.
typedef struct {
int (*keygen_deterministic)(uint8_t *ctx,
uint8_t *pkey,
const uint8_t *seed);

int (*keygen)(uint8_t *public_key,
uint8_t *secret_key);

int (*encaps_deterministic)(uint8_t *ciphertext,
uint8_t *shared_secret,
const uint8_t *public_key,
const uint8_t *seed);

int (*encaps)(uint8_t *ciphertext,
uint8_t *shared_secret,
const uint8_t *public_key);
Expand Down
Loading

0 comments on commit 91ca50f

Please sign in to comment.