-
Notifications
You must be signed in to change notification settings - Fork 11
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 and ML-KEM hybrid #103
base: main
Are you sure you want to change the base?
Conversation
edb7ed4
to
3b662e7
Compare
SymCryptProvider/src/p_scossl_base.c
Outdated
TLS_GROUP_ENTRY("mlkem768", SCOSSL_SN_MLKEM768, "MLKEM", scossl_tls_group_info_mlkem768), | ||
TLS_GROUP_ENTRY("mlkem1024", SCOSSL_SN_MLKEM1024, "MLKEM", scossl_tls_group_info_mlkem1024), | ||
TLS_GROUP_ENTRY("secp256r1mlkem768", SCOSSL_SN_P256_MLKEM768, "MLKEM", scossl_tls_group_info_secp256r1mlkem768), | ||
TLS_GROUP_ENTRY("P256mlkem768", SCOSSL_SN_P256_MLKEM768, "MLKEM", scossl_tls_group_info_secp256r1mlkem768), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These strings are only for setting supported groups from configuration as far as I'm aware. Now that OpenSSL has an ML-KEM implementation I'll update these entries to match what they have: https://github.com/openssl/openssl/blob/c2ab75e30a211aa278f8da1f0f040f9368adb81d/providers/common/capabilities.c#L193-L194
Not sure why this was 11 before - it looks like Seems error prone to have the magic number. Refers to: SymCryptProvider/src/p_scossl_base.c:186 in 3b662e7. [](commit_id = 3b662e7, deletion_comment = False) |
ScosslCommon/src/scossl_ecc.c
Outdated
if (curve == NULL) | ||
{ | ||
return 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _In_
annotation on this function indicates that curve
is a required parameter which must not be NULL. In Windows and SymCrypt, we'd typically not do runtime parameter validation here, but would instead rely on assertions and/or PREfast to catch invalid calls.
That said, if this is public function, it probably makes sense to be more defensive. I'm also not sure what the OpenSSL philosophy is regarding input validation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair point. This is an internal function so I can ensure we do the validation outside of this function. I don't think this needs to be changed to _In_opt_
// | ||
|
||
// Digests | ||
#define SCOSSL_ALG_NAME_MD5 SN_md5"SSL3-MD5:1.2.840.113549.2.5" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
// KDF | ||
#define SCOSSL_ALG_NAME_HKDF SN_hkdf | ||
#define SCOSSL_ALG_NAME_KBKKDF "KBKDF" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -586,23 +729,15 @@ SCOSSL_STATUS OSSL_provider_init(_In_ const OSSL_CORE_HANDLE *handle, | |||
{ | |||
SYMCRYPT_MODULE_INIT(); | |||
if (!scossl_dh_init_static() || | |||
!scossl_ecc_init_static()) | |||
!scossl_ecc_init_static() || | |||
!p_scossl_register_extended_algorithms()) | |||
{ | |||
ERR_raise(ERR_LIB_PROV, ERR_R_INIT_FAIL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was fixed in #104. I'll rebase after the current round of comments.
} | ||
|
||
p_ctx->handle = handle; | ||
p_ctx->libctx = OSSL_LIB_CTX_new_child(handle, in); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also addressed in #104 and will be updated with a rebase.
{ | ||
for (SIZE_T i = 0; i < sizeof(p_scossl_mlkem_groups) / sizeof(SCOSSL_MLKEM_GROUP_INFO); i++) | ||
{ | ||
p_scossl_mlkem_groups[i].nid = OBJ_create(p_scossl_mlkem_groups[i].oid, p_scossl_mlkem_groups[i].groupName, p_scossl_mlkem_groups[i].groupName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's OK for now since it's only used internally, but it should be updated to match the incoming OpenSSL implementation: https://github.com/openssl/openssl/blob/c2ab75e30a211aa278f8da1f0f040f9368adb81d/include/openssl/obj_mac.h#L6575
SymCryptProvider/src/p_scossl_bio.c
Outdated
size_t *bytes_read) | ||
{ | ||
if (core_bio_read_ex == NULL) | ||
return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just required for supporting encoder/decoder so I probably won't bother with annotating parameters but I think that makes sense.
p_scossl_bio_core_gets
, p_scossl_bio_core_puts
, and p_scossl_bio_core_ctrl
should keep their current return types as their return isn't success/fail.
SymCryptProvider/src/p_scossl_bio.c
Outdated
switch (dispatch->function_id) | ||
{ | ||
case OSSL_FUNC_BIO_READ_EX: | ||
core_bio_read_ex = (OSSL_FUNC_BIO_read_ex_fn *)dispatch->function; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
|
||
BIO_free(bio); | ||
ctx->desc->freeKeyCtx(keyCtx); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NP: Probably doesn't matter, but probably should free keyCtx
before bio
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't really matter but it does look cleaner.
|
||
typedef struct scossl_decode_ctx_st | ||
{ | ||
SCOSSL_PROVCTX *provctx; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like we currently have a recipe for potential use-after-free with the number of provider structures keeping a pointer to the single provider context associated with a OSSL_provider_init
call. (i.e. can we ever legitimately have a situation where p_scossl_teardown
is called while p_scossl_decode
is in progress?)
Should we have a refcount-ed provctx, or is OpenSSL guaranteed to ensure that all provider structures are freed before the provider is torn down? Perhaps it's not a huge deal if SCOSSL is the default provider, but could definitely be more of an issue for a place where an application dynamically loads/unloads SCOSSL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there's a legitimate case. The OpenSSL default provider doesn't refcount this so we should be ok.
It seems like the providers were originally built with that being a valid use case (which is why we saw use-after-free issues in openssl speed
), but it seems like the assumption now is that no provider operations should ever take place after p_scossl_teardown
SymCryptProvider/src/p_scossl_bio.c
Outdated
return NULL; | ||
} | ||
|
||
if ((bio = BIO_new(provctx->coreBioMeth)) != NULL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's probably better.
SymCryptProvider/src/p_scossl_ecc.c
Outdated
pbPrivateKey, cbPrivateKey, | ||
NULL, 0, | ||
numFormat, | ||
pointFormat, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copyCtx->isX25519 = keyCtx->isX25519; | ||
copyCtx->libctx = keyCtx->libctx; | ||
copyCtx->modifiedPrivateBits = keyCtx->modifiedPrivateBits; | ||
copyCtx->conversionFormat = keyCtx->conversionFormat; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huge NP: this should probably only be copied when copying private key material
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There might be a weird edge case where a key's conversion format is set, the key is duplicated, and the private key is imported later.
I'm not sure if that's even a valid case but I don't think it hurts to just copy the value since it technically falls under OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I misclicked when leaving the comment - I meant this to refer to the modifiedPrivateBits
field
if (copyCtx != NULL) | ||
{ | ||
#ifdef KEYSINUSE_ENABLED | ||
copyCtx->keysinuseLock = CRYPTO_THREAD_lock_new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should copy isImported
in here too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is missing in RSA too.
Yeah that was easy to miss... I'll pull that out into a constant defined by the In reply to: 2686350183 Refers to: SymCryptProvider/src/p_scossl_base.c:186 in 3b662e7. [](commit_id = 3b662e7, deletion_comment = False) |
return SCOSSL_SUCCESS; | ||
} | ||
|
||
SIZE_T p_scossl_ecc_get_max_size(_In_ SCOSSL_ECC_KEY_CTX *keyCtx, BOOL isEcdh) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ | ||
return SymCryptEcurveSizeofScalarMultiplier(keyCtx->curve); | ||
} | ||
else if (keyCtx->isX25519) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
|
||
cbKey = SymCryptEcurveSizeofFieldElement(keyCtx->curve); | ||
if (!keyCtx->isX25519) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In p_scossl_ecc_get_encoded_public_key
we compute the same thing with:
pointFormat = keyCtx->conversionFormat == POINT_CONVERSION_COMPRESSED ? SYMCRYPT_ECPOINT_FORMAT_X : SYMCRYPT_ECPOINT_FORMAT_XY;
// Allocate one extra byte for point compression type
cbPublicKey = SymCryptEckeySizeofPublicKey(keyCtx->key, pointFormat) + 1;
Which I think is probably a bit clearer
{ | ||
SYMCRYPT_NUMBER_FORMAT numFormat; | ||
SYMCRYPT_ECPOINT_FORMAT pointFormat; | ||
PBYTE pbPublicKey, pbPublicKeyStart; | ||
PBYTE pbPublicKeyStart = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ret = SCOSSL_SUCCESS; | ||
|
||
cleanup: | ||
if (!ret && allocatedKey) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (keyCtx->key != NULL) | ||
{ | ||
SymCryptEckeyFree(keyCtx->key); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NP: Prefer to keep SymCryptEckeyFree(keyCtx->key);
right next to the SymCryptEckeyAllocate(keyCtx->curve)
, or set keyCtx->key to NULL after free.
Want to make it harder to introduce a double free in future (i.e. if adding some new parameter validation which goto cleanup
between Free and Allocate).
|
||
// Preserve original bits for export | ||
keyCtx->modifiedPrivateBits = pbPrivateKey[0] & 0x07; | ||
keyCtx->modifiedPrivateBits |= pbPrivateKey[cbPrivateKey-1] & 0xc0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It actually looks like there is no need for cbPrivateKey
at all, and you can just use cbEncodedPrivateKey
everywhere instead in this function?
// to copy and decode the public key. | ||
if (keyCtx->isX25519) | ||
{ | ||
OPENSSL_secure_clear_free(pbPrivateKey, cbPrivateKey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR adds support for ML-KEM and hybrid ECDHE-MLKEM to the SymCrypt provider. This PR also refactors a significant portion of ECC key management and exposes the ECDH provider interface for ML-KEM hybrid operations. This implementation has been tested against BoringSSL and the OQS provider for compatibility.
These algorithms are not currently present in the default OpenSSL implementation. This PR adds the encoder and decoder interface, key management, and KEM interfaces for ML-KEM and ML-KEM hybrid. Their behavior is subject to change to align with the default OpenSSL behavior when OpenSSL adds their own implementation.
Code points are taken from IETF drafts and are subject to change. These are the same code points used by BoringSSL and the OQS provider. ML-KEM hybrid is implemented according to the latest IETF draft but is subject to changes.
The following algorithms are added:
draft-connolly-tls-mlkem-key-agreement (ML-KEM standalone usage and code points)
draft-kwiatkowski-tls-ecdhe-mlkem (ML-KEM hybrid implementation and code points)