Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ML-KEM Service Indicator for EVP_PKEY_keygen, EVP_PKEY_encapsulate, EVP_PKEY_decapsulate #1844

Merged
merged 3 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion crypto/fipsmodule/evp/digestsign.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,9 @@ int EVP_DigestSign(EVP_MD_CTX *ctx, uint8_t *out_sig, size_t *out_sig_len,
data_len);
end:
FIPS_service_indicator_unlock_state();
if (ret > 0) {
if (ret > 0 && out_sig != NULL) {
// Indicator should only be set if we performed crypto, don't set if we only
// performed a size check.
EVP_DigestSign_verify_service_indicator(ctx);
}
return ret;
Expand Down
54 changes: 40 additions & 14 deletions crypto/fipsmodule/evp/evp_ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -591,30 +591,56 @@ int EVP_PKEY_encapsulate_deterministic(EVP_PKEY_CTX *ctx,
seed, seed_len);
}

int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx,
uint8_t *ciphertext, size_t *ciphertext_len,
uint8_t *shared_secret, size_t *shared_secret_len) {
int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx, uint8_t *ciphertext,
size_t *ciphertext_len, uint8_t *shared_secret,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may not be a question for this PR; should shared_secret be const?

Copy link
Member Author

@skmcgrail skmcgrail Sep 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shared secret is generated/written by the ML-KEM algorithm, it's not an input value provided by the user.

size_t *shared_secret_len) {
// We have to avoid potential underlying services updating the indicator
// state, so we lock the state here.
FIPS_service_indicator_lock_state();
SET_DIT_AUTO_DISABLE;
int ret = 0;
if (ctx == NULL || ctx->pmeth == NULL || ctx->pmeth->encapsulate == NULL) {
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
return 0;
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
goto end;
}

return ctx->pmeth->encapsulate(ctx, ciphertext, ciphertext_len,
shared_secret, shared_secret_len);
if (!ctx->pmeth->encapsulate(ctx, ciphertext, ciphertext_len, shared_secret,
shared_secret_len)) {
goto end;
}
ret = 1;
end:
FIPS_service_indicator_unlock_state();
if (ret && ciphertext != NULL && shared_secret != NULL) {
EVP_PKEY_encapsulate_verify_service_indicator(ctx);
}
return ret;
}

int EVP_PKEY_decapsulate(EVP_PKEY_CTX *ctx,
uint8_t *shared_secret, size_t *shared_secret_len,
const uint8_t *ciphertext, size_t ciphertext_len) {
int EVP_PKEY_decapsulate(EVP_PKEY_CTX *ctx, uint8_t *shared_secret,
size_t *shared_secret_len, const uint8_t *ciphertext,
size_t ciphertext_len) {
// We have to avoid potential underlying services updating the indicator
// state, so we lock the state here.
FIPS_service_indicator_lock_state();
SET_DIT_AUTO_DISABLE;
int ret = 0;
if (ctx == NULL || ctx->pmeth == NULL || ctx->pmeth->decapsulate == NULL) {
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
return 0;
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
goto end;
}

return ctx->pmeth->decapsulate(ctx, shared_secret, shared_secret_len,
ciphertext, ciphertext_len);
if (!ctx->pmeth->decapsulate(ctx, shared_secret, shared_secret_len,
ciphertext, ciphertext_len)) {
goto end;
}
ret = 1;
end:
FIPS_service_indicator_unlock_state();
if (ret && shared_secret != NULL) {
EVP_PKEY_decapsulate_verify_service_indicator(ctx);
}
return ret;
}

// Deprecated keygen NO-OP functions
Expand Down
3 changes: 2 additions & 1 deletion crypto/fipsmodule/evp/p_kem.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,11 @@ static int pkey_kem_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
if (key == NULL ||
!KEM_KEY_init(key, kem) ||
!kem->method->keygen(key->public_key, key->secret_key) ||
!EVP_PKEY_assign(pkey, EVP_PKEY_KEM, key)) {
!EVP_PKEY_set_type(pkey, EVP_PKEY_KEM)) {
KEM_KEY_free(key);
return 0;
}
pkey->pkey.kem_key = key;

return 1;
}
Expand Down
6 changes: 6 additions & 0 deletions crypto/fipsmodule/service_indicator/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ void TLSKDF_verify_service_indicator(const EVP_MD *dgst, const char *label,
void SSKDF_digest_verify_service_indicator(const EVP_MD *dgst);
void SSKDF_hmac_verify_service_indicator(const EVP_MD *dgst);
void KBKDF_ctr_hmac_verify_service_indicator(const EVP_MD *dgst);
void EVP_PKEY_encapsulate_verify_service_indicator(const EVP_PKEY_CTX* ctx);
void EVP_PKEY_decapsulate_verify_service_indicator(const EVP_PKEY_CTX* ctx);

#else

Expand Down Expand Up @@ -127,6 +129,10 @@ OPENSSL_INLINE void SSKDF_hmac_verify_service_indicator(

OPENSSL_INLINE void KBKDF_ctr_hmac_verify_service_indicator(OPENSSL_UNUSED const EVP_MD *dgst) {}

OPENSSL_INLINE void EVP_PKEY_encapsulate_verify_service_indicator(OPENSSL_UNUSED const EVP_PKEY_CTX* ctx) {}

OPENSSL_INLINE void EVP_PKEY_decapsulate_verify_service_indicator(OPENSSL_UNUSED const EVP_PKEY_CTX* ctx) {}

#endif // AWSLC_FIPS

// is_fips_build is similar to |FIPS_mode| but returns 1 including in the case
Expand Down
41 changes: 41 additions & 0 deletions crypto/fipsmodule/service_indicator/service_indicator.c
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,17 @@ void EVP_PKEY_keygen_verify_service_indicator(const EVP_PKEY *pkey) {
if (is_ec_fips_approved(curve_nid)) {
FIPS_service_indicator_update_state();
}
} else if (pkey->type == EVP_PKEY_KEM) {
const KEM *kem = KEM_KEY_get0_kem(pkey->pkey.kem_key);
switch (kem->nid) {
case NID_MLKEM512:
case NID_MLKEM768:
case NID_MLKEM1024:
FIPS_service_indicator_update_state();
break;
default:
break;
}
}
}

Expand Down Expand Up @@ -571,6 +582,36 @@ void KBKDF_ctr_hmac_verify_service_indicator(const EVP_MD *dgst) {
}
}

void EVP_PKEY_encapsulate_verify_service_indicator(const EVP_PKEY_CTX* ctx) {
if (ctx->pkey->type == EVP_PKEY_KEM) {
const KEM *kem = KEM_KEY_get0_kem(ctx->pkey->pkey.kem_key);
switch (kem->nid) {
case NID_MLKEM512:
case NID_MLKEM768:
case NID_MLKEM1024:
FIPS_service_indicator_update_state();
break;
default:
break;
}
}
}

void EVP_PKEY_decapsulate_verify_service_indicator(const EVP_PKEY_CTX* ctx) {
if (ctx->pkey->type == EVP_PKEY_KEM) {
const KEM *kem = KEM_KEY_get0_kem(ctx->pkey->pkey.kem_key);
switch (kem->nid) {
case NID_MLKEM512:
case NID_MLKEM768:
case NID_MLKEM1024:
FIPS_service_indicator_update_state();
break;
default:
break;
}
}
}

#else

uint64_t FIPS_service_indicator_before_call(void) { return 0; }
Expand Down
71 changes: 68 additions & 3 deletions crypto/fipsmodule/service_indicator/service_indicator_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2297,21 +2297,31 @@ TEST_P(RSAServiceIndicatorTest, RSASigGen) {
&sig_len)));
EXPECT_EQ(approved, test.sig_gen_expect_approved);

// Test using the one-shot |EVP_DigestSign| function for approval.
md_ctx.Reset();
std::vector<uint8_t> oneshot_output(sig_len);
CALL_SERVICE_AND_CHECK_APPROVED(
approved, ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), &pctx, test.func(),
nullptr, pkey.get())));
EXPECT_EQ(approved, AWSLC_NOT_APPROVED);

if (test.use_pss) {
CALL_SERVICE_AND_CHECK_APPROVED(approved,
ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING)));
CALL_SERVICE_AND_CHECK_APPROVED(
approved,
ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING)));
EXPECT_EQ(approved, AWSLC_NOT_APPROVED);
CALL_SERVICE_AND_CHECK_APPROVED(
approved, ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1)));
EXPECT_EQ(approved, AWSLC_NOT_APPROVED);
}

// Test the one-shot |EVP_DigestSign| function to determine size.
// This should not set the indicator.
CALL_SERVICE_AND_CHECK_APPROVED(
approved,
ASSERT_TRUE(EVP_DigestSign(md_ctx.get(), nullptr, &sig_len, nullptr, 0)));
EXPECT_EQ(approved, AWSLC_NOT_APPROVED);

// Now test using the one-shot |EVP_DigestSign| function for approval.
CALL_SERVICE_AND_CHECK_APPROVED(approved,
ASSERT_TRUE(EVP_DigestSign(md_ctx.get(), oneshot_output.data(), &sig_len,
kPlaintext, sizeof(kPlaintext))));
Expand Down Expand Up @@ -4557,6 +4567,61 @@ TEST_P(KBKDFCtrHmacIndicatorTest, KBKDF) {
ASSERT_EQ(vector.expectation, approved);
}

TEST(ServiceIndicatorTest, ML_KEM) {
for (int nid : {NID_MLKEM512, NID_MLKEM768, NID_MLKEM1024}) {
bssl::UniquePtr<EVP_PKEY_CTX> ctx(
EVP_PKEY_CTX_new_id(EVP_PKEY_KEM, nullptr));
ASSERT_TRUE(EVP_PKEY_CTX_kem_set_params(ctx.get(), nid));
ASSERT_TRUE(EVP_PKEY_keygen_init(ctx.get()));

FIPSStatus approved = AWSLC_NOT_APPROVED;
EVP_PKEY *raw = nullptr;
// keygen for ML-KEM algorithms should be approved
CALL_SERVICE_AND_CHECK_APPROVED(approved, EVP_PKEY_keygen(ctx.get(), &raw));
bssl::UniquePtr<EVP_PKEY> pkey(raw);
ASSERT_EQ(approved, AWSLC_APPROVED);

size_t ciphertext_len = 0;
size_t shared_secret_len = 0;

ctx.reset(EVP_PKEY_CTX_new(pkey.get(), nullptr));

approved = AWSLC_NOT_APPROVED;
// encapsulate size check should not set indicator
CALL_SERVICE_AND_CHECK_APPROVED(
approved, EVP_PKEY_encapsulate(ctx.get(), nullptr, &ciphertext_len,
nullptr, &shared_secret_len));
ASSERT_EQ(approved, AWSLC_NOT_APPROVED);

std::vector<uint8_t> ciphertext(ciphertext_len);
std::vector<uint8_t> encap_shared_secret(shared_secret_len);

// encapsulate should set indicator
CALL_SERVICE_AND_CHECK_APPROVED(
approved,
EVP_PKEY_encapsulate(ctx.get(), ciphertext.data(), &ciphertext_len,
encap_shared_secret.data(), &shared_secret_len));
ASSERT_EQ(approved, AWSLC_APPROVED);

shared_secret_len = 0;
approved = AWSLC_NOT_APPROVED;
// decapsulate size check should not set indicator
CALL_SERVICE_AND_CHECK_APPROVED(
approved, EVP_PKEY_decapsulate(ctx.get(), nullptr, &shared_secret_len,
ciphertext.data(), ciphertext.size()));
ASSERT_EQ(approved, AWSLC_NOT_APPROVED);

std::vector<uint8_t> decap_shared_secret(shared_secret_len);
// decapsulate should set indicator
CALL_SERVICE_AND_CHECK_APPROVED(
approved, EVP_PKEY_decapsulate(ctx.get(), decap_shared_secret.data(),
&shared_secret_len, ciphertext.data(),
ciphertext.size()));
ASSERT_EQ(approved, AWSLC_APPROVED);
ASSERT_EQ(encap_shared_secret, decap_shared_secret);
}
}

// Verifies that the awslc_version_string is as expected.
// Since this is running in FIPS mode it should end in FIPS
// Update this when the AWS-LC version number is modified
Expand Down
Loading