diff --git a/ScosslCommon/inc/scossl_ecc.h b/ScosslCommon/inc/scossl_ecc.h index 711738a..2182635 100644 --- a/ScosslCommon/inc/scossl_ecc.h +++ b/ScosslCommon/inc/scossl_ecc.h @@ -16,6 +16,7 @@ void scossl_ecc_destroy_ecc_curves(); // x25519 is a separate interface in the provider and not supported in the // engine, so there's no need to fetch the hidden group from an EC_GROUP. +PCSYMCRYPT_ECURVE scossl_ecc_nid_to_symcrypt_curve(int groupNid); PCSYMCRYPT_ECURVE scossl_ecc_group_to_symcrypt_curve(_In_ const EC_GROUP *group); PCSYMCRYPT_ECURVE scossl_ecc_get_x25519_curve(); EC_GROUP *scossl_ecc_symcrypt_curve_to_ecc_group(_In_ PCSYMCRYPT_ECURVE pCurve); diff --git a/ScosslCommon/src/scossl_ecc.c b/ScosslCommon/src/scossl_ecc.c index ad86a50..daf57df 100644 --- a/ScosslCommon/src/scossl_ecc.c +++ b/ScosslCommon/src/scossl_ecc.c @@ -78,14 +78,8 @@ void scossl_ecc_destroy_ecc_curves() } } -_Use_decl_annotations_ -PCSYMCRYPT_ECURVE scossl_ecc_group_to_symcrypt_curve(const EC_GROUP *group) +PCSYMCRYPT_ECURVE scossl_ecc_nid_to_symcrypt_curve(int groupNid) { - if (group == NULL) - return NULL; - - int groupNid = EC_GROUP_get_curve_name(group); - // Only reroute NIST Prime curves to SymCrypt for now switch (groupNid) { @@ -107,6 +101,15 @@ PCSYMCRYPT_ECURVE scossl_ecc_group_to_symcrypt_curve(const EC_GROUP *group) return NULL; } +_Use_decl_annotations_ +PCSYMCRYPT_ECURVE scossl_ecc_group_to_symcrypt_curve(const EC_GROUP *group) +{ + if (group == NULL) + return NULL; + + return scossl_ecc_nid_to_symcrypt_curve(EC_GROUP_get_curve_name(group)); +} + PCSYMCRYPT_ECURVE scossl_ecc_get_x25519_curve() { return _hidden_curve_X25519; @@ -519,6 +522,11 @@ static SCOSSL_STATUS scossl_ecdsa_apply_der(_In_reads_bytes_(cbSymCryptSignature _Use_decl_annotations_ SIZE_T scossl_ecdsa_size(PCSYMCRYPT_ECURVE curve) { + if (curve == NULL) + { + return 0; + } + return 2*SymCryptEcurveSizeofScalarMultiplier(curve) + 8; } diff --git a/SymCryptProvider/CMakeLists.txt b/SymCryptProvider/CMakeLists.txt index 2a9f598..78b2054 100644 --- a/SymCryptProvider/CMakeLists.txt +++ b/SymCryptProvider/CMakeLists.txt @@ -21,28 +21,35 @@ set(SCOSSL_SOURCES ./src/ciphers/p_scossl_aes.c ./src/ciphers/p_scossl_aes_aead.c ./src/ciphers/p_scossl_aes_xts.c + ./src/decoder/p_scossl_decode_common.c + ./src/decoder/p_scossl_decode_mlkem.c ./src/digests/p_scossl_digest_common.c ./src/digests/p_scossl_digest_generic.c ./src/digests/p_scossl_shake.c ./src/digests/p_scossl_cshake.c + ./src/encoder/p_scossl_encode_common.c + ./src/encoder/p_scossl_encode_mlkem.c ./src/kdf/p_scossl_hkdf.c ./src/kdf/p_scossl_kbkdf.c ./src/kdf/p_scossl_srtpkdf.c ./src/kdf/p_scossl_sshkdf.c ./src/kdf/p_scossl_sskdf.c ./src/kdf/p_scossl_tls1prf.c + ./src/kem/p_scossl_mlkem.c ./src/keyexch/p_scossl_dh.c ./src/keyexch/p_scossl_ecdh.c ./src/keyexch/p_scossl_kdf_keyexch.c ./src/keymgmt/p_scossl_dh_keymgmt.c ./src/keymgmt/p_scossl_ecc_keymgmt.c ./src/keymgmt/p_scossl_kdf_keymgmt.c + ./src/keymgmt/p_scossl_mlkem_keymgmt.c ./src/keymgmt/p_scossl_rsa_keymgmt.c ./src/mac/p_scossl_cmac.c ./src/mac/p_scossl_hmac.c ./src/mac/p_scossl_kmac.c ./src/signature/p_scossl_ecdsa_signature.c ./src/signature/p_scossl_rsa_signature.c + ./src/p_scossl_bio.c ./src/p_scossl_ecc.c ./src/p_scossl_rand.c ./src/p_scossl_rsa.c diff --git a/SymCryptProvider/inc/p_scossl_base.h.in b/SymCryptProvider/inc/p_scossl_base.h.in index 4ddf394..fbdcb50 100644 --- a/SymCryptProvider/inc/p_scossl_base.h.in +++ b/SymCryptProvider/inc/p_scossl_base.h.in @@ -4,9 +4,11 @@ #pragma once +#include "scossl_helpers.h" + +#include #include #include -#include "scossl_helpers.h" #ifdef __cplusplus extern "C" { @@ -20,6 +22,7 @@ typedef struct { OSSL_LIB_CTX *libctx; const OSSL_CORE_HANDLE *handle; + BIO_METHOD *coreBioMeth; } SCOSSL_PROVCTX; static const OSSL_PARAM p_scossl_param_types[] = { diff --git a/SymCryptProvider/inc/scossl_provider.h b/SymCryptProvider/inc/scossl_provider.h index 2ad7a37..79ec559 100644 --- a/SymCryptProvider/inc/scossl_provider.h +++ b/SymCryptProvider/inc/scossl_provider.h @@ -14,14 +14,12 @@ extern "C" { // // Digest parameters // - #define SCOSSL_DIGEST_PARAM_FUNCTION_NAME_STRING "function-name-string" #define SCOSSL_DIGEST_PARAM_CUSTOMIZATION_STRING "customization-string" // // KDF parameters // - #define SCOSSL_KDF_PARAM_SRTP_RATE "rate" #define SCOSSL_KDF_PARAM_SRTP_INDEX "index" #define SCOSSL_KDF_PARAM_SRTP_INDEX_WIDTH "index-width" @@ -29,11 +27,31 @@ extern "C" { // // SRTP labels // - #define SCOSSL_SRTP_LABEL_ENCRYPTION "encryption" #define SCOSSL_SRTP_LABEL_AUTHENTICATION "authentication" #define SCOSSL_SRTP_LABEL_SALTING "salting" +// +// Extended algorithms not found in default OpenSSL implementation +// +#define SCOSSL_SN_MLKEM512 "mlkem512" +#define SCOSSL_OID_MLKEM512 "2.16.840.1.101.3.4.4.1" + +#define SCOSSL_SN_MLKEM768 "mlkem768" +#define SCOSSL_OID_MLKEM768 "2.16.840.1.101.3.4.4.2" + +#define SCOSSL_SN_MLKEM1024 "mlkem1024" +#define SCOSSL_OID_MLKEM1024 "2.16.840.1.101.3.4.4.3" + +#define SCOSSL_SN_P256_MLKEM768 "secp256r1"SCOSSL_SN_MLKEM768 +#define SCOSSL_OID_P256_MLKEM768 "2.16.840.1.101.3.4.4.4" + +#define SCOSSL_SN_X25519_MLKEM768 SN_X25519 SCOSSL_SN_MLKEM768 +#define SCOSSL_OID_X25519_MLKEM768 "2.16.840.1.101.3.4.4.5" + +#define SCOSSL_SN_P384_MLKEM1024 SN_secp384r1 SCOSSL_SN_MLKEM1024 +#define SCOSSL_OID_P384_MLKEM1024 "2.16.840.1.101.3.4.4.6" + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/SymCryptProvider/src/decoder/p_scossl_decode_common.c b/SymCryptProvider/src/decoder/p_scossl_decode_common.c new file mode 100644 index 0000000..af54844 --- /dev/null +++ b/SymCryptProvider/src/decoder/p_scossl_decode_common.c @@ -0,0 +1,123 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#include "p_scossl_bio.h" +#include "p_scossl_decode_common.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +ASN1_NDEF_SEQUENCE(SUBJECT_PUBKEY_INFO) = { + ASN1_SIMPLE(SUBJECT_PUBKEY_INFO, x509Alg, X509_ALGOR), + ASN1_SIMPLE(SUBJECT_PUBKEY_INFO, subjectPublicKey, ASN1_BIT_STRING), +} ASN1_SEQUENCE_END(SUBJECT_PUBKEY_INFO) + +IMPLEMENT_ASN1_FUNCTIONS(SUBJECT_PUBKEY_INFO) + +const OSSL_PARAM p_scossl_der_to_key_settable_param_types[] = { + OSSL_PARAM_END}; + +_Use_decl_annotations_ +SCOSSL_DECODE_CTX *p_scossl_decode_newctx(SCOSSL_PROVCTX *provctx, const SCOSSL_DECODE_KEYTYPE_DESC *desc) +{ + SCOSSL_DECODE_CTX *ctx = OPENSSL_zalloc(sizeof(SCOSSL_DECODE_CTX)); + + if (ctx != NULL) + { + ctx->provctx = provctx; + ctx->desc = desc; + } + + return ctx; +} + +_Use_decl_annotations_ +void p_scossl_decode_freectx(SCOSSL_DECODE_CTX *ctx) +{ + if (ctx == NULL) + return; + + OPENSSL_free(ctx); +} + +SCOSSL_STATUS p_scossl_decode_set_ctx_params(ossl_unused void *ctx, ossl_unused const OSSL_PARAM params[]) +{ + return SCOSSL_SUCCESS; +} + +const OSSL_PARAM *p_scossl_decode_settable_ctx_params(ossl_unused void *ctx) +{ + return p_scossl_der_to_key_settable_param_types; +} + +_Use_decl_annotations_ +BOOL p_scossl_decode_does_selection(const SCOSSL_DECODE_KEYTYPE_DESC *desc, int selection) +{ + if (selection == 0) + { + return TRUE; + } + + // Supporting private key implies supporting public key. + // Both imply supporting key parameters + return ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0 && (desc->selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) || + ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0 && (desc->selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) || + ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0 && (desc->selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0); +} + +// This function should return SCOSSL_SUCCESS if it successfully decodes something, +// or decodes nothing at all. Another decoder may be able to decode the data into something. +// This function should only return SCOSSL_FAILURE if the data could be decoded, but further +// validation of the data failed in a way that another decoder could not handle. +_Use_decl_annotations_ +SCOSSL_STATUS p_scossl_decode(SCOSSL_DECODE_CTX *ctx, OSSL_CORE_BIO *in, int selection, + OSSL_CALLBACK *dataCb, void *dataCbArg, + ossl_unused OSSL_PASSPHRASE_CALLBACK *passphraseCb, ossl_unused void *passphraseCbArg) +{ + BIO *bio = NULL; + PVOID *keyCtx = NULL; + OSSL_PARAM cbParams[4]; + SCOSSL_STATUS ret = SCOSSL_SUCCESS; + + if (selection == 0) + { + selection = ctx->desc->selection; + } + + if ((selection & ctx->desc->selection) != 0 && + (bio = p_scossl_bio_new_from_core_bio(ctx->provctx, in)) != NULL) + { + keyCtx = ctx->desc->decodeInternal(ctx, bio); + } + + if (keyCtx != NULL) + { + int objectType = OSSL_OBJECT_PKEY; + + cbParams[0] = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &objectType); + cbParams[1] = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, (char *)ctx->desc->dataType, 0); + cbParams[2] = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE, &keyCtx, sizeof(keyCtx)); + cbParams[3] = OSSL_PARAM_construct_end(); + + ret = dataCb(cbParams, dataCbArg); + } + + BIO_free(bio); + ctx->desc->freeKeyCtx(keyCtx); + + return ret; +} + +const ASN1_ITEM *p_scossl_decode_subject_pubkey_asn1_item() +{ + return ASN1_ITEM_rptr(SUBJECT_PUBKEY_INFO); +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/SymCryptProvider/src/decoder/p_scossl_decode_common.h b/SymCryptProvider/src/decoder/p_scossl_decode_common.h new file mode 100644 index 0000000..5fde32d --- /dev/null +++ b/SymCryptProvider/src/decoder/p_scossl_decode_common.h @@ -0,0 +1,55 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#include "p_scossl_base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define select_PrivateKeyInfo OSSL_KEYMGMT_SELECT_PRIVATE_KEY +#define select_SubjectPublicKeyInfo OSSL_KEYMGMT_SELECT_PUBLIC_KEY + +typedef PVOID (*PSCOSSL_DECODE_INTERNAL_FN) (_In_ PVOID decodeCtx, _In_ BIO *bio); + +typedef struct +{ + const char *dataType; + int selection; + + PSCOSSL_DECODE_INTERNAL_FN decodeInternal; + OSSL_FUNC_keymgmt_free_fn *freeKeyCtx; +} SCOSSL_DECODE_KEYTYPE_DESC; + +typedef struct scossl_decode_ctx_st +{ + SCOSSL_PROVCTX *provctx; + + const SCOSSL_DECODE_KEYTYPE_DESC *desc; +} SCOSSL_DECODE_CTX; + +typedef struct +{ + X509_ALGOR *x509Alg; + ASN1_BIT_STRING *subjectPublicKey; +} SUBJECT_PUBKEY_INFO; + +SCOSSL_DECODE_CTX *p_scossl_decode_newctx(_In_ SCOSSL_PROVCTX *provctx, _In_ const SCOSSL_DECODE_KEYTYPE_DESC *desc); +void p_scossl_decode_freectx(_Inout_ SCOSSL_DECODE_CTX *ctx); + +SCOSSL_STATUS p_scossl_decode_set_ctx_params(ossl_unused void *ctx, ossl_unused const OSSL_PARAM params[]); +const OSSL_PARAM *p_scossl_decode_settable_ctx_params(ossl_unused void *ctx); + +BOOL p_scossl_decode_does_selection(_In_ const SCOSSL_DECODE_KEYTYPE_DESC *desc, int selection); + +SCOSSL_STATUS p_scossl_decode(_In_ SCOSSL_DECODE_CTX *ctx, _In_ OSSL_CORE_BIO *in, + int selection, + _In_ OSSL_CALLBACK *dataCb, _In_ void *dataCbArg, + ossl_unused OSSL_PASSPHRASE_CALLBACK *passphraseCb, ossl_unused void *passphraseCbArg); + +const ASN1_ITEM *p_scossl_decode_subject_pubkey_asn1_item(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/SymCryptProvider/src/decoder/p_scossl_decode_mlkem.c b/SymCryptProvider/src/decoder/p_scossl_decode_mlkem.c new file mode 100644 index 0000000..1d893a8 --- /dev/null +++ b/SymCryptProvider/src/decoder/p_scossl_decode_mlkem.c @@ -0,0 +1,163 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#include "scossl_provider.h" +#include "p_scossl_decode_common.h" +#include "keymgmt/p_scossl_mlkem_keymgmt.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static SCOSSL_MLKEM_KEY_CTX *p_scossl_mlkem_decode_key_bytes(_In_ SCOSSL_DECODE_CTX *ctx, _In_ const ASN1_OBJECT *algorithm, int selection, + _In_reads_bytes_(cbKey) PCBYTE pbKey, SIZE_T cbKey) +{ + SCOSSL_MLKEM_KEY_CTX *keyCtx = NULL; + SCOSSL_STATUS status = SCOSSL_FAILURE; + + if (pbKey == NULL || cbKey == 0) + { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + goto cleanup; + } + + if ((keyCtx = p_scossl_mlkem_keymgmt_new_ctx(ctx->provctx)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + if ((keyCtx->groupInfo = p_scossl_mlkem_get_group_info_by_nid(OBJ_obj2nid(algorithm))) == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED); + goto cleanup; + } + + status = p_scossl_mlkem_keymgmt_set_encoded_key(keyCtx, selection, pbKey, cbKey); + +cleanup: + if (status != SCOSSL_SUCCESS) + { + OPENSSL_free(keyCtx); + keyCtx = NULL; + } + + return keyCtx; +} + +static SCOSSL_MLKEM_KEY_CTX *p_scossl_PrivateKeyInfo_to_mlkem(_In_ SCOSSL_DECODE_CTX *ctx, _In_ BIO *bio) +{ + PKCS8_PRIV_KEY_INFO *p8Info = NULL; + const ASN1_OBJECT *algorithm; + const unsigned char *pbKey; + int cbKey; + ASN1_OCTET_STRING *p8Data = NULL; + SCOSSL_MLKEM_KEY_CTX *keyCtx = NULL; + + if (d2i_PKCS8_PRIV_KEY_INFO_bio(bio, &p8Info) == NULL || + !PKCS8_pkey_get0(&algorithm, &pbKey, &cbKey, NULL, p8Info) || + d2i_ASN1_OCTET_STRING(&p8Data, &pbKey, cbKey) == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_ENCODING); + goto cleanup; + } + + keyCtx = p_scossl_mlkem_decode_key_bytes(ctx, algorithm, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, + ASN1_STRING_get0_data(p8Data), ASN1_STRING_length(p8Data)); + +cleanup: + ASN1_OCTET_STRING_free(p8Data); + PKCS8_PRIV_KEY_INFO_free(p8Info); + + return keyCtx; +} + +static SCOSSL_MLKEM_KEY_CTX *p_scossl_SubjectPublicKeyInfo_to_mlkem(_In_ SCOSSL_DECODE_CTX *ctx, _In_ BIO *bio) +{ + SUBJECT_PUBKEY_INFO *subjPubKeyInfo = NULL; + const ASN1_OBJECT *algorithm; + SCOSSL_MLKEM_KEY_CTX *keyCtx = NULL; + + if ((subjPubKeyInfo = OPENSSL_zalloc(sizeof(SUBJECT_PUBKEY_INFO))) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + if (ASN1_item_d2i_bio(p_scossl_decode_subject_pubkey_asn1_item(), bio, (ASN1_VALUE **)&subjPubKeyInfo) == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_ENCODING); + goto cleanup; + } + + X509_ALGOR_get0(&algorithm, NULL, NULL, subjPubKeyInfo->x509Alg); + + keyCtx = p_scossl_mlkem_decode_key_bytes(ctx, algorithm, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + ASN1_STRING_get0_data(subjPubKeyInfo->subjectPublicKey), ASN1_STRING_length(subjPubKeyInfo->subjectPublicKey)); + +cleanup: + OPENSSL_free(subjPubKeyInfo); + + return keyCtx; +} + +static SCOSSL_STATUS p_scossl_der_to_mlkem_export_object(_In_ SCOSSL_DECODE_CTX *ctx, + _In_reads_bytes_(cbObjRef) const void *pbObjRef, _In_ size_t cbObjRef, + _In_ OSSL_CALLBACK *exportCb, _In_ void *exportCbArg) +{ + SCOSSL_MLKEM_KEY_CTX *keyCtx = *(SCOSSL_MLKEM_KEY_CTX **)pbObjRef; + + if (cbObjRef != sizeof(SCOSSL_MLKEM_KEY_CTX *) || keyCtx == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + return SCOSSL_FAILURE; + } + + return p_scossl_mlkem_keymgmt_export(keyCtx, ctx->desc->selection, exportCb, exportCbArg); +} + +#define SCOSSL_MAKE_MLKEM_DECODER(decoderType) \ + static const SCOSSL_DECODE_KEYTYPE_DESC p_scossl_mlkem_##decoderType##_desc = { \ + "MLKEM", \ + select_##decoderType, \ + (PSCOSSL_DECODE_INTERNAL_FN)p_scossl_##decoderType##_to_mlkem, \ + (OSSL_FUNC_keymgmt_free_fn *)p_scossl_mlkem_keymgmt_free_key_ctx}; \ + \ + static SCOSSL_DECODE_CTX * \ + p_scossl_der_to_mlkem_##decoderType##_newctx(_In_ SCOSSL_PROVCTX *provctx) \ + { \ + return p_scossl_decode_newctx( \ + provctx, \ + &p_scossl_mlkem_##decoderType##_desc); \ + } \ + \ + static BOOL \ + p_scossl_der_to_mlkem_##decoderType##_does_selection( \ + ossl_unused void *provctx, \ + int selection) \ + { \ + return p_scossl_decode_does_selection( \ + &p_scossl_mlkem_##decoderType##_desc, \ + selection); \ + } \ + \ + const OSSL_DISPATCH p_scossl_der_to_mlkem_##decoderType##_functions[] = { \ + {OSSL_FUNC_DECODER_NEWCTX, (void (*)(void))p_scossl_der_to_mlkem_##decoderType##_newctx}, \ + {OSSL_FUNC_DECODER_FREECTX, (void (*)(void))p_scossl_decode_freectx}, \ + {OSSL_FUNC_DECODER_SET_CTX_PARAMS, (void (*)(void))p_scossl_decode_set_ctx_params}, \ + {OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS, (void (*)(void))p_scossl_decode_settable_ctx_params}, \ + {OSSL_FUNC_DECODER_DOES_SELECTION, (void (*)(void)) p_scossl_der_to_mlkem_##decoderType##_does_selection}, \ + {OSSL_FUNC_DECODER_DECODE, (void (*)(void))p_scossl_decode}, \ + {OSSL_FUNC_DECODER_EXPORT_OBJECT, (void (*)(void))p_scossl_der_to_mlkem_export_object}, \ + {0, NULL}}; + +SCOSSL_MAKE_MLKEM_DECODER(PrivateKeyInfo); +SCOSSL_MAKE_MLKEM_DECODER(SubjectPublicKeyInfo); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/SymCryptProvider/src/encoder/p_scossl_encode_common.c b/SymCryptProvider/src/encoder/p_scossl_encode_common.c new file mode 100644 index 0000000..f1ecb5b --- /dev/null +++ b/SymCryptProvider/src/encoder/p_scossl_encode_common.c @@ -0,0 +1,174 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#include "p_scossl_bio.h" +#include "p_scossl_encode_common.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define KEY_TO_TEXT_PRINT_WIDTH 15 + +static const OSSL_PARAM p_scossl_encode_settable_param_types[] = { + OSSL_PARAM_utf8_string(OSSL_ENCODER_PARAM_CIPHER, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_ENCODER_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END}; + +_Use_decl_annotations_ +SCOSSL_ENCODE_CTX *p_scossl_encode_newctx(SCOSSL_PROVCTX *provctx, const SCOSSL_ENCODE_KEYTYPE_DESC *desc) +{ + SCOSSL_ENCODE_CTX *ctx = OPENSSL_zalloc(sizeof(SCOSSL_ENCODE_CTX)); + + if (ctx != NULL) + { + ctx->provctx = provctx; + ctx->desc = desc; + } + + return ctx; +} + +_Use_decl_annotations_ +void p_scossl_encode_freectx(SCOSSL_ENCODE_CTX *ctx) +{ + if (ctx == NULL) + return; + + OPENSSL_free(ctx); +} + +_Use_decl_annotations_ +SCOSSL_STATUS p_scossl_encode_set_ctx_params(SCOSSL_ENCODE_CTX *ctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_ENCODER_PARAM_CIPHER)) != NULL) + { + OSSL_LIB_CTX *libctx = ctx->provctx != NULL ? ctx->provctx->libctx : NULL; + const char *cipherName = NULL; + const char *propQ = NULL; + + if (!OSSL_PARAM_get_utf8_string_ptr(p, &cipherName)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return SCOSSL_FAILURE; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_ENCODER_PARAM_PROPERTIES)) != NULL && + !OSSL_PARAM_get_utf8_string_ptr(p, &propQ)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return SCOSSL_FAILURE; + } + + EVP_CIPHER_free(ctx->cipher); + + if (cipherName != NULL) + { + if ((ctx->cipher = EVP_CIPHER_fetch(libctx, cipherName, propQ)) == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return SCOSSL_FAILURE; + } + } + else + { + ctx->cipher = NULL; + } + } + + return SCOSSL_SUCCESS; +} + +const OSSL_PARAM *p_scossl_encode_settable_ctx_params(ossl_unused void *provctx) +{ + return p_scossl_encode_settable_param_types; +} + +BOOL p_scossl_encode_does_selection(const SCOSSL_ENCODE_KEYTYPE_DESC *desc, int selection) +{ + if (selection == 0) + { + return TRUE; + } + + // Supporting private key implies supporting public key. + // Both imply supporting key parameters + return ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0 && (desc->selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) || + ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0 && (desc->selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) || + ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0 && (desc->selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0); +} + +_Use_decl_annotations_ +SCOSSL_STATUS p_scossl_encode(SCOSSL_ENCODE_CTX *ctx, OSSL_CORE_BIO *out, + const void *keyCtx, + const OSSL_PARAM keyAbstract[], + int selection, + OSSL_PASSPHRASE_CALLBACK *passphraseCb, void *passphraseCbArgs) +{ + BIO *bio = NULL; + SCOSSL_STATUS ret = SCOSSL_FAILURE; + + if (keyCtx == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + goto cleanup; + } + + if (ctx->desc->encodeInternal == NULL || + keyAbstract != NULL || + (ctx->desc->outFormat != SCOSSL_ENCODE_TEXT && ((selection & ctx->desc->selection) == 0))) + { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + goto cleanup; + } + + if ((bio = p_scossl_bio_new_from_core_bio(ctx->provctx, out)) != NULL) + { + ret = ctx->desc->encodeInternal( + ctx, bio, + keyCtx, + selection, + passphraseCb, passphraseCbArgs); + } + +cleanup: + BIO_free(bio); + + return ret; +} + +_Use_decl_annotations_ +SCOSSL_STATUS p_scossl_encode_write_key_bytes(PCBYTE pbKey, SIZE_T cbKey, BIO *out) +{ + for(SIZE_T i = 0; i < cbKey; i++) + { + if (i % KEY_TO_TEXT_PRINT_WIDTH == 0) + { + if (BIO_printf(out, "\n ") <= 0) + { + return SCOSSL_FAILURE; + } + } + + if (BIO_printf(out, "%02x%s", pbKey[i], (i < cbKey - 1) ? ":" : "") <= 0) + { + return SCOSSL_FAILURE; + } + } + + if (BIO_printf(out, "\n") <= 0) + { + return SCOSSL_FAILURE; + } + + return SCOSSL_SUCCESS; +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/SymCryptProvider/src/encoder/p_scossl_encode_common.h b/SymCryptProvider/src/encoder/p_scossl_encode_common.h new file mode 100644 index 0000000..f791f61 --- /dev/null +++ b/SymCryptProvider/src/encoder/p_scossl_encode_common.h @@ -0,0 +1,61 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#include "p_scossl_base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define select_PrivateKeyInfo OSSL_KEYMGMT_SELECT_PRIVATE_KEY +#define select_EncryptedPrivateKeyInfo OSSL_KEYMGMT_SELECT_PRIVATE_KEY +#define select_SubjectPublicKeyInfo OSSL_KEYMGMT_SELECT_PUBLIC_KEY + +typedef enum { + SCOSSL_ENCODE_DER = 1, + SCOSSL_ENCODE_PEM, + SCOSSL_ENCODE_TEXT +} SCOSSL_ENCODE_OUT_FORMAT; + +typedef SCOSSL_STATUS (*PSCOSSL_ENCODE_INTERNAL_FN) (_In_ PVOID encodeCtx, _Inout_ BIO *out, + _In_ PCVOID keyCtx, + int selection, + _In_ OSSL_PASSPHRASE_CALLBACK *passphraseCb, _In_ void *passphraseCbArgs); + +typedef struct +{ + int selection; + SCOSSL_ENCODE_OUT_FORMAT outFormat; + + PSCOSSL_ENCODE_INTERNAL_FN encodeInternal; +} SCOSSL_ENCODE_KEYTYPE_DESC; + +typedef struct +{ + SCOSSL_PROVCTX *provctx; + + EVP_CIPHER *cipher; + + const SCOSSL_ENCODE_KEYTYPE_DESC *desc; +} SCOSSL_ENCODE_CTX; + +SCOSSL_ENCODE_CTX *p_scossl_encode_newctx(_In_ SCOSSL_PROVCTX *provctx, const SCOSSL_ENCODE_KEYTYPE_DESC *desc); +void p_scossl_encode_freectx(_Inout_ SCOSSL_ENCODE_CTX *ctx); + +SCOSSL_STATUS p_scossl_encode_set_ctx_params(_In_ SCOSSL_ENCODE_CTX *ctx, _In_ const OSSL_PARAM params[]); +const OSSL_PARAM *p_scossl_encode_settable_ctx_params(ossl_unused void *provctx); + +BOOL p_scossl_encode_does_selection(_In_ const SCOSSL_ENCODE_KEYTYPE_DESC *desc, int selection); + +SCOSSL_STATUS p_scossl_encode(_In_ SCOSSL_ENCODE_CTX *ctx, _In_ OSSL_CORE_BIO *coreOut, + _In_ const void *keyCtx, + _In_ const OSSL_PARAM keyAbstract[], + int selection, + _In_ OSSL_PASSPHRASE_CALLBACK *passphraseCb, _In_ void *passphraseCbArgs); + +SCOSSL_STATUS p_scossl_encode_write_key_bytes(_In_reads_bytes_(cbKey) PCBYTE pbKey, SIZE_T cbKey, _Inout_ BIO *out); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/SymCryptProvider/src/encoder/p_scossl_encode_mlkem.c b/SymCryptProvider/src/encoder/p_scossl_encode_mlkem.c new file mode 100644 index 0000000..da7ab98 --- /dev/null +++ b/SymCryptProvider/src/encoder/p_scossl_encode_mlkem.c @@ -0,0 +1,474 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#include "scossl_provider.h" +#include "p_scossl_encode_common.h" +#include "keymgmt/p_scossl_mlkem_keymgmt.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const OSSL_DISPATCH p_scossl_mlkem_keymgmt_functions[]; + +static ASN1_OBJECT *p_scossl_encode_mlkem_get_oid(_In_ const SCOSSL_MLKEM_KEY_CTX *keyCtx) +{ + return keyCtx->groupInfo != NULL ? OBJ_nid2obj(keyCtx->groupInfo->nid) : NULL; +} + +static PKCS8_PRIV_KEY_INFO *p_scossl_mlkem_key_to_p8info(_In_ const SCOSSL_MLKEM_KEY_CTX *keyCtx) +{ + PBYTE pbKey = NULL; + SIZE_T cbKey; + unsigned char *pbDer = NULL; + int cbDer; + ASN1_OCTET_STRING *p8Data = NULL; + PKCS8_PRIV_KEY_INFO *p8Info = NULL; + ASN1_OBJECT *p8Obj = NULL; + SCOSSL_STATUS status = SCOSSL_FAILURE; + + if (keyCtx->key == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + goto cleanup; + } + + if ((p8Data = ASN1_OCTET_STRING_new()) == NULL || + (p8Info = PKCS8_PRIV_KEY_INFO_new()) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + if (p_scossl_mlkem_keymgmt_get_encoded_key(keyCtx, SYMCRYPT_MLKEMKEY_FORMAT_PRIVATE_SEED, &pbKey, &cbKey) != SCOSSL_SUCCESS) + { + goto cleanup; + } + + if (!ASN1_OCTET_STRING_set(p8Data, pbKey, cbKey) || + (cbDer = i2d_ASN1_OCTET_STRING(p8Data, &pbDer)) == 0) + { + ERR_raise(ERR_LIB_PROV, ASN1_R_ENCODE_ERROR); + goto cleanup; + } + + if ((p8Obj = p_scossl_encode_mlkem_get_oid(keyCtx)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if (!PKCS8_pkey_set0(p8Info, p8Obj, 0, V_ASN1_UNDEF, NULL, pbDer, cbDer)) + { + ERR_raise(ERR_LIB_PROV, ASN1_R_ENCODE_ERROR); + goto cleanup; + } + + status = SCOSSL_SUCCESS; + +cleanup: + if (status != SCOSSL_SUCCESS) + { + PKCS8_PRIV_KEY_INFO_free(p8Info); + OPENSSL_free(pbDer); + p8Info = NULL; + } + + ASN1_OCTET_STRING_free(p8Data); + ASN1_OBJECT_free(p8Obj); + OPENSSL_secure_clear_free(pbKey, cbKey); + + return p8Info; +} + +static X509_PUBKEY *p_scossl_mlkem_key_to_pubkey(_In_ const SCOSSL_MLKEM_KEY_CTX *keyCtx) +{ + PBYTE pbKey = NULL; + SIZE_T cbKey; + X509_PUBKEY *pubKey = NULL; + ASN1_OBJECT *p8Obj = NULL; + SCOSSL_STATUS status = SCOSSL_FAILURE; + + if (keyCtx->key == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + goto cleanup; + } + + if ((pubKey = X509_PUBKEY_new()) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + if (p_scossl_mlkem_keymgmt_get_encoded_key(keyCtx, SYMCRYPT_MLKEMKEY_FORMAT_ENCAPSULATION_KEY, &pbKey, &cbKey) != SCOSSL_SUCCESS) + { + goto cleanup; + } + + if ((p8Obj = p_scossl_encode_mlkem_get_oid(keyCtx)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if (!X509_PUBKEY_set0_param(pubKey, p8Obj, V_ASN1_NULL, NULL, pbKey, cbKey)) + { + ERR_raise(ERR_LIB_PROV, ASN1_R_ENCODE_ERROR); + goto cleanup; + } + + status = SCOSSL_SUCCESS; + +cleanup: + if (status != SCOSSL_SUCCESS) + { + if (pubKey == NULL) + { + OPENSSL_secure_free(pbKey); + ASN1_OBJECT_free(p8Obj); + } + X509_PUBKEY_free(pubKey); + pubKey = NULL; + } + + return pubKey; +} + + +static SCOSSL_STATUS p_scossl_mlkem_to_EncryptedPrivateKeyInfo(_In_ SCOSSL_ENCODE_CTX *ctx, _Inout_ BIO *out, + _In_ const SCOSSL_MLKEM_KEY_CTX *keyCtx, + ossl_unused int selection, + _In_ OSSL_PASSPHRASE_CALLBACK *passphraseCb, _In_ void *passphraseCbArgs) +{ + int encodeSuccess; + PKCS8_PRIV_KEY_INFO *p8Info = NULL; + X509_SIG *p8 = NULL; + char pbPass[PEM_BUFSIZE]; + SIZE_T cbPass = 0; + SCOSSL_STATUS ret = SCOSSL_FAILURE; + + if (ctx->cipher == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_CIPHER); + goto cleanup; + } + + if ((p8Info = p_scossl_mlkem_key_to_p8info(keyCtx)) == NULL) + { + goto cleanup; + } + + if (!passphraseCb(pbPass, sizeof(pbPass), &cbPass, NULL, passphraseCbArgs)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PASSPHRASE); + goto cleanup; + } + + if ((p8 = PKCS8_encrypt_ex(-1, ctx->cipher, pbPass, cbPass, NULL, 0, 0, p8Info, ctx->provctx->libctx, NULL)) == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + goto cleanup; + } + + if (ctx->desc->outFormat == SCOSSL_ENCODE_PEM) + { + encodeSuccess = PEM_write_bio_PKCS8(out, p8); + } + else + { + encodeSuccess = i2d_PKCS8_bio(out, p8); + } + + if (!encodeSuccess) + { + ERR_raise(ERR_LIB_PROV, ASN1_R_ENCODE_ERROR); + goto cleanup; + } + + ret = SCOSSL_SUCCESS; + +cleanup: + OPENSSL_cleanse(pbPass, sizeof(pbPass)); + PKCS8_PRIV_KEY_INFO_free(p8Info); + X509_SIG_free(p8); + + return ret; +} + +static SCOSSL_STATUS p_scossl_mlkem_to_PrivateKeyInfo(_In_ SCOSSL_ENCODE_CTX *ctx, _Inout_ BIO *out, + _In_ const SCOSSL_MLKEM_KEY_CTX *keyCtx, + ossl_unused int selection, + _In_ OSSL_PASSPHRASE_CALLBACK *passphraseCb, _In_ void *passphraseCbArgs) +{ + int encodeSuccess; + PKCS8_PRIV_KEY_INFO *p8Info = NULL; + SCOSSL_STATUS ret = SCOSSL_FAILURE; + + if (ctx->cipher != NULL) + { + return p_scossl_mlkem_to_EncryptedPrivateKeyInfo(ctx, out, keyCtx, selection, passphraseCb, passphraseCbArgs); + } + + if ((p8Info = p_scossl_mlkem_key_to_p8info(keyCtx)) == NULL) + { + goto cleanup; + } + + if (ctx->desc->outFormat == SCOSSL_ENCODE_PEM) + { + encodeSuccess = PEM_write_bio_PKCS8_PRIV_KEY_INFO(out, p8Info); + } + else + { + encodeSuccess = i2d_PKCS8_PRIV_KEY_INFO_bio(out, p8Info); + } + + if (!encodeSuccess) + { + ERR_raise(ERR_LIB_PROV, ASN1_R_ENCODE_ERROR); + goto cleanup; + } + + ret = SCOSSL_SUCCESS; + +cleanup: + PKCS8_PRIV_KEY_INFO_free(p8Info); + + return ret; +} + +static SCOSSL_STATUS p_scossl_mlkem_to_SubjectPublicKeyInfo(ossl_unused SCOSSL_ENCODE_CTX *ctx, _Inout_ BIO *out, + _In_ const SCOSSL_MLKEM_KEY_CTX *keyCtx, + ossl_unused int selection, + ossl_unused OSSL_PASSPHRASE_CALLBACK *passphraseCb, ossl_unused void *passphraseCbArgs) +{ + int encodeSuccess; + X509_PUBKEY *pubKey = NULL; + SCOSSL_STATUS ret = SCOSSL_FAILURE; + + if ((pubKey = p_scossl_mlkem_key_to_pubkey(keyCtx)) == NULL) + { + goto cleanup; + } + + if (ctx->desc->outFormat == SCOSSL_ENCODE_PEM) + { + encodeSuccess = PEM_write_bio_X509_PUBKEY(out, pubKey); + } + else + { + encodeSuccess = i2d_X509_PUBKEY_bio(out, pubKey); + } + + if (!encodeSuccess) + { + ERR_raise(ERR_LIB_PROV, ASN1_R_ENCODE_ERROR); + goto cleanup; + } + + ret = SCOSSL_SUCCESS; + +cleanup: + X509_PUBKEY_free(pubKey); + + return ret; +} + +static SCOSSL_STATUS p_scossl_mlkem_to_text(ossl_unused SCOSSL_ENCODE_CTX *ctx, _Inout_ BIO *out, + _In_ const SCOSSL_MLKEM_KEY_CTX *keyCtx, + int selection, + ossl_unused OSSL_PASSPHRASE_CALLBACK *passphraseCb, ossl_unused void *passphraseCbArgs) +{ + BOOL printPrivateSeed = FALSE; + BOOL printDecapsulationKey = FALSE; + BOOL printEncapsulationKey = FALSE; + PBYTE pbKey = NULL; + SIZE_T cbKey = 0; + SCOSSL_STATUS ret = SCOSSL_FAILURE; + + if (keyCtx->key == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + goto cleanup; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + { + if (keyCtx->format == SYMCRYPT_MLKEMKEY_FORMAT_ENCAPSULATION_KEY) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY); + goto cleanup; + } + + printDecapsulationKey = TRUE; + printEncapsulationKey = TRUE; + } + else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + { + printEncapsulationKey = TRUE; + } + + if (printDecapsulationKey) + { + // Try to get the private seed. Otherwise this key was encoded using the whole decapsulation key. + if (p_scossl_mlkem_keymgmt_get_encoded_key(keyCtx, SYMCRYPT_MLKEMKEY_FORMAT_PRIVATE_SEED, &pbKey, &cbKey)) + { + if (BIO_printf(out, "MLKEM Decapsulation-Key (512 bit private seed encoding):\n") <= 0) + { + goto cleanup; + } + + if (BIO_printf(out, "private-seed") <= 0 || + p_scossl_encode_write_key_bytes(pbKey, cbKey, out) != SCOSSL_SUCCESS) + { + goto cleanup; + } + + printPrivateSeed = TRUE; + } + + OPENSSL_secure_clear_free(pbKey, cbKey); + if (!p_scossl_mlkem_keymgmt_get_encoded_key(keyCtx, SYMCRYPT_MLKEMKEY_FORMAT_DECAPSULATION_KEY, &pbKey, &cbKey)) + { + goto cleanup; + } + + if (!printPrivateSeed && + BIO_printf(out, "MLKEM Decapsulation-Key (%ld bit decapsulation key encoding):", cbKey * 8) <= 0) + { + goto cleanup; + } + + if (BIO_printf(out, "decapsulation-key") <= 0 || + p_scossl_encode_write_key_bytes(pbKey, cbKey, out) != SCOSSL_SUCCESS) + { + goto cleanup; + } + } + + if (printEncapsulationKey) + { + OPENSSL_secure_clear_free(pbKey, cbKey); + if (!p_scossl_mlkem_keymgmt_get_encoded_key(keyCtx, SYMCRYPT_MLKEMKEY_FORMAT_ENCAPSULATION_KEY, &pbKey, &cbKey)) + { + goto cleanup; + } + + if (!printDecapsulationKey && // Implies no private seed + BIO_printf(out, "MLKEM Encapsulation-Key (%ld bit):", cbKey * 8) <= 0) + { + goto cleanup; + } + + if (BIO_printf(out, "encapsulation-key") <= 0 || + p_scossl_encode_write_key_bytes(pbKey, cbKey, out) != SCOSSL_SUCCESS) + { + goto cleanup; + } + } + + if (BIO_printf(out, "PARAMETER SET: %s\n", keyCtx->groupInfo->groupName) <= 0) + { + goto cleanup; + } + + ret = SCOSSL_SUCCESS; + +cleanup: + OPENSSL_secure_clear_free(pbKey, cbKey); + + return ret; +} + +static SCOSSL_MLKEM_KEY_CTX *p_scossl_mlkem_encoder_import_object(_In_ SCOSSL_ENCODE_CTX *ctx, + int selection, _In_ const OSSL_PARAM params[]) +{ + SCOSSL_MLKEM_KEY_CTX *keyCtx = p_scossl_mlkem_keymgmt_new_ctx(ctx->provctx); + + if (keyCtx != NULL) + { + p_scossl_mlkem_keymgmt_import(keyCtx, selection, params); + } + + return keyCtx; +} + +#define MAKE_MLKEM_ASN1_ENCODER(encoderType) \ + static SCOSSL_ENCODE_KEYTYPE_DESC p_scossl_mlkem_##encoderType##_der_desc = { \ + select_##encoderType, \ + SCOSSL_ENCODE_DER, \ + (PSCOSSL_ENCODE_INTERNAL_FN)p_scossl_mlkem_to_##encoderType}; \ + \ + static SCOSSL_ENCODE_KEYTYPE_DESC p_scossl_mlkem_##encoderType##_pem_desc = { \ + select_##encoderType, \ + SCOSSL_ENCODE_PEM, \ + (PSCOSSL_ENCODE_INTERNAL_FN)p_scossl_mlkem_to_##encoderType}; \ + \ + static SCOSSL_ENCODE_CTX *p_scossl_mlkem_to_##encoderType##_der_newctx(_In_ SCOSSL_PROVCTX *provctx) \ + { \ + return p_scossl_encode_newctx(provctx, &p_scossl_mlkem_##encoderType##_der_desc); \ + } \ + \ + static SCOSSL_ENCODE_CTX *p_scossl_mlkem_to_##encoderType##_pem_newctx(_In_ SCOSSL_PROVCTX *provctx) \ + { \ + return p_scossl_encode_newctx(provctx, &p_scossl_mlkem_##encoderType##_pem_desc); \ + } \ + \ + static BOOL p_scossl_der_to_mlkem_##encoderType##_does_selection(ossl_unused void *provctx, int selection) \ + { \ + return p_scossl_encode_does_selection(&p_scossl_mlkem_##encoderType##_der_desc, selection); \ + } \ + \ + const OSSL_DISPATCH p_scossl_mlkem_to_##encoderType##_der_functions[] = { \ + {OSSL_FUNC_ENCODER_NEWCTX, (void (*)(void))p_scossl_mlkem_to_##encoderType##_der_newctx}, \ + {OSSL_FUNC_ENCODER_FREECTX, (void (*)(void))p_scossl_encode_freectx}, \ + {OSSL_FUNC_ENCODER_SET_CTX_PARAMS, (void (*)(void))p_scossl_encode_set_ctx_params}, \ + {OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS, (void (*)(void))p_scossl_encode_settable_ctx_params}, \ + {OSSL_FUNC_ENCODER_DOES_SELECTION, (void (*)(void))p_scossl_der_to_mlkem_##encoderType##_does_selection}, \ + {OSSL_FUNC_ENCODER_ENCODE, (void (*)(void))p_scossl_encode}, \ + {OSSL_FUNC_ENCODER_IMPORT_OBJECT, (void (*)(void))p_scossl_mlkem_encoder_import_object}, \ + {OSSL_FUNC_ENCODER_FREE_OBJECT, (void (*)(void))p_scossl_mlkem_keymgmt_free_key_ctx}, \ + {0, NULL}}; \ + \ + const OSSL_DISPATCH p_scossl_mlkem_to_##encoderType##_pem_functions[] = { \ + {OSSL_FUNC_ENCODER_NEWCTX, (void (*)(void))p_scossl_mlkem_to_##encoderType##_pem_newctx}, \ + {OSSL_FUNC_ENCODER_FREECTX, (void (*)(void))p_scossl_encode_freectx}, \ + {OSSL_FUNC_ENCODER_SET_CTX_PARAMS, (void (*)(void))p_scossl_encode_set_ctx_params}, \ + {OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS, (void (*)(void))p_scossl_encode_settable_ctx_params}, \ + {OSSL_FUNC_ENCODER_DOES_SELECTION, (void (*)(void))p_scossl_der_to_mlkem_##encoderType##_does_selection}, \ + {OSSL_FUNC_ENCODER_ENCODE, (void (*)(void))p_scossl_encode}, \ + {OSSL_FUNC_ENCODER_IMPORT_OBJECT, (void (*)(void))p_scossl_mlkem_encoder_import_object}, \ + {OSSL_FUNC_ENCODER_FREE_OBJECT, (void (*)(void))p_scossl_mlkem_keymgmt_free_key_ctx}, \ + {0, NULL}}; + +MAKE_MLKEM_ASN1_ENCODER(PrivateKeyInfo) +MAKE_MLKEM_ASN1_ENCODER(EncryptedPrivateKeyInfo) +MAKE_MLKEM_ASN1_ENCODER(SubjectPublicKeyInfo) + +static SCOSSL_ENCODE_KEYTYPE_DESC p_scossl_mlkem_text_desc = { + 0, + SCOSSL_ENCODE_TEXT, + (PSCOSSL_ENCODE_INTERNAL_FN)p_scossl_mlkem_to_text}; + +static SCOSSL_ENCODE_CTX *p_scossl_mlkem_to_text_newctx(_In_ SCOSSL_PROVCTX *provctx) +{ + return p_scossl_encode_newctx(provctx, &p_scossl_mlkem_text_desc); +} + +const OSSL_DISPATCH p_scossl_mlkem_to_text_functions[] = { + {OSSL_FUNC_ENCODER_NEWCTX, (void (*)(void))p_scossl_mlkem_to_text_newctx}, + {OSSL_FUNC_ENCODER_FREECTX, (void (*)(void))p_scossl_encode_freectx}, + {OSSL_FUNC_ENCODER_ENCODE, (void (*)(void))p_scossl_encode}, + {OSSL_FUNC_ENCODER_IMPORT_OBJECT, (void (*)(void))p_scossl_mlkem_encoder_import_object}, + {OSSL_FUNC_ENCODER_FREE_OBJECT, (void (*)(void))p_scossl_mlkem_keymgmt_free_key_ctx}, + {0, NULL}}; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/SymCryptProvider/src/kem/p_scossl_mlkem.c b/SymCryptProvider/src/kem/p_scossl_mlkem.c new file mode 100644 index 0000000..616bf30 --- /dev/null +++ b/SymCryptProvider/src/kem/p_scossl_mlkem.c @@ -0,0 +1,524 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#include "scossl_provider.h" +#include "p_scossl_base.h" +#include "p_scossl_mlkem.h" +#include "keyexch/p_scossl_ecdh.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SYMCRYPT_MLKEM_SECRET_LENGTH 32 + +static SCOSSL_MLKEM_GROUP_INFO p_scossl_mlkem_groups[] = { + {NID_undef, SCOSSL_OID_MLKEM512, SCOSSL_SN_MLKEM512, NULL, SYMCRYPT_MLKEM_PARAMS_MLKEM512}, + {NID_undef, SCOSSL_OID_MLKEM768, SCOSSL_SN_MLKEM768, NULL, SYMCRYPT_MLKEM_PARAMS_MLKEM768}, + {NID_undef, SCOSSL_OID_MLKEM1024, SCOSSL_SN_MLKEM1024, NULL, SYMCRYPT_MLKEM_PARAMS_MLKEM1024}, + {NID_undef, SCOSSL_OID_P256_MLKEM768, SCOSSL_SN_P256_MLKEM768, SN_X9_62_prime256v1, SYMCRYPT_MLKEM_PARAMS_MLKEM768}, + {NID_undef, SCOSSL_OID_X25519_MLKEM768, SCOSSL_SN_X25519_MLKEM768, SN_X25519, SYMCRYPT_MLKEM_PARAMS_MLKEM768}, + {NID_undef, SCOSSL_OID_P384_MLKEM1024, SCOSSL_SN_P384_MLKEM1024, SN_secp384r1, SYMCRYPT_MLKEM_PARAMS_MLKEM1024}}; + +typedef struct +{ + // Unused by MLKEM, but forwarded to the classic key exchange + SCOSSL_PROVCTX *provCtx; + + int operation; + SCOSSL_MLKEM_KEY_CTX *keyCtx; + + SCOSSL_ECDH_CTX *classicKeyexchCtx; +} SCOSSL_MLKEM_CTX; + +static const OSSL_PARAM p_scossl_mlkem_param_types[] = { + OSSL_PARAM_END}; + +/* Context management */ +static SCOSSL_MLKEM_CTX *p_scossl_mlkem_newctx(_In_ SCOSSL_PROVCTX *provctx) +{ + SCOSSL_MLKEM_CTX *ctx = OPENSSL_zalloc(sizeof(SCOSSL_MLKEM_CTX)); + + if (ctx != NULL) + { + ctx->provCtx = provctx; + } + + return ctx; +} + +static void p_scossl_mlkem_freectx(_Inout_ SCOSSL_MLKEM_CTX *ctx) +{ + if (ctx == NULL) + return; + + p_scossl_ecdh_freectx(ctx->classicKeyexchCtx); + OPENSSL_free(ctx); +} + +static SCOSSL_MLKEM_CTX *p_scossl_mlkem_dupctx(_In_ SCOSSL_MLKEM_CTX *ctx) +{ + SCOSSL_MLKEM_CTX *copyCtx = OPENSSL_malloc(sizeof(SCOSSL_MLKEM_CTX)); + + if (copyCtx != NULL) + { + copyCtx->keyCtx = ctx->keyCtx; + copyCtx->operation = ctx->operation; + copyCtx->provCtx = ctx->provCtx; + + if (ctx->classicKeyexchCtx != NULL) + { + copyCtx->classicKeyexchCtx = NULL; + } + else if ((copyCtx->classicKeyexchCtx = p_scossl_ecdh_dupctx(ctx->classicKeyexchCtx)) == NULL) + { + OPENSSL_free(copyCtx); + copyCtx = NULL; + } + } + + return copyCtx; +} + +static SCOSSL_STATUS p_scossl_mlkem_classic_keyexch_init(_Inout_ SCOSSL_MLKEM_CTX *ctx, _In_ SCOSSL_ECC_KEY_CTX *classicKeyCtx) +{ + if (ctx->classicKeyexchCtx == NULL && + (ctx->classicKeyexchCtx = p_scossl_ecdh_newctx(ctx->provCtx)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + return SCOSSL_FAILURE; + } + + if (p_scossl_ecdh_init(ctx->classicKeyexchCtx, classicKeyCtx, NULL) != SCOSSL_SUCCESS) + { + return SCOSSL_FAILURE; + } + + return SCOSSL_SUCCESS; +} + +static SCOSSL_STATUS p_scossl_mlkem_init(_Inout_ SCOSSL_MLKEM_CTX *ctx, _In_ SCOSSL_MLKEM_KEY_CTX *keyCtx, + int operation) +{ + if (ctx == NULL || keyCtx == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return SCOSSL_FAILURE; + } + + if (keyCtx->key == NULL || + keyCtx->groupInfo == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return SCOSSL_FAILURE; + } + + ctx->keyCtx = keyCtx; + ctx->operation = operation; + + return SCOSSL_SUCCESS; +} + +// +// Encapsulation +// + +// We don't initialize the classic key context for hybrid here. +// ctx->keyCtx->classicKeyCtx contains the peer key. Our ephemeral key +// is generated during encapsulation. +static SCOSSL_STATUS p_scossl_mlkem_encapsulate_init(_Inout_ SCOSSL_MLKEM_CTX *ctx, _In_ SCOSSL_MLKEM_KEY_CTX *keyCtx, + ossl_unused const OSSL_PARAM params[]) +{ + return p_scossl_mlkem_init(ctx, keyCtx, EVP_PKEY_OP_ENCAPSULATE); +} + +// Performs ML-KEM encapsulation using the previously initialized context. If +// this is a hybrid group, then hybrid encapsulation is performed. +// ctx->keyCtx->classicKeyCtx is used as the peer key, and our ephemeral +// ECDH key is generated as to derive the shared ECDH secret. The concatenated +// order of classic and ML-KEM data depends on the classic group. +// +// - secret +// X25519: MLKEM shared secret || ECDH shared secret +// P-256/P-384: ECDH shared secret || MLKEM shared secret +// - out +// X25519: MLKEM ciphertext || Ephemeral ECDH public key +// P-256/P-384: Ephemeral ECDH public key || MLKEM ciphertext +static SCOSSL_STATUS p_scossl_mlkem_encapsulate(_In_ SCOSSL_MLKEM_CTX *ctx, + _Out_writes_bytes_opt_(*outlen) unsigned char *out, _Out_ size_t *outlen, + _Out_writes_bytes_(*secretlen) unsigned char *secret, _Out_ size_t *secretlen) +{ + PBYTE pbMlkemCipherText = NULL; + PBYTE pbClassicKey = NULL; + PBYTE pbMlkemSecret = NULL; + PBYTE pbClassicSecret = NULL; + SIZE_T cbClassicKey = 0; + SIZE_T cbMlkemCiphertext = 0; + SIZE_T cbClassicSecret = 0; + SCOSSL_ECC_KEY_CTX *classicKeyCtxPeer = NULL; + SCOSSL_ECC_KEY_CTX *classicKeyCtxPrivate = NULL; + const SCOSSL_MLKEM_GROUP_INFO *groupInfo; + SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR; + SCOSSL_STATUS ret = SCOSSL_FAILURE; + + if (ctx->keyCtx == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + goto cleanup; + } + + if (ctx->operation != EVP_PKEY_OP_ENCAPSULATE) + { + ERR_raise(ERR_LIB_PROV, ERR_R_OPERATION_FAIL); + goto cleanup; + } + + groupInfo = ctx->keyCtx->groupInfo; + + if (groupInfo->classicGroupName != NULL) + { + if ((classicKeyCtxPeer = ctx->keyCtx->classicKeyCtx) == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + goto cleanup; + } + + cbClassicKey = p_scossl_ecc_get_encoded_key_size(classicKeyCtxPeer, OSSL_KEYMGMT_SELECT_PUBLIC_KEY); + + if ((cbClassicSecret = p_scossl_ecc_get_max_size(classicKeyCtxPeer, TRUE)) == 0) + { + goto cleanup; + } + } + + scError = SymCryptMlKemSizeofCiphertextFromParams(groupInfo->mlkemParams, &cbMlkemCiphertext); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if (out != NULL) + { + if ((outlen != NULL && *outlen < cbClassicKey + cbMlkemCiphertext) || + (secretlen != NULL && *secretlen < cbClassicSecret + SYMCRYPT_MLKEM_SECRET_LENGTH)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + goto cleanup; + } + + if (groupInfo->classicGroupName != NULL) + { + if (classicKeyCtxPeer->isX25519) + { + pbMlkemCipherText = out; + pbClassicKey = out + cbMlkemCiphertext; + pbMlkemSecret = secret; + pbClassicSecret = secret + SYMCRYPT_MLKEM_SECRET_LENGTH; + } + else + { + pbClassicKey = out; + pbMlkemCipherText = out + cbClassicKey; + pbClassicSecret = secret; + pbMlkemSecret = secret + cbClassicSecret; + } + + // Generate ephemeral ECDH key + if ((classicKeyCtxPrivate = p_scossl_ecc_new_ctx(ctx->provCtx)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + if (p_scossl_ecc_set_group(classicKeyCtxPrivate, groupInfo->classicGroupName) != SCOSSL_SUCCESS || + p_scossl_ecc_gen(classicKeyCtxPrivate) != SCOSSL_SUCCESS) + { + goto cleanup; + } + + // Write encoded public key bytes + if (p_scossl_ecc_get_encoded_key(classicKeyCtxPrivate, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, &pbClassicKey, &cbClassicKey) != SCOSSL_SUCCESS) + { + goto cleanup; + } + + // Derive ECDH secret + if (p_scossl_mlkem_classic_keyexch_init(ctx, classicKeyCtxPrivate) != SCOSSL_SUCCESS || + p_scossl_ecdh_set_peer(ctx->classicKeyexchCtx, classicKeyCtxPeer) != SCOSSL_SUCCESS || + p_scossl_ecdh_derive(ctx->classicKeyexchCtx, pbClassicSecret, &cbClassicSecret, cbClassicSecret) != SCOSSL_SUCCESS) + { + goto cleanup; + } + } + else + { + pbMlkemCipherText = out; + pbMlkemSecret = secret; + } + + scError = SymCryptMlKemEncapsulate(ctx->keyCtx->key, pbMlkemSecret, SYMCRYPT_MLKEM_SECRET_LENGTH, pbMlkemCipherText, cbMlkemCiphertext); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + } + else if (outlen == NULL && secretlen == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + goto cleanup; + } + + if (outlen != NULL) + { + *outlen = cbClassicKey + cbMlkemCiphertext; + } + + if (secretlen != NULL) + { + *secretlen = cbClassicSecret + SYMCRYPT_MLKEM_SECRET_LENGTH; + } + + ret = SCOSSL_SUCCESS; + +cleanup: + p_scossl_ecc_free_ctx(classicKeyCtxPrivate); + + return ret; +} + +// +// Decapsulation +// + +// Unlike encapsulation, we initialize the classic key context for hybrid here, +// since ctx->keyCtx->classicKeyCtx contains our private key. The peer key is +// extracted from the public data passed to decapsulate. +static SCOSSL_STATUS p_scossl_mlkem_decapsulate_init(_Inout_ SCOSSL_MLKEM_CTX *ctx, _In_ SCOSSL_MLKEM_KEY_CTX *keyCtx, + ossl_unused const OSSL_PARAM params[]) +{ + return p_scossl_mlkem_init(ctx, keyCtx, EVP_PKEY_OP_DECAPSULATE) && + (keyCtx->classicKeyCtx == NULL || p_scossl_mlkem_classic_keyexch_init(ctx, ctx->keyCtx->classicKeyCtx)); +} + +// Performs ML-KEM decapsulation using the previously initialized context. If +// this is a hybrid group, then hybrid decapsulation is performed. +// ctx->keyCtx->classicKeyCtx is used as our key, and the peer key is +// extracted from the beginning of 'in'. The concatenated +// order of classic and ML-KEM data depends on the classic group. +// +// - out +// X25519: MLKEM shared secret || ECDH shared secret +// P-256/P-384: ECDH shared secret || MLKEM shared secret +static SCOSSL_STATUS p_scossl_mlkem_decapsulate(_In_ SCOSSL_MLKEM_CTX *ctx, + _Out_writes_bytes_opt_(*outlen) unsigned char *out, _Out_ size_t *outlen, + _In_reads_bytes_(inlen) const unsigned char *in, size_t inlen) +{ + PCBYTE pbMlkemCipherText = NULL; + PCBYTE pbClassicKey = NULL; + PBYTE pbMlkemSecret = NULL; + PBYTE pbClassicSecret = NULL; + SIZE_T cbClassicKey = 0; + SIZE_T cbMlkemCiphertext = 0; + SIZE_T cbClassicSecret = 0; + SCOSSL_ECC_KEY_CTX *classicKeyCtxPeer = NULL; + SCOSSL_ECC_KEY_CTX *classicKeyCtxPrivate = NULL; + const SCOSSL_MLKEM_GROUP_INFO *groupInfo; + SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR; + SCOSSL_STATUS ret = SCOSSL_FAILURE; + + if (ctx->keyCtx == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + goto cleanup; + } + + if (ctx->operation != EVP_PKEY_OP_DECAPSULATE) + { + ERR_raise(ERR_LIB_PROV, ERR_R_OPERATION_FAIL); + goto cleanup; + } + + groupInfo = ctx->keyCtx->groupInfo; + + if (groupInfo->classicGroupName != NULL) + { + if ((classicKeyCtxPrivate = ctx->keyCtx->classicKeyCtx) == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + goto cleanup; + } + + // Get key size + cbClassicKey = p_scossl_ecc_get_encoded_key_size(classicKeyCtxPrivate, OSSL_KEYMGMT_SELECT_PUBLIC_KEY); + + // Get secret size + if ((cbClassicSecret = p_scossl_ecc_get_max_size(classicKeyCtxPrivate, TRUE)) == 0) + { + goto cleanup; + } + } + + scError = SymCryptMlKemSizeofCiphertextFromParams(groupInfo->mlkemParams, &cbMlkemCiphertext); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if (inlen != cbMlkemCiphertext + cbClassicKey) + { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH); + goto cleanup; + } + + if (out != NULL) + { + if (outlen != NULL && *outlen < SYMCRYPT_MLKEM_SECRET_LENGTH + cbClassicSecret) + { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + goto cleanup; + } + + if (groupInfo->classicGroupName != NULL) + { + if (classicKeyCtxPrivate->isX25519) + { + pbMlkemCipherText = in; + pbClassicKey = in + cbMlkemCiphertext; + pbMlkemSecret = out; + pbClassicSecret = out + SYMCRYPT_MLKEM_SECRET_LENGTH; + } + else + { + pbClassicKey = in; + pbMlkemCipherText = in + cbClassicKey; + pbClassicSecret = out; + pbMlkemSecret = out + cbClassicSecret; + } + + // Extract ECDH public key from in + if ((classicKeyCtxPeer = p_scossl_ecc_new_ctx(ctx->provCtx)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + if (p_scossl_ecc_set_group(classicKeyCtxPeer, groupInfo->classicGroupName) != SCOSSL_SUCCESS || + p_scossl_ecc_set_encoded_key(classicKeyCtxPeer, pbClassicKey, cbClassicKey, NULL, 0) != SCOSSL_SUCCESS) + { + goto cleanup; + } + + // Derive shared ECDH secret + if (p_scossl_ecdh_set_peer(ctx->classicKeyexchCtx, classicKeyCtxPeer) != SCOSSL_SUCCESS || + p_scossl_ecdh_derive(ctx->classicKeyexchCtx, pbClassicSecret, &cbClassicSecret, cbClassicSecret) != SCOSSL_SUCCESS) + { + goto cleanup; + } + } + else + { + pbMlkemCipherText = in; + pbMlkemSecret = out; + } + + scError = SymCryptMlKemDecapsulate(ctx->keyCtx->key, pbMlkemCipherText, cbMlkemCiphertext, pbMlkemSecret, SYMCRYPT_MLKEM_SECRET_LENGTH); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + } + else if (outlen == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + goto cleanup; + } + + if (outlen != NULL) + { + *outlen = SYMCRYPT_MLKEM_SECRET_LENGTH + cbClassicSecret; + } + + ret = SCOSSL_SUCCESS; + +cleanup: + p_scossl_ecc_free_ctx(classicKeyCtxPeer); + + return ret; +} + +// +// Parameters +// +static const OSSL_PARAM *p_scossl_mlkem_ctx_param_types(ossl_unused void *ctx, ossl_unused void *provctx) +{ + return p_scossl_mlkem_param_types; +} + +static SCOSSL_STATUS p_scossl_mlkem_set_ctx_params(ossl_unused void *ctx, ossl_unused const OSSL_PARAM params[]) +{ + return SCOSSL_SUCCESS; +} + +static SCOSSL_STATUS p_scossl_mlkem_get_ctx_params(ossl_unused void *ctx, ossl_unused OSSL_PARAM params[]) +{ + return SCOSSL_SUCCESS; +} + +const OSSL_DISPATCH p_scossl_mlkem_functions[] = { + {OSSL_FUNC_KEM_NEWCTX, (void (*)(void))p_scossl_mlkem_newctx}, + {OSSL_FUNC_KEM_FREECTX, (void (*)(void))p_scossl_mlkem_freectx}, + {OSSL_FUNC_KEM_DUPCTX, (void (*)(void))p_scossl_mlkem_dupctx}, + {OSSL_FUNC_KEM_ENCAPSULATE_INIT, (void (*)(void))p_scossl_mlkem_encapsulate_init}, + {OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))p_scossl_mlkem_encapsulate}, + {OSSL_FUNC_KEM_DECAPSULATE_INIT, (void (*)(void))p_scossl_mlkem_decapsulate_init}, + {OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))p_scossl_mlkem_decapsulate}, + {OSSL_FUNC_KEM_SET_CTX_PARAMS, (void (*)(void))p_scossl_mlkem_set_ctx_params}, + {OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (void (*)(void))p_scossl_mlkem_ctx_param_types}, + {OSSL_FUNC_KEM_GET_CTX_PARAMS, (void (*)(void))p_scossl_mlkem_get_ctx_params}, + {OSSL_FUNC_KEM_GETTABLE_CTX_PARAMS, (void (*)(void))p_scossl_mlkem_ctx_param_types}, + {0, NULL}}; + +_Use_decl_annotations_ +SCOSSL_MLKEM_GROUP_INFO *p_scossl_mlkem_get_group_info_by_nid(int nid) +{ + for (SIZE_T i = 0; i < sizeof(p_scossl_mlkem_groups) / sizeof(SCOSSL_MLKEM_GROUP_INFO); i++) + { + if (p_scossl_mlkem_groups[i].nid == nid) + { + return &p_scossl_mlkem_groups[i]; + } + } + + return NULL; +} + +_Use_decl_annotations_ +SCOSSL_MLKEM_GROUP_INFO *p_scossl_mlkem_get_group_info(_In_ const char *groupName) +{ + return p_scossl_mlkem_get_group_info_by_nid(OBJ_sn2nid(groupName)); +} + +SCOSSL_STATUS p_scossl_mlkem_register_algorithms() +{ + 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); + if (p_scossl_mlkem_groups[i].nid == NID_undef) + { + return SCOSSL_FAILURE; + } + } + + return SCOSSL_SUCCESS; +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/SymCryptProvider/src/kem/p_scossl_mlkem.h b/SymCryptProvider/src/kem/p_scossl_mlkem.h new file mode 100644 index 0000000..3b6dcbb --- /dev/null +++ b/SymCryptProvider/src/kem/p_scossl_mlkem.h @@ -0,0 +1,36 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#include "p_scossl_base.h" +#include "p_scossl_ecc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int nid; + const char *oid; + const char *groupName; + const char *classicGroupName; + SYMCRYPT_MLKEM_PARAMS mlkemParams; +} SCOSSL_MLKEM_GROUP_INFO; + +typedef struct { + SCOSSL_PROVCTX *provCtx; + + const SCOSSL_MLKEM_GROUP_INFO *groupInfo; + PSYMCRYPT_MLKEMKEY key; + SYMCRYPT_MLKEMKEY_FORMAT format; + + SCOSSL_ECC_KEY_CTX *classicKeyCtx; +} SCOSSL_MLKEM_KEY_CTX; + +SCOSSL_STATUS p_scossl_mlkem_register_algorithms(); +SCOSSL_MLKEM_GROUP_INFO *p_scossl_mlkem_get_group_info_by_nid(int nid); +SCOSSL_MLKEM_GROUP_INFO *p_scossl_mlkem_get_group_info(_In_ const char *groupName); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/SymCryptProvider/src/keyexch/p_scossl_ecdh.c b/SymCryptProvider/src/keyexch/p_scossl_ecdh.c index e616369..18893c5 100644 --- a/SymCryptProvider/src/keyexch/p_scossl_ecdh.c +++ b/SymCryptProvider/src/keyexch/p_scossl_ecdh.c @@ -2,9 +2,9 @@ // Copyright (c) Microsoft Corporation. Licensed under the MIT license. // -#include "scossl_ecc.h" #include "p_scossl_ecc.h" #include "p_scossl_base.h" +#include "p_scossl_ecdh.h" #include @@ -12,19 +12,13 @@ extern "C" { #endif -typedef struct -{ - OSSL_LIB_CTX *libctx; - SCOSSL_ECC_KEY_CTX *keyCtx; - SCOSSL_ECC_KEY_CTX *peerKeyCtx; -} SCOSSL_ECDH_CTX; - static const OSSL_PARAM p_scossl_ecdh_ctx_param_types[] = { OSSL_PARAM_END}; static SCOSSL_STATUS p_scossl_ecdh_set_ctx_params(_Inout_ SCOSSL_ECDH_CTX *ctx, _In_ const OSSL_PARAM params[]); -static SCOSSL_ECDH_CTX *p_scossl_ecdh_newctx(_In_ SCOSSL_PROVCTX *provctx) +_Use_decl_annotations_ +SCOSSL_ECDH_CTX *p_scossl_ecdh_newctx(SCOSSL_PROVCTX *provctx) { SCOSSL_ECDH_CTX *ctx = OPENSSL_malloc(sizeof(SCOSSL_ECDH_CTX)); if (ctx != NULL) @@ -37,12 +31,14 @@ static SCOSSL_ECDH_CTX *p_scossl_ecdh_newctx(_In_ SCOSSL_PROVCTX *provctx) return ctx; } -static void p_scossl_ecdh_freectx(_In_ SCOSSL_ECDH_CTX *ctx) +_Use_decl_annotations_ +void p_scossl_ecdh_freectx(SCOSSL_ECDH_CTX *ctx) { OPENSSL_free(ctx); } -static SCOSSL_ECDH_CTX *p_scossl_ecdh_dupctx(_In_ SCOSSL_ECDH_CTX *ctx) +_Use_decl_annotations_ +SCOSSL_ECDH_CTX *p_scossl_ecdh_dupctx(SCOSSL_ECDH_CTX *ctx) { SCOSSL_ECDH_CTX *copyCtx = OPENSSL_malloc(sizeof(SCOSSL_ECDH_CTX)); if (copyCtx != NULL) @@ -55,8 +51,9 @@ static SCOSSL_ECDH_CTX *p_scossl_ecdh_dupctx(_In_ SCOSSL_ECDH_CTX *ctx) return copyCtx; } -static SCOSSL_STATUS p_scossl_ecdh_init(_In_ SCOSSL_ECDH_CTX *ctx, _In_ SCOSSL_ECC_KEY_CTX *keyCtx, - ossl_unused const OSSL_PARAM params[]) +_Use_decl_annotations_ +SCOSSL_STATUS p_scossl_ecdh_init(SCOSSL_ECDH_CTX *ctx, SCOSSL_ECC_KEY_CTX *keyCtx, + ossl_unused const OSSL_PARAM params[]) { if (ctx == NULL || keyCtx == NULL) { @@ -69,7 +66,8 @@ static SCOSSL_STATUS p_scossl_ecdh_init(_In_ SCOSSL_ECDH_CTX *ctx, _In_ SCOSSL_E return SCOSSL_SUCCESS; } -static SCOSSL_STATUS p_scossl_ecdh_set_peer(_Inout_ SCOSSL_ECDH_CTX *ctx, _In_ SCOSSL_ECC_KEY_CTX *peerKeyCtx) +_Use_decl_annotations_ +SCOSSL_STATUS p_scossl_ecdh_set_peer(SCOSSL_ECDH_CTX *ctx, SCOSSL_ECC_KEY_CTX *peerKeyCtx) { SCOSSL_STATUS ret = SCOSSL_FAILURE; @@ -93,16 +91,17 @@ static SCOSSL_STATUS p_scossl_ecdh_set_peer(_Inout_ SCOSSL_ECDH_CTX *ctx, _In_ S return ret; } -static SCOSSL_STATUS p_scossl_ecdh_derive(_In_ SCOSSL_ECDH_CTX *ctx, - _Out_writes_bytes_opt_(*secretlen) unsigned char *secret, _Out_ size_t *secretlen, - size_t outlen) +_Use_decl_annotations_ +SCOSSL_STATUS p_scossl_ecdh_derive(SCOSSL_ECDH_CTX *ctx, + unsigned char *secret, size_t *secretlen, + size_t outlen) { PBYTE pbSecret = secret; PBYTE pbSecretBuf = NULL; SIZE_T cbSecretBuf = 0; SCOSSL_STATUS ret = SCOSSL_FAILURE; SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR; - SYMCRYPT_NUMBER_FORMAT numberFormat = ctx->keyCtx->isX25519 ? SYMCRYPT_NUMBER_FORMAT_LSB_FIRST : SYMCRYPT_NUMBER_FORMAT_MSB_FIRST; + SYMCRYPT_NUMBER_FORMAT numberFormat; if (ctx == NULL || secretlen == NULL) { @@ -110,7 +109,8 @@ static SCOSSL_STATUS p_scossl_ecdh_derive(_In_ SCOSSL_ECDH_CTX *ctx, return SCOSSL_FAILURE; } - if (ctx->keyCtx == NULL || ctx->peerKeyCtx == NULL) { + if (ctx->keyCtx == NULL || + (secret != NULL && ctx->peerKeyCtx == NULL)) { ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); return SCOSSL_FAILURE; } @@ -122,6 +122,8 @@ static SCOSSL_STATUS p_scossl_ecdh_derive(_In_ SCOSSL_ECDH_CTX *ctx, return SCOSSL_SUCCESS; } + numberFormat = ctx->keyCtx->isX25519 ? SYMCRYPT_NUMBER_FORMAT_LSB_FIRST : SYMCRYPT_NUMBER_FORMAT_MSB_FIRST; + if (outlen < cbSecretBuf) { if ((pbSecretBuf = OPENSSL_secure_malloc(cbSecretBuf)) == NULL) diff --git a/SymCryptProvider/src/keyexch/p_scossl_ecdh.h b/SymCryptProvider/src/keyexch/p_scossl_ecdh.h new file mode 100644 index 0000000..72c874e --- /dev/null +++ b/SymCryptProvider/src/keyexch/p_scossl_ecdh.h @@ -0,0 +1,33 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#include "scossl_ecc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + OSSL_LIB_CTX *libctx; + SCOSSL_ECC_KEY_CTX *keyCtx; + SCOSSL_ECC_KEY_CTX *peerKeyCtx; +} SCOSSL_ECDH_CTX; + +// ECDH functions are exposed here for use in hybrid key exchange +SCOSSL_ECDH_CTX *p_scossl_ecdh_newctx(_In_ SCOSSL_PROVCTX *provctx); +void p_scossl_ecdh_freectx(_In_ SCOSSL_ECDH_CTX *ctx); +SCOSSL_ECDH_CTX *p_scossl_ecdh_dupctx(_In_ SCOSSL_ECDH_CTX *ctx); + +SCOSSL_STATUS p_scossl_ecdh_init(_In_ SCOSSL_ECDH_CTX *ctx, _In_ SCOSSL_ECC_KEY_CTX *keyCtx, + ossl_unused const OSSL_PARAM params[]); +SCOSSL_STATUS p_scossl_ecdh_set_peer(_Inout_ SCOSSL_ECDH_CTX *ctx, _In_ SCOSSL_ECC_KEY_CTX *peerKeyCtx); +SCOSSL_STATUS p_scossl_ecdh_derive(_In_ SCOSSL_ECDH_CTX *ctx, + _Out_writes_bytes_opt_(*secretlen) unsigned char *secret, _Out_ size_t *secretlen, + size_t outlen); + + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/SymCryptProvider/src/keymgmt/p_scossl_ecc_keymgmt.c b/SymCryptProvider/src/keymgmt/p_scossl_ecc_keymgmt.c index 3a1d0de..ca4feac 100644 --- a/SymCryptProvider/src/keymgmt/p_scossl_ecc_keymgmt.c +++ b/SymCryptProvider/src/keymgmt/p_scossl_ecc_keymgmt.c @@ -4,7 +4,6 @@ #include "scossl_ecc.h" #include "p_scossl_ecc.h" -#include "p_scossl_base.h" #include "p_scossl_ecc_keymgmt.h" #include @@ -79,8 +78,6 @@ static const OSSL_ITEM p_scossl_ecc_keymgmt_conversion_formats[] = { {POINT_CONVERSION_UNCOMPRESSED, OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_UNCOMPRESSED}, {POINT_CONVERSION_HYBRID, OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_HYBRID}}; -static SCOSSL_STATUS p_scossl_ecc_keymgmt_get_private_key(_In_ SCOSSL_ECC_KEY_CTX *keyCtx, - _Out_writes_bytes_(*pcbPrivateKey) PBYTE *ppbPrivateKey, _Out_ SIZE_T *pcbPrivateKey); static SCOSSL_STATUS p_scossl_ecc_keymgmt_get_private_key_bn(_In_ SCOSSL_ECC_KEY_CTX *keyCtx, _Out_ BIGNUM **pbnPrivateKey, _Out_opt_ SIZE_T *pcbPrivateKey); @@ -96,12 +93,9 @@ static point_conversion_form_t p_scossl_ecc_keymgmt_conversion_name_to_id(_In_ c static SCOSSL_ECC_KEY_CTX *p_scossl_ecc_keymgmt_new_ctx(_In_ SCOSSL_PROVCTX *provctx) { - SCOSSL_ECC_KEY_CTX *keyCtx = OPENSSL_zalloc(sizeof(SCOSSL_ECC_KEY_CTX)); + SCOSSL_ECC_KEY_CTX *keyCtx = p_scossl_ecc_new_ctx(provctx); if (keyCtx != NULL) { - keyCtx->libctx = provctx->libctx; - keyCtx->includePublic = 1; - keyCtx->conversionFormat = POINT_CONVERSION_UNCOMPRESSED; #ifdef KEYSINUSE_ENABLED keyCtx->keysinuseLock = CRYPTO_THREAD_lock_new(); #endif @@ -112,7 +106,7 @@ static SCOSSL_ECC_KEY_CTX *p_scossl_ecc_keymgmt_new_ctx(_In_ SCOSSL_PROVCTX *pro static SCOSSL_ECC_KEY_CTX *p_scossl_x25519_keymgmt_new_ctx(_In_ SCOSSL_PROVCTX *provctx) { - SCOSSL_ECC_KEY_CTX *keyCtx = p_scossl_ecc_keymgmt_new_ctx(provctx); + SCOSSL_ECC_KEY_CTX *keyCtx = p_scossl_ecc_new_ctx(provctx); if (keyCtx != NULL) { keyCtx->curve = scossl_ecc_get_x25519_curve(); @@ -125,152 +119,6 @@ static SCOSSL_ECC_KEY_CTX *p_scossl_x25519_keymgmt_new_ctx(_In_ SCOSSL_PROVCTX * return keyCtx; } -void p_scossl_ecc_keymgmt_free_ctx(_In_ SCOSSL_ECC_KEY_CTX *keyCtx) -{ - if (keyCtx == NULL) - return; - if (keyCtx->key != NULL) - { - SymCryptEckeyFree(keyCtx->key); - } -#ifdef KEYSINUSE_ENABLED - p_scossl_ecc_reset_keysinuse(keyCtx); - CRYPTO_THREAD_lock_free(keyCtx->keysinuseLock); -#endif - - OPENSSL_free(keyCtx); -} - -static SCOSSL_ECC_KEY_CTX *p_scossl_ecc_keymgmt_dup_ctx(_In_ const SCOSSL_ECC_KEY_CTX *keyCtx, int selection) -{ - PBYTE pbData = NULL; - PBYTE pbPrivateKey = NULL; - PBYTE pbPublicKey = NULL; - SIZE_T cbData = 0; - SIZE_T cbPublicKey = 0; - SIZE_T cbPrivateKey = 0; - SCOSSL_STATUS success = SCOSSL_FAILURE; - SYMCRYPT_ECPOINT_FORMAT pointFormat = keyCtx->isX25519 ? SYMCRYPT_ECPOINT_FORMAT_X : SYMCRYPT_ECPOINT_FORMAT_XY; - SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR; - - SCOSSL_ECC_KEY_CTX *copyCtx = OPENSSL_malloc(sizeof(SCOSSL_ECC_KEY_CTX)); - - if (copyCtx != NULL) - { -#ifdef KEYSINUSE_ENABLED - copyCtx->keysinuseLock = CRYPTO_THREAD_lock_new(); - - if (keyCtx->keysinuseInfo == NULL || - p_scossl_keysinuse_upref(keyCtx->keysinuseInfo, NULL)) - { - copyCtx->keysinuseInfo = keyCtx->keysinuseInfo; - } -#endif - - copyCtx->isX25519 = keyCtx->isX25519; - copyCtx->libctx = keyCtx->libctx; - copyCtx->modifiedPrivateBits = keyCtx->modifiedPrivateBits; - copyCtx->conversionFormat = keyCtx->conversionFormat; - - if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) - { - copyCtx->curve = keyCtx->curve; - } - else - { - copyCtx->curve = NULL; - } - - if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0 && keyCtx->initialized) - { - if (copyCtx->curve == NULL) - { - ERR_raise(ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET); - goto cleanup; - } - - if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0 && - SymCryptEckeyHasPrivateKey(keyCtx->key)) - { - cbPrivateKey = SymCryptEckeySizeofPrivateKey(keyCtx->key); - } - - if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) - { - cbPublicKey = SymCryptEckeySizeofPublicKey(keyCtx->key, pointFormat); - } - - cbData = cbPrivateKey + cbPublicKey; - if ((pbData = OPENSSL_secure_malloc(cbData)) == NULL) - { - ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); - goto cleanup; - } - - pbPrivateKey = cbPrivateKey != 0 ? pbData : NULL; - pbPublicKey = cbPublicKey != 0 ? pbData + cbPrivateKey : NULL; - - scError = SymCryptEckeyGetValue( - keyCtx->key, - pbPrivateKey, cbPrivateKey, - pbPublicKey, cbPublicKey, - SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, - pointFormat, - 0); - if (scError != SYMCRYPT_NO_ERROR) - { - ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); - goto cleanup; - } - - if ((copyCtx->key = SymCryptEckeyAllocate(keyCtx->curve)) == NULL) - { - ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); - goto cleanup; - } - - // Default ECDH only. If the key is used for ECDSA then we call SymCryptEckeyExtendKeyUsage - scError = SymCryptEckeySetValue( - pbPrivateKey, cbPrivateKey, - pbPublicKey, cbPublicKey, - SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, - pointFormat, - SYMCRYPT_FLAG_ECKEY_ECDH, - copyCtx->key); - if (scError != SYMCRYPT_NO_ERROR) - { - ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); - goto cleanup; - } - - copyCtx->initialized = 1; - copyCtx->includePublic = keyCtx->includePublic; - } - else - { - copyCtx->key = NULL; - copyCtx->initialized = 0; - copyCtx->includePublic = 1; - } - } - - success = SCOSSL_SUCCESS; - -cleanup: - if (pbData != NULL) - { - OPENSSL_secure_clear_free(pbData, cbData); - } - - if (!success) - { - p_scossl_ecc_keymgmt_free_ctx(copyCtx); - copyCtx = NULL; - } - - return copyCtx; -} - // // Key Generation // @@ -417,8 +265,6 @@ static SCOSSL_STATUS p_scossl_ecc_keygen_set_template(_Inout_ SCOSSL_ECC_KEYGEN_ static SCOSSL_ECC_KEY_CTX *p_scossl_ecc_keygen(_In_ SCOSSL_ECC_KEYGEN_CTX *genCtx, ossl_unused OSSL_CALLBACK *cb, ossl_unused void *cbarg) { - SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR; - SCOSSL_ECC_KEY_CTX *keyCtx = OPENSSL_malloc(sizeof(SCOSSL_ECC_KEY_CTX)); if (keyCtx == NULL) { @@ -429,35 +275,13 @@ static SCOSSL_ECC_KEY_CTX *p_scossl_ecc_keygen(_In_ SCOSSL_ECC_KEYGEN_CTX *genCt keyCtx->libctx = genCtx->libctx; keyCtx->curve = genCtx->curve; keyCtx->isX25519 = genCtx->isX25519; -#ifdef KEYSINUSE_ENABLED - keyCtx->isImported = FALSE; - keyCtx->keysinuseLock = CRYPTO_THREAD_lock_new(); - keyCtx->keysinuseInfo = NULL; -#endif keyCtx->conversionFormat = genCtx->conversionFormat; + keyCtx->key = NULL; - keyCtx->key = SymCryptEckeyAllocate(keyCtx->curve); - if (keyCtx->key == NULL) + if (p_scossl_ecc_gen(keyCtx) != SCOSSL_SUCCESS) { - ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); - goto cleanup; - } - - // Default ECDH only. If the key is used for ECDSA then we call SymCryptEckeyExtendKeyUsage - scError = SymCryptEckeySetRandom(SYMCRYPT_FLAG_ECKEY_ECDH, keyCtx->key); - if (scError != SYMCRYPT_NO_ERROR) - { - ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); - goto cleanup; - } - - keyCtx->initialized = TRUE; - -cleanup: - if (!keyCtx->initialized) - { - p_scossl_ecc_keymgmt_free_ctx(keyCtx); - keyCtx = NULL; + p_scossl_ecc_free_ctx(keyCtx); + return NULL; } return keyCtx; @@ -551,28 +375,15 @@ static SCOSSL_STATUS p_scossl_ecc_keymgmt_get_params(_In_ SCOSSL_ECC_KEY_CTX *ke SCOSSL_STATUS ret = SCOSSL_FAILURE; OSSL_PARAM *p; - if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL) + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL && + !OSSL_PARAM_set_uint32(p, p_scossl_ecc_get_max_size(keyCtx, FALSE))) { - if (keyCtx->isX25519) - { - if (!OSSL_PARAM_set_uint32(p, SCOSSL_X25519_MAX_SIZE)) - { - ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); - goto cleanup; - } - } - // We don't know if the key will be used for ECDSA or ECDH so return - // the larger size - else if (keyCtx->curve == NULL || - !OSSL_PARAM_set_uint32(p, scossl_ecdsa_size(keyCtx->curve))) - { - ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); - goto cleanup; - } + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + goto cleanup; } if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL && - (keyCtx->curve == NULL || + (keyCtx->curve == NULL || !OSSL_PARAM_set_int(p, SymCryptEcurveBitsizeofGroupOrder(keyCtx->curve)))) { ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); @@ -580,7 +391,7 @@ static SCOSSL_STATUS p_scossl_ecc_keymgmt_get_params(_In_ SCOSSL_ECC_KEY_CTX *ke } if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL && - (keyCtx->curve == NULL || + (keyCtx->curve == NULL || !OSSL_PARAM_set_int(p, scossl_ecc_get_curve_security_bits(keyCtx->curve)))) { ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); @@ -590,7 +401,7 @@ static SCOSSL_STATUS p_scossl_ecc_keymgmt_get_params(_In_ SCOSSL_ECC_KEY_CTX *ke if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY)) != NULL) { SIZE_T cbEncodedKey; - if (!p_scossl_ecc_get_encoded_public_key(keyCtx, &pbEncodedKey, &cbEncodedKey) || + if (!p_scossl_ecc_get_encoded_key(keyCtx, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, &pbEncodedKey, &cbEncodedKey) || !OSSL_PARAM_set_octet_string(p, pbEncodedKey, cbEncodedKey)) { ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); @@ -620,7 +431,7 @@ static SCOSSL_STATUS p_scossl_ecc_keymgmt_get_params(_In_ SCOSSL_ECC_KEY_CTX *ke if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY)) != NULL) { SIZE_T cbEncodedKey; - if (!p_scossl_ecc_get_encoded_public_key(keyCtx, &pbEncodedKey, &cbEncodedKey) || + if (!p_scossl_ecc_get_encoded_key(keyCtx, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, &pbEncodedKey, &cbEncodedKey) || !OSSL_PARAM_set_octet_string(p, pbEncodedKey, cbEncodedKey)) { ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); @@ -634,7 +445,7 @@ static SCOSSL_STATUS p_scossl_ecc_keymgmt_get_params(_In_ SCOSSL_ECC_KEY_CTX *ke { if (keyCtx->isX25519) { - if (!p_scossl_ecc_keymgmt_get_private_key(keyCtx, &pbPrivateKey, &cbPrivateKey) || + if (!p_scossl_ecc_get_encoded_key(keyCtx, OSSL_KEYMGMT_SELECT_KEYPAIR, &pbPrivateKey, &cbPrivateKey) || !OSSL_PARAM_set_octet_string(p, pbPrivateKey, cbPrivateKey)) { ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); @@ -1024,15 +835,14 @@ static const OSSL_PARAM *p_scossl_ecc_keymgmt_impexp_types(int selection) static SCOSSL_STATUS p_scossl_ecc_keymgmt_import(_Inout_ SCOSSL_ECC_KEY_CTX *keyCtx, int selection, _In_ const OSSL_PARAM params[]) { - SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR; SCOSSL_STATUS ret = SCOSSL_FAILURE; EC_GROUP *ecGroup = NULL; PCSYMCRYPT_ECURVE pCurve; + PCBYTE pbPublicKey = NULL; + SIZE_T cbPublicKey = 0; PBYTE pbPrivateKey = NULL; SIZE_T cbPrivateKey = 0; BIGNUM *bnPrivateKey = NULL; - PBYTE pbPublicKey = NULL; - SIZE_T cbPublicKey = 0; BN_CTX *bnCtx = NULL; EC_POINT *ecPoint = NULL; const OSSL_PARAM *p; @@ -1101,49 +911,13 @@ static SCOSSL_STATUS p_scossl_ecc_keymgmt_import(_Inout_ SCOSSL_ECC_KEY_CTX *key // Keypair if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { - if (keyCtx->key != NULL) + if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY)) != NULL && + !OSSL_PARAM_get_octet_string_ptr(p, (const void **)&pbPublicKey, &cbPublicKey)) { - SymCryptEckeyFree(keyCtx->key); - } - -#ifdef KEYSINUSE_ENABLED - // Reset keysinuse in case new key material is overwriting existing - p_scossl_ecc_reset_keysinuse(keyCtx); -#endif - - if ((keyCtx->key = SymCryptEckeyAllocate(keyCtx->curve)) == NULL) - { - ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); goto cleanup; } - if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY)) != NULL) - { - PCBYTE encodedPoint; - SIZE_T encodedLen; - if (!OSSL_PARAM_get_octet_string_ptr(p, (const void **)&encodedPoint, &encodedLen)) - { - ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); - goto cleanup; - } - - cbPublicKey = SymCryptEckeySizeofPublicKey(keyCtx->key, SYMCRYPT_ECPOINT_FORMAT_XY); - if (((ecPoint = EC_POINT_new(ecGroup)) == NULL) || - ((bnCtx = BN_CTX_new_ex(keyCtx->libctx)) == NULL) || - ((pbPublicKey = OPENSSL_malloc(cbPublicKey)) == NULL)) - { - ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); - goto cleanup; - } - - if (!EC_POINT_oct2point(ecGroup, ecPoint, encodedPoint, encodedLen, bnCtx) || - !scossl_ec_point_to_pubkey(ecPoint, ecGroup, bnCtx, pbPublicKey, cbPublicKey)) - { - ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); - goto cleanup; - } - } - if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY)) != NULL) { if ((bnPrivateKey = BN_secure_new()) == NULL) @@ -1159,7 +933,7 @@ static SCOSSL_STATUS p_scossl_ecc_keymgmt_import(_Inout_ SCOSSL_ECC_KEY_CTX *key goto cleanup; } - cbPrivateKey = SymCryptEckeySizeofPrivateKey(keyCtx->key); + cbPrivateKey = p_scossl_ecc_get_encoded_key_size(keyCtx, OSSL_KEYMGMT_SELECT_PRIVATE_KEY); if ((pbPrivateKey = OPENSSL_secure_malloc(cbPrivateKey)) == NULL) { ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); @@ -1173,20 +947,16 @@ static SCOSSL_STATUS p_scossl_ecc_keymgmt_import(_Inout_ SCOSSL_ECC_KEY_CTX *key } } - scError = SymCryptEckeySetValue( - pbPrivateKey, cbPrivateKey, + ret = p_scossl_ecc_set_encoded_key( + keyCtx, pbPublicKey, cbPublicKey, - SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, - SYMCRYPT_ECPOINT_FORMAT_XY, - SYMCRYPT_FLAG_ECKEY_ECDH, - keyCtx->key); - if (scError != SYMCRYPT_NO_ERROR) + pbPrivateKey, cbPrivateKey); + + if (ret != SCOSSL_SUCCESS) { - ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); goto cleanup; } - keyCtx->initialized = TRUE; #ifdef KEYSINUSE_ENABLED keyCtx->isImported = TRUE; #endif @@ -1194,13 +964,9 @@ static SCOSSL_STATUS p_scossl_ecc_keymgmt_import(_Inout_ SCOSSL_ECC_KEY_CTX *key ret = SCOSSL_SUCCESS; cleanup: - if (pbPrivateKey != NULL) - { - OPENSSL_secure_clear_free(pbPrivateKey, cbPrivateKey); - } - EC_GROUP_free(ecGroup); + OPENSSL_secure_clear_free(pbPrivateKey, cbPrivateKey); BN_clear_free(bnPrivateKey); - OPENSSL_free(pbPublicKey); + EC_GROUP_free(ecGroup); EC_POINT_free(ecPoint); BN_CTX_free(bnCtx); @@ -1255,7 +1021,7 @@ static SCOSSL_STATUS p_scossl_ecc_keymgmt_export(_In_ SCOSSL_ECC_KEY_CTX *keyCtx if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { - if (!p_scossl_ecc_get_encoded_public_key(keyCtx, &pbPublicKey, &cbPublicKey) || + if (!p_scossl_ecc_get_encoded_key(keyCtx, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, &pbPublicKey, &cbPublicKey) || !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pbPublicKey, cbPublicKey)) { ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); @@ -1495,8 +1261,8 @@ static const char *p_scossl_ecc_keymgmt_query_operation_name(int operation_id) const OSSL_DISPATCH p_scossl_ecc_keymgmt_functions[] = { {OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))p_scossl_ecc_keymgmt_new_ctx}, - {OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))p_scossl_ecc_keymgmt_free_ctx}, - {OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))p_scossl_ecc_keymgmt_dup_ctx}, + {OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))p_scossl_ecc_free_ctx}, + {OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))p_scossl_ecc_dup_ctx}, {OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))p_scossl_ecc_keygen_set_params}, {OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (void (*)(void))p_scossl_ecc_keygen_settable_params}, {OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))p_scossl_ecc_keygen_cleanup}, @@ -1519,8 +1285,8 @@ const OSSL_DISPATCH p_scossl_ecc_keymgmt_functions[] = { const OSSL_DISPATCH p_scossl_x25519_keymgmt_functions[] = { {OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))p_scossl_x25519_keymgmt_new_ctx}, - {OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))p_scossl_ecc_keymgmt_free_ctx}, - {OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))p_scossl_ecc_keymgmt_dup_ctx}, + {OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))p_scossl_ecc_free_ctx}, + {OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))p_scossl_ecc_dup_ctx}, {OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))p_scossl_x25519_keygen_set_params}, {OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (void (*)(void))p_scossl_ecc_keygen_settable_params}, {OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))p_scossl_ecc_keygen_cleanup}, @@ -1543,58 +1309,6 @@ const OSSL_DISPATCH p_scossl_x25519_keymgmt_functions[] = { // Helpers // -_Use_decl_annotations_ -static SCOSSL_STATUS p_scossl_ecc_keymgmt_get_private_key(SCOSSL_ECC_KEY_CTX *keyCtx, - PBYTE *ppbPrivateKey, SIZE_T *pcbPrivateKey) -{ - PBYTE pbPrivateKey = NULL; - SIZE_T cbPrivateKey = 0; - SYMCRYPT_NUMBER_FORMAT numFormat = keyCtx->isX25519 ? SYMCRYPT_NUMBER_FORMAT_LSB_FIRST : SYMCRYPT_NUMBER_FORMAT_MSB_FIRST; - SYMCRYPT_ECPOINT_FORMAT pointFormat = keyCtx->isX25519 ? SYMCRYPT_ECPOINT_FORMAT_X : SYMCRYPT_ECPOINT_FORMAT_XY; - SYMCRYPT_ERROR scError; - SCOSSL_STATUS ret = SCOSSL_FAILURE; - - cbPrivateKey = SymCryptEckeySizeofPrivateKey(keyCtx->key); - if ((pbPrivateKey = OPENSSL_secure_malloc(cbPrivateKey)) == NULL) - { - ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); - goto cleanup; - } - - scError = SymCryptEckeyGetValue( - keyCtx->key, - pbPrivateKey, cbPrivateKey, - NULL, 0, - numFormat, - pointFormat, - 0); - - if (scError != SYMCRYPT_NO_ERROR) - { - ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); - goto cleanup; - } - - if (keyCtx->isX25519) - { - pbPrivateKey[0] = (keyCtx->modifiedPrivateBits & 0x07) | (pbPrivateKey[0] & 0xf8); - pbPrivateKey[cbPrivateKey-1] = (keyCtx->modifiedPrivateBits & 0xc0) | (pbPrivateKey[cbPrivateKey-1] & 0x3f); - } - - *ppbPrivateKey = pbPrivateKey; - *pcbPrivateKey = cbPrivateKey; - - ret = SCOSSL_SUCCESS; - -cleanup: - if (!ret) - { - OPENSSL_secure_clear_free(pbPrivateKey, cbPrivateKey); - } - - return ret; -} - // General ECC case exports private key as a BIGNUM, while x25519 exports as an octet string _Use_decl_annotations_ static SCOSSL_STATUS p_scossl_ecc_keymgmt_get_private_key_bn(SCOSSL_ECC_KEY_CTX *keyCtx, @@ -1605,7 +1319,7 @@ static SCOSSL_STATUS p_scossl_ecc_keymgmt_get_private_key_bn(SCOSSL_ECC_KEY_CTX BIGNUM *bnPrivateKey = NULL; SCOSSL_STATUS ret = SCOSSL_FAILURE; - if (!p_scossl_ecc_keymgmt_get_private_key(keyCtx, &pbPrivateKey, &cbPrivateKey)) + if (!p_scossl_ecc_get_encoded_key(keyCtx, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, &pbPrivateKey, &cbPrivateKey)) { goto cleanup; } diff --git a/SymCryptProvider/src/keymgmt/p_scossl_mlkem_keymgmt.c b/SymCryptProvider/src/keymgmt/p_scossl_mlkem_keymgmt.c new file mode 100644 index 0000000..ebdb012 --- /dev/null +++ b/SymCryptProvider/src/keymgmt/p_scossl_mlkem_keymgmt.c @@ -0,0 +1,1069 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#include "scossl_provider.h" +#include "p_scossl_mlkem_keymgmt.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + SCOSSL_PROVCTX *provCtx; + + const SCOSSL_MLKEM_GROUP_INFO *groupInfo; +} SCOSSL_MLKEM_KEYGEN_CTX; + +#define SCOSSL_MLKEM_PKEY_PARAMETER_TYPES \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), \ + +static const OSSL_PARAM p_scossl_mlkem_keygen_settable_param_types[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + OSSL_PARAM_END}; + +static const OSSL_PARAM p_scossl_mlkem_keymgmt_settable_param_types[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_END}; + +static const OSSL_PARAM p_scossl_mlkem_keymgmt_gettable_param_types[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_size_t(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + SCOSSL_MLKEM_PKEY_PARAMETER_TYPES + OSSL_PARAM_END}; + +static const OSSL_PARAM p_scossl_mlkem_param_types[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + OSSL_PARAM_END}; + +static const OSSL_PARAM p_scossl_mlkem_pkey_types[] = { + SCOSSL_MLKEM_PKEY_PARAMETER_TYPES + OSSL_PARAM_END}; + +static const OSSL_PARAM p_scossl_mlkem_all_types[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + SCOSSL_MLKEM_PKEY_PARAMETER_TYPES + OSSL_PARAM_END}; + +static const OSSL_PARAM *p_scossl_mlkem_impexp_types[] = { + NULL, + p_scossl_mlkem_param_types, + p_scossl_mlkem_pkey_types, + p_scossl_mlkem_all_types}; + +static int p_scossl_mlkem_keymgmt_get_security_bits(_In_ const SCOSSL_MLKEM_KEY_CTX *keyCtx); + +SCOSSL_MLKEM_KEY_CTX *p_scossl_mlkem_keymgmt_new_ctx(_In_ SCOSSL_PROVCTX *provCtx) +{ + SCOSSL_MLKEM_KEY_CTX *keyCtx = OPENSSL_zalloc(sizeof(SCOSSL_MLKEM_KEY_CTX)); + + if (keyCtx != NULL) + { + keyCtx->provCtx = provCtx; + } + + return keyCtx; +} + +void p_scossl_mlkem_keymgmt_free_key_ctx(_In_ SCOSSL_MLKEM_KEY_CTX *keyCtx) +{ + if (keyCtx == NULL) + return; + + if (keyCtx->key != NULL) + { + SymCryptMlKemkeyFree(keyCtx->key); + } + + p_scossl_ecc_free_ctx(keyCtx->classicKeyCtx); + OPENSSL_free(keyCtx); +} + +static SCOSSL_MLKEM_KEY_CTX *p_scossl_mlkem_keymgmt_dup_key_ctx(_In_ const SCOSSL_MLKEM_KEY_CTX *keyCtx, int selection) +{ + PBYTE pbKey = NULL; + SIZE_T cbKey = 0; + SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR; + SCOSSL_STATUS status = SCOSSL_FAILURE; + SCOSSL_MLKEM_KEY_CTX *copyCtx = OPENSSL_zalloc(sizeof(SCOSSL_MLKEM_KEY_CTX)); + + if (copyCtx != NULL) + { + copyCtx->provCtx = keyCtx->provCtx; + + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + { + copyCtx->groupInfo = keyCtx->groupInfo; + } + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + { + if (keyCtx->key != NULL) + { + if (copyCtx->groupInfo == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET); + goto cleanup; + } + + scError = SymCryptMlKemSizeofKeyFormatFromParams(keyCtx->groupInfo->mlkemParams, keyCtx->format, &cbKey); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if ((copyCtx->key = SymCryptMlKemkeyAllocate(copyCtx->groupInfo->mlkemParams)) == NULL || + (pbKey = OPENSSL_secure_malloc(cbKey)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + scError = SymCryptMlKemkeyGetValue(keyCtx->key, pbKey, cbKey, keyCtx->format, 0); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + scError = SymCryptMlKemkeySetValue(pbKey, cbKey, keyCtx->format, 0, copyCtx->key); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + copyCtx->format = keyCtx->format; + } + + if (keyCtx->classicKeyCtx != NULL) + { + copyCtx->classicKeyCtx = p_scossl_ecc_dup_ctx(keyCtx->classicKeyCtx, selection); + } + } + } + + status = SCOSSL_SUCCESS; + +cleanup: + if (status != SCOSSL_SUCCESS) + { + p_scossl_mlkem_keymgmt_free_key_ctx(copyCtx); + copyCtx = NULL; + } + + OPENSSL_secure_clear_free(pbKey, cbKey); + + return copyCtx; +} + +static SCOSSL_STATUS p_scossl_mlkem_keygen_set_params(_Inout_ SCOSSL_MLKEM_KEYGEN_CTX *genCtx, _In_ const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME)) != NULL) + { + const char *groupName; + + if (!OSSL_PARAM_get_utf8_string_ptr(p, &groupName)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return SCOSSL_FAILURE; + } + + if ((genCtx->groupInfo = p_scossl_mlkem_get_group_info(groupName)) == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED); + return SCOSSL_FAILURE; + } + } + + return SCOSSL_SUCCESS;; +} + +static const OSSL_PARAM *p_scossl_mlkem_keygen_settable_params(ossl_unused void *genCtx, ossl_unused void *provCtx) +{ + return p_scossl_mlkem_keygen_settable_param_types; +} + +static void p_scossl_mlkem_keygen_cleanup(_Inout_ SCOSSL_MLKEM_KEYGEN_CTX *genCtx) +{ + OPENSSL_free(genCtx); +} + +static SCOSSL_MLKEM_KEYGEN_CTX *p_scossl_mlkem_keygen_init(_In_ SCOSSL_PROVCTX *provCtx, ossl_unused int selection, + _In_ const OSSL_PARAM params[]) +{ + SCOSSL_STATUS status = SCOSSL_FAILURE; + SCOSSL_MLKEM_KEYGEN_CTX *genCtx = OPENSSL_zalloc(sizeof(SCOSSL_MLKEM_KEYGEN_CTX)); + + if (genCtx != NULL) + { + genCtx->provCtx = provCtx; + status = p_scossl_mlkem_keygen_set_params(genCtx, params); + + if (status == SCOSSL_SUCCESS && genCtx->groupInfo == NULL) + { + genCtx->groupInfo = p_scossl_mlkem_get_group_info(SCOSSL_SN_MLKEM768); + } + } + + if (status != SCOSSL_SUCCESS) + { + p_scossl_mlkem_keygen_cleanup(genCtx); + genCtx = NULL; + } + + return genCtx; +} + +static SCOSSL_STATUS p_scossl_mlkem_keygen_set_template(_Inout_ SCOSSL_MLKEM_KEYGEN_CTX *genCtx, _In_ SCOSSL_MLKEM_KEY_CTX *tmplCtx) +{ + if (genCtx == NULL || tmplCtx == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return SCOSSL_FAILURE; + } + + if (tmplCtx->groupInfo == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET); + return SCOSSL_FAILURE; + } + + genCtx->groupInfo = tmplCtx->groupInfo; + + return SCOSSL_SUCCESS; +} + +static SCOSSL_MLKEM_KEY_CTX *p_scossl_mlkem_keygen(_In_ SCOSSL_MLKEM_KEYGEN_CTX *genCtx, ossl_unused OSSL_CALLBACK *cb, ossl_unused void *cbarg) +{ + SCOSSL_MLKEM_KEY_CTX *keyCtx; + SCOSSL_STATUS status = SCOSSL_FAILURE; + SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR; + + if (genCtx == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return SCOSSL_FAILURE; + } + + if ((keyCtx = p_scossl_mlkem_keymgmt_new_ctx(genCtx->provCtx)) == NULL || + (keyCtx->key = SymCryptMlKemkeyAllocate(genCtx->groupInfo->mlkemParams)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + scError = SymCryptMlKemkeyGenerate(keyCtx->key, 0); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if (genCtx->groupInfo->classicGroupName != NULL) + { + if ((keyCtx->classicKeyCtx = p_scossl_ecc_new_ctx(keyCtx->provCtx)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + if (p_scossl_ecc_set_group(keyCtx->classicKeyCtx, genCtx->groupInfo->classicGroupName) != SCOSSL_SUCCESS || + p_scossl_ecc_gen(keyCtx->classicKeyCtx) != SCOSSL_SUCCESS) + { + goto cleanup; + } + } + + keyCtx->provCtx = genCtx->provCtx; + keyCtx->groupInfo = genCtx->groupInfo; + keyCtx->format = SYMCRYPT_MLKEMKEY_FORMAT_PRIVATE_SEED; + + status = SCOSSL_SUCCESS; + +cleanup: + if (status != SCOSSL_SUCCESS) + { + if (keyCtx != NULL) + { + p_scossl_ecc_free_ctx(keyCtx->classicKeyCtx); + } + + p_scossl_mlkem_keymgmt_free_key_ctx(keyCtx); + keyCtx = NULL; + } + + return keyCtx; +} + +static const SCOSSL_MLKEM_KEY_CTX *p_scossl_mlkem_keymgmt_load(const void *reference, size_t reference_size) +{ + SCOSSL_MLKEM_KEY_CTX *keyCtx = NULL; + + if (reference_size == sizeof(keyCtx)) + { + keyCtx = *(SCOSSL_MLKEM_KEY_CTX **)reference; + *(SCOSSL_MLKEM_KEY_CTX **)reference = NULL; + } + + return keyCtx; +} + +static const OSSL_PARAM *p_scossl_mlkem_keymgmt_settable_params(ossl_unused void *provCtx) +{ + return p_scossl_mlkem_keymgmt_settable_param_types; +} + +static SCOSSL_STATUS p_scossl_mlkem_keymgmt_set_params(_In_ SCOSSL_MLKEM_KEY_CTX *keyCtx, _In_ const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY)) != NULL) + { + PCBYTE pbKey; + SIZE_T cbKey; + + if (!OSSL_PARAM_get_octet_string_ptr(p, (const void **)&pbKey, &cbKey)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return SCOSSL_FAILURE; + } + + if (p_scossl_mlkem_keymgmt_set_encoded_key(keyCtx, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, pbKey, cbKey) != SCOSSL_SUCCESS) + { + return SCOSSL_FAILURE; + } + } + + return SCOSSL_SUCCESS; +} + +static const OSSL_PARAM *p_scossl_mlkem_keymgmt_gettable_params(ossl_unused void *provCtx) +{ + return p_scossl_mlkem_keymgmt_gettable_param_types; +} + +static SCOSSL_STATUS p_scossl_mlkem_keymgmt_get_key_params(_In_ SCOSSL_MLKEM_KEY_CTX *keyCtx, _Inout_ OSSL_PARAM params[]) +{ + PBYTE pbKey = NULL; + SIZE_T cbKey = 0; + SCOSSL_STATUS status; + SCOSSL_STATUS ret = SCOSSL_FAILURE; + OSSL_PARAM *paramEncodedKey = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + OSSL_PARAM *paramPubKey = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY); + OSSL_PARAM *paramPrivKey = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY); + + if (keyCtx->key == NULL && + (paramEncodedKey != NULL || + paramPubKey != NULL || + paramPrivKey != NULL)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return SCOSSL_FAILURE; + } + + if (paramEncodedKey != NULL || paramPubKey != NULL) + { + status = p_scossl_mlkem_keymgmt_get_encoded_key( + keyCtx, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + &pbKey, &cbKey); + if (status != SCOSSL_SUCCESS) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if (paramEncodedKey != NULL && + !OSSL_PARAM_set_octet_string(paramEncodedKey, pbKey, cbKey)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + goto cleanup; + } + + if (paramPubKey != NULL && + !OSSL_PARAM_set_octet_string(paramPubKey, pbKey, cbKey)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + goto cleanup; + } + } + + if (paramPrivKey != NULL) + { + OPENSSL_secure_clear_free(pbKey, cbKey); + + if (keyCtx->format != SYMCRYPT_MLKEMKEY_FORMAT_DECAPSULATION_KEY) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY); + goto cleanup; + } + + status = p_scossl_mlkem_keymgmt_get_encoded_key( + keyCtx, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, + &pbKey, &cbKey); + if (status != SCOSSL_SUCCESS) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if (!OSSL_PARAM_set_octet_string(paramPrivKey, pbKey, cbKey)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + goto cleanup; + } + } + + ret = SCOSSL_SUCCESS; + +cleanup: + OPENSSL_secure_clear_free(pbKey, cbKey); + + return ret; +} + +static SCOSSL_STATUS p_scossl_mlkem_keymgmt_get_params(_In_ SCOSSL_MLKEM_KEY_CTX *keyCtx, _Inout_ OSSL_PARAM params[]) +{ + SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR; + OSSL_PARAM *p; + + if (keyCtx->groupInfo == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET); + return SCOSSL_FAILURE; + } + + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL && + !OSSL_PARAM_set_int(p, p_scossl_mlkem_keymgmt_get_security_bits(keyCtx))) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return SCOSSL_FAILURE; + } + + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL) + { + SIZE_T cbMax; + SYMCRYPT_MLKEMKEY_FORMAT format = keyCtx->format; + + // Default to larger size if key data is not set (and therefore format is unknown) + if (format == SYMCRYPT_MLKEMKEY_FORMAT_NULL) + { + format = SYMCRYPT_MLKEMKEY_FORMAT_DECAPSULATION_KEY; + } + + scError = SymCryptMlKemSizeofKeyFormatFromParams(keyCtx->groupInfo->mlkemParams, format, &cbMax); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return SCOSSL_FAILURE; + } + + if (keyCtx->classicKeyCtx != NULL) + { + cbMax += p_scossl_ecc_get_max_size(keyCtx->classicKeyCtx, TRUE); + } + + if (!OSSL_PARAM_set_size_t(p, cbMax)) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return SCOSSL_FAILURE; + } + } + + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_GROUP_NAME)) != NULL && + !OSSL_PARAM_set_utf8_string(p, keyCtx->groupInfo->groupName != NULL ? keyCtx->groupInfo->groupName : "")) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return SCOSSL_FAILURE; + } + + return p_scossl_mlkem_keymgmt_get_key_params(keyCtx, params); +} + +static BOOL p_scossl_mlkem_keymgmt_has(_In_ SCOSSL_MLKEM_KEY_CTX *keyCtx, int selection) +{ + if (keyCtx == NULL) + { + return FALSE; + } + + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0 && + keyCtx->groupInfo == NULL) + { + return FALSE; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0 && + keyCtx->key == NULL) + { + return FALSE; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0 && + keyCtx->format != SYMCRYPT_MLKEMKEY_FORMAT_PRIVATE_SEED && + keyCtx->format != SYMCRYPT_MLKEMKEY_FORMAT_DECAPSULATION_KEY) + { + return FALSE; + } + + return TRUE; +} + +static BOOL p_scossl_mlkem_keymgmt_match(_In_ SCOSSL_MLKEM_KEY_CTX *keyCtx1, _In_ SCOSSL_MLKEM_KEY_CTX *keyCtx2, + int selection) +{ + PBYTE pbKey1 = NULL; + PBYTE pbKey2 = NULL; + SIZE_T cbKey1 = 0; + SIZE_T cbKey2 = 0; + BOOL ret = FALSE; + SCOSSL_STATUS success; + + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0 && + keyCtx1->groupInfo != keyCtx2->groupInfo) + { + goto cleanup; + } + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR)) + { + if (keyCtx1->key != NULL || keyCtx2->key != NULL) + { + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + { + // Both keys must be decapsulation keys to compare + if (keyCtx1->format != keyCtx2->format) + { + goto cleanup; + } + + // Reset pbKeys in case they was used for encapsulation key compare + OPENSSL_secure_clear_free(pbKey1, cbKey1); + OPENSSL_secure_clear_free(pbKey2, cbKey2); + + success = p_scossl_mlkem_keymgmt_get_encoded_key( + keyCtx1, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY, + &pbKey1, &cbKey1); + if (!success) + { + goto cleanup; + } + + success = p_scossl_mlkem_keymgmt_get_encoded_key( + keyCtx2, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY, + &pbKey2, &cbKey2); + if (!success) + { + goto cleanup; + } + + if (cbKey1 != cbKey2 || + memcmp(pbKey1, pbKey2, cbKey1) != 0) + { + goto cleanup; + } + } + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + { + success = p_scossl_mlkem_keymgmt_get_encoded_key( + keyCtx1, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + &pbKey1, &cbKey1); + if (!success) + { + goto cleanup; + } + + success = p_scossl_mlkem_keymgmt_get_encoded_key( + keyCtx2, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + &pbKey2, &cbKey2); + if (!success) + { + goto cleanup; + } + + if (cbKey1 != cbKey2 || + memcmp(pbKey1, pbKey2, cbKey1) != 0) + { + goto cleanup; + } + } + } + } + + ret = TRUE; + +cleanup: + OPENSSL_secure_clear_free(pbKey1, cbKey1); + OPENSSL_secure_clear_free(pbKey2, cbKey2); + + return ret; +} + +// +// Key import/export +// +static const OSSL_PARAM *p_scossl_mlkem_keymgmt_impexp_types(int selection) +{ + int idx = 0; + if ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0) + { + idx += 1; + } + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + { + idx += 2; + } + + return p_scossl_mlkem_impexp_types[idx]; +} + +SCOSSL_STATUS p_scossl_mlkem_keymgmt_import(_Inout_ SCOSSL_MLKEM_KEY_CTX *keyCtx, int selection, _In_ const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + PCBYTE pbKey; + SIZE_T cbKey; + + // Domain parameters are required for import + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0) + { + return SCOSSL_FAILURE; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME)) != NULL) + { + const char *groupName; + + if (!OSSL_PARAM_get_utf8_string_ptr(p, &groupName)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return SCOSSL_FAILURE; + } + + if ((keyCtx->groupInfo = p_scossl_mlkem_get_group_info(groupName)) == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED); + return SCOSSL_FAILURE; + } + + if (keyCtx->classicKeyCtx != NULL && + keyCtx->groupInfo->classicGroupName != NULL && + p_scossl_ecc_set_group(keyCtx->classicKeyCtx, keyCtx->groupInfo->classicGroupName) != SCOSSL_SUCCESS) + { + return SCOSSL_FAILURE; + } + } + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + { + if (keyCtx->key != NULL) + { + SymCryptMlKemkeyFree(keyCtx->key); + keyCtx->key = NULL; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0 && + (p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY)) != NULL) + { + if (!OSSL_PARAM_get_octet_string_ptr(p, (const void **)&pbKey, &cbKey)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return SCOSSL_FAILURE; + } + } + else + { + if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY)) == NULL && + (p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY)) == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return SCOSSL_FAILURE; + } + + if (!OSSL_PARAM_get_octet_string_ptr(p, (const void **)&pbKey, &cbKey)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return SCOSSL_FAILURE; + } + } + + if (p_scossl_mlkem_keymgmt_set_encoded_key(keyCtx, selection, pbKey, cbKey) != SCOSSL_SUCCESS) + { + return SCOSSL_FAILURE; + } + } + + return SCOSSL_SUCCESS; +} + +SCOSSL_STATUS p_scossl_mlkem_keymgmt_export(_In_ SCOSSL_MLKEM_KEY_CTX *keyCtx, int selection, + _In_ OSSL_CALLBACK *param_cb, _In_ void *cbarg) +{ + const char *mlkemParamsName; + PBYTE pbKey = NULL; + SIZE_T cbKey = 0; + OSSL_PARAM_BLD *bld = NULL; + OSSL_PARAM *params = NULL; + SCOSSL_STATUS ret = SCOSSL_FAILURE; + + // Domain parameters are required for export + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0) + { + goto cleanup; + } + + if (keyCtx->groupInfo == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET); + goto cleanup; + } + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0 && + keyCtx->key == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + goto cleanup; + } + + if ((bld = OSSL_PARAM_BLD_new()) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + mlkemParamsName = keyCtx->groupInfo->groupName != NULL ? keyCtx->groupInfo->groupName : ""; + if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, mlkemParamsName, strlen(mlkemParamsName))) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + goto cleanup; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + { + ret = p_scossl_mlkem_keymgmt_get_encoded_key( + keyCtx, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + &pbKey, &cbKey); + + if (ret != SCOSSL_SUCCESS) + { + goto cleanup; + } + + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pbKey, cbKey)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + goto cleanup; + } + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + { + if (keyCtx->format != SYMCRYPT_MLKEMKEY_FORMAT_DECAPSULATION_KEY) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY); + goto cleanup; + } + + // Reset pbKey in case it was used for encapsulation key export + OPENSSL_secure_clear_free(pbKey, cbKey); + + ret = p_scossl_mlkem_keymgmt_get_encoded_key( + keyCtx, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY, + &pbKey, &cbKey); + + if (ret != SCOSSL_SUCCESS) + { + goto cleanup; + } + + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PRIV_KEY, pbKey, cbKey)) + { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + goto cleanup; + } + } + + ret = param_cb(params, cbarg); + +cleanup: + OSSL_PARAM_BLD_free(bld); + OSSL_PARAM_free(params); + OPENSSL_secure_clear_free(pbKey, cbKey); + + return ret; +} + +const OSSL_DISPATCH p_scossl_mlkem_keymgmt_functions[] = { + {OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))p_scossl_mlkem_keymgmt_new_ctx}, + {OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))p_scossl_mlkem_keymgmt_dup_key_ctx}, + {OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))p_scossl_mlkem_keymgmt_free_key_ctx}, + {OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))p_scossl_mlkem_keygen_set_params}, + {OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (void (*)(void))p_scossl_mlkem_keygen_settable_params}, + {OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))p_scossl_mlkem_keygen_cleanup}, + {OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))p_scossl_mlkem_keygen_init}, + {OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, (void (*)(void))p_scossl_mlkem_keygen_set_template}, + {OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))p_scossl_mlkem_keygen}, + {OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))p_scossl_mlkem_keymgmt_load}, + {OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*)(void))p_scossl_mlkem_keymgmt_settable_params}, + {OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*)(void))p_scossl_mlkem_keymgmt_set_params}, + {OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*)(void))p_scossl_mlkem_keymgmt_gettable_params}, + {OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*)(void))p_scossl_mlkem_keymgmt_get_params}, + {OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))p_scossl_mlkem_keymgmt_has}, + {OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))p_scossl_mlkem_keymgmt_match}, + {OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))p_scossl_mlkem_keymgmt_impexp_types}, + {OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))p_scossl_mlkem_keymgmt_impexp_types}, + {OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))p_scossl_mlkem_keymgmt_import}, + {OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))p_scossl_mlkem_keymgmt_export}, + {0, NULL}}; + +// +// Helper functions +// +_Use_decl_annotations_ +SCOSSL_STATUS p_scossl_mlkem_keymgmt_get_encoded_key(const SCOSSL_MLKEM_KEY_CTX *keyCtx, int selection, + PBYTE *ppbKey, SIZE_T *pcbKey) +{ + PBYTE pbKey = NULL; + SIZE_T cbKey = 0; + PBYTE pbMlKemKey = NULL; + SIZE_T cbMlKemKey = 0; + PBYTE pbClassicKey = NULL; + SIZE_T cbClassicKey = 0; + SYMCRYPT_MLKEMKEY_FORMAT format; + SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR; + SCOSSL_STATUS ret = SCOSSL_FAILURE; + + if (keyCtx->key == NULL || keyCtx->groupInfo == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return SCOSSL_FAILURE; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + { + if (keyCtx->format == SYMCRYPT_MLKEMKEY_FORMAT_NULL || + keyCtx->format == SYMCRYPT_MLKEMKEY_FORMAT_ENCAPSULATION_KEY) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY); + return SCOSSL_FAILURE; + } + + format = keyCtx->format; + } + else + { + format = SYMCRYPT_MLKEMKEY_FORMAT_ENCAPSULATION_KEY; + } + + scError = SymCryptMlKemSizeofKeyFormatFromParams(keyCtx->groupInfo->mlkemParams, format, &cbMlKemKey); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if (keyCtx->classicKeyCtx != NULL && + (cbClassicKey = p_scossl_ecc_get_encoded_key_size(keyCtx->classicKeyCtx, selection)) == 0) + { + goto cleanup; + } + + cbKey = cbMlKemKey + cbClassicKey; + + // Always using OPENSSL_secure_malloc so caller doesn't have to worry about + // calling separate free functions for encapsulation and decapsulation keys + if ((pbKey = OPENSSL_secure_malloc(cbKey)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + if (keyCtx->classicKeyCtx != NULL && + keyCtx->classicKeyCtx->isX25519) + { + pbMlKemKey = pbKey; + pbClassicKey = pbKey + cbMlKemKey; + } + else + { + pbClassicKey = pbKey; + pbMlKemKey = pbKey + cbClassicKey; + } + + scError = SymCryptMlKemkeyGetValue(keyCtx->key, pbMlKemKey, cbMlKemKey, format, 0); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if (keyCtx->classicKeyCtx != NULL && + p_scossl_ecc_get_encoded_key(keyCtx->classicKeyCtx, selection, &pbClassicKey, &cbClassicKey) != SCOSSL_SUCCESS) + { + goto cleanup; + } + + ret = SCOSSL_SUCCESS; + + *ppbKey = pbKey; + *pcbKey = cbKey; + +cleanup: + if (ret != SCOSSL_SUCCESS) + { + OPENSSL_secure_clear_free(pbKey, cbKey); + } + + return SCOSSL_SUCCESS; +} + +_Use_decl_annotations_ +SCOSSL_STATUS p_scossl_mlkem_keymgmt_set_encoded_key(SCOSSL_MLKEM_KEY_CTX *keyCtx, int selection, + PCBYTE pbKey, SIZE_T cbKey) +{ + PCBYTE pbMlKemKey = NULL; + SIZE_T cbMlKemKey = 0; + PCBYTE pbClassicKey = NULL; + SIZE_T cbClassicKey = 0; + SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR; + SCOSSL_STATUS ret = SCOSSL_FAILURE; + + if (keyCtx->groupInfo == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + goto cleanup; + } + + if (keyCtx->key == NULL && + (keyCtx->key = SymCryptMlKemkeyAllocate(keyCtx->groupInfo->mlkemParams)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + if (keyCtx->groupInfo->classicGroupName != NULL) + { + if (keyCtx->classicKeyCtx == NULL && + (keyCtx->classicKeyCtx = p_scossl_ecc_new_ctx(keyCtx->provCtx)) == NULL) + { + goto cleanup; + } + + if (p_scossl_ecc_set_group(keyCtx->classicKeyCtx, keyCtx->groupInfo->classicGroupName) != SCOSSL_SUCCESS || + (cbClassicKey = p_scossl_ecc_get_encoded_key_size(keyCtx->classicKeyCtx, selection)) == 0) + { + goto cleanup; + } + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + { + keyCtx->format = cbKey - cbClassicKey == 64 ? SYMCRYPT_MLKEMKEY_FORMAT_PRIVATE_SEED : SYMCRYPT_MLKEMKEY_FORMAT_DECAPSULATION_KEY; + } + else + { + keyCtx->format = SYMCRYPT_MLKEMKEY_FORMAT_ENCAPSULATION_KEY; + } + + scError = SymCryptMlKemSizeofKeyFormatFromParams(keyCtx->groupInfo->mlkemParams, keyCtx->format, &cbMlKemKey); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if (cbKey != cbClassicKey + cbMlKemKey) + { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + goto cleanup; + } + + if (keyCtx->classicKeyCtx != NULL && + keyCtx->classicKeyCtx->isX25519) + { + pbMlKemKey = pbKey; + pbClassicKey = pbKey + cbMlKemKey; + } + else + { + pbClassicKey = pbKey; + pbMlKemKey = pbKey + cbClassicKey; + } + + scError = SymCryptMlKemkeySetValue(pbMlKemKey, cbMlKemKey, keyCtx->format, 0, keyCtx->key); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if (keyCtx->classicKeyCtx != NULL) + { + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + { + ret = p_scossl_ecc_set_encoded_key(keyCtx->classicKeyCtx, NULL, 0, pbClassicKey, cbClassicKey); + } + else + { + ret = p_scossl_ecc_set_encoded_key(keyCtx->classicKeyCtx, pbClassicKey, cbClassicKey, NULL, 0); + } + + if (ret != SCOSSL_SUCCESS) + { + goto cleanup; + } + } + + ret = SCOSSL_SUCCESS; + +cleanup: + if (ret != SCOSSL_SUCCESS) + { + SymCryptMlKemkeyFree(keyCtx->key); + keyCtx->key = NULL; + keyCtx->format = SYMCRYPT_MLKEMKEY_FORMAT_NULL; + } + + return ret; +} + +_Use_decl_annotations_ +static int p_scossl_mlkem_keymgmt_get_security_bits(_In_ const SCOSSL_MLKEM_KEY_CTX *keyCtx) +{ + if (keyCtx->groupInfo != NULL) + { + switch(keyCtx->groupInfo->mlkemParams) + { + case SYMCRYPT_MLKEM_PARAMS_MLKEM512: + return 128; + case SYMCRYPT_MLKEM_PARAMS_MLKEM768: + return 192; + case SYMCRYPT_MLKEM_PARAMS_MLKEM1024: + return 256; + default: + break; + } + } + + ERR_raise(ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET); + return 0; +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/SymCryptProvider/src/keymgmt/p_scossl_mlkem_keymgmt.h b/SymCryptProvider/src/keymgmt/p_scossl_mlkem_keymgmt.h new file mode 100644 index 0000000..ce453da --- /dev/null +++ b/SymCryptProvider/src/keymgmt/p_scossl_mlkem_keymgmt.h @@ -0,0 +1,28 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#pragma once + +#include "p_scossl_base.h" +#include "kem/p_scossl_mlkem.h" + +#ifdef __cplusplus +extern "C" { +#endif + +SCOSSL_MLKEM_KEY_CTX *p_scossl_mlkem_keymgmt_new_ctx(_In_ SCOSSL_PROVCTX *provCtx); +void p_scossl_mlkem_keymgmt_free_key_ctx(_In_ SCOSSL_MLKEM_KEY_CTX *keyCtx); + +SCOSSL_STATUS p_scossl_mlkem_keymgmt_import(_Inout_ SCOSSL_MLKEM_KEY_CTX *keyCtx, int selection, _In_ const OSSL_PARAM params[]); +SCOSSL_STATUS p_scossl_mlkem_keymgmt_export(_In_ SCOSSL_MLKEM_KEY_CTX *keyCtx, int selection, + _In_ OSSL_CALLBACK *param_cb, _In_ void *cbarg); + +SCOSSL_STATUS p_scossl_mlkem_keymgmt_get_encoded_key(_In_ const SCOSSL_MLKEM_KEY_CTX *keyCtx, int selection, + _Out_writes_bytes_(*pcbKey) PBYTE *ppbKey, _Out_ SIZE_T *pcbKey); +SCOSSL_STATUS p_scossl_mlkem_keymgmt_set_encoded_key(_Inout_ SCOSSL_MLKEM_KEY_CTX *keyCtx, int selection, + _In_reads_bytes_(cbKey) PCBYTE pbKey, SIZE_T cbKey); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/SymCryptProvider/src/p_scossl_base.c b/SymCryptProvider/src/p_scossl_base.c index ab88318..c5bb7ed 100644 --- a/SymCryptProvider/src/p_scossl_base.c +++ b/SymCryptProvider/src/p_scossl_base.c @@ -8,8 +8,10 @@ #include "scossl_dh.h" #include "scossl_ecc.h" -#include "p_scossl_keysinuse.h" -#include "p_scossl_base.h" +#include "scossl_provider.h" +#include "p_scossl_bio.h" +#include "p_scossl_names.h" +#include "kem/p_scossl_mlkem.h" #ifdef __cplusplus extern "C" { @@ -24,22 +26,60 @@ extern "C" { #define SCOSSL_MAX_CONFIGURABLE_FILE_SIZE (2 << 30) #endif -#define OSSL_TLS_GROUP_ID_secp192r1 0x0013 -#define OSSL_TLS_GROUP_ID_secp224r1 0x0015 -#define OSSL_TLS_GROUP_ID_secp256r1 0x0017 -#define OSSL_TLS_GROUP_ID_secp384r1 0x0018 -#define OSSL_TLS_GROUP_ID_secp521r1 0x0019 -#define OSSL_TLS_GROUP_ID_x25519 0x001D -#define OSSL_TLS_GROUP_ID_ffdhe2048 0x0100 -#define OSSL_TLS_GROUP_ID_ffdhe3072 0x0101 -#define OSSL_TLS_GROUP_ID_ffdhe4096 0x0102 - -#define ALG(names, funcs) {names, "provider="P_SCOSSL_NAME",fips=yes", funcs, NULL} +#define SCOSSL_TLS_GROUP_ID_secp192r1 0x0013 +#define SCOSSL_TLS_GROUP_ID_secp224r1 0x0015 +#define SCOSSL_TLS_GROUP_ID_secp256r1 0x0017 +#define SCOSSL_TLS_GROUP_ID_secp384r1 0x0018 +#define SCOSSL_TLS_GROUP_ID_secp521r1 0x0019 +#define SCOSSL_TLS_GROUP_ID_x25519 0x001d +#define SCOSSL_TLS_GROUP_ID_ffdhe2048 0x0100 +#define SCOSSL_TLS_GROUP_ID_ffdhe3072 0x0101 +#define SCOSSL_TLS_GROUP_ID_ffdhe4096 0x0102 +#define SCOSSL_TLS_GROUP_ID_mlkem512 0x0200 +#define SCOSSL_TLS_GROUP_ID_mlkem768 0x0201 +#define SCOSSL_TLS_GROUP_ID_mlkem1024 0x0202 +#define SCOSSL_TLS_GROUP_ID_secp256r1mlkem768 0x11eb +#define SCOSSL_TLS_GROUP_ID_x25519mlkem768 0x11ec +#define SCOSSL_TLS_GROUP_ID_secp384r1mlkem1024 0x11ed + +#define ALG(names, funcs) { \ + names, \ + "provider="P_SCOSSL_NAME \ + ",fips=yes", \ + funcs, \ + NULL} + +#define ALG_DECODER(algNames, name, decoderType) { \ + algNames, \ + "provider="P_SCOSSL_NAME \ + ",fips=yes" \ + ",input=der" \ + ",structure="#decoderType, \ + p_scossl_der_to_##name##_##decoderType##_functions, \ + NULL} + +#define ALG_ENCODER(algNames, name, encoderType, format) { \ + algNames, \ + "provider="P_SCOSSL_NAME \ + ",fips=yes" \ + ",output="#format \ + ",structure="#encoderType, \ + p_scossl_##name##_to_##encoderType##_##format##_functions, \ + NULL} + +#define ALG_TEXT_ENCODER(algNames, name) { \ + algNames, \ + "provider="P_SCOSSL_NAME \ + ",fips=yes,output=text", \ + p_scossl_##name##_to_text_functions, \ + NULL} + #define ALG_TABLE_END {NULL, NULL, NULL, NULL} typedef struct { unsigned int groupId; unsigned int securityBits; + int is_kem; int minTls; int maxTls; int minDtls; @@ -47,60 +87,91 @@ typedef struct { } SCOSSL_TLS_GROUP_INFO; const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_p192 = { - OSSL_TLS_GROUP_ID_secp192r1, 80, + SCOSSL_TLS_GROUP_ID_secp192r1, 80, 0, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION}; const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_p224 = { - OSSL_TLS_GROUP_ID_secp224r1, 112, + SCOSSL_TLS_GROUP_ID_secp224r1, 112, 0, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION}; const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_p256 = { - OSSL_TLS_GROUP_ID_secp256r1, 128, + SCOSSL_TLS_GROUP_ID_secp256r1, 128, 0, TLS1_VERSION, 0, DTLS1_VERSION, 0}; const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_p384 = { - OSSL_TLS_GROUP_ID_secp384r1, 192, + SCOSSL_TLS_GROUP_ID_secp384r1, 192, 0, TLS1_VERSION, 0, DTLS1_VERSION, 0}; const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_p521 = { - OSSL_TLS_GROUP_ID_secp521r1, 256, + SCOSSL_TLS_GROUP_ID_secp521r1, 256, 0, TLS1_VERSION, 0, DTLS1_VERSION, 0}; const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_x25519 = { - OSSL_TLS_GROUP_ID_x25519, 128, + SCOSSL_TLS_GROUP_ID_x25519, 128, 0, TLS1_VERSION, 0, DTLS1_VERSION, 0}; const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_ffdhe2048 = { - OSSL_TLS_GROUP_ID_ffdhe2048, 112, + SCOSSL_TLS_GROUP_ID_ffdhe2048, 112, 0, TLS1_3_VERSION, 0, -1, -1}; const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_ffdhe3072 = { - OSSL_TLS_GROUP_ID_ffdhe3072, 128, + SCOSSL_TLS_GROUP_ID_ffdhe3072, 128, 0, TLS1_3_VERSION, 0, -1, -1}; const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_ffdhe4096 = { - OSSL_TLS_GROUP_ID_ffdhe4096, 128, + SCOSSL_TLS_GROUP_ID_ffdhe4096, 128, 0, + TLS1_3_VERSION, 0, + -1, -1}; + +const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_mlkem512 = { + SCOSSL_TLS_GROUP_ID_mlkem512, 128, 1, + TLS1_3_VERSION, 0, + -1, -1}; + +const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_mlkem768 = { + SCOSSL_TLS_GROUP_ID_mlkem768, 192, 1, + TLS1_3_VERSION, 0, + -1, -1}; + +const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_mlkem1024 = { + SCOSSL_TLS_GROUP_ID_mlkem1024, 256, 1, + TLS1_3_VERSION, 0, + -1, -1}; + +const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_secp256r1mlkem768 = { + SCOSSL_TLS_GROUP_ID_secp256r1mlkem768, 192, 1, TLS1_3_VERSION, 0, -1, -1}; -#define TLS_GROUP_ENTRY(tlsname, realname, algorithm, group_info) { \ - OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME, tlsname, sizeof(tlsname)), \ - OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL, realname, sizeof(realname)), \ - OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_ALG, algorithm, sizeof(algorithm)), \ - OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_ID, (unsigned int *)&group_info.groupId), \ +const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_x25519mlkem768 = { + SCOSSL_TLS_GROUP_ID_x25519mlkem768, 192, 1, + TLS1_3_VERSION, 0, + -1, -1}; + +const SCOSSL_TLS_GROUP_INFO scossl_tls_group_info_secp384r1mlkem1024 = { + SCOSSL_TLS_GROUP_ID_secp384r1mlkem1024, 256, 1, + TLS1_3_VERSION, 0, + -1, -1}; + +#define TLS_GROUP_ENTRY(tlsname, realname, algorithm, group_info) { \ + OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME, tlsname, sizeof(tlsname)), \ + OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL, realname, sizeof(realname)), \ + OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_ALG, algorithm, sizeof(algorithm)), \ + OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_ID, (unsigned int *)&group_info.groupId), \ OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS, (unsigned int *)&group_info.securityBits), \ - OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_TLS, (int *)&group_info.minTls), \ - OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_TLS, (int *)&group_info.maxTls), \ - OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS, (int *)&group_info.minTls), \ - OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS, (int *)&group_info.maxTls), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_TLS, (int *)&group_info.minTls), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_TLS, (int *)&group_info.maxTls), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS, (int *)&group_info.minTls), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS, (int *)&group_info.maxTls), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_IS_KEM, (int *)&group_info.is_kem), \ OSSL_PARAM_END} static int scossl_prov_initialized = 0; @@ -121,7 +192,15 @@ static const OSSL_PARAM p_scossl_supported_group_list[][11] = { TLS_GROUP_ENTRY("x25519", SN_X25519, "X25519", scossl_tls_group_info_x25519), TLS_GROUP_ENTRY("ffdhe2048", SN_ffdhe2048, "DH", scossl_tls_group_info_ffdhe2048), TLS_GROUP_ENTRY("ffdhe3072", SN_ffdhe3072, "DH", scossl_tls_group_info_ffdhe3072), - TLS_GROUP_ENTRY("ffdhe4096", SN_ffdhe4096, "DH", scossl_tls_group_info_ffdhe4096)}; + TLS_GROUP_ENTRY("ffdhe4096", SN_ffdhe4096, "DH", scossl_tls_group_info_ffdhe4096), + TLS_GROUP_ENTRY("mlkem512", SCOSSL_SN_MLKEM512, "MLKEM", scossl_tls_group_info_mlkem512), + 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), + TLS_GROUP_ENTRY("X25519mlkem768", SCOSSL_SN_X25519_MLKEM768, "MLKEM", scossl_tls_group_info_x25519mlkem768), + TLS_GROUP_ENTRY("secp384r1mlkem1024", SCOSSL_SN_P384_MLKEM1024, "MLKEM", scossl_tls_group_info_secp384r1mlkem1024), + TLS_GROUP_ENTRY("P384mlkem1024", SCOSSL_SN_P384_MLKEM1024, "MLKEM", scossl_tls_group_info_secp384r1mlkem1024)}; // Digest extern const OSSL_DISPATCH p_scossl_md5_functions[]; @@ -142,22 +221,22 @@ extern const OSSL_DISPATCH p_scossl_cshake_128_functions[]; extern const OSSL_DISPATCH p_scossl_cshake_256_functions[]; static const OSSL_ALGORITHM p_scossl_digest[] = { - ALG("MD5:SSL3-MD5:1.2.840.113549.2.5", p_scossl_md5_functions), - ALG("SHA1:SHA-1:SSL3-SHA1:1.3.14.3.2.26", p_scossl_sha1_functions), - ALG("SHA2-224:SHA-224:SHA224:2.16.840.1.101.3.4.2.4", p_scossl_sha224_functions), - ALG("SHA2-256:SHA-256:SHA256:2.16.840.1.101.3.4.2.1", p_scossl_sha256_functions), - ALG("SHA2-384:SHA-384:SHA384:2.16.840.1.101.3.4.2.2", p_scossl_sha384_functions), - ALG("SHA2-512:SHA-512:SHA512:2.16.840.1.101.3.4.2.3", p_scossl_sha512_functions), - ALG("SHA2-512/224:SHA-512/224:SHA512-224:2.16.840.1.101.3.4.2.5", p_scossl_sha512_224_functions), - ALG("SHA2-512/256:SHA-512/256:SHA512-256:2.16.840.1.101.3.4.2.6", p_scossl_sha512_256_functions), - ALG("SHA3-224:2.16.840.1.101.3.4.2.7", p_scossl_sha3_224_functions), - ALG("SHA3-256:2.16.840.1.101.3.4.2.8", p_scossl_sha3_256_functions), - ALG("SHA3-384:2.16.840.1.101.3.4.2.9", p_scossl_sha3_384_functions), - ALG("SHA3-512:2.16.840.1.101.3.4.2.10", p_scossl_sha3_512_functions), - ALG("SHAKE-128:SHAKE128:2.16.840.1.101.3.4.2.11", p_scossl_shake_128_functions), - ALG("SHAKE-256:SHAKE256:2.16.840.1.101.3.4.2.12", p_scossl_shake_256_functions), - ALG("CSHAKE-128:CSHAKE128", p_scossl_cshake_128_functions), - ALG("CSHAKE-256:CSHAKE256", p_scossl_cshake_256_functions), + ALG(SCOSSL_ALG_NAME_MD5, p_scossl_md5_functions), + ALG(SCOSSL_ALG_NAME_SHA1, p_scossl_sha1_functions), + ALG(SCOSSL_ALG_NAME_SHA224, p_scossl_sha224_functions), + ALG(SCOSSL_ALG_NAME_SHA256, p_scossl_sha256_functions), + ALG(SCOSSL_ALG_NAME_SHA384, p_scossl_sha384_functions), + ALG(SCOSSL_ALG_NAME_SHA512, p_scossl_sha512_functions), + ALG(SCOSSL_ALG_NAME_SHA512_224, p_scossl_sha512_224_functions), + ALG(SCOSSL_ALG_NAME_SHA512_256, p_scossl_sha512_256_functions), + ALG(SCOSSL_ALG_NAME_SHA3_224, p_scossl_sha3_224_functions), + ALG(SCOSSL_ALG_NAME_SHA3_256, p_scossl_sha3_256_functions), + ALG(SCOSSL_ALG_NAME_SHA3_384, p_scossl_sha3_384_functions), + ALG(SCOSSL_ALG_NAME_SHA3_512, p_scossl_sha3_512_functions), + ALG(SCOSSL_ALG_NAME_SHAKE128, p_scossl_shake_128_functions), + ALG(SCOSSL_ALG_NAME_SHAKE256, p_scossl_shake_256_functions), + ALG(SCOSSL_ALG_NAME_CSHAKE128, p_scossl_cshake_128_functions), + ALG(SCOSSL_ALG_NAME_CSHAKE256, p_scossl_cshake_256_functions), ALG_TABLE_END}; // Cipher @@ -183,26 +262,26 @@ extern const OSSL_DISPATCH p_scossl_aes128xts_functions[]; extern const OSSL_DISPATCH p_scossl_aes256xts_functions[]; static const OSSL_ALGORITHM p_scossl_cipher[] = { - ALG("AES-128-CBC:AES128:2.16.840.1.101.3.4.1.2", p_scossl_aes128cbc_functions), - ALG("AES-192-CBC:AES192:2.16.840.1.101.3.4.1.22", p_scossl_aes192cbc_functions), - ALG("AES-256-CBC:AES256:2.16.840.1.101.3.4.1.42", p_scossl_aes256cbc_functions), - ALG("AES-128-ECB:2.16.840.1.101.3.4.1.1", p_scossl_aes128ecb_functions), - ALG("AES-192-ECB:2.16.840.1.101.3.4.1.21", p_scossl_aes192ecb_functions), - ALG("AES-256-ECB:2.16.840.1.101.3.4.1.41", p_scossl_aes256ecb_functions), - ALG("AES-128-CFB:2.16.840.1.101.3.4.1.4", p_scossl_aes128cfb_functions), - ALG("AES-192-CFB:2.16.840.1.101.3.4.1.24", p_scossl_aes192cfb_functions), - ALG("AES-256-CFB:2.16.840.1.101.3.4.1.44", p_scossl_aes256cfb_functions), - ALG("AES-128-CFB8", p_scossl_aes128cfb8_functions), - ALG("AES-192-CFB8", p_scossl_aes192cfb8_functions), - ALG("AES-256-CFB8", p_scossl_aes256cfb8_functions), - ALG("AES-128-GCM:id-aes128-GCM:2.16.840.1.101.3.4.1.6", p_scossl_aes128gcm_functions), - ALG("AES-192-GCM:id-aes192-GCM:2.16.840.1.101.3.4.1.26", p_scossl_aes192gcm_functions), - ALG("AES-256-GCM:id-aes256-GCM:2.16.840.1.101.3.4.1.46", p_scossl_aes256gcm_functions), - ALG("AES-128-CCM:id-aes128-CCM:2.16.840.1.101.3.4.1.7", p_scossl_aes128ccm_functions), - ALG("AES-192-CCM:id-aes192-CCM:2.16.840.1.101.3.4.1.27", p_scossl_aes192ccm_functions), - ALG("AES-256-CCM:id-aes256-CCM:2.16.840.1.101.3.4.1.47", p_scossl_aes256ccm_functions), - ALG("AES-128-XTS:1.3.111.2.1619.0.1.1", p_scossl_aes128xts_functions), - ALG("AES-256-XTS:1.3.111.2.1619.0.1.2", p_scossl_aes256xts_functions), + ALG(SCOSSL_ALG_NAME_AES_128_CBC, p_scossl_aes128cbc_functions), + ALG(SCOSSL_ALG_NAME_AES_192_CBC, p_scossl_aes192cbc_functions), + ALG(SCOSSL_ALG_NAME_AES_256_CBC, p_scossl_aes256cbc_functions), + ALG(SCOSSL_ALG_NAME_AES_128_ECB, p_scossl_aes128ecb_functions), + ALG(SCOSSL_ALG_NAME_AES_192_ECB, p_scossl_aes192ecb_functions), + ALG(SCOSSL_ALG_NAME_AES_256_ECB, p_scossl_aes256ecb_functions), + ALG(SCOSSL_ALG_NAME_AES_128_CFB, p_scossl_aes128cfb_functions), + ALG(SCOSSL_ALG_NAME_AES_192_CFB, p_scossl_aes192cfb_functions), + ALG(SCOSSL_ALG_NAME_AES_256_CFB, p_scossl_aes256cfb_functions), + ALG(SCOSSL_ALG_NAME_AES_128_CFB8, p_scossl_aes128cfb8_functions), + ALG(SCOSSL_ALG_NAME_AES_192_CFB8, p_scossl_aes192cfb8_functions), + ALG(SCOSSL_ALG_NAME_AES_256_CFB8, p_scossl_aes256cfb8_functions), + ALG(SCOSSL_ALG_NAME_AES_128_GCM, p_scossl_aes128gcm_functions), + ALG(SCOSSL_ALG_NAME_AES_192_GCM, p_scossl_aes192gcm_functions), + ALG(SCOSSL_ALG_NAME_AES_256_GCM, p_scossl_aes256gcm_functions), + ALG(SCOSSL_ALG_NAME_AES_128_CCM, p_scossl_aes128ccm_functions), + ALG(SCOSSL_ALG_NAME_AES_192_CCM, p_scossl_aes192ccm_functions), + ALG(SCOSSL_ALG_NAME_AES_256_CCM, p_scossl_aes256ccm_functions), + ALG(SCOSSL_ALG_NAME_AES_128_XTS, p_scossl_aes128xts_functions), + ALG(SCOSSL_ALG_NAME_AES_256_XTS, p_scossl_aes256xts_functions), ALG_TABLE_END}; // MAC @@ -212,10 +291,10 @@ extern const OSSL_DISPATCH p_scossl_kmac128_functions[]; extern const OSSL_DISPATCH p_scossl_kmac256_functions[]; static const OSSL_ALGORITHM p_scossl_mac[] = { - ALG("CMAC", p_scossl_cmac_functions), - ALG("HMAC", p_scossl_hmac_functions), - ALG("KMAC-128:KMAC128:2.16.840.1.101.3.4.2.19", p_scossl_kmac128_functions), - ALG("KMAC-256:KMAC256:2.16.840.1.101.3.4.2.20", p_scossl_kmac256_functions), + ALG(SCOSSL_ALG_NAME_CMAC, p_scossl_cmac_functions), + ALG(SCOSSL_ALG_NAME_HMAC, p_scossl_hmac_functions), + ALG(SCOSSL_ALG_NAME_KMAC128, p_scossl_kmac128_functions), + ALG(SCOSSL_ALG_NAME_KMAC256, p_scossl_kmac256_functions), ALG_TABLE_END}; // KDF @@ -228,20 +307,20 @@ extern const OSSL_DISPATCH p_scossl_sskdf_kdf_functions[]; extern const OSSL_DISPATCH p_scossl_tls1prf_kdf_functions[]; static const OSSL_ALGORITHM p_scossl_kdf[] = { - ALG("HKDF", p_scossl_hkdf_kdf_functions), - ALG("KBKDF", p_scossl_kbkdf_kdf_functions), - ALG("SRTPKDF", p_scossl_srtpkdf_kdf_functions), - ALG("SRTCPKDF", p_scossl_srtcpkdf_kdf_functions), - ALG("SSHKDF", p_scossl_sshkdf_kdf_functions), - ALG("SSKDF", p_scossl_sskdf_kdf_functions), - ALG("TLS1-PRF", p_scossl_tls1prf_kdf_functions), + ALG(SCOSSL_ALG_NAME_HKDF, p_scossl_hkdf_kdf_functions), + ALG(SCOSSL_ALG_NAME_KBKKDF, p_scossl_kbkdf_kdf_functions), + ALG(SCOSSL_ALG_NAME_SRTPKDF, p_scossl_srtpkdf_kdf_functions), + ALG(SCOSSL_ALG_NAME_SRTCPKDF, p_scossl_srtcpkdf_kdf_functions), + ALG(SCOSSL_ALG_NAME_SSHKDF, p_scossl_sshkdf_kdf_functions), + ALG(SCOSSL_ALG_NAME_SSKDF, p_scossl_sskdf_kdf_functions), + ALG(SCOSSL_ALG_NAME_TLS1_PRF, p_scossl_tls1prf_kdf_functions), ALG_TABLE_END}; // Rand extern const OSSL_DISPATCH p_scossl_rand_functions[]; static const OSSL_ALGORITHM p_scossl_rand[] = { - ALG("CTR-DRBG", p_scossl_rand_functions), + ALG(SCOSSL_ALG_NAME_CTR_DBG, p_scossl_rand_functions), ALG_TABLE_END}; // Key management @@ -251,15 +330,17 @@ extern const OSSL_DISPATCH p_scossl_kdf_keymgmt_functions[]; extern const OSSL_DISPATCH p_scossl_rsa_keymgmt_functions[]; extern const OSSL_DISPATCH p_scossl_rsapss_keymgmt_functions[]; extern const OSSL_DISPATCH p_scossl_x25519_keymgmt_functions[]; +extern const OSSL_DISPATCH p_scossl_mlkem_keymgmt_functions[]; static const OSSL_ALGORITHM p_scossl_keymgmt[] = { - ALG("DH:dhKeyAgreement:1.2.840.113549.1.3.1", p_scossl_dh_keymgmt_functions), - ALG("EC:id-ecPublicKey:1.2.840.10045.2.1", p_scossl_ecc_keymgmt_functions), - ALG("HKDF", p_scossl_kdf_keymgmt_functions), - ALG("RSA:rsaEncryption:1.2.840.113549.1.1.1:", p_scossl_rsa_keymgmt_functions), - ALG("RSA-PSS:RSASSA-PSS:1.2.840.113549.1.1.10", p_scossl_rsapss_keymgmt_functions), - ALG("TLS1-PRF", p_scossl_kdf_keymgmt_functions), - ALG("X25519:1.3.101.110", p_scossl_x25519_keymgmt_functions), + ALG(SCOSSL_ALG_NAME_DH, p_scossl_dh_keymgmt_functions), + ALG(SCOSSL_ALG_NAME_EC, p_scossl_ecc_keymgmt_functions), + ALG(SCOSSL_ALG_NAME_HKDF, p_scossl_kdf_keymgmt_functions), + ALG(SCOSSL_ALG_NAME_MLKEM, p_scossl_mlkem_keymgmt_functions), + ALG(SCOSSL_ALG_NAME_RSA, p_scossl_rsa_keymgmt_functions), + ALG(SCOSSL_ALG_NAME_RSA_PSS, p_scossl_rsapss_keymgmt_functions), + ALG(SCOSSL_ALG_NAME_TLS1_PRF, p_scossl_kdf_keymgmt_functions), + ALG(SCOSSL_ALG_NAME_X25519, p_scossl_x25519_keymgmt_functions), ALG_TABLE_END}; // Key exchange @@ -270,29 +351,69 @@ extern const OSSL_DISPATCH p_scossl_tls1prf_keyexch_functions[]; extern const OSSL_DISPATCH p_scossl_x25519_functions[]; static const OSSL_ALGORITHM p_scossl_keyexch[] = { - ALG("DH:dhKeyAgreement:1.2.840.113549.1.3.1", p_scossl_dh_functions), - ALG("ECDH", p_scossl_ecdh_functions), - ALG("HKDF", p_scossl_hkdf_keyexch_functions), - ALG("TLS1-PRF", p_scossl_tls1prf_keyexch_functions), - ALG("X25519:1.3.101.110", p_scossl_ecdh_functions), + ALG(SCOSSL_ALG_NAME_DH, p_scossl_dh_functions), + ALG(SCOSSL_ALG_NAME_ECDH, p_scossl_ecdh_functions), + ALG(SCOSSL_ALG_NAME_HKDF, p_scossl_hkdf_keyexch_functions), + ALG(SCOSSL_ALG_NAME_TLS1_PRF, p_scossl_tls1prf_keyexch_functions), + ALG(SCOSSL_ALG_NAME_X25519, p_scossl_ecdh_functions), ALG_TABLE_END}; // Signature -extern const OSSL_DISPATCH p_scossl_rsa_signature_functions[]; extern const OSSL_DISPATCH p_scossl_ecdsa_signature_functions[]; +extern const OSSL_DISPATCH p_scossl_rsa_signature_functions[]; static const OSSL_ALGORITHM p_scossl_signature[] = { - ALG("RSA:rsaEncryption:1.2.840.113549.1.1.1", p_scossl_rsa_signature_functions), - ALG("ECDSA", p_scossl_ecdsa_signature_functions), + ALG(SCOSSL_ALG_NAME_ECDSA, p_scossl_ecdsa_signature_functions), + ALG(SCOSSL_ALG_NAME_RSA, p_scossl_rsa_signature_functions), ALG_TABLE_END}; // Asymmetric Cipher extern const OSSL_DISPATCH p_scossl_rsa_cipher_functions[]; static const OSSL_ALGORITHM p_scossl_asym_cipher[] = { - ALG("RSA:rsaEncryption:1.2.840.113549.1.1.1", p_scossl_rsa_cipher_functions), + ALG(SCOSSL_ALG_NAME_RSA, p_scossl_rsa_cipher_functions), + ALG_TABLE_END}; + +// Key encapsulation +extern const OSSL_DISPATCH p_scossl_mlkem_functions[]; + +static const OSSL_ALGORITHM p_scossl_kem[] = { + ALG(SCOSSL_ALG_NAME_MLKEM, p_scossl_mlkem_functions), + ALG_TABLE_END}; + +// Decoders +extern const OSSL_DISPATCH p_scossl_der_to_mlkem_PrivateKeyInfo_functions[]; +extern const OSSL_DISPATCH p_scossl_der_to_mlkem_SubjectPublicKeyInfo_functions[]; + +static const OSSL_ALGORITHM p_scossl_decoder[] = { + ALG_DECODER(SCOSSL_ALG_NAME_MLKEM_DECODER, mlkem, PrivateKeyInfo), + ALG_DECODER(SCOSSL_ALG_NAME_MLKEM_DECODER, mlkem, SubjectPublicKeyInfo), + ALG_TABLE_END}; + +// Encoders +extern const OSSL_DISPATCH p_scossl_mlkem_to_PrivateKeyInfo_der_functions[]; +extern const OSSL_DISPATCH p_scossl_mlkem_to_PrivateKeyInfo_pem_functions[]; +extern const OSSL_DISPATCH p_scossl_mlkem_to_EncryptedPrivateKeyInfo_der_functions[]; +extern const OSSL_DISPATCH p_scossl_mlkem_to_EncryptedPrivateKeyInfo_pem_functions[]; +extern const OSSL_DISPATCH p_scossl_mlkem_to_SubjectPublicKeyInfo_der_functions[]; +extern const OSSL_DISPATCH p_scossl_mlkem_to_SubjectPublicKeyInfo_pem_functions[]; +extern const OSSL_DISPATCH p_scossl_mlkem_to_text_functions[]; + +static const OSSL_ALGORITHM p_scossl_encoder[] = { + ALG_ENCODER(SCOSSL_ALG_NAME_MLKEM, mlkem, PrivateKeyInfo, der), + ALG_ENCODER(SCOSSL_ALG_NAME_MLKEM, mlkem, PrivateKeyInfo, pem), + ALG_ENCODER(SCOSSL_ALG_NAME_MLKEM, mlkem, EncryptedPrivateKeyInfo, der), + ALG_ENCODER(SCOSSL_ALG_NAME_MLKEM, mlkem, EncryptedPrivateKeyInfo, pem), + ALG_ENCODER(SCOSSL_ALG_NAME_MLKEM, mlkem, SubjectPublicKeyInfo, der), + ALG_ENCODER(SCOSSL_ALG_NAME_MLKEM, mlkem, SubjectPublicKeyInfo, pem), + ALG_TEXT_ENCODER(SCOSSL_ALG_NAME_MLKEM, mlkem), ALG_TABLE_END}; +static SCOSSL_STATUS p_scossl_register_extended_algorithms() +{ + return p_scossl_mlkem_register_algorithms(); +} + static int p_scossl_get_status() { return scossl_prov_initialized; @@ -303,6 +424,7 @@ static void p_scossl_teardown(_Inout_ SCOSSL_PROVCTX *provctx) scossl_destroy_logging(); scossl_destroy_safeprime_dlgroups(); scossl_ecc_destroy_ecc_curves(); + BIO_meth_free(provctx->coreBioMeth); #ifdef KEYSINUSE_ENABLED p_scossl_keysinuse_teardown(); #endif @@ -370,6 +492,12 @@ static const OSSL_ALGORITHM *p_scossl_query_operation(ossl_unused void *provctx, return p_scossl_signature; case OSSL_OP_ASYM_CIPHER: return p_scossl_asym_cipher; + case OSSL_OP_KEM: + return p_scossl_kem; + case OSSL_OP_DECODER: + return p_scossl_decoder; + case OSSL_OP_ENCODER: + return p_scossl_encoder; } return NULL; @@ -508,7 +636,22 @@ SCOSSL_STATUS OSSL_provider_init(_In_ const OSSL_CORE_HANDLE *handle, _Out_ const OSSL_DISPATCH **out, _Out_ void **provctx) { - SCOSSL_PROVCTX *p_ctx = NULL; + SCOSSL_PROVCTX *p_ctx = OPENSSL_malloc(sizeof(SCOSSL_PROVCTX)); + if (p_ctx == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + return SCOSSL_FAILURE; + } + + p_ctx->handle = handle; + p_ctx->libctx = OSSL_LIB_CTX_new_child(handle, in); + + p_scossl_set_core_bio(in); + if ((p_ctx->coreBioMeth = p_scossl_bio_init()) == NULL) + { + OPENSSL_free(p_ctx); + return SCOSSL_FAILURE; + } for (; in->function_id != 0; in++) { @@ -526,7 +669,8 @@ 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); return SCOSSL_FAILURE; @@ -534,15 +678,6 @@ SCOSSL_STATUS OSSL_provider_init(_In_ const OSSL_CORE_HANDLE *handle, scossl_prov_initialized = 1; } - p_ctx = OPENSSL_malloc(sizeof(SCOSSL_PROVCTX)); - if (p_ctx == NULL) - { - ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); - return SCOSSL_FAILURE; - } - - p_ctx->handle = handle; - p_ctx->libctx = OSSL_LIB_CTX_new_child(handle, in); *provctx = p_ctx; *out = p_scossl_base_dispatch; diff --git a/SymCryptProvider/src/p_scossl_bio.c b/SymCryptProvider/src/p_scossl_bio.c new file mode 100644 index 0000000..ea00ca6 --- /dev/null +++ b/SymCryptProvider/src/p_scossl_bio.c @@ -0,0 +1,157 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#include "p_scossl_bio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +OSSL_FUNC_BIO_read_ex_fn *core_bio_read_ex = NULL; +OSSL_FUNC_BIO_write_ex_fn *core_bio_write_ex = NULL; +OSSL_FUNC_BIO_up_ref_fn *core_bio_up_ref = NULL; +OSSL_FUNC_BIO_free_fn *core_bio_free = NULL; +OSSL_FUNC_BIO_gets_fn *core_bio_gets = NULL; +OSSL_FUNC_BIO_puts_fn *core_bio_puts = NULL; +OSSL_FUNC_BIO_ctrl_fn *core_bio_ctrl = NULL; + +static int p_scossl_bio_core_read_ex(BIO *bio, char *data, size_t data_len, + size_t *bytes_read) +{ + if (core_bio_read_ex == NULL) + return 0; + + return core_bio_read_ex(BIO_get_data(bio), data, data_len, bytes_read); +} + +static int p_scossl_bio_core_write_ex(BIO *bio, const char *data, size_t data_len, + size_t *written) +{ + if (core_bio_write_ex == NULL) + return 0; + + return core_bio_write_ex(BIO_get_data(bio), data, data_len, written); +} + +static int p_scossl_bio_core_create(BIO *bio) +{ + BIO_set_init(bio, 1); + + return 1; +} + +static int p_scossl_bio_core_destroy(BIO *bio) +{ + BIO_set_init(bio, 0); + + if (core_bio_free != NULL) + core_bio_free(BIO_get_data(bio)); + + return 1; +} + +static long p_scossl_bio_core_ctrl(BIO *bio, int cmd, long num, void *ptr) +{ + if (core_bio_ctrl == NULL) + return 0; + + return core_bio_ctrl(BIO_get_data(bio), cmd, num, ptr); +} + +static int p_scossl_bio_core_gets(BIO *bio, char *buf, int size) +{ + if (core_bio_gets == NULL) + return 0; + + return core_bio_gets(BIO_get_data(bio), buf, size); +} + +static int p_scossl_bio_core_puts(BIO *bio, const char *str) +{ + if (core_bio_puts == NULL) + return 0; + + return core_bio_puts(BIO_get_data(bio), str); +} + +_Use_decl_annotations_ +void p_scossl_set_core_bio(const OSSL_DISPATCH *dispatch) +{ + for (; dispatch->function_id != 0; dispatch++) + { + switch (dispatch->function_id) + { + case OSSL_FUNC_BIO_READ_EX: + core_bio_read_ex = (OSSL_FUNC_BIO_read_ex_fn *)dispatch->function; + break; + case OSSL_FUNC_BIO_WRITE_EX: + core_bio_write_ex = (OSSL_FUNC_BIO_write_ex_fn *)dispatch->function; + break; + case OSSL_FUNC_BIO_UP_REF: + core_bio_up_ref = (OSSL_FUNC_BIO_up_ref_fn *)dispatch->function; + break; + case OSSL_FUNC_BIO_FREE: + core_bio_free = (OSSL_FUNC_BIO_free_fn *)dispatch->function; + break; + case OSSL_FUNC_BIO_PUTS: + core_bio_puts = (OSSL_FUNC_BIO_puts_fn *)dispatch->function; + break; + case OSSL_FUNC_BIO_GETS: + core_bio_gets = (OSSL_FUNC_BIO_gets_fn *)dispatch->function; + break; + case OSSL_FUNC_BIO_CTRL: + core_bio_ctrl = (OSSL_FUNC_BIO_ctrl_fn *)dispatch->function; + break; + } + } +} + +BIO_METHOD *p_scossl_bio_init() +{ + BIO_METHOD *coreBioMeth = BIO_meth_new(BIO_TYPE_CORE_TO_PROV, "SCOSSL BIO to core filter"); + + if (coreBioMeth != NULL && + (!BIO_meth_set_read_ex(coreBioMeth, p_scossl_bio_core_read_ex) || + !BIO_meth_set_write_ex(coreBioMeth, p_scossl_bio_core_write_ex) || + !BIO_meth_set_create(coreBioMeth, p_scossl_bio_core_create) || + !BIO_meth_set_destroy(coreBioMeth, p_scossl_bio_core_destroy) || + !BIO_meth_set_gets(coreBioMeth, p_scossl_bio_core_gets) || + !BIO_meth_set_puts(coreBioMeth, p_scossl_bio_core_puts) || + !BIO_meth_set_ctrl(coreBioMeth, p_scossl_bio_core_ctrl))) + { + BIO_meth_free(coreBioMeth); + coreBioMeth = NULL; + } + + return coreBioMeth; +} + +_Use_decl_annotations_ +BIO *p_scossl_bio_new_from_core_bio(SCOSSL_PROVCTX *provctx, OSSL_CORE_BIO *coreBio) +{ + BIO *bio; + + if (provctx->coreBioMeth == NULL) + { + return NULL; + } + + if ((bio = BIO_new(provctx->coreBioMeth)) != NULL) + { + if (core_bio_up_ref == NULL || + !core_bio_up_ref(coreBio)) + { + BIO_free(bio); + return NULL; + } + + BIO_set_data(bio, coreBio); + } + + return bio; +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/SymCryptProvider/src/p_scossl_bio.h b/SymCryptProvider/src/p_scossl_bio.h new file mode 100644 index 0000000..769b8ca --- /dev/null +++ b/SymCryptProvider/src/p_scossl_bio.h @@ -0,0 +1,17 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#include "p_scossl_base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void p_scossl_set_core_bio(_In_ const OSSL_DISPATCH *dispatch); +BIO_METHOD *p_scossl_bio_init(); +BIO *p_scossl_bio_new_from_core_bio(_In_ SCOSSL_PROVCTX *provctx, _In_ OSSL_CORE_BIO *coreBio); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/SymCryptProvider/src/p_scossl_ecc.c b/SymCryptProvider/src/p_scossl_ecc.c index 26970e3..8e823ab 100644 --- a/SymCryptProvider/src/p_scossl_ecc.c +++ b/SymCryptProvider/src/p_scossl_ecc.c @@ -4,23 +4,299 @@ #include +#include "scossl_ecc.h" #include "p_scossl_ecc.h" #ifdef __cplusplus extern "C" { #endif +#define SCOSSL_X25519_MAX_SIZE (32) + +_Use_decl_annotations_ +SCOSSL_ECC_KEY_CTX *p_scossl_ecc_new_ctx(SCOSSL_PROVCTX *provctx) +{ + SCOSSL_ECC_KEY_CTX *keyCtx = OPENSSL_zalloc(sizeof(SCOSSL_ECC_KEY_CTX)); + if (keyCtx != NULL) + { + keyCtx->libctx = provctx->libctx; + keyCtx->includePublic = 1; + keyCtx->conversionFormat = POINT_CONVERSION_UNCOMPRESSED; + } + return keyCtx; +} + +_Use_decl_annotations_ +void p_scossl_ecc_free_ctx(SCOSSL_ECC_KEY_CTX *keyCtx) +{ + if (keyCtx == NULL) + return; + if (keyCtx->key != NULL) + { + SymCryptEckeyFree(keyCtx->key); + } +#ifdef KEYSINUSE_ENABLED + p_scossl_ecc_reset_keysinuse(keyCtx); + CRYPTO_THREAD_lock_free(keyCtx->keysinuseLock); +#endif + + OPENSSL_free(keyCtx); +} + +_Use_decl_annotations_ +SCOSSL_ECC_KEY_CTX *p_scossl_ecc_dup_ctx(SCOSSL_ECC_KEY_CTX *keyCtx, int selection) +{ + PBYTE pbData = NULL; + PBYTE pbPrivateKey = NULL; + PBYTE pbPublicKey = NULL; + SIZE_T cbData = 0; + SIZE_T cbPublicKey = 0; + SIZE_T cbPrivateKey = 0; + SCOSSL_STATUS success = SCOSSL_FAILURE; + SYMCRYPT_ECPOINT_FORMAT pointFormat = keyCtx->isX25519 ? SYMCRYPT_ECPOINT_FORMAT_X : SYMCRYPT_ECPOINT_FORMAT_XY; + SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR; + + SCOSSL_ECC_KEY_CTX *copyCtx = OPENSSL_malloc(sizeof(SCOSSL_ECC_KEY_CTX)); + + if (copyCtx != NULL) + { +#ifdef KEYSINUSE_ENABLED + copyCtx->keysinuseLock = CRYPTO_THREAD_lock_new(); + + if (keyCtx->keysinuseInfo == NULL || + p_scossl_keysinuse_upref(keyCtx->keysinuseInfo, NULL)) + { + copyCtx->keysinuseInfo = keyCtx->keysinuseInfo; + } +#endif + + copyCtx->isX25519 = keyCtx->isX25519; + copyCtx->libctx = keyCtx->libctx; + copyCtx->modifiedPrivateBits = keyCtx->modifiedPrivateBits; + copyCtx->conversionFormat = keyCtx->conversionFormat; + + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + { + copyCtx->curve = keyCtx->curve; + } + else + { + copyCtx->curve = NULL; + } + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0 && keyCtx->initialized) + { + if (copyCtx->curve == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET); + goto cleanup; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0 && + SymCryptEckeyHasPrivateKey(keyCtx->key)) + { + cbPrivateKey = SymCryptEckeySizeofPrivateKey(keyCtx->key); + } + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + { + cbPublicKey = SymCryptEckeySizeofPublicKey(keyCtx->key, pointFormat); + } + + cbData = cbPrivateKey + cbPublicKey; + if ((pbData = OPENSSL_secure_malloc(cbData)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + pbPrivateKey = cbPrivateKey != 0 ? pbData : NULL; + pbPublicKey = cbPublicKey != 0 ? pbData + cbPrivateKey : NULL; + + scError = SymCryptEckeyGetValue( + keyCtx->key, + pbPrivateKey, cbPrivateKey, + pbPublicKey, cbPublicKey, + SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, + pointFormat, + 0); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if ((copyCtx->key = SymCryptEckeyAllocate(keyCtx->curve)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + // Default ECDH only. If the key is used for ECDSA then we call SymCryptEckeyExtendKeyUsage + scError = SymCryptEckeySetValue( + pbPrivateKey, cbPrivateKey, + pbPublicKey, cbPublicKey, + SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, + pointFormat, + SYMCRYPT_FLAG_ECKEY_ECDH, + copyCtx->key); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + copyCtx->initialized = 1; + copyCtx->includePublic = keyCtx->includePublic; + } + else + { + copyCtx->key = NULL; + copyCtx->initialized = 0; + copyCtx->includePublic = 1; + } + } + + success = SCOSSL_SUCCESS; + +cleanup: + if (pbData != NULL) + { + OPENSSL_secure_clear_free(pbData, cbData); + } + + if (!success) + { + p_scossl_ecc_free_ctx(copyCtx); + copyCtx = NULL; + } + + return copyCtx; +} + +_Use_decl_annotations_ +SCOSSL_STATUS p_scossl_ecc_set_group(SCOSSL_ECC_KEY_CTX *keyCtx, const char *groupName) +{ + PCSYMCRYPT_ECURVE curve; + int nid = OBJ_sn2nid(groupName); + + if (nid == NID_X25519) + { + keyCtx->isX25519 = TRUE; + curve = scossl_ecc_get_x25519_curve(); + } + else + { + keyCtx->isX25519 = FALSE; + curve = scossl_ecc_nid_to_symcrypt_curve(nid); + + if (curve == NULL) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED); + return SCOSSL_FAILURE; + } + } + + keyCtx->curve = curve; + + return SCOSSL_SUCCESS; +} + +SCOSSL_STATUS p_scossl_ecc_gen(_Inout_ SCOSSL_ECC_KEY_CTX *keyCtx) +{ + SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR; + +#ifdef KEYSINUSE_ENABLED + keyCtx->isImported = FALSE; + keyCtx->keysinuseLock = CRYPTO_THREAD_lock_new(); + keyCtx->keysinuseInfo = NULL; +#endif + + if (keyCtx->key != NULL) + { + SymCryptEckeyFree(keyCtx->key); + } + + keyCtx->key = SymCryptEckeyAllocate(keyCtx->curve); + if (keyCtx->key == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + return SCOSSL_FAILURE; + } + + // Default ECDH only. If the key is used for ECDSA then we call SymCryptEckeyExtendKeyUsage + scError = SymCryptEckeySetRandom(SYMCRYPT_FLAG_ECKEY_ECDH, keyCtx->key); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return SCOSSL_FAILURE; + } + + keyCtx->initialized = TRUE; + + return SCOSSL_SUCCESS; +} + +SIZE_T p_scossl_ecc_get_max_size(_In_ SCOSSL_ECC_KEY_CTX *keyCtx, BOOL isEcdh) +{ + if (keyCtx->isX25519) + { + return SCOSSL_X25519_MAX_SIZE; + } + else if (isEcdh) + { + return keyCtx->key == NULL ? 0 : SymCryptEckeySizeofPublicKey(keyCtx->key, SYMCRYPT_ECPOINT_FORMAT_X); + } + + return scossl_ecdsa_size(keyCtx->curve); +} + +_Use_decl_annotations_ +SIZE_T p_scossl_ecc_get_encoded_key_size(SCOSSL_ECC_KEY_CTX *keyCtx, int selection) +{ + SIZE_T cbKey; + + if (keyCtx->curve == NULL) + { + return 0; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + { + return SymCryptEcurveSizeofScalarMultiplier(keyCtx->curve); + } + else if (keyCtx->isX25519) + { + return SymCryptEckeySizeofPublicKey(keyCtx->key, SYMCRYPT_ECPOINT_FORMAT_X); + } + + cbKey = SymCryptEcurveSizeofFieldElement(keyCtx->curve); + if (!keyCtx->isX25519) + { + if (keyCtx->conversionFormat != POINT_CONVERSION_COMPRESSED) + { + cbKey *= 2; + } + + cbKey++; + } + + return cbKey; +} + // Gets the public key as an encoded octet string // For x25519, the encoding rules defined in RFC 7748 are used // Otherwise, the encoding rules defined in SECG SEC 1 are used, according to the conversion format of keyCtx _Use_decl_annotations_ SCOSSL_STATUS p_scossl_ecc_get_encoded_public_key(const SCOSSL_ECC_KEY_CTX *keyCtx, - PBYTE *ppbEncodedKey, SIZE_T *pcbEncodedKey) + PBYTE *ppbPublicKey, SIZE_T *pcbPublicKey) { SYMCRYPT_NUMBER_FORMAT numFormat; SYMCRYPT_ECPOINT_FORMAT pointFormat; - PBYTE pbPublicKey, pbPublicKeyStart; + PBYTE pbPublicKeyStart = NULL; + PBYTE pbPublicKey = NULL; SIZE_T cbPublicKey; + BOOL allocatedKey = FALSE; SYMCRYPT_ERROR scError; SCOSSL_STATUS ret = SCOSSL_FAILURE; @@ -39,15 +315,29 @@ SCOSSL_STATUS p_scossl_ecc_get_encoded_public_key(const SCOSSL_ECC_KEY_CTX *keyC else { numFormat = SYMCRYPT_NUMBER_FORMAT_MSB_FIRST; - pointFormat = SYMCRYPT_ECPOINT_FORMAT_XY; + 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; } - if ((pbPublicKeyStart = OPENSSL_malloc(cbPublicKey)) == NULL) + if (*ppbPublicKey == NULL) { - ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + if ((pbPublicKeyStart = OPENSSL_malloc(cbPublicKey)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + allocatedKey = TRUE; + } + else if (*pcbPublicKey >= cbPublicKey) + { + pbPublicKeyStart = *ppbPublicKey; + } + else + { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); goto cleanup; } @@ -89,34 +379,256 @@ SCOSSL_STATUS p_scossl_ecc_get_encoded_public_key(const SCOSSL_ECC_KEY_CTX *keyC { pbPublicKeyStart[0]++; } + } + + cbPublicKey++; + } + + if (allocatedKey) + { + *ppbPublicKey = pbPublicKeyStart; + } + *pcbPublicKey = cbPublicKey; + + ret = SCOSSL_SUCCESS; + +cleanup: + if (!ret && allocatedKey) + { + OPENSSL_free(pbPublicKeyStart); + } + + return ret; +} + +SCOSSL_STATUS p_scossl_ecc_get_private_key(_In_ SCOSSL_ECC_KEY_CTX *keyCtx, + _Out_writes_bytes_(*pcbPrivateKey) PBYTE *ppbPrivateKey, _Out_ SIZE_T *pcbPrivateKey) +{ + PBYTE pbPrivateKey = NULL; + SIZE_T cbPrivateKey; + SYMCRYPT_NUMBER_FORMAT numFormat = keyCtx->isX25519 ? SYMCRYPT_NUMBER_FORMAT_LSB_FIRST : SYMCRYPT_NUMBER_FORMAT_MSB_FIRST; + SYMCRYPT_ECPOINT_FORMAT pointFormat = keyCtx->isX25519 ? SYMCRYPT_ECPOINT_FORMAT_X : SYMCRYPT_ECPOINT_FORMAT_XY; + BOOL allocatedKey = FALSE; + SYMCRYPT_ERROR scError; + SCOSSL_STATUS ret = SCOSSL_FAILURE; + + if (!keyCtx->initialized) + { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return SCOSSL_FAILURE; + } + + cbPrivateKey = SymCryptEckeySizeofPrivateKey(keyCtx->key); + + if (*ppbPrivateKey == NULL) + { + if ((pbPrivateKey = OPENSSL_malloc(cbPrivateKey)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + allocatedKey = TRUE; + } + else if (*pcbPrivateKey >= cbPrivateKey) + { + pbPrivateKey = *ppbPrivateKey; + } + else + { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + goto cleanup; + } + + scError = SymCryptEckeyGetValue( + keyCtx->key, + pbPrivateKey, cbPrivateKey, + NULL, 0, + numFormat, + pointFormat, + 0); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + if (keyCtx->isX25519) + { + pbPrivateKey[0] = (keyCtx->modifiedPrivateBits & 0x07) | (pbPrivateKey[0] & 0xf8); + pbPrivateKey[cbPrivateKey-1] = (keyCtx->modifiedPrivateBits & 0xc0) | (pbPrivateKey[cbPrivateKey-1] & 0x3f); + } + + if (allocatedKey) + { + *ppbPrivateKey = pbPrivateKey; + } + *pcbPrivateKey = cbPrivateKey; + + ret = SCOSSL_SUCCESS; + +cleanup: + if (ret != SCOSSL_SUCCESS && allocatedKey) + { + OPENSSL_secure_clear_free(pbPrivateKey, cbPrivateKey); + } + + return ret; +} + +// Gets the ECC Key following the proper encoding rules. If *ppbKey is non-NULL, then the key material +// is written directly to the supplied buffer. Otherwise, a new buffer is allocated to contain the key. +// The caller is responsible for freeing *ppbKey. +_Use_decl_annotations_ +SCOSSL_STATUS p_scossl_ecc_get_encoded_key(SCOSSL_ECC_KEY_CTX *keyCtx, int selection, + PBYTE *ppbKey, SIZE_T *pcbKey) +{ + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + { + return p_scossl_ecc_get_private_key(keyCtx, ppbKey, pcbKey); + } + + return p_scossl_ecc_get_encoded_public_key(keyCtx, ppbKey, pcbKey); +} + +_Use_decl_annotations_ +SCOSSL_STATUS p_scossl_ecc_set_encoded_key(SCOSSL_ECC_KEY_CTX *keyCtx, + PCBYTE pbEncodedPublicKey, SIZE_T cbEncodedPublicKey, + PCBYTE pbEncodedPrivateKey, SIZE_T cbEncodedPrivateKey) +{ + SYMCRYPT_NUMBER_FORMAT numFormat; + SYMCRYPT_ECPOINT_FORMAT pointFormat; + EC_GROUP *ecGroup = NULL; + EC_POINT *ecPoint = NULL; + BN_CTX *bnCtx = NULL; + PBYTE pbPublicKey = NULL; + SIZE_T cbPublicKey = 0; + PBYTE pbPrivateKey = NULL; + SIZE_T cbPrivateKey = 0; + SYMCRYPT_ERROR scError; + SCOSSL_STATUS ret = SCOSSL_FAILURE; + + if (keyCtx->key != NULL) + { + SymCryptEckeyFree(keyCtx->key); + } + +#ifdef KEYSINUSE_ENABLED + // Reset keysinuse in case new key material is overwriting existing + p_scossl_ecc_reset_keysinuse(keyCtx); +#endif + + if (keyCtx->isX25519) + { + numFormat = SYMCRYPT_NUMBER_FORMAT_LSB_FIRST; + pointFormat = SYMCRYPT_ECPOINT_FORMAT_X; + } + else + { + numFormat = SYMCRYPT_NUMBER_FORMAT_MSB_FIRST; + pointFormat = SYMCRYPT_ECPOINT_FORMAT_XY; + } + + if ((keyCtx->key = SymCryptEckeyAllocate(keyCtx->curve)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + if (pbEncodedPublicKey != NULL) + { + if (keyCtx->isX25519) + { + pbPublicKey = (PBYTE) pbEncodedPublicKey; + cbPublicKey = cbEncodedPublicKey; + } + else + { + cbPublicKey = SymCryptEckeySizeofPublicKey(keyCtx->key, SYMCRYPT_ECPOINT_FORMAT_XY); + if (((ecGroup = scossl_ecc_symcrypt_curve_to_ecc_group(keyCtx->curve)) == NULL) || + ((ecPoint = EC_POINT_new(ecGroup)) == NULL) || + ((bnCtx = BN_CTX_new_ex(keyCtx->libctx)) == NULL) || + ((pbPublicKey = OPENSSL_malloc(cbPublicKey)) == NULL)) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } - if (keyCtx->conversionFormat == POINT_CONVERSION_COMPRESSED) + if (!EC_POINT_oct2point(ecGroup, ecPoint, pbEncodedPublicKey, cbEncodedPublicKey, bnCtx) || + !scossl_ec_point_to_pubkey(ecPoint, ecGroup, bnCtx, pbPublicKey, cbPublicKey)) { - // We only need the X coordinate, so copy that and the format byte for return. - // Copy to pbPublicKey in case OPENSSL_memdup fails so we still free the original buffer - if ((pbPublicKey = OPENSSL_memdup(pbPublicKeyStart, (cbPublicKey/2) + 1)) == NULL) - { - ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); - goto cleanup; - } - OPENSSL_free(pbPublicKeyStart); - pbPublicKeyStart = pbPublicKey; + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + goto cleanup; } } + } - cbPublicKey++; + if (pbEncodedPrivateKey != NULL) + { + if (keyCtx->isX25519) + { + if ((pbPrivateKey = OPENSSL_secure_malloc(cbEncodedPrivateKey)) == NULL) + { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + goto cleanup; + } + + memcpy(pbPrivateKey, pbEncodedPrivateKey, cbEncodedPrivateKey); + + // Preserve original bits for export + keyCtx->modifiedPrivateBits = pbPrivateKey[0] & 0x07; + keyCtx->modifiedPrivateBits |= pbPrivateKey[cbPrivateKey-1] & 0xc0; + + pbPrivateKey[0] &= 0xf8; + pbPrivateKey[cbPrivateKey-1] &= 0x7f; + pbPrivateKey[cbPrivateKey-1] |= 0x40; + } + else + { + pbPrivateKey = (PBYTE) pbEncodedPrivateKey; + cbPrivateKey = cbEncodedPrivateKey; + } } - *ppbEncodedKey = pbPublicKeyStart; - *pcbEncodedKey = cbPublicKey; + scError = SymCryptEckeySetValue( + pbPrivateKey, cbPrivateKey, + pbPublicKey, cbPublicKey, + numFormat, + pointFormat, + SYMCRYPT_FLAG_ECKEY_ECDH, + keyCtx->key); + if (scError != SYMCRYPT_NO_ERROR) + { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + + keyCtx->initialized = TRUE; ret = SCOSSL_SUCCESS; cleanup: - if (!ret) + if (ret != SCOSSL_SUCCESS && + keyCtx->key != NULL) { - OPENSSL_free(pbPublicKeyStart); + SymCryptEckeyFree(keyCtx->key); + keyCtx->key = NULL; } + // X25519 needs to copy and decode the private key, other ECC needs + // to copy and decode the public key. + if (keyCtx->isX25519) + { + OPENSSL_secure_clear_free(pbPrivateKey, cbPrivateKey); + } + else + { + OPENSSL_free(pbPublicKey); + } + + EC_GROUP_free(ecGroup); + EC_POINT_free(ecPoint); + BN_CTX_free(bnCtx); + return ret; } diff --git a/SymCryptProvider/src/p_scossl_ecc.h b/SymCryptProvider/src/p_scossl_ecc.h index 681cc71..33f4972 100644 --- a/SymCryptProvider/src/p_scossl_ecc.h +++ b/SymCryptProvider/src/p_scossl_ecc.h @@ -1,8 +1,7 @@ // // Copyright (c) Microsoft Corporation. Licensed under the MIT license. // - -#include "scossl_helpers.h" +#include "p_scossl_base.h" #ifdef KEYSINUSE_ENABLED #include "p_scossl_keysinuse.h" #endif @@ -40,10 +39,21 @@ typedef struct { #endif } SCOSSL_ECC_KEY_CTX; -// Helper function for retrieving the properly formatted encoded public key. -// The format differs for x25519 keys. Caller is responsible for freeing *ppbEncodedKey. -SCOSSL_STATUS p_scossl_ecc_get_encoded_public_key(_In_ const SCOSSL_ECC_KEY_CTX *keyCtx, - _Inout_ PBYTE *ppbEncodedKey, _Inout_ SIZE_T *pcbEncodedKey); +SCOSSL_ECC_KEY_CTX *p_scossl_ecc_new_ctx(_In_ SCOSSL_PROVCTX *provctx); +void p_scossl_ecc_free_ctx(_Inout_ SCOSSL_ECC_KEY_CTX *keyCtx); +SCOSSL_ECC_KEY_CTX *p_scossl_ecc_dup_ctx(_In_ SCOSSL_ECC_KEY_CTX *keyCtx, int selection); + +SCOSSL_STATUS p_scossl_ecc_set_group(_Inout_ SCOSSL_ECC_KEY_CTX *keyCtx, _In_ const char *groupName); + +SCOSSL_STATUS p_scossl_ecc_gen(_Inout_ SCOSSL_ECC_KEY_CTX *keyCtx); + +SIZE_T p_scossl_ecc_get_max_size(_In_ SCOSSL_ECC_KEY_CTX *keyCtx, BOOL isEcdh); +SIZE_T p_scossl_ecc_get_encoded_key_size(_In_ SCOSSL_ECC_KEY_CTX *keyCtx, int selection); +SCOSSL_STATUS p_scossl_ecc_get_encoded_key(_In_ SCOSSL_ECC_KEY_CTX *keyCtx, int selection, + _Out_writes_bytes_(*pcbKey) PBYTE *ppbKey, _Out_ SIZE_T *pcbKey); +SCOSSL_STATUS p_scossl_ecc_set_encoded_key(_In_ SCOSSL_ECC_KEY_CTX *keyCtx, + _In_reads_bytes_opt_(cbEncodedPublicKey) PCBYTE pbEncodedPublicKey, SIZE_T cbEncodedPublicKey, + _In_reads_bytes_opt_(cbEncodedPrivateKey) PCBYTE pbEncodedPrivateKey, SIZE_T cbEncodedPrivateKey); #ifdef KEYSINUSE_ENABLED void p_scossl_ecc_init_keysinuse(_In_ SCOSSL_ECC_KEY_CTX *keyCtx); diff --git a/SymCryptProvider/src/p_scossl_names.h b/SymCryptProvider/src/p_scossl_names.h new file mode 100644 index 0000000..777967d --- /dev/null +++ b/SymCryptProvider/src/p_scossl_names.h @@ -0,0 +1,98 @@ +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT license. +// + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +// +// Provider algorithm names +// + +// Digests +#define SCOSSL_ALG_NAME_MD5 SN_md5"SSL3-MD5:1.2.840.113549.2.5" +#define SCOSSL_ALG_NAME_SHA1 SN_sha1"SHA-1:SSL3-SHA1:1.3.14.3.2.26" +#define SCOSSL_ALG_NAME_SHA224 SN_sha224"SHA2-224:SHA-224:2.16.840.1.101.3.4.2.4" +#define SCOSSL_ALG_NAME_SHA256 SN_sha256"SHA2-256:SHA-256:2.16.840.1.101.3.4.2.1" +#define SCOSSL_ALG_NAME_SHA384 SN_sha384"SHA2-384:SHA-384:2.16.840.1.101.3.4.2.2" +#define SCOSSL_ALG_NAME_SHA512 SN_sha512"SHA2-512:SHA-512:2.16.840.1.101.3.4.2.3" +#define SCOSSL_ALG_NAME_SHA512_224 SN_sha512_224"SHA2-512/224:SHA-512/224:2.16.840.1.101.3.4.2.5" +#define SCOSSL_ALG_NAME_SHA512_256 SN_sha512_256"SHA2-512/256:SHA-512/256:2.16.840.1.101.3.4.2.6" +#define SCOSSL_ALG_NAME_SHA3_224 SN_sha3_224"2.16.840.1.101.3.4.2.7" +#define SCOSSL_ALG_NAME_SHA3_256 SN_sha3_256"2.16.840.1.101.3.4.2.8" +#define SCOSSL_ALG_NAME_SHA3_384 SN_sha3_384"2.16.840.1.101.3.4.2.9" +#define SCOSSL_ALG_NAME_SHA3_512 SN_sha3_512"2.16.840.1.101.3.4.2.10" +#define SCOSSL_ALG_NAME_SHAKE128 SN_shake128"SHAKE-128:2.16.840.1.101.3.4.2.11" +#define SCOSSL_ALG_NAME_SHAKE256 SN_shake256"SHAKE-256:2.16.840.1.101.3.4.2.12" +#define SCOSSL_ALG_NAME_CSHAKE128 "CSHAKE-128:CSHAKE128" +#define SCOSSL_ALG_NAME_CSHAKE256 "CSHAKE-256:CSHAKE256" + +// Ciphers +#define SCOSSL_ALG_NAME_AES_128_CBC SN_aes_128_cbc":AES128:2.16.840.1.101.3.4.1.2" +#define SCOSSL_ALG_NAME_AES_192_CBC SN_aes_192_cbc":AES192:2.16.840.1.101.3.4.1.22" +#define SCOSSL_ALG_NAME_AES_256_CBC SN_aes_256_cbc":AES256:2.16.840.1.101.3.4.1.42" +#define SCOSSL_ALG_NAME_AES_128_ECB SN_aes_128_ecb":2.16.840.1.101.3.4.1.1" +#define SCOSSL_ALG_NAME_AES_192_ECB SN_aes_192_ecb":2.16.840.1.101.3.4.1.21" +#define SCOSSL_ALG_NAME_AES_256_ECB SN_aes_256_ecb":2.16.840.1.101.3.4.1.41" +#define SCOSSL_ALG_NAME_AES_128_CFB SN_aes_128_cfb128":2.16.840.1.101.3.4.1.4" +#define SCOSSL_ALG_NAME_AES_192_CFB SN_aes_192_cfb128":2.16.840.1.101.3.4.1.24" +#define SCOSSL_ALG_NAME_AES_256_CFB SN_aes_256_cfb128":2.16.840.1.101.3.4.1.44" +#define SCOSSL_ALG_NAME_AES_128_CFB8 SN_aes_128_cfb8 +#define SCOSSL_ALG_NAME_AES_192_CFB8 SN_aes_192_cfb8 +#define SCOSSL_ALG_NAME_AES_256_CFB8 SN_aes_256_cfb8 +#define SCOSSL_ALG_NAME_AES_128_GCM SN_aes_128_gcm":AES-128-GCM:2.16.840.1.101.3.4.1.6" +#define SCOSSL_ALG_NAME_AES_192_GCM SN_aes_192_gcm":AES-192-GCM:2.16.840.1.101.3.4.1.26" +#define SCOSSL_ALG_NAME_AES_256_GCM SN_aes_256_gcm":AES-256-GCM:2.16.840.1.101.3.4.1.46" +#define SCOSSL_ALG_NAME_AES_128_CCM SN_aes_128_ccm":AES-128-CCM:2.16.840.1.101.3.4.1.7" +#define SCOSSL_ALG_NAME_AES_192_CCM SN_aes_192_ccm":AES-192-CCM:2.16.840.1.101.3.4.1.27" +#define SCOSSL_ALG_NAME_AES_256_CCM SN_aes_256_ccm":AES-256-CCM:2.16.840.1.101.3.4.1.47" +#define SCOSSL_ALG_NAME_AES_128_XTS SN_aes_128_xts":1.3.111.2.1619.0.1.1" +#define SCOSSL_ALG_NAME_AES_256_XTS SN_aes_256_xts":1.3.111.2.1619.0.1.2" + +// MAC +#define SCOSSL_ALG_NAME_CMAC SN_cmac +#define SCOSSL_ALG_NAME_HMAC SN_hmac +#define SCOSSL_ALG_NAME_KMAC128 SN_kmac128":KMAC-128:KMAC128:2.16.840.1.101.3.4.2.19" +#define SCOSSL_ALG_NAME_KMAC256 SN_kmac256":KMAC-256:KMAC256:2.16.840.1.101.3.4.2.20" + +// KDF +#define SCOSSL_ALG_NAME_HKDF SN_hkdf +#define SCOSSL_ALG_NAME_KBKKDF "KBKDF" +#define SCOSSL_ALG_NAME_SRTPKDF "SRTPKDF" +#define SCOSSL_ALG_NAME_SRTCPKDF "SRTCPKDF" +#define SCOSSL_ALG_NAME_SSHKDF SN_sshkdf +#define SCOSSL_ALG_NAME_SSKDF SN_sskdf +#define SCOSSL_ALG_NAME_TLS1_PRF SN_tls1_prf + +// Rand +#define SCOSSL_ALG_NAME_CTR_DBG "CTR-DRBG" + +// Key management +#define SCOSSL_ALG_NAME_EC SN_X9_62_id_ecPublicKey":EC:1.2.840.10045.2.1" +#define SCOSSL_ALG_NAME_RSA_PSS SN_rsassaPss":RSA-PSS:1.2.840.113549.1.1.10" + +// Key exchange +#define SCOSSL_ALG_NAME_DH LN_dhKeyAgreement":DH:1.2.840.113549.1.3.1" +#define SCOSSL_ALG_NAME_ECDH "ECDH" +#define SCOSSL_ALG_NAME_X25519 SN_X25519":1.3.101.110" + +// Signature +#define SCOSSL_ALG_NAME_RSA SN_rsa":"LN_rsaEncryption":1.2.840.113549.1.1.1" +#define SCOSSL_ALG_NAME_ECDSA "ECDSA" + +// Key encapsulation +#define SCOSSL_ALG_NAME_MLKEM "MLKEM" +#define SCOSSL_ALG_NAME_MLKEM_DECODER SCOSSL_ALG_NAME_MLKEM":" \ + SCOSSL_SN_MLKEM512":" \ + SCOSSL_OID_MLKEM512":" \ + SCOSSL_SN_MLKEM768":" \ + SCOSSL_OID_MLKEM768":" \ + SCOSSL_SN_MLKEM1024":" \ + SCOSSL_OID_MLKEM1024 + +#ifdef __cplusplus +} +#endif \ No newline at end of file