548 lines
15 KiB
C
548 lines
15 KiB
C
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
|
|
// SPDX-License-Identifier: Apache-2.0 OR ISC
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
#include <openssl/err.h>
|
|
#include <openssl/mem.h>
|
|
|
|
#include "../crypto/evp_extra/internal.h"
|
|
#include "internal.h"
|
|
#include "../delocate.h"
|
|
#include "../kem/internal.h"
|
|
#include "../../internal.h"
|
|
#include "internal.h"
|
|
|
|
typedef struct {
|
|
const KEM *kem;
|
|
} KEM_PKEY_CTX;
|
|
|
|
static int pkey_kem_init(EVP_PKEY_CTX *ctx) {
|
|
KEM_PKEY_CTX *dctx;
|
|
dctx = OPENSSL_zalloc(sizeof(KEM_PKEY_CTX));
|
|
if (dctx == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
ctx->data = dctx;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void pkey_kem_cleanup(EVP_PKEY_CTX *ctx) {
|
|
OPENSSL_free(ctx->data);
|
|
}
|
|
|
|
static int pkey_kem_keygen_deterministic(EVP_PKEY_CTX *ctx,
|
|
EVP_PKEY *pkey,
|
|
const uint8_t *seed,
|
|
size_t *seed_len) {
|
|
GUARD_PTR(ctx);
|
|
KEM_PKEY_CTX *dctx = ctx->data;
|
|
GUARD_PTR(dctx);
|
|
const KEM *kem = dctx->kem;
|
|
if (kem == NULL) {
|
|
if (ctx->pkey == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET);
|
|
return 0;
|
|
}
|
|
kem = KEM_KEY_get0_kem(ctx->pkey->pkey.kem_key);
|
|
}
|
|
|
|
// Check that size buffers can be written to.
|
|
if (seed_len == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
// Caller is getting parameter values.
|
|
if (seed == NULL) {
|
|
*seed_len = kem->keygen_seed_len;
|
|
return 1;
|
|
}
|
|
|
|
// The seed should be of the correct length.
|
|
if (*seed_len != kem->keygen_seed_len) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PARAMETERS);
|
|
return 0;
|
|
}
|
|
|
|
KEM_KEY *key = KEM_KEY_new();
|
|
size_t pubkey_len = kem->public_key_len;
|
|
size_t secret_len = kem->secret_key_len;
|
|
if (key == NULL ||
|
|
!KEM_KEY_init(key, kem) ||
|
|
!kem->method->keygen_deterministic(key->public_key, &pubkey_len, key->secret_key, &secret_len, seed) ||
|
|
!EVP_PKEY_assign(pkey, EVP_PKEY_KEM, key)) {
|
|
KEM_KEY_free(key);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int pkey_kem_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
|
|
GUARD_PTR(ctx);
|
|
KEM_PKEY_CTX *dctx = ctx->data;
|
|
GUARD_PTR(dctx);
|
|
const KEM *kem = dctx->kem;
|
|
if (kem == NULL) {
|
|
if (ctx->pkey == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET);
|
|
return 0;
|
|
}
|
|
kem = KEM_KEY_get0_kem(ctx->pkey->pkey.kem_key);
|
|
}
|
|
|
|
KEM_KEY *key = KEM_KEY_new();
|
|
size_t pubkey_len = kem->public_key_len;
|
|
size_t secret_len = kem->secret_key_len;
|
|
if (key == NULL ||
|
|
!KEM_KEY_init(key, kem) ||
|
|
!kem->method->keygen(key->public_key, &pubkey_len, key->secret_key, &secret_len) ||
|
|
!EVP_PKEY_set_type(pkey, EVP_PKEY_KEM)) {
|
|
KEM_KEY_free(key);
|
|
return 0;
|
|
}
|
|
pkey->pkey.kem_key = key;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int pkey_kem_encapsulate_deterministic(EVP_PKEY_CTX *ctx,
|
|
uint8_t *ciphertext,
|
|
size_t *ciphertext_len,
|
|
uint8_t *shared_secret,
|
|
size_t *shared_secret_len,
|
|
const uint8_t *seed,
|
|
size_t *seed_len) {
|
|
GUARD_PTR(ctx);
|
|
KEM_PKEY_CTX *dctx = ctx->data;
|
|
GUARD_PTR(dctx);
|
|
const KEM *kem = dctx->kem;
|
|
if (kem == NULL) {
|
|
if (ctx->pkey == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET);
|
|
return 0;
|
|
}
|
|
kem = KEM_KEY_get0_kem(ctx->pkey->pkey.kem_key);
|
|
}
|
|
|
|
// Check that size buffers can be written to.
|
|
if (ciphertext_len == NULL || shared_secret_len == NULL || seed_len == NULL ) {
|
|
OPENSSL_PUT_ERROR(EVP, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
// Caller is getting parameter values.
|
|
if (ciphertext == NULL && shared_secret == NULL && seed == NULL) {
|
|
*ciphertext_len = kem->ciphertext_len;
|
|
*shared_secret_len = kem->shared_secret_len;
|
|
*seed_len = kem->encaps_seed_len;
|
|
return 1;
|
|
}
|
|
|
|
// If not getting parameter values, then all three
|
|
// output buffers need to be valid (non-NULL)
|
|
if (ciphertext == NULL || shared_secret == NULL || seed == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_MISSING_PARAMETERS);
|
|
return 0;
|
|
}
|
|
|
|
// The output buffers need to be large enough.
|
|
if (*ciphertext_len < kem->ciphertext_len ||
|
|
*shared_secret_len < kem->shared_secret_len) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
|
|
return 0;
|
|
}
|
|
|
|
// The seed should be of the correct length.
|
|
if (*seed_len != kem->encaps_seed_len) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PARAMETERS);
|
|
return 0;
|
|
}
|
|
|
|
// Check that the context is properly configured.
|
|
if (ctx->pkey == NULL ||
|
|
ctx->pkey->pkey.kem_key == NULL ||
|
|
ctx->pkey->type != EVP_PKEY_KEM) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATON_NOT_INITIALIZED);
|
|
return 0;
|
|
}
|
|
|
|
// Check that the key has a public key set.
|
|
KEM_KEY *key = ctx->pkey->pkey.kem_key;
|
|
if (key->public_key == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_KEY_SET);
|
|
return 0;
|
|
}
|
|
|
|
if (!kem->method->encaps_deterministic(ciphertext, ciphertext_len, shared_secret, shared_secret_len, key->public_key, seed)) {
|
|
return 0;
|
|
}
|
|
|
|
// The size of the ciphertext and the shared secret
|
|
// that has been writen to the output buffers.
|
|
*ciphertext_len = kem->ciphertext_len;
|
|
*shared_secret_len = kem->shared_secret_len;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int pkey_kem_encapsulate(EVP_PKEY_CTX *ctx,
|
|
uint8_t *ciphertext,
|
|
size_t *ciphertext_len,
|
|
uint8_t *shared_secret,
|
|
size_t *shared_secret_len) {
|
|
KEM_PKEY_CTX *dctx = ctx->data;
|
|
const KEM *kem = dctx->kem;
|
|
if (kem == NULL) {
|
|
if (ctx->pkey == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET);
|
|
return 0;
|
|
}
|
|
kem = KEM_KEY_get0_kem(ctx->pkey->pkey.kem_key);
|
|
}
|
|
|
|
// Caller is getting parameter values.
|
|
if (ciphertext == NULL && shared_secret == NULL) {
|
|
*ciphertext_len = kem->ciphertext_len;
|
|
*shared_secret_len = kem->shared_secret_len;
|
|
return 1;
|
|
}
|
|
|
|
// If not getting parameter values, then both
|
|
// output buffers need to be valid (non-NULL)
|
|
if (ciphertext == NULL || shared_secret == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_MISSING_PARAMETERS);
|
|
return 0;
|
|
}
|
|
|
|
// The output buffers need to be large enough.
|
|
if (*ciphertext_len < kem->ciphertext_len ||
|
|
*shared_secret_len < kem->shared_secret_len) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
|
|
return 0;
|
|
}
|
|
|
|
// Check that the context is properly configured.
|
|
if (ctx->pkey == NULL ||
|
|
ctx->pkey->pkey.kem_key == NULL ||
|
|
ctx->pkey->type != EVP_PKEY_KEM) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATON_NOT_INITIALIZED);
|
|
return 0;
|
|
}
|
|
|
|
// Check that the key has a public key set.
|
|
KEM_KEY *key = ctx->pkey->pkey.kem_key;
|
|
if (key->public_key == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_KEY_SET);
|
|
return 0;
|
|
}
|
|
|
|
if (!kem->method->encaps(ciphertext, ciphertext_len, shared_secret, shared_secret_len, key->public_key)) {
|
|
return 0;
|
|
}
|
|
|
|
// The size of the ciphertext and the shared secret
|
|
// that has been writen to the output buffers.
|
|
*ciphertext_len = kem->ciphertext_len;
|
|
*shared_secret_len = kem->shared_secret_len;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int pkey_kem_decapsulate(EVP_PKEY_CTX *ctx,
|
|
uint8_t *shared_secret,
|
|
size_t *shared_secret_len,
|
|
const uint8_t *ciphertext,
|
|
size_t ciphertext_len) {
|
|
KEM_PKEY_CTX *dctx = ctx->data;
|
|
const KEM *kem = dctx->kem;
|
|
if (kem == NULL) {
|
|
if (ctx->pkey == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET);
|
|
return 0;
|
|
}
|
|
kem = KEM_KEY_get0_kem(ctx->pkey->pkey.kem_key);
|
|
}
|
|
|
|
// Caller is getting parameter values.
|
|
if (shared_secret == NULL) {
|
|
*shared_secret_len = kem->shared_secret_len;
|
|
return 1;
|
|
}
|
|
|
|
// The input and output buffers need to be large enough.
|
|
if (ciphertext_len != kem->ciphertext_len ||
|
|
*shared_secret_len < kem->shared_secret_len) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
// Check that the context is properly configured.
|
|
if (ctx->pkey == NULL ||
|
|
ctx->pkey->pkey.kem_key == NULL ||
|
|
ctx->pkey->type != EVP_PKEY_KEM) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATON_NOT_INITIALIZED);
|
|
return 0;
|
|
}
|
|
|
|
// Check that the key has a secret key set.
|
|
KEM_KEY *key = ctx->pkey->pkey.kem_key;
|
|
if (key->secret_key == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_KEY_SET);
|
|
return 0;
|
|
}
|
|
|
|
if (!kem->method->decaps(shared_secret, shared_secret_len, ciphertext, key->secret_key)) {
|
|
return 0;
|
|
}
|
|
|
|
// The size of the shared secret that has been written to the output buffer.
|
|
*shared_secret_len = kem->shared_secret_len;
|
|
|
|
return 1;
|
|
}
|
|
|
|
DEFINE_METHOD_FUNCTION(EVP_PKEY_METHOD, EVP_PKEY_kem_pkey_meth) {
|
|
out->pkey_id = EVP_PKEY_KEM;
|
|
out->init = pkey_kem_init;
|
|
out->copy = NULL;
|
|
out->cleanup = pkey_kem_cleanup;
|
|
out->keygen = pkey_kem_keygen;
|
|
out->sign_init = NULL;
|
|
out->sign = NULL;
|
|
out->sign_message = NULL;
|
|
out->verify_init = NULL;
|
|
out->verify = NULL;
|
|
out->verify_message = NULL;
|
|
out->verify_recover = NULL;
|
|
out->encrypt = NULL;
|
|
out->decrypt = NULL;
|
|
out->derive = NULL;
|
|
out->paramgen = NULL;
|
|
out->ctrl = NULL;
|
|
out->ctrl_str = NULL;
|
|
out->keygen_deterministic = pkey_kem_keygen_deterministic;
|
|
out->encapsulate_deterministic = pkey_kem_encapsulate_deterministic;
|
|
out->encapsulate = pkey_kem_encapsulate;
|
|
out->decapsulate = pkey_kem_decapsulate;
|
|
}
|
|
|
|
// Additional KEM specific EVP functions.
|
|
|
|
int EVP_PKEY_CTX_kem_set_params(EVP_PKEY_CTX *ctx, int nid) {
|
|
if (ctx == NULL || ctx->data == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
// It's not allowed to change context parameters if
|
|
// a PKEY is already associated with the context.
|
|
if (ctx->pkey != NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_OPERATION);
|
|
return 0;
|
|
}
|
|
|
|
const KEM *kem = KEM_find_kem_by_nid(nid);
|
|
if (kem == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM);
|
|
return 0;
|
|
}
|
|
|
|
KEM_PKEY_CTX *dctx = ctx->data;
|
|
dctx->kem = kem;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
// This function sets KEM parameters defined by |nid| in |pkey|.
|
|
int EVP_PKEY_kem_set_params(EVP_PKEY *pkey, int nid) {
|
|
const KEM *kem = KEM_find_kem_by_nid(nid);
|
|
if (kem == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM);
|
|
return 0;
|
|
}
|
|
|
|
evp_pkey_set_method(pkey, &kem_asn1_meth);
|
|
|
|
KEM_KEY *key = KEM_KEY_new();
|
|
if (key == NULL) {
|
|
// KEM_KEY_new sets the appropriate error.
|
|
return 0;
|
|
}
|
|
|
|
key->kem = kem;
|
|
pkey->pkey.kem_key = key;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Returns a fresh EVP_PKEY object of type EVP_PKEY_KEM,
|
|
// and sets KEM parameters defined by |nid|.
|
|
static EVP_PKEY *EVP_PKEY_kem_new(int nid) {
|
|
EVP_PKEY *ret = EVP_PKEY_new();
|
|
if (ret == NULL || !EVP_PKEY_kem_set_params(ret, nid)) {
|
|
EVP_PKEY_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
EVP_PKEY *EVP_PKEY_kem_new_raw_public_key(int nid, const uint8_t *in, size_t len) {
|
|
if (in == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, ERR_R_PASSED_NULL_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
EVP_PKEY *ret = EVP_PKEY_kem_new(nid);
|
|
if (ret == NULL || ret->pkey.kem_key == NULL) {
|
|
// EVP_PKEY_kem_new sets the appropriate error.
|
|
goto err;
|
|
}
|
|
|
|
const KEM *kem = KEM_KEY_get0_kem(ret->pkey.kem_key);
|
|
if (kem->public_key_len != len) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE);
|
|
goto err;
|
|
}
|
|
|
|
if (!KEM_KEY_set_raw_public_key(ret->pkey.kem_key, in)) {
|
|
// KEM_KEY_set_raw_public_key sets the appropriate error.
|
|
goto err;
|
|
}
|
|
|
|
return ret;
|
|
|
|
err:
|
|
EVP_PKEY_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
EVP_PKEY *EVP_PKEY_kem_new_raw_secret_key(int nid, const uint8_t *in, size_t len) {
|
|
if (in == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, ERR_R_PASSED_NULL_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
EVP_PKEY *ret = EVP_PKEY_kem_new(nid);
|
|
if (ret == NULL || ret->pkey.kem_key == NULL) {
|
|
// EVP_PKEY_kem_new sets the appropriate error.
|
|
goto err;
|
|
}
|
|
|
|
const KEM *kem = KEM_KEY_get0_kem(ret->pkey.kem_key);
|
|
if (kem->secret_key_len != len) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE);
|
|
goto err;
|
|
}
|
|
|
|
if (!KEM_KEY_set_raw_secret_key(ret->pkey.kem_key, in)) {
|
|
// KEM_KEY_set_raw_secret_key sets the appropriate error.
|
|
goto err;
|
|
}
|
|
|
|
return ret;
|
|
|
|
err:
|
|
EVP_PKEY_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
EVP_PKEY *EVP_PKEY_kem_new_raw_key(int nid,
|
|
const uint8_t *in_public, size_t len_public,
|
|
const uint8_t *in_secret, size_t len_secret) {
|
|
if (in_public == NULL || in_secret == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, ERR_R_PASSED_NULL_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
EVP_PKEY *ret = EVP_PKEY_kem_new(nid);
|
|
if (ret == NULL || ret->pkey.kem_key == NULL) {
|
|
// EVP_PKEY_kem_new sets the appropriate error.
|
|
goto err;
|
|
}
|
|
|
|
const KEM *kem = KEM_KEY_get0_kem(ret->pkey.kem_key);
|
|
if (kem->public_key_len != len_public || kem->secret_key_len != len_secret) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE);
|
|
goto err;
|
|
}
|
|
|
|
if (!KEM_KEY_set_raw_key(ret->pkey.kem_key, in_public, in_secret)) {
|
|
// KEM_KEY_set_raw_key sets the appropriate error.
|
|
goto err;
|
|
}
|
|
|
|
return ret;
|
|
|
|
err:
|
|
EVP_PKEY_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
// EVP_PKEY_kem_check_key validates that the public key in |key| corresponds
|
|
// to the secret key in |key| by performing encapsulation and decapsulation
|
|
// and checking that the generated shared secrets are equal.
|
|
int EVP_PKEY_kem_check_key(EVP_PKEY *key) {
|
|
if (key == NULL || key->pkey.kem_key == NULL ||
|
|
key->pkey.kem_key->public_key == NULL ||
|
|
key->pkey.kem_key->secret_key == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key, NULL);
|
|
if (ctx == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
|
|
return 0;
|
|
}
|
|
|
|
int ret = 0;
|
|
|
|
// Get the required buffer lengths and allocate the buffers.
|
|
size_t ct_len, ss_len;
|
|
uint8_t *ct = NULL, *ss_a = NULL, *ss_b = NULL;
|
|
if (!EVP_PKEY_encapsulate(ctx, NULL, &ct_len, NULL, &ss_len)) {
|
|
OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
|
|
goto end;
|
|
}
|
|
ct = OPENSSL_malloc(ct_len);
|
|
ss_a = OPENSSL_malloc(ss_len);
|
|
ss_b = OPENSSL_malloc(ss_len);
|
|
if (ct == NULL || ss_a == NULL || ss_b == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
|
|
goto end;
|
|
}
|
|
|
|
// Encapsulate and decapsulate.
|
|
if (!EVP_PKEY_encapsulate(ctx, ct, &ct_len, ss_b, &ss_len) ||
|
|
!EVP_PKEY_decapsulate(ctx, ss_a, &ss_len, ct, ct_len)) {
|
|
OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
|
|
goto end;
|
|
}
|
|
|
|
// Compare the shared secrets.
|
|
uint8_t res = 0;
|
|
for (size_t i = 0; i < ss_len; i++) {
|
|
res |= ss_a[i] ^ ss_b[i];
|
|
}
|
|
|
|
// If the shared secrets |ss_a| and |ss_b| are the same then |res| is equal
|
|
// to zero, otherwise it's not. |constant_time_is_zero_8| returns 0xff when
|
|
// |res| is equal to zero, and returns 0 otherwise. To be consistent with the
|
|
// rest of the library, we extract only the first bit so that |ret| is either
|
|
// 0 or 1.
|
|
ret = constant_time_is_zero_8(res) & 1;
|
|
|
|
end:
|
|
OPENSSL_free(ct);
|
|
OPENSSL_free(ss_a);
|
|
OPENSSL_free(ss_b);
|
|
EVP_PKEY_CTX_free(ctx);
|
|
return ret;
|
|
}
|