373 lines
11 KiB
C
373 lines
11 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 "../delocate.h"
|
|
#include "../ml_dsa/ml_dsa.h"
|
|
#include "../crypto/internal.h"
|
|
#include "../pqdsa/internal.h"
|
|
|
|
// PQDSA PKEY functions
|
|
|
|
typedef struct {
|
|
const PQDSA *pqdsa;
|
|
} PQDSA_PKEY_CTX;
|
|
|
|
static int pkey_pqdsa_init(EVP_PKEY_CTX *ctx) {
|
|
PQDSA_PKEY_CTX *dctx;
|
|
dctx = OPENSSL_zalloc(sizeof(PQDSA_PKEY_CTX));
|
|
if (dctx == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
ctx->data = dctx;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void pkey_pqdsa_cleanup(EVP_PKEY_CTX *ctx) {
|
|
OPENSSL_free(ctx->data);
|
|
}
|
|
|
|
static int pkey_pqdsa_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
|
|
GUARD_PTR(ctx);
|
|
PQDSA_PKEY_CTX *dctx = ctx->data;
|
|
GUARD_PTR(dctx);
|
|
const PQDSA *pqdsa = dctx->pqdsa;
|
|
if (pqdsa == NULL) {
|
|
if (ctx->pkey == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET);
|
|
return 0;
|
|
}
|
|
pqdsa = PQDSA_KEY_get0_dsa(ctx->pkey->pkey.pqdsa_key);
|
|
}
|
|
|
|
PQDSA_KEY *key = PQDSA_KEY_new();
|
|
if (key == NULL ||
|
|
!PQDSA_KEY_init(key, pqdsa) ||
|
|
!pqdsa->method->pqdsa_keygen(key->public_key, key->private_key, key->seed) ||
|
|
!EVP_PKEY_assign(pkey, EVP_PKEY_PQDSA, key)) {
|
|
PQDSA_KEY_free(key);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int pkey_pqdsa_sign_generic(EVP_PKEY_CTX *ctx, uint8_t *sig,
|
|
size_t *sig_len, const uint8_t *message,
|
|
size_t message_len, int sign_digest) {
|
|
GUARD_PTR(sig_len);
|
|
|
|
PQDSA_PKEY_CTX *dctx = ctx->data;
|
|
const PQDSA *pqdsa = dctx->pqdsa;
|
|
if (pqdsa == NULL) {
|
|
if (ctx->pkey == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET);
|
|
return 0;
|
|
}
|
|
pqdsa = PQDSA_KEY_get0_dsa(ctx->pkey->pkey.pqdsa_key);
|
|
}
|
|
|
|
// Caller is getting parameter values.
|
|
if (sig == NULL) {
|
|
*sig_len = pqdsa->signature_len;
|
|
return 1;
|
|
}
|
|
|
|
if (*sig_len != pqdsa->signature_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.pqdsa_key == NULL ||
|
|
ctx->pkey->type != EVP_PKEY_PQDSA) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATON_NOT_INITIALIZED);
|
|
return 0;
|
|
}
|
|
|
|
PQDSA_KEY *key = ctx->pkey->pkey.pqdsa_key;
|
|
if (!key || !key->private_key) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_KEY_SET);
|
|
return 0;
|
|
}
|
|
|
|
// |sign_digest| is a flag we use to indicate that the message to be signed has
|
|
// already been pre-processed and hashed into a message digest.
|
|
// When the PQDSA algorithm is selected as ML-DSA (i.e., NID_MLDSA{44/65/87}),
|
|
// |sign_digest| indicates that the input is |mu| which is the result of a SHAKE256
|
|
// hash of the associated public key concatenated with a zero byte to indicate
|
|
// pure-mode, the context string length, the contents of the context string,
|
|
// and the input message in this order e.g.
|
|
// mu = SHAKE256(SHAKE256(pk) || 0 || |ctx| || ctx || M).
|
|
|
|
// RAW sign mode
|
|
if (!sign_digest) {
|
|
if (!pqdsa->method->pqdsa_sign_message(key->private_key, sig, sig_len, message, message_len, NULL, 0)) {
|
|
OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
|
|
return 0;
|
|
}
|
|
}
|
|
// DIGEST sign mode
|
|
else {
|
|
if (!pqdsa->method->pqdsa_sign(key->private_key, sig, sig_len, message, message_len)) {
|
|
OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// DIGEST signing
|
|
static int pkey_pqdsa_sign(EVP_PKEY_CTX *ctx, uint8_t *sig,
|
|
size_t *sig_len, const uint8_t *digest,
|
|
size_t digest_len) {
|
|
return pkey_pqdsa_sign_generic(ctx, sig, sig_len, digest, digest_len, 1);
|
|
}
|
|
|
|
// RAW message signing
|
|
static int pkey_pqdsa_sign_message(EVP_PKEY_CTX *ctx, uint8_t *sig,
|
|
size_t *sig_len, const uint8_t *message,
|
|
size_t message_len) {
|
|
return pkey_pqdsa_sign_generic(ctx, sig, sig_len, message, message_len, 0);
|
|
}
|
|
|
|
static int pkey_pqdsa_verify_generic(EVP_PKEY_CTX *ctx, const uint8_t *sig,
|
|
size_t sig_len, const uint8_t *message,
|
|
size_t message_len, int verify_digest) {
|
|
PQDSA_PKEY_CTX *dctx = ctx->data;
|
|
const PQDSA *pqdsa = dctx->pqdsa;
|
|
|
|
if (sig == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_MISSING_PARAMETERS);
|
|
return 0;
|
|
}
|
|
|
|
if (pqdsa == NULL) {
|
|
if (ctx->pkey == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET);
|
|
return 0;
|
|
}
|
|
|
|
pqdsa = PQDSA_KEY_get0_dsa(ctx->pkey->pkey.pqdsa_key);
|
|
}
|
|
// Check that the context is properly configured.
|
|
if (ctx->pkey == NULL ||
|
|
ctx->pkey->pkey.pqdsa_key == NULL ||
|
|
ctx->pkey->type != EVP_PKEY_PQDSA) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATON_NOT_INITIALIZED);
|
|
return 0;
|
|
}
|
|
|
|
PQDSA_KEY *key = ctx->pkey->pkey.pqdsa_key;
|
|
if (!key || !key->public_key) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_KEY_SET);
|
|
return 0;
|
|
}
|
|
|
|
// |verify_digest| is a flag we use to indicate that the message to be verified has
|
|
// already been pre-processed and hashed into a message digest.
|
|
// When the PQDSA algorithm is selected as ML-DSA (i.e., NID_MLDSA{44/65/87}),
|
|
// |verify_digest| indicates that the input is |mu| which is the result of a SHAKE256
|
|
// hash of the associated public key concatenated with a zero byte to indicate
|
|
// pure-mode, the context string length, the contents of the context string,
|
|
// and the input message in this order e.g.
|
|
// mu = SHAKE256(SHAKE256(pk) || 0 || |ctx| || ctx || M).
|
|
|
|
// RAW verify mode
|
|
if(!verify_digest) {
|
|
if (sig_len != pqdsa->signature_len ||
|
|
!pqdsa->method->pqdsa_verify_message(key->public_key, sig, sig_len, message, message_len, NULL, 0)) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_SIGNATURE);
|
|
return 0;
|
|
}
|
|
}
|
|
// DIGEST verify mode
|
|
else {
|
|
if (sig_len != pqdsa->signature_len ||
|
|
!pqdsa->method->pqdsa_verify(key->public_key, sig, sig_len, message, message_len)) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_SIGNATURE);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// DIGEST verification
|
|
static int pkey_pqdsa_verify(EVP_PKEY_CTX *ctx, const uint8_t *sig,
|
|
size_t sig_len, const uint8_t *message,
|
|
size_t message_len) {
|
|
return pkey_pqdsa_verify_generic(ctx, sig, sig_len, message, message_len, 1);
|
|
}
|
|
|
|
// RAW message verification
|
|
static int pkey_pqdsa_verify_message(EVP_PKEY_CTX *ctx, const uint8_t *sig,
|
|
size_t sig_len, const uint8_t *message,
|
|
size_t message_len) {
|
|
return pkey_pqdsa_verify_generic(ctx, sig, sig_len, message, message_len, 0);
|
|
}
|
|
|
|
// Additional PQDSA specific EVP functions.
|
|
|
|
// This function sets pqdsa parameters defined by |nid| in |pkey|.
|
|
int EVP_PKEY_pqdsa_set_params(EVP_PKEY *pkey, int nid) {
|
|
const PQDSA *pqdsa = PQDSA_find_dsa_by_nid(nid);
|
|
|
|
if (pqdsa == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM);
|
|
return 0;
|
|
}
|
|
|
|
evp_pkey_set_method(pkey, &pqdsa_asn1_meth);
|
|
|
|
PQDSA_KEY *key = PQDSA_KEY_new();
|
|
if (key == NULL) {
|
|
// PQDSA_KEY_new sets the appropriate error.
|
|
return 0;
|
|
}
|
|
|
|
key->pqdsa = pqdsa;
|
|
pkey->pkey.pqdsa_key = key;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Takes an EVP_PKEY_CTX object |ctx| and sets pqdsa parameters defined
|
|
// by |nid|
|
|
int EVP_PKEY_CTX_pqdsa_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 PQDSA *pqdsa = PQDSA_find_dsa_by_nid(nid);
|
|
if (pqdsa == NULL) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM);
|
|
return 0;
|
|
}
|
|
|
|
PQDSA_PKEY_CTX *dctx = ctx->data;
|
|
dctx->pqdsa = pqdsa;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Returns a fresh EVP_PKEY object of type EVP_PKEY_PQDSA,
|
|
// and sets PQDSA parameters defined by |nid|.
|
|
static EVP_PKEY *EVP_PKEY_pqdsa_new(int nid) {
|
|
EVP_PKEY *ret = EVP_PKEY_new();
|
|
if (ret == NULL || !EVP_PKEY_pqdsa_set_params(ret, nid)) {
|
|
EVP_PKEY_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
EVP_PKEY *EVP_PKEY_pqdsa_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_pqdsa_new(nid);
|
|
if (ret == NULL || ret->pkey.pqdsa_key == NULL) {
|
|
// EVP_PKEY_pqdsa_new sets the appropriate error.
|
|
goto err;
|
|
}
|
|
|
|
CBS cbs;
|
|
CBS_init(&cbs, in, len);
|
|
if (!PQDSA_KEY_set_raw_public_key(ret->pkey.pqdsa_key, &cbs)) {
|
|
// PQDSA_KEY_set_raw_public_key sets the appropriate error.
|
|
goto err;
|
|
}
|
|
|
|
return ret;
|
|
|
|
err:
|
|
EVP_PKEY_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
EVP_PKEY *EVP_PKEY_pqdsa_new_raw_private_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_pqdsa_new(nid);
|
|
if (ret == NULL || ret->pkey.pqdsa_key == NULL) {
|
|
// EVP_PKEY_pqdsa_new sets the appropriate error.
|
|
goto err;
|
|
}
|
|
|
|
// Get PQDSA instance and validate lengths
|
|
const PQDSA *pqdsa = PQDSA_KEY_get0_dsa(ret->pkey.pqdsa_key);
|
|
if (len != pqdsa->private_key_len && len != pqdsa->keygen_seed_len) {
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE);
|
|
goto err;
|
|
}
|
|
|
|
CBS cbs;
|
|
CBS_init(&cbs, in, len);
|
|
|
|
// Set key based on input length
|
|
if (len == pqdsa->private_key_len) {
|
|
if (!PQDSA_KEY_set_raw_private_key(ret->pkey.pqdsa_key, &cbs)) {
|
|
// PQDSA_KEY_set_raw_private_key sets the appropriate error.
|
|
goto err;
|
|
}
|
|
} else if (len == pqdsa->keygen_seed_len) {
|
|
if (!PQDSA_KEY_set_raw_keypair_from_seed(ret->pkey.pqdsa_key, &cbs)) {
|
|
// PQDSA_KEY_set_raw_keypair_from_seed sets the appropriate error.
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
|
|
err:
|
|
EVP_PKEY_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
DEFINE_METHOD_FUNCTION(EVP_PKEY_METHOD, EVP_PKEY_pqdsa_pkey_meth) {
|
|
out->pkey_id = EVP_PKEY_PQDSA;
|
|
out->init = pkey_pqdsa_init;
|
|
out->copy = NULL;
|
|
out->cleanup = pkey_pqdsa_cleanup;
|
|
out->keygen = pkey_pqdsa_keygen;
|
|
out->sign_init = NULL;
|
|
out->sign = pkey_pqdsa_sign;
|
|
out->sign_message = pkey_pqdsa_sign_message;
|
|
out->verify_init = NULL;
|
|
out->verify = pkey_pqdsa_verify;
|
|
out->verify_message = pkey_pqdsa_verify_message;
|
|
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 = NULL;
|
|
out->encapsulate_deterministic = NULL;
|
|
out->encapsulate = NULL;
|
|
out->decapsulate = NULL;
|
|
}
|