Implement seed/key preference when decoding
- Moved the codec code out of `ml_kem.c` into its own file in the provider tree. Will be easier to share some code with ML-DSA, and possible to use PROV_CTX, to do config lookups directly in the functions doing the work. - Update and fixes of the EVP_PKEY-ML-KEM(8) documentation, which had accumulated some stale/inaccurate material, and needed new text for the "prefer_seed" parameter. - Test the "prefer_seed=no" behaviour. Reviewed-by: Shane Lontis <shane.lontis@oracle.com> Reviewed-by: Paul Dale <ppzgs1@gmail.com> (Merged from https://github.com/openssl/openssl/pull/26569)
This commit is contained in:
parent
cc5403f33a
commit
5b2d996f91
13 changed files with 873 additions and 731 deletions
|
@ -7,17 +7,12 @@
|
||||||
* https://www.openssl.org/source/license.html
|
* https://www.openssl.org/source/license.html
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <openssl/rand.h>
|
|
||||||
#include <openssl/byteorder.h>
|
#include <openssl/byteorder.h>
|
||||||
#include <openssl/proverr.h>
|
#include <openssl/rand.h>
|
||||||
#include <openssl/x509.h>
|
|
||||||
#include "crypto/ctype.h"
|
|
||||||
#include "crypto/ml_kem.h"
|
#include "crypto/ml_kem.h"
|
||||||
#include "internal/common.h"
|
#include "internal/common.h"
|
||||||
#include "internal/constant_time.h"
|
#include "internal/constant_time.h"
|
||||||
#include "internal/sha3.h"
|
#include "internal/sha3.h"
|
||||||
#include "internal/encoder.h"
|
|
||||||
|
|
||||||
#if defined(OPENSSL_CONSTANT_TIME_VALIDATION)
|
#if defined(OPENSSL_CONSTANT_TIME_VALIDATION)
|
||||||
#include <valgrind/memcheck.h>
|
#include <valgrind/memcheck.h>
|
||||||
|
@ -179,89 +174,12 @@ static void scalar_encode(uint8_t *out, const scalar *s, int bits);
|
||||||
#define ML_KEM_768_VINFO 1
|
#define ML_KEM_768_VINFO 1
|
||||||
#define ML_KEM_1024_VINFO 2
|
#define ML_KEM_1024_VINFO 2
|
||||||
|
|
||||||
/*-
|
|
||||||
* Tables describing supported ASN.1 input/output formats.
|
|
||||||
*
|
|
||||||
* On output the PKCS8 info table order is important:
|
|
||||||
* - When we have a seed we'll use the first entry with a non-zero seed offset.
|
|
||||||
* - Otherwise, the first entry with a zero seed offset.
|
|
||||||
*
|
|
||||||
* As written, when possible, we prefer to output both the seed and private
|
|
||||||
* key, otherwise, just the private key ([1] IMPLICIT OCTET STRING form).
|
|
||||||
*
|
|
||||||
* The various lengths in the PKCS#8 tag/len fields could have been left
|
|
||||||
* zeroed, and filled in on the fly from the algorithm parameters, but that
|
|
||||||
* makes the code more complex, so a choice was made to embed them directly
|
|
||||||
* into the tables. Had they been zeroed, one table could cover all three
|
|
||||||
* algorithms.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* ML-KEM-512:
|
|
||||||
* Public key bytes: 800 (0x0320)
|
|
||||||
* Private key bytes: 1632 (0x0660)
|
|
||||||
*/
|
|
||||||
static const ML_KEM_SPKI_INFO ml_kem_512_spki_info = {
|
|
||||||
{ 0x30, 0x82, 0x03, 0x32, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48,
|
|
||||||
0x01, 0x65, 0x03, 0x04, 0x04, 0x01, 0x03, 0x82, 0x03, 0x21, 0x00, }
|
|
||||||
};
|
|
||||||
static const ML_KEM_PKCS8_INFO ml_kem_512_pkcs8_info[] = {
|
|
||||||
{ "seed-priv", 1706, 0x308206a6, 0x0440, 6, 0x81820660, 74, 0, },
|
|
||||||
{ "priv-only", 1640, 0x30820664, 0, 0, 0x81820660, 8, 0, },
|
|
||||||
{ "seed-only", 68, 0x30420440, 0x0440, 4, 0, 0, 0, },
|
|
||||||
{ "priv-oqs", 1636, 0x04820660, 0, 0, 0x04820660, 4, 0, },
|
|
||||||
{ "pair-oqs", 2436, 0x04820980, 0, 0, 0x04820980, 4, 1636, },
|
|
||||||
};
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* ML-KEM-768:
|
|
||||||
* Public key bytes: 1184 (0x04a0)
|
|
||||||
* Private key bytes: 2400 (0x0960)
|
|
||||||
*/
|
|
||||||
static const ML_KEM_SPKI_INFO ml_kem_768_spki_info = {
|
|
||||||
{ 0x30, 0x82, 0x04, 0xb2, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48,
|
|
||||||
0x01, 0x65, 0x03, 0x04, 0x04, 0x02, 0x03, 0x82, 0x04, 0xa1, 0x00, }
|
|
||||||
};
|
|
||||||
static const ML_KEM_PKCS8_INFO ml_kem_768_pkcs8_info[] = {
|
|
||||||
{ "seed-priv", 2474, 0x308209a6, 0x0440, 6, 0x81820960, 74, 0, },
|
|
||||||
{ "priv-only", 2408, 0x30820964, 0, 0, 0x81820960, 8, 0, },
|
|
||||||
{ "seed-only", 68, 0x30420440, 0x0440, 4, 0, 0, 0, },
|
|
||||||
{ "priv-oqs", 2404, 0x04820960, 0, 0, 0x04820960, 4, 0, },
|
|
||||||
{ "pair-oqs", 3588, 0x04820e00, 0, 0, 0x04820e00, 4, 2404, },
|
|
||||||
};
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* ML-KEM-1024:
|
|
||||||
* Private key bytes: 3168 (0x0c60)
|
|
||||||
* Public key bytes: 1568 (0x0620)
|
|
||||||
*/
|
|
||||||
static const ML_KEM_SPKI_INFO ml_kem_1024_spki_info = {
|
|
||||||
{ 0x30, 0x82, 0x06, 0x32, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48,
|
|
||||||
0x01, 0x65, 0x03, 0x04, 0x04, 0x03, 0x03, 0x82, 0x06, 0x21, 0x00, }
|
|
||||||
};
|
|
||||||
static const ML_KEM_PKCS8_INFO ml_kem_1024_pkcs8_info[] = {
|
|
||||||
{ "seed-priv", 3242, 0x30820ca6, 0x0440, 6, 0x81820c60, 74, 0, },
|
|
||||||
{ "priv-only", 3176, 0x30820c64, 0, 0, 0x81820c60, 8, 0, },
|
|
||||||
{ "seed-only", 68, 0x30420440, 0x0440, 4, 0, 0, 0, },
|
|
||||||
{ "priv-oqs", 3172, 0x04820c60, 0, 0, 0x04820c60, 4, 0, },
|
|
||||||
{ "pair-oqs", 4740, 0x04821280, 0, 0, 0x04821280, 4, 3172, },
|
|
||||||
};
|
|
||||||
|
|
||||||
#define NUM_PKCS8_FORMATS 5
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const ML_KEM_PKCS8_INFO *vp8_entry;
|
|
||||||
int vp8_pref;
|
|
||||||
} ML_KEM_PKCS8_PREF;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Per-variant fixed parameters
|
* Per-variant fixed parameters
|
||||||
*/
|
*/
|
||||||
static const ML_KEM_VINFO vinfo_map[3] = {
|
static const ML_KEM_VINFO vinfo_map[3] = {
|
||||||
{
|
{
|
||||||
"ML-KEM-512",
|
"ML-KEM-512",
|
||||||
&ml_kem_512_spki_info,
|
|
||||||
ml_kem_512_pkcs8_info,
|
|
||||||
PRVKEY_BYTES(512),
|
PRVKEY_BYTES(512),
|
||||||
sizeof(struct prvkey_512_alloc),
|
sizeof(struct prvkey_512_alloc),
|
||||||
PUBKEY_BYTES(512),
|
PUBKEY_BYTES(512),
|
||||||
|
@ -278,8 +196,6 @@ static const ML_KEM_VINFO vinfo_map[3] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ML-KEM-768",
|
"ML-KEM-768",
|
||||||
&ml_kem_768_spki_info,
|
|
||||||
ml_kem_768_pkcs8_info,
|
|
||||||
PRVKEY_BYTES(768),
|
PRVKEY_BYTES(768),
|
||||||
sizeof(struct prvkey_768_alloc),
|
sizeof(struct prvkey_768_alloc),
|
||||||
PUBKEY_BYTES(768),
|
PUBKEY_BYTES(768),
|
||||||
|
@ -296,8 +212,6 @@ static const ML_KEM_VINFO vinfo_map[3] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ML-KEM-1024",
|
"ML-KEM-1024",
|
||||||
&ml_kem_1024_spki_info,
|
|
||||||
ml_kem_1024_pkcs8_info,
|
|
||||||
PRVKEY_BYTES(1024),
|
PRVKEY_BYTES(1024),
|
||||||
sizeof(struct prvkey_1024_alloc),
|
sizeof(struct prvkey_1024_alloc),
|
||||||
PUBKEY_BYTES(1024),
|
PUBKEY_BYTES(1024),
|
||||||
|
@ -1637,8 +1551,6 @@ free_storage(ML_KEM_KEY *key)
|
||||||
if (ossl_ml_kem_have_prvkey(key))
|
if (ossl_ml_kem_have_prvkey(key))
|
||||||
OPENSSL_cleanse(key->s,
|
OPENSSL_cleanse(key->s,
|
||||||
key->vinfo->vector_bytes + 2 * ML_KEM_RANDOM_BYTES);
|
key->vinfo->vector_bytes + 2 * ML_KEM_RANDOM_BYTES);
|
||||||
else if (ossl_ml_kem_have_seed(key))
|
|
||||||
OPENSSL_cleanse(key->seedbuf, sizeof(key->seedbuf));
|
|
||||||
OPENSSL_free(key->t);
|
OPENSSL_free(key->t);
|
||||||
key->d = key->z = (uint8_t *)(key->s = key->m = key->t = NULL);
|
key->d = key->z = (uint8_t *)(key->s = key->m = key->t = NULL);
|
||||||
}
|
}
|
||||||
|
@ -1669,7 +1581,7 @@ const ML_KEM_VINFO *ossl_ml_kem_get_vinfo(int evp_type)
|
||||||
* once the key is generated.
|
* once the key is generated.
|
||||||
*/
|
*/
|
||||||
ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties,
|
ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties,
|
||||||
int retain_seed, int evp_type)
|
int evp_type)
|
||||||
{
|
{
|
||||||
const ML_KEM_VINFO *vinfo = ossl_ml_kem_get_vinfo(evp_type);
|
const ML_KEM_VINFO *vinfo = ossl_ml_kem_get_vinfo(evp_type);
|
||||||
ML_KEM_KEY *key;
|
ML_KEM_KEY *key;
|
||||||
|
@ -1682,12 +1594,13 @@ ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties,
|
||||||
|
|
||||||
key->vinfo = vinfo;
|
key->vinfo = vinfo;
|
||||||
key->libctx = libctx;
|
key->libctx = libctx;
|
||||||
key->retain_seed = retain_seed;
|
key->prefer_seed = 1;
|
||||||
|
key->retain_seed = 1;
|
||||||
key->shake128_md = EVP_MD_fetch(libctx, "SHAKE128", properties);
|
key->shake128_md = EVP_MD_fetch(libctx, "SHAKE128", properties);
|
||||||
key->shake256_md = EVP_MD_fetch(libctx, "SHAKE256", properties);
|
key->shake256_md = EVP_MD_fetch(libctx, "SHAKE256", properties);
|
||||||
key->sha3_256_md = EVP_MD_fetch(libctx, "SHA3-256", properties);
|
key->sha3_256_md = EVP_MD_fetch(libctx, "SHA3-256", properties);
|
||||||
key->sha3_512_md = EVP_MD_fetch(libctx, "SHA3-512", properties);
|
key->sha3_512_md = EVP_MD_fetch(libctx, "SHA3-512", properties);
|
||||||
key->d = key->z = key->rho = key->pkhash = NULL;
|
key->d = key->z = key->rho = key->pkhash = key->encoded_dk = NULL;
|
||||||
key->s = key->m = key->t = NULL;
|
key->s = key->m = key->t = NULL;
|
||||||
|
|
||||||
if (key->shake128_md != NULL
|
if (key->shake128_md != NULL
|
||||||
|
@ -1705,20 +1618,19 @@ ML_KEM_KEY *ossl_ml_kem_key_dup(const ML_KEM_KEY *key, int selection)
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
ML_KEM_KEY *ret;
|
ML_KEM_KEY *ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Partially decoded keys, not yet imported or loaded, should never be
|
||||||
|
* duplicated.
|
||||||
|
*/
|
||||||
|
if (ossl_ml_kem_decoded_key(key))
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (key == NULL
|
if (key == NULL
|
||||||
|| (ret = OPENSSL_memdup(key, sizeof(*key))) == NULL)
|
|| (ret = OPENSSL_memdup(key, sizeof(*key))) == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
ret->d = ret->z = ret->rho = ret->pkhash = NULL;
|
ret->d = ret->z = ret->rho = ret->pkhash = NULL;
|
||||||
ret->s = ret->m = ret->t = NULL;
|
ret->s = ret->m = ret->t = NULL;
|
||||||
|
|
||||||
/* Handle seed-only keys */
|
|
||||||
if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
|
|
||||||
&& !ossl_ml_kem_have_prvkey(key)
|
|
||||||
&& ossl_ml_kem_have_seed(key)) {
|
|
||||||
ret->z = ret->seedbuf;
|
|
||||||
ret->d = ret->z + ML_KEM_RANDOM_BYTES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear selection bits we can't fulfill */
|
/* Clear selection bits we can't fulfill */
|
||||||
if (!ossl_ml_kem_have_pubkey(key))
|
if (!ossl_ml_kem_have_pubkey(key))
|
||||||
selection = 0;
|
selection = 0;
|
||||||
|
@ -1765,6 +1677,13 @@ void ossl_ml_kem_key_free(ML_KEM_KEY *key)
|
||||||
EVP_MD_free(key->sha3_256_md);
|
EVP_MD_free(key->sha3_256_md);
|
||||||
EVP_MD_free(key->sha3_512_md);
|
EVP_MD_free(key->sha3_512_md);
|
||||||
|
|
||||||
|
if (ossl_ml_kem_decoded_key(key)) {
|
||||||
|
OPENSSL_cleanse(key->seedbuf, sizeof(key->seedbuf));
|
||||||
|
if (ossl_ml_kem_have_dkenc(key)) {
|
||||||
|
OPENSSL_cleanse(key->encoded_dk, key->vinfo->prvkey_bytes);
|
||||||
|
OPENSSL_free(key->encoded_dk);
|
||||||
|
}
|
||||||
|
}
|
||||||
free_storage(key);
|
free_storage(key);
|
||||||
OPENSSL_free(key);
|
OPENSSL_free(key);
|
||||||
}
|
}
|
||||||
|
@ -1838,7 +1757,9 @@ int ossl_ml_kem_parse_public_key(const uint8_t *in, size_t len, ML_KEM_KEY *key)
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* Keys with key material are immutable */
|
/* Keys with key material are immutable */
|
||||||
if (key == NULL || ossl_ml_kem_have_pubkey(key))
|
if (key == NULL
|
||||||
|
|| ossl_ml_kem_have_pubkey(key)
|
||||||
|
|| ossl_ml_kem_have_dkenc(key))
|
||||||
return 0;
|
return 0;
|
||||||
vinfo = key->vinfo;
|
vinfo = key->vinfo;
|
||||||
|
|
||||||
|
@ -1864,7 +1785,9 @@ int ossl_ml_kem_parse_private_key(const uint8_t *in, size_t len,
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* Keys with key material are immutable */
|
/* Keys with key material are immutable */
|
||||||
if (key == NULL || ossl_ml_kem_have_pubkey(key))
|
if (key == NULL
|
||||||
|
|| ossl_ml_kem_have_pubkey(key)
|
||||||
|
|| ossl_ml_kem_have_dkenc(key))
|
||||||
return 0;
|
return 0;
|
||||||
vinfo = key->vinfo;
|
vinfo = key->vinfo;
|
||||||
|
|
||||||
|
@ -1892,7 +1815,9 @@ int ossl_ml_kem_genkey(uint8_t *pubenc, size_t publen, ML_KEM_KEY *key)
|
||||||
const ML_KEM_VINFO *vinfo;
|
const ML_KEM_VINFO *vinfo;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (key == NULL || ossl_ml_kem_have_pubkey(key))
|
if (key == NULL
|
||||||
|
|| ossl_ml_kem_have_pubkey(key)
|
||||||
|
|| ossl_ml_kem_have_dkenc(key))
|
||||||
return 0;
|
return 0;
|
||||||
vinfo = key->vinfo;
|
vinfo = key->vinfo;
|
||||||
|
|
||||||
|
@ -1901,7 +1826,8 @@ int ossl_ml_kem_genkey(uint8_t *pubenc, size_t publen, ML_KEM_KEY *key)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (ossl_ml_kem_have_seed(key)) {
|
if (ossl_ml_kem_have_seed(key)) {
|
||||||
ossl_ml_kem_encode_seed(seed, sizeof(seed), key);
|
if (!ossl_ml_kem_encode_seed(seed, sizeof(seed), key))
|
||||||
|
return 0;
|
||||||
key->d = key->z = NULL;
|
key->d = key->z = NULL;
|
||||||
} else if (RAND_priv_bytes_ex(key->libctx, seed, sizeof(seed),
|
} else if (RAND_priv_bytes_ex(key->libctx, seed, sizeof(seed),
|
||||||
key->vinfo->secbits) <= 0) {
|
key->vinfo->secbits) <= 0) {
|
||||||
|
@ -2087,406 +2013,3 @@ int ossl_ml_kem_pubkey_cmp(const ML_KEM_KEY *key1, const ML_KEM_KEY *key2)
|
||||||
*/
|
*/
|
||||||
return (ossl_ml_kem_have_pubkey(key1) ^ ossl_ml_kem_have_pubkey(key2));
|
return (ossl_ml_kem_have_pubkey(key1) ^ ossl_ml_kem_have_pubkey(key2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef FIPS_MODULE
|
|
||||||
static int vp8_pref_cmp(const void *va, const void *vb)
|
|
||||||
{
|
|
||||||
const ML_KEM_PKCS8_PREF *a = va;
|
|
||||||
const ML_KEM_PKCS8_PREF *b = vb;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Zeros sort last, otherwise the sort is in increasing order.
|
|
||||||
*
|
|
||||||
* The preferences are small enough to ensure the comparison is monotone as
|
|
||||||
* required. Some versions of qsort(3) have been known to crash when the
|
|
||||||
* comparison is not monotone.
|
|
||||||
*/
|
|
||||||
if (a->vp8_pref > 0 && b->vp8_pref > 0)
|
|
||||||
return a->vp8_pref - b->vp8_pref;
|
|
||||||
if (a->vp8_pref == 0)
|
|
||||||
return b->vp8_pref;
|
|
||||||
return -a->vp8_pref;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ML_KEM_PKCS8_PREF *vp8_order(const ML_KEM_VINFO *v,
|
|
||||||
const char *direction, const char *formats)
|
|
||||||
{
|
|
||||||
ML_KEM_PKCS8_PREF *ret;
|
|
||||||
int i, count = 0;
|
|
||||||
const char *fmt = formats, *end;
|
|
||||||
const char *sep = "\t ,";
|
|
||||||
|
|
||||||
if ((ret = OPENSSL_zalloc((NUM_PKCS8_FORMATS + 1) * sizeof(*ret))) == NULL)
|
|
||||||
return NULL;
|
|
||||||
for (i = 0; i < NUM_PKCS8_FORMATS; ++i) {
|
|
||||||
ret[i].vp8_entry = &v->pkcs8_info[i];
|
|
||||||
ret[i].vp8_pref = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Default to compile-time table order. */
|
|
||||||
if (formats == NULL)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Formats are case-insensitive, separated by spaces, tabs and/or commas */
|
|
||||||
do {
|
|
||||||
if (*(fmt += strspn(fmt, sep)) == '\0')
|
|
||||||
break;
|
|
||||||
end = fmt + strcspn(fmt, sep);
|
|
||||||
for (i = 0; i < NUM_PKCS8_FORMATS; ++i) {
|
|
||||||
if (ret[i].vp8_pref > 0
|
|
||||||
|| OPENSSL_strncasecmp(ret[i].vp8_entry->p8_name,
|
|
||||||
fmt, (end - fmt)) != 0)
|
|
||||||
continue;
|
|
||||||
ret[i].vp8_pref = ++count;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fmt = end;
|
|
||||||
} while (count < NUM_PKCS8_FORMATS);
|
|
||||||
|
|
||||||
if (count == 0) {
|
|
||||||
OPENSSL_free(ret);
|
|
||||||
ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_KEM_NO_FORMAT,
|
|
||||||
"no %s private key %s formats are enabled",
|
|
||||||
v->algorithm_name, direction);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
qsort(ret, NUM_PKCS8_FORMATS, sizeof(*ret), vp8_pref_cmp);
|
|
||||||
ret[count].vp8_entry = NULL;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ML_KEM_KEY *
|
|
||||||
ossl_ml_kem_d2i_PUBKEY(const uint8_t *pubenc, int publen, int evp_type,
|
|
||||||
OSSL_LIB_CTX *libctx, const char *propq)
|
|
||||||
{
|
|
||||||
const ML_KEM_VINFO *v;
|
|
||||||
const ML_KEM_SPKI_INFO *vspki;
|
|
||||||
ML_KEM_KEY *ret;
|
|
||||||
|
|
||||||
if ((v = ossl_ml_kem_get_vinfo(evp_type)) == NULL)
|
|
||||||
return NULL;
|
|
||||||
vspki = v->spki_info;
|
|
||||||
if (publen != ML_KEM_SPKI_OVERHEAD + (ossl_ssize_t) v->pubkey_bytes
|
|
||||||
|| memcmp(pubenc, vspki->asn1_prefix, ML_KEM_SPKI_OVERHEAD) != 0)
|
|
||||||
return NULL;
|
|
||||||
publen -= ML_KEM_SPKI_OVERHEAD;
|
|
||||||
pubenc += ML_KEM_SPKI_OVERHEAD;
|
|
||||||
|
|
||||||
if ((ret = ossl_ml_kem_key_new(libctx, propq, 0, evp_type)) == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (!ossl_ml_kem_parse_public_key(pubenc, (size_t) publen, ret)) {
|
|
||||||
ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
|
|
||||||
"errror parsing %s public key from input SPKI",
|
|
||||||
v->algorithm_name);
|
|
||||||
ossl_ml_kem_key_free(ret);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ML_KEM_KEY *
|
|
||||||
ossl_ml_kem_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
|
|
||||||
int retain_seed, const char *formats,
|
|
||||||
int evp_type, OSSL_LIB_CTX *libctx,
|
|
||||||
const char *propq)
|
|
||||||
{
|
|
||||||
const ML_KEM_VINFO *v;
|
|
||||||
ML_KEM_PKCS8_PREF *vp8_alloc = NULL, *vp8_slot;
|
|
||||||
const ML_KEM_PKCS8_INFO *vp8;
|
|
||||||
ML_KEM_KEY *key = NULL, *ret = NULL;
|
|
||||||
PKCS8_PRIV_KEY_INFO *p8inf = NULL;
|
|
||||||
const uint8_t *buf, *pos;
|
|
||||||
const X509_ALGOR *alg = NULL;
|
|
||||||
int len, ptype;
|
|
||||||
uint32_t magic;
|
|
||||||
uint16_t seed_magic;
|
|
||||||
|
|
||||||
/* Which ML-KEM variant? */
|
|
||||||
if ((v = ossl_ml_kem_get_vinfo(evp_type)) == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Extract the key OID and any parameters. */
|
|
||||||
if ((p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &prvenc, prvlen)) == NULL)
|
|
||||||
return 0;
|
|
||||||
/* Shortest prefix is 4 bytes: seq tag/len + octet string tag/len */
|
|
||||||
if (!PKCS8_pkey_get0(NULL, &buf, &len, &alg, p8inf))
|
|
||||||
goto end;
|
|
||||||
/* Bail out early if this is some other key type. */
|
|
||||||
if (OBJ_obj2nid(alg->algorithm) != evp_type)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
/* Get the list of enabled decoders. Their order is not important here. */
|
|
||||||
vp8_slot = vp8_alloc = vp8_order(v, "input", formats);
|
|
||||||
if (vp8_alloc == NULL)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
/* Parameters must be absent. */
|
|
||||||
X509_ALGOR_get0(NULL, &ptype, NULL, alg);
|
|
||||||
if (ptype != V_ASN1_UNDEF) {
|
|
||||||
ERR_raise_data(ERR_LIB_PROV, PROV_R_UNEXPECTED_KEY_PARAMETERS,
|
|
||||||
"unexpected parameters with a PKCS#8 %s private key",
|
|
||||||
v->algorithm_name);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
if ((ossl_ssize_t)len < (ossl_ssize_t)sizeof(magic))
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
/* Find the matching p8 info slot, this has the expected length. */
|
|
||||||
pos = OPENSSL_load_u32_be(&magic, buf);
|
|
||||||
for (vp8_slot = vp8_alloc; vp8_slot->vp8_entry != NULL; ++vp8_slot) {
|
|
||||||
if (magic == vp8_slot->vp8_entry->p8_magic
|
|
||||||
&& len == (ossl_ssize_t)vp8_slot->vp8_entry->p8_bytes)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ((vp8 = vp8_slot->vp8_entry) == NULL)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
if (vp8->seed_offset > 0) {
|
|
||||||
/* Check |seed| tag/len, if not subsumed by |magic|. */
|
|
||||||
if (pos == buf + vp8->seed_offset - 2) {
|
|
||||||
pos = OPENSSL_load_u16_be(&seed_magic, pos);
|
|
||||||
if (seed_magic != vp8->seed_magic)
|
|
||||||
goto end;
|
|
||||||
} else if (pos != buf + vp8->seed_offset) {
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
pos += ML_KEM_SEED_BYTES;
|
|
||||||
}
|
|
||||||
if (vp8->priv_offset > 0) {
|
|
||||||
/* Check |priv| tag/len */
|
|
||||||
if (pos == buf + vp8->priv_offset - 4) {
|
|
||||||
pos = OPENSSL_load_u32_be(&magic, pos);
|
|
||||||
if (magic != vp8->priv_magic)
|
|
||||||
goto end;
|
|
||||||
} else if (pos != buf + vp8->priv_offset) {
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
pos += v->prvkey_bytes;
|
|
||||||
}
|
|
||||||
if (vp8->pub_offset > 0) {
|
|
||||||
if (pos != buf + vp8->pub_offset)
|
|
||||||
goto end;
|
|
||||||
pos += v->pubkey_bytes;
|
|
||||||
}
|
|
||||||
if (pos != buf + len)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
if (vp8->seed_offset > 0) {
|
|
||||||
if ((key = ossl_ml_kem_key_new(libctx, propq,
|
|
||||||
retain_seed, evp_type)) != NULL)
|
|
||||||
ret = ossl_ml_kem_set_seed(buf + vp8->seed_offset,
|
|
||||||
ML_KEM_SEED_BYTES, key);
|
|
||||||
else
|
|
||||||
ERR_raise_data(ERR_LIB_OSSL_DECODER, ERR_R_INTERNAL_ERROR,
|
|
||||||
"error storing %s private key seed",
|
|
||||||
v->algorithm_name);
|
|
||||||
} else {
|
|
||||||
if ((key = ossl_ml_kem_key_new(libctx, propq, 1, evp_type)) != NULL
|
|
||||||
&& ossl_ml_kem_parse_private_key(buf + vp8->priv_offset,
|
|
||||||
v->prvkey_bytes, key))
|
|
||||||
ret = key;
|
|
||||||
else
|
|
||||||
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
|
|
||||||
"error parsing %s private key",
|
|
||||||
v->algorithm_name);
|
|
||||||
}
|
|
||||||
/* OQS public key content is ignored */
|
|
||||||
|
|
||||||
end:
|
|
||||||
OPENSSL_free(vp8_alloc);
|
|
||||||
PKCS8_PRIV_KEY_INFO_free(p8inf);
|
|
||||||
if (ret == NULL)
|
|
||||||
ossl_ml_kem_key_free(key);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Same as ossl_ml_kem_encode_pubkey, but allocates the output buffer. */
|
|
||||||
int ossl_ml_kem_i2d_pubkey(const ML_KEM_KEY *key, unsigned char **out)
|
|
||||||
{
|
|
||||||
size_t publen;
|
|
||||||
|
|
||||||
if (!ossl_ml_kem_have_pubkey(key)) {
|
|
||||||
ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY,
|
|
||||||
"no %s public key data available",
|
|
||||||
key->vinfo->algorithm_name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
publen = key->vinfo->pubkey_bytes;
|
|
||||||
|
|
||||||
if (out != NULL
|
|
||||||
&& (*out = OPENSSL_malloc(publen)) == NULL)
|
|
||||||
return 0;
|
|
||||||
if (!ossl_ml_kem_encode_public_key(*out, publen, key)) {
|
|
||||||
ERR_raise_data(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR,
|
|
||||||
"error encoding %s public key",
|
|
||||||
key->vinfo->algorithm_name);
|
|
||||||
OPENSSL_free(*out);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int)publen;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate and encode PKCS#8 private key payload. */
|
|
||||||
int ossl_ml_kem_i2d_prvkey(const ML_KEM_KEY *key, uint8_t **out,
|
|
||||||
const char *formats)
|
|
||||||
{
|
|
||||||
const ML_KEM_VINFO *v = key->vinfo;
|
|
||||||
ML_KEM_PKCS8_PREF *vp8_alloc, *vp8_slot;
|
|
||||||
const ML_KEM_PKCS8_INFO *vp8;
|
|
||||||
int len = ML_KEM_SEED_BYTES;
|
|
||||||
uint8_t *buf = NULL, *pos;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (!ossl_ml_kem_have_prvkey(key)) {
|
|
||||||
ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY,
|
|
||||||
"no %s private key data available",
|
|
||||||
key->vinfo->algorithm_name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
vp8_slot = vp8_alloc = vp8_order(v, "output", formats);
|
|
||||||
if (vp8_alloc == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* If we don't have a seed, skip seedful entries */
|
|
||||||
if (!ossl_ml_kem_have_seed(key))
|
|
||||||
while (vp8_slot->vp8_entry != NULL
|
|
||||||
&& vp8_slot->vp8_entry->seed_offset != 0)
|
|
||||||
++vp8_slot;
|
|
||||||
/* No matching table entries, give up */
|
|
||||||
if ((vp8 = vp8_slot->vp8_entry) == NULL) {
|
|
||||||
ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_KEM_NO_FORMAT,
|
|
||||||
"no matching enabled %s private key output formats",
|
|
||||||
v->algorithm_name);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
len = vp8->p8_bytes;
|
|
||||||
|
|
||||||
if (out == NULL) {
|
|
||||||
ret = len;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((pos = buf = OPENSSL_malloc((size_t) len)) == NULL)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
pos = OPENSSL_store_u32_be(pos, vp8->p8_magic);
|
|
||||||
if (vp8->seed_offset != 0) {
|
|
||||||
/*
|
|
||||||
* Either the tag/len were already included in |magic| or they require
|
|
||||||
* us to write two bytes now.
|
|
||||||
*/
|
|
||||||
if (pos == buf + vp8->seed_offset - 2)
|
|
||||||
pos = OPENSSL_store_u16_be(pos, vp8->seed_magic);
|
|
||||||
if (pos != buf + vp8->seed_offset
|
|
||||||
|| !ossl_ml_kem_encode_seed(pos, ML_KEM_SEED_BYTES, key)) {
|
|
||||||
ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
|
|
||||||
"error encoding %s private key",
|
|
||||||
v->algorithm_name);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
pos += ML_KEM_SEED_BYTES;
|
|
||||||
}
|
|
||||||
if (vp8->priv_offset != 0) {
|
|
||||||
if (pos == buf + vp8->priv_offset - 4)
|
|
||||||
pos = OPENSSL_store_u32_be(pos, vp8->priv_magic);
|
|
||||||
if (pos != buf + vp8->priv_offset
|
|
||||||
|| !ossl_ml_kem_encode_private_key(pos, v->prvkey_bytes, key)) {
|
|
||||||
ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
|
|
||||||
"error encoding %s private key",
|
|
||||||
v->algorithm_name);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
pos += v->prvkey_bytes;
|
|
||||||
}
|
|
||||||
/* OQS form output with tacked-on public key */
|
|
||||||
if (vp8->pub_offset != 0) {
|
|
||||||
/* The OQS pubkey is never separately DER-wrapped */
|
|
||||||
if (pos != buf + vp8->pub_offset
|
|
||||||
|| !ossl_ml_kem_encode_public_key(pos, v->pubkey_bytes, key)) {
|
|
||||||
ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
|
|
||||||
"error encoding %s private key",
|
|
||||||
v->algorithm_name);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
pos += v->pubkey_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pos == buf + len) {
|
|
||||||
*out = buf;
|
|
||||||
ret = len;
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
OPENSSL_free(vp8_alloc);
|
|
||||||
if (ret == 0)
|
|
||||||
OPENSSL_free(buf);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ossl_ml_kem_key_to_text(BIO *out, const ML_KEM_KEY *key, int selection)
|
|
||||||
{
|
|
||||||
uint8_t seed[ML_KEM_SEED_BYTES], *prvenc = NULL, *pubenc = NULL;
|
|
||||||
size_t publen, prvlen;
|
|
||||||
const char *type_label = NULL;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (out == NULL || key == NULL) {
|
|
||||||
ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
type_label = key->vinfo->algorithm_name;
|
|
||||||
publen = key->vinfo->pubkey_bytes;
|
|
||||||
prvlen = key->vinfo->prvkey_bytes;
|
|
||||||
|
|
||||||
if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
|
|
||||||
&& (ossl_ml_kem_have_prvkey(key)
|
|
||||||
|| ossl_ml_kem_have_seed(key))) {
|
|
||||||
if (BIO_printf(out, "%s Private-Key:\n", type_label) <= 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (ossl_ml_kem_have_seed(key)) {
|
|
||||||
if (!ossl_ml_kem_encode_seed(seed, sizeof(seed), key))
|
|
||||||
goto end;
|
|
||||||
if (!ossl_bio_print_labeled_buf(out, "seed:", seed, sizeof(seed)))
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
if (ossl_ml_kem_have_prvkey(key)) {
|
|
||||||
if ((prvenc = OPENSSL_malloc(prvlen)) == NULL)
|
|
||||||
return 0;
|
|
||||||
if (!ossl_ml_kem_encode_private_key(prvenc, prvlen, key))
|
|
||||||
goto end;
|
|
||||||
if (!ossl_bio_print_labeled_buf(out, "dk:", prvenc, prvlen))
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The public key is output regardless of the selection */
|
|
||||||
if (ossl_ml_kem_have_pubkey(key)) {
|
|
||||||
/* If we did not output private key bits, this is a public key */
|
|
||||||
if (ret == 0 && BIO_printf(out, "%s Public-Key:\n", type_label) <= 0)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
if ((pubenc = OPENSSL_malloc(key->vinfo->pubkey_bytes)) == NULL
|
|
||||||
|| !ossl_ml_kem_encode_public_key(pubenc, publen, key)
|
|
||||||
|| !ossl_bio_print_labeled_buf(out, "ek:", pubenc, publen))
|
|
||||||
goto end;
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we got here, and ret == 0, there was no key material */
|
|
||||||
if (ret == 0)
|
|
||||||
ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY,
|
|
||||||
"no %s key material available",
|
|
||||||
type_label);
|
|
||||||
|
|
||||||
end:
|
|
||||||
OPENSSL_free(pubenc);
|
|
||||||
OPENSSL_free(prvenc);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ in OpenSSL's default and FIPS providers.
|
||||||
=head2 Keygen Parameters
|
=head2 Keygen Parameters
|
||||||
|
|
||||||
No mandatory parameters are required for generating a key pair.
|
No mandatory parameters are required for generating a key pair.
|
||||||
|
To set explicit parameters, use EVP_PKEY_CTX_set_params() after calling
|
||||||
|
EVP_PKEY_keygen_init().
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
|
@ -33,48 +35,91 @@ Generated keys default to retaining the seed used.
|
||||||
The seed is also by default retained when keys are loaded from B<PKCS#8> files
|
The seed is also by default retained when keys are loaded from B<PKCS#8> files
|
||||||
in the seed format.
|
in the seed format.
|
||||||
When available, the seed parameter is also used during key export and import,
|
When available, the seed parameter is also used during key export and import,
|
||||||
with keys regenerated from the seed even if separately provided on import.
|
with keys (by default) regenerated from the seed even when also provided on import.
|
||||||
|
See L</Provider configuration parameters> below for related controls.
|
||||||
|
|
||||||
When the seed is retained, it is also available as a B<gettable> parameter,
|
When the seed is retained, it is also available as a B<gettable> parameter,
|
||||||
and private key output to B<PKCS#8> files will be in seed format.
|
and private key output to B<PKCS#8> files will default to seed format.
|
||||||
When the seed is not available, because not included in the B<PKCS#8> file, not
|
When the seed is not available, because not included in the B<PKCS#8> file, not
|
||||||
available on import, or not retained, B<PKCS#8> private key files will have the
|
available on import, or not retained, B<PKCS#8> private key files will have the
|
||||||
private key in FIPS 203 C<dk> format, without DER octet-string wrapping.
|
private key in FIPS 203 C<dk> format.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head2 Common parameters
|
||||||
|
|
||||||
|
In addition to the common parameters that all keytypes should support (see
|
||||||
|
L<provider-keymgmt(7)/Common Information Parameters>), B<ML-KEM> keys
|
||||||
|
keys support the following.
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item "pub" (B<OSSL_PKEY_PARAM_PUB_KEY>) <octet string>
|
||||||
|
|
||||||
|
The public key value.
|
||||||
|
|
||||||
|
This parameter is used when importing or exporting the public key value with
|
||||||
|
the EVP_PKEY_fromdata() and EVP_PKEY_todata() functions.
|
||||||
|
The key length and content is that of the FIPS 203 (Algorithm 16:
|
||||||
|
B<ML-KEM.KeyGen_internal>) B<ek> public key for the given ML-KEM variant.
|
||||||
|
Initial import aside, this parameter is otherwise only gettable.
|
||||||
|
|
||||||
|
=item "priv" (B<OSSL_PKEY_PARAM_PRIV_KEY>) <octet string>
|
||||||
|
|
||||||
|
The private key value.
|
||||||
|
|
||||||
|
This parameter is used when importing or exporting the private key value with
|
||||||
|
the EVP_PKEY_fromdata() and EVP_PKEY_todata() functions.
|
||||||
|
The key length and content is that of the FIPS 203 (Algorithm 16:
|
||||||
|
B<ML-KEM.KeyGen_internal>) B<dk> private key for the given ML-KEM variant.
|
||||||
|
Initial import aside, this parameter is otherwise only gettable.
|
||||||
|
|
||||||
|
=item "encoded-pub-key" (B<OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY>) <octet string>
|
||||||
|
|
||||||
|
Used for getting and setting the encoding of a public key.
|
||||||
|
The key format is that of B<ek> in FIPS 203, Algorithm 16:
|
||||||
|
B<ML-KEM.KeyGen_internal>.
|
||||||
|
Updates of the public and private key components are only allowed on keys that
|
||||||
|
are empty.
|
||||||
|
Once a public or private key component is set, no further changes are allowed.
|
||||||
|
This parameter is gettable and settable (once only).
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head2 Provider configuration parameters
|
||||||
|
|
||||||
Retention of the seed can be disabled by setting the C<pkey_seed_retain>
|
|
||||||
parameter of the C<default> provider to C<no>.
|
|
||||||
See the description of the B<-provparam> option in L<openssl(1)> to learn
|
See the description of the B<-provparam> option in L<openssl(1)> to learn
|
||||||
how to set provider options in the command line tools.
|
how to set provider configuration parameters in the command line tools.
|
||||||
See L<OSSL_PROVIDER_add_conf_parameter(3)> to learn how to set provider
|
See L<OSSL_PROVIDER_add_conf_parameter(3)> to learn how to set provider
|
||||||
configuration options programmatically.
|
configuration options programmatically.
|
||||||
When using the FIPS provider, the parameter may need to be set in the C<base>
|
|
||||||
provider instead (or perhaps in all providers) if the intent is to retain seeds
|
|
||||||
read from PKCS#8 files.
|
|
||||||
|
|
||||||
In addition to its use in key generation, this parameter is also gettable and
|
=over 4
|
||||||
settable.
|
|
||||||
|
|
||||||
See L<provider-keymgmt(7)/Common Information Parameters> for further
|
=item C<ml-kem.retain_seed> (B<OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED>) <UTF8 string>
|
||||||
information.
|
|
||||||
|
|
||||||
=item C<ml-kem.retain_seed> (B<OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED>) <int>
|
When set to a string representing a false boolean value (see
|
||||||
|
L<OSSL_PROVIDER_conf_get_bool(3)>), the seed will not be retained after key
|
||||||
|
generation or key import from a seed value.
|
||||||
|
If the resulting key is then written to a PKCS#8 object, it will contain
|
||||||
|
only the FIPS 203 C<dk> key.
|
||||||
|
|
||||||
This parameter is used only in key generation.
|
=item C<ml-kem.prefer_seed> (B<OSSL_PKEY_PARAM_ML_KEM_PREFER_SEED>) <UTF8 string>
|
||||||
When keys are generated, by default the seed is retained and used as the
|
|
||||||
private key form on output when encoding.
|
When decoding PKCS#8 objects that contain both a seed and the FIPS 203 C<dk>
|
||||||
When this parameter is set to a nonzero value, the seed is retained during key
|
private key, the seed is by default used to regenerate the key, and the
|
||||||
generation, when set to 0, the seed is not retained.
|
companion key is ignored.
|
||||||
When this parameter is simply omitted, and the C<ml-kem.retain_seed> provider
|
When this configuration parameter is set to a string representing a false
|
||||||
configuration parameter is set to a false boolean value, the seed is not
|
boolean value (see L<OSSL_PROVIDER_conf_get_bool(3)>), the seed is ignored
|
||||||
retained, otherwise it is retained (see also L<OSSL_PROVIDER_conf_get_bool(3)>).
|
(neither used to regenerate the key, nor retained), and the companion key is
|
||||||
|
used instead.
|
||||||
|
|
||||||
=item C<ml-kem.input_formats> (B<OSSL_PKEY_PARAM_ML_KEM_INPUT_FORMATS>) <UTF8 string>
|
=item C<ml-kem.input_formats> (B<OSSL_PKEY_PARAM_ML_KEM_INPUT_FORMATS>) <UTF8 string>
|
||||||
|
|
||||||
List of enabled private key input formats in PKCS#8 files.
|
List of enabled private key input formats when parsing PKCS#8 objects.
|
||||||
List elements are separated by commas and/or spaces or tabs.
|
List elements are separated by commas and/or spaces or tabs.
|
||||||
The list of enabled formats can be specified in the configuration file, as seen
|
The list of enabled formats can be specified in the configuration file, as seen
|
||||||
in the L</EXAMPLES> section below, or the via the B<-provparam> command-line
|
in the L</EXAMPLES> section below, or the via the B<-provparam> command-line
|
||||||
option.
|
option (see also L<OSSL_PROVIDER_add_conf_parameter(3)>).
|
||||||
|
|
||||||
Values specified on the command-line override any configuration file settings.
|
Values specified on the command-line override any configuration file settings.
|
||||||
By default all the supported formats are enabled.
|
By default all the supported formats are enabled.
|
||||||
|
@ -138,7 +183,7 @@ recognised on input.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item C<ml-kem.output_formats> (B<OSSL_PKEY_PARAM_ML_KEM_INPUT_FORMATS>) <UTF8 string>
|
=item C<ml-kem.output_formats> (B<OSSL_PKEY_PARAM_ML_KEM_OUTPUT_FORMATS>) <UTF8 string>
|
||||||
|
|
||||||
Ordered list of enabled private key output formats when writing PKCS#8 files.
|
Ordered list of enabled private key output formats when writing PKCS#8 files.
|
||||||
List elements are separated by commas and/or spaces or tabs.
|
List elements are separated by commas and/or spaces or tabs.
|
||||||
|
@ -152,50 +197,14 @@ The order in which elements are listed is important, the selected format will be
|
||||||
the first one that is possible to output.
|
the first one that is possible to output.
|
||||||
If the key seed is known, the first listed format will be selected.
|
If the key seed is known, the first listed format will be selected.
|
||||||
If the key seed is not known, the first format that omits the seed will be selected.
|
If the key seed is not known, the first format that omits the seed will be selected.
|
||||||
By default C<seed-priv> is listed first and C<priv-only> second.
|
The default order is equivalent to C<seed-priv> and C<priv-only> second, with
|
||||||
|
both seed and key output when the seed is available, and otherwise just the
|
||||||
=back
|
key is output.
|
||||||
|
If C<seed-only> is listed first, then the seed will be output without the key
|
||||||
Use EVP_PKEY_CTX_set_params() after calling EVP_PKEY_keygen_init().
|
when available, otherwise the output will have just the key.
|
||||||
|
If C<priv-only> is listed first, then just the key is output regardless of
|
||||||
=head2 Common parameters
|
whether the seed is present.
|
||||||
|
The legacy C<oqs> formats can also be output, by listing either of those first.
|
||||||
In addition to the common parameters that all keytypes should support (see
|
|
||||||
L<provider-keymgmt(7)/Common Information Parameters>), B<ML-KEM> keys
|
|
||||||
keys support the following.
|
|
||||||
|
|
||||||
=over 4
|
|
||||||
|
|
||||||
=item "pub" (B<OSSL_PKEY_PARAM_PUB_KEY>) <octet string>
|
|
||||||
|
|
||||||
The public key value.
|
|
||||||
|
|
||||||
This parameter is used when importing or exporting the public key value with
|
|
||||||
the EVP_PKEY_fromdata() and EVP_PKEY_todata() functions. The same underlying
|
|
||||||
FIPS 203 (Algorithm 16: B<ML-KEM.KeyGen_internal>) B<ek> public key format is
|
|
||||||
used for import, export, get and set operations.
|
|
||||||
It is otherwise only gettable.
|
|
||||||
|
|
||||||
=item "priv" (B<OSSL_PKEY_PARAM_PRIV_KEY>) <octet string>
|
|
||||||
|
|
||||||
The private key value.
|
|
||||||
|
|
||||||
This parameter is used when importing or exporting the private key value with
|
|
||||||
the EVP_PKEY_fromdata() and EVP_PKEY_todata() functions.
|
|
||||||
The key length and content must be that of the FIPS 203 (Algorithm 16:
|
|
||||||
B<ML-KEM.KeyGen_internal>) B<dk> private key for the given ML-KEM variant.
|
|
||||||
It is otherwise only gettable.
|
|
||||||
|
|
||||||
=item "encoded-pub-key" (B<OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY>) <octet string>
|
|
||||||
|
|
||||||
Used for getting and setting the encoding of a public key.
|
|
||||||
The key format is that of B<ek> in FIPS 203, Algorithm 16:
|
|
||||||
B<ML-KEM.KeyGen_internal>.
|
|
||||||
Updates of the public and private key components are only allowed on keys that
|
|
||||||
are empty.
|
|
||||||
Once a public or private key component is set, no further changes are allowed.
|
|
||||||
|
|
||||||
This parameter is gettable and settable.
|
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
|
@ -220,40 +229,49 @@ An B<ML-KEM-768> key can be generated like this:
|
||||||
|
|
||||||
Equivalent calls are available for B<ML-KEM-512> and B<ML-KEM-1024>.
|
Equivalent calls are available for B<ML-KEM-512> and B<ML-KEM-1024>.
|
||||||
|
|
||||||
An B<ML-KEM> private key in seed format can be converted to a key in expanded
|
An B<ML-KEM> private key in seed format can be converted to a key in the FIPS
|
||||||
FIPS 203 B<dk> format by running:
|
203 B<dk> format by running:
|
||||||
|
|
||||||
$ openssl pkey -provparam ml-kem.retain_seed=no -in seed.pem -out long.pem
|
$ openssl pkey -provparam ml-kem.retain_seed=no \
|
||||||
|
-in seed-only.pem -out priv-only.pem
|
||||||
|
|
||||||
To generate an, e.g., B<ML-KEM-768> key, in expanded format, you can run:
|
To generate an, e.g., B<ML-KEM-768> key, in FIPS 203 B<dk> format, you can run:
|
||||||
|
|
||||||
$ openssl genpkey -provparam ml-kem.retain_seed=no \
|
$ openssl genpkey -provparam ml-kem.retain_seed=no \
|
||||||
-algorithm ml-kem-768 -out long.pem
|
-algorithm ml-kem-768 -out long.pem
|
||||||
|
|
||||||
|
If you have B<PKCS#8> file with both a seed and a key, and prefer to import the
|
||||||
|
companion key rather than the seed, you can run:
|
||||||
|
|
||||||
|
$ openssl pkey -provparam ml-kem.prefer_seed=no \
|
||||||
|
-in seed-priv.pem -out priv-only.pem
|
||||||
|
|
||||||
In the B<openssl.cnf> file, this looks like:
|
In the B<openssl.cnf> file, this looks like:
|
||||||
|
|
||||||
openssl_conf = openssl_init
|
openssl_conf = openssl_init
|
||||||
|
|
||||||
[openssl_init]
|
[openssl_init]
|
||||||
providers = providers_sect
|
providers = providers_sect
|
||||||
|
|
||||||
# Can be referenced in one or more provider sections
|
# Can be referenced in one or more provider sections
|
||||||
[ml_kem_sect]
|
[ml_kem_sect]
|
||||||
retain_seed = yes
|
prefer_seed = yes
|
||||||
# OQS legacy formats disabled
|
retain_seed = yes
|
||||||
input_formats = seed-priv, seed-only, priv-only
|
# OQS legacy formats disabled
|
||||||
# Output either the seed alone, or else the key alone
|
input_formats = seed-priv, seed-only, priv-only
|
||||||
output_formats = seed-only, priv-only
|
# Output either the seed alone, or else the key alone
|
||||||
|
output_formats = seed-only, priv-only
|
||||||
|
|
||||||
[providers_sect]
|
[providers_sect]
|
||||||
default = default_sect
|
default = default_sect
|
||||||
base = base_sect
|
# Or perhaps just: base = default_sect
|
||||||
|
base = base_sect
|
||||||
|
|
||||||
[default_sect]
|
[default_sect]
|
||||||
ml-kem = ml_kem_sect
|
ml-kem = ml_kem_sect
|
||||||
|
|
||||||
[base_sect]
|
[base_sect]
|
||||||
ml-kem = ml_kem_sect
|
ml-kem = ml_kem_sect
|
||||||
|
|
||||||
=head1 SEE ALSO
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
|
|
@ -122,49 +122,8 @@
|
||||||
* -----------------------------
|
* -----------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*-
|
|
||||||
* The DER ASN.1 encoding of ML-KEM (and ML-DSA) public keys prepends 22 bytes
|
|
||||||
* to the encoded public key:
|
|
||||||
*
|
|
||||||
* - 4 byte outer sequence tag and length
|
|
||||||
* - 2 byte algorithm sequence tag and length
|
|
||||||
* - 2 byte algorithm OID tag and length
|
|
||||||
* - 9 byte algorithm OID (from NIST CSOR OID arc)
|
|
||||||
* - 4 byte bit string tag and length
|
|
||||||
* - 1 bitstring lead byte
|
|
||||||
*/
|
|
||||||
# define ML_KEM_SPKI_OVERHEAD 22
|
|
||||||
typedef struct {
|
|
||||||
const uint8_t asn1_prefix[ML_KEM_SPKI_OVERHEAD];
|
|
||||||
} ML_KEM_SPKI_INFO;
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* For each algorithm we support a few PKCS#8 input formats,
|
|
||||||
*
|
|
||||||
* - Seed: SEQUENCE(OCTET STRING)
|
|
||||||
* - Private key: SEQUENCE([1] IMPLICIT OCTET STRING)
|
|
||||||
* - Seed & private key: SEQUENCE(OCTET STRING, [1] IMPLICIT OCTET STRING)
|
|
||||||
* - OQS private key: OCTET STRING
|
|
||||||
* - OQS private + public key: OCTET STRING
|
|
||||||
* (The public key is ignored, just as with PKCS#8 v2.)
|
|
||||||
*
|
|
||||||
* An offset of zero means that particular field is absent.
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
const char *p8_name;
|
|
||||||
size_t p8_bytes;
|
|
||||||
uint32_t p8_magic;
|
|
||||||
uint16_t seed_magic;
|
|
||||||
size_t seed_offset;
|
|
||||||
uint32_t priv_magic;
|
|
||||||
size_t priv_offset;
|
|
||||||
size_t pub_offset;
|
|
||||||
} ML_KEM_PKCS8_INFO;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *algorithm_name;
|
const char *algorithm_name;
|
||||||
const ML_KEM_SPKI_INFO *spki_info;
|
|
||||||
const ML_KEM_PKCS8_INFO *pkcs8_info;
|
|
||||||
size_t prvkey_bytes;
|
size_t prvkey_bytes;
|
||||||
size_t prvalloc;
|
size_t prvalloc;
|
||||||
size_t pubkey_bytes;
|
size_t pubkey_bytes;
|
||||||
|
@ -212,6 +171,7 @@ typedef struct ossl_ml_kem_key_st {
|
||||||
struct ossl_ml_kem_scalar_st *s; /* Private key secret vector */
|
struct ossl_ml_kem_scalar_st *s; /* Private key secret vector */
|
||||||
uint8_t *z; /* Private key FO failure secret */
|
uint8_t *z; /* Private key FO failure secret */
|
||||||
uint8_t *d; /* Private key seed */
|
uint8_t *d; /* Private key seed */
|
||||||
|
int prefer_seed; /* Given seed and key use seed? */
|
||||||
int retain_seed; /* Retain the seed after keygen? */
|
int retain_seed; /* Retain the seed after keygen? */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -221,13 +181,17 @@ typedef struct ossl_ml_kem_key_st {
|
||||||
* |z| and |d| components in that order.
|
* |z| and |d| components in that order.
|
||||||
*/
|
*/
|
||||||
uint8_t seedbuf[64]; /* |rho| + |pkhash| / |z| + |d| */
|
uint8_t seedbuf[64]; /* |rho| + |pkhash| / |z| + |d| */
|
||||||
|
uint8_t *encoded_dk; /* Unparsed P8 private key */
|
||||||
} ML_KEM_KEY;
|
} ML_KEM_KEY;
|
||||||
|
|
||||||
/* The public key is always present, when the private is */
|
/* The public key is always present, when the private is */
|
||||||
# define ossl_ml_kem_key_vinfo(key) ((key)->vinfo)
|
# define ossl_ml_kem_key_vinfo(key) ((key)->vinfo)
|
||||||
# define ossl_ml_kem_have_pubkey(key) ((key)->t != NULL)
|
# define ossl_ml_kem_have_pubkey(key) ((key)->t != NULL)
|
||||||
# define ossl_ml_kem_have_prvkey(key) ((key)->s != NULL)
|
# define ossl_ml_kem_have_prvkey(key) ((key)->s != NULL)
|
||||||
# define ossl_ml_kem_have_seed(key) ((key)->d != NULL)
|
# define ossl_ml_kem_have_seed(key) ((key)->d != NULL)
|
||||||
|
# define ossl_ml_kem_have_dkenc(key) ((key)->encoded_dk != NULL)
|
||||||
|
# define ossl_ml_kem_decoded_key(key) ((key)->encoded_dk != NULL \
|
||||||
|
|| ((key)->s == NULL && (key)->d != NULL))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ----- ML-KEM key lifecycle
|
* ----- ML-KEM key lifecycle
|
||||||
|
@ -237,9 +201,8 @@ typedef struct ossl_ml_kem_key_st {
|
||||||
* Allocate a "bare" key for given ML-KEM variant. Initially without any public
|
* Allocate a "bare" key for given ML-KEM variant. Initially without any public
|
||||||
* or private key material.
|
* or private key material.
|
||||||
*/
|
*/
|
||||||
ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx,
|
ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties,
|
||||||
const char *properties,
|
int evp_type);
|
||||||
int retain_seed, int evp_type);
|
|
||||||
/* Deallocate the key */
|
/* Deallocate the key */
|
||||||
void ossl_ml_kem_key_free(ML_KEM_KEY *key);
|
void ossl_ml_kem_key_free(ML_KEM_KEY *key);
|
||||||
/*
|
/*
|
||||||
|
@ -268,17 +231,6 @@ int ossl_ml_kem_parse_private_key(const uint8_t *in, size_t len,
|
||||||
ML_KEM_KEY *ossl_ml_kem_set_seed(const uint8_t *seed, size_t seedlen,
|
ML_KEM_KEY *ossl_ml_kem_set_seed(const uint8_t *seed, size_t seedlen,
|
||||||
ML_KEM_KEY *key);
|
ML_KEM_KEY *key);
|
||||||
__owur
|
__owur
|
||||||
ML_KEM_KEY *ossl_ml_kem_d2i_PUBKEY(const uint8_t *pubenc, int publen,
|
|
||||||
int evp_type, OSSL_LIB_CTX *libctx,
|
|
||||||
const char *propq);
|
|
||||||
__owur
|
|
||||||
ML_KEM_KEY *ossl_ml_kem_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
|
|
||||||
int retain_seed, const char *formats,
|
|
||||||
int evp_type, OSSL_LIB_CTX *ctx,
|
|
||||||
const char *propq);
|
|
||||||
__owur
|
|
||||||
int ossl_ml_kem_key_to_text(BIO *out, const ML_KEM_KEY *key, int selection);
|
|
||||||
__owur
|
|
||||||
int ossl_ml_kem_genkey(uint8_t *pubenc, size_t publen, ML_KEM_KEY *key);
|
int ossl_ml_kem_genkey(uint8_t *pubenc, size_t publen, ML_KEM_KEY *key);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -290,13 +242,9 @@ __owur
|
||||||
int ossl_ml_kem_encode_public_key(uint8_t *out, size_t len,
|
int ossl_ml_kem_encode_public_key(uint8_t *out, size_t len,
|
||||||
const ML_KEM_KEY *key);
|
const ML_KEM_KEY *key);
|
||||||
__owur
|
__owur
|
||||||
int ossl_ml_kem_i2d_pubkey(const ML_KEM_KEY *key, unsigned char **out);
|
|
||||||
__owur
|
|
||||||
int ossl_ml_kem_encode_private_key(uint8_t *out, size_t len,
|
int ossl_ml_kem_encode_private_key(uint8_t *out, size_t len,
|
||||||
const ML_KEM_KEY *key);
|
const ML_KEM_KEY *key);
|
||||||
__owur
|
__owur
|
||||||
int ossl_ml_kem_i2d_prvkey(const ML_KEM_KEY *key, unsigned char **out,
|
|
||||||
const char *formats);
|
|
||||||
int ossl_ml_kem_encode_seed(uint8_t *out, size_t len,
|
int ossl_ml_kem_encode_seed(uint8_t *out, size_t len,
|
||||||
const ML_KEM_KEY *key);
|
const ML_KEM_KEY *key);
|
||||||
|
|
||||||
|
|
|
@ -22,3 +22,7 @@ DEPEND[encode_key2any.o]=../../common/include/prov/der_rsa.h
|
||||||
IF[{- !$disabled{'ml-dsa'} -}]
|
IF[{- !$disabled{'ml-dsa'} -}]
|
||||||
SOURCE[$DECODER_GOAL]=ml_dsa_codecs.c
|
SOURCE[$DECODER_GOAL]=ml_dsa_codecs.c
|
||||||
ENDIF
|
ENDIF
|
||||||
|
|
||||||
|
IF[{- !$disabled{'ml-kem'} -}]
|
||||||
|
SOURCE[$DECODER_GOAL]=ml_kem_codecs.c
|
||||||
|
ENDIF
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "endecoder_local.h"
|
#include "endecoder_local.h"
|
||||||
#include "internal/nelem.h"
|
#include "internal/nelem.h"
|
||||||
#include "ml_dsa_codecs.h"
|
#include "ml_dsa_codecs.h"
|
||||||
|
#include "ml_kem_codecs.h"
|
||||||
|
|
||||||
struct der2key_ctx_st; /* Forward declaration */
|
struct der2key_ctx_st; /* Forward declaration */
|
||||||
typedef int check_key_fn(void *, struct der2key_ctx_st *ctx);
|
typedef int check_key_fn(void *, struct der2key_ctx_st *ctx);
|
||||||
|
@ -566,14 +567,9 @@ static void *
|
||||||
ml_kem_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx)
|
ml_kem_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx)
|
||||||
{
|
{
|
||||||
ML_KEM_KEY *key;
|
ML_KEM_KEY *key;
|
||||||
OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
|
|
||||||
int retain_seed = ossl_prov_ctx_get_bool_param(
|
|
||||||
ctx->provctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1);
|
|
||||||
const char *formats = ossl_prov_ctx_get_param(
|
|
||||||
ctx->provctx, OSSL_PKEY_PARAM_ML_KEM_INPUT_FORMATS, NULL);
|
|
||||||
|
|
||||||
key = ossl_ml_kem_d2i_PKCS8(*der, der_len, retain_seed, formats,
|
key = ossl_ml_kem_d2i_PKCS8(*der, der_len, ctx->desc->evp_type,
|
||||||
ctx->desc->evp_type, libctx, ctx->propq);
|
ctx->provctx, ctx->propq);
|
||||||
if (key != NULL)
|
if (key != NULL)
|
||||||
*der += der_len;
|
*der += der_len;
|
||||||
return key;
|
return key;
|
||||||
|
@ -586,7 +582,7 @@ ml_kem_d2i_PUBKEY(const uint8_t **der, long der_len,
|
||||||
ML_KEM_KEY *key;
|
ML_KEM_KEY *key;
|
||||||
|
|
||||||
key = ossl_ml_kem_d2i_PUBKEY(*der, der_len, ctx->desc->evp_type,
|
key = ossl_ml_kem_d2i_PUBKEY(*der, der_len, ctx->desc->evp_type,
|
||||||
PROV_LIBCTX_OF(ctx->provctx), ctx->propq);
|
ctx->provctx, ctx->propq);
|
||||||
if (key != NULL)
|
if (key != NULL)
|
||||||
*der += der_len;
|
*der += der_len;
|
||||||
return key;
|
return key;
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "prov/der_rsa.h"
|
#include "prov/der_rsa.h"
|
||||||
#include "endecoder_local.h"
|
#include "endecoder_local.h"
|
||||||
#include "ml_dsa_codecs.h"
|
#include "ml_dsa_codecs.h"
|
||||||
|
#include "ml_kem_codecs.h"
|
||||||
|
|
||||||
#if defined(OPENSSL_NO_DH) && defined(OPENSSL_NO_DSA) && defined(OPENSSL_NO_EC)
|
#if defined(OPENSSL_NO_DH) && defined(OPENSSL_NO_DSA) && defined(OPENSSL_NO_EC)
|
||||||
# define OPENSSL_NO_KEYPARAMS
|
# define OPENSSL_NO_KEYPARAMS
|
||||||
|
@ -891,10 +892,8 @@ static int ml_kem_pki_priv_to_der(const void *vkey, unsigned char **pder,
|
||||||
void *vctx)
|
void *vctx)
|
||||||
{
|
{
|
||||||
KEY2ANY_CTX *ctx = vctx;
|
KEY2ANY_CTX *ctx = vctx;
|
||||||
const char *fmtkey = OSSL_PKEY_PARAM_ML_KEM_OUTPUT_FORMATS;
|
|
||||||
const char *formats = ossl_prov_ctx_get_param(ctx->provctx, fmtkey, NULL);
|
|
||||||
|
|
||||||
return ossl_ml_kem_i2d_prvkey(vkey, pder, formats);
|
return ossl_ml_kem_i2d_prvkey(vkey, pder, ctx->provctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
# define ml_kem_epki_priv_to_der ml_kem_pki_priv_to_der
|
# define ml_kem_epki_priv_to_der ml_kem_pki_priv_to_der
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "internal/encoder.h"
|
#include "internal/encoder.h"
|
||||||
#include "endecoder_local.h"
|
#include "endecoder_local.h"
|
||||||
#include "ml_dsa_codecs.h"
|
#include "ml_dsa_codecs.h"
|
||||||
|
#include "ml_kem_codecs.h"
|
||||||
|
|
||||||
DEFINE_SPECIAL_STACK_OF_CONST(BIGNUM_const, BIGNUM)
|
DEFINE_SPECIAL_STACK_OF_CONST(BIGNUM_const, BIGNUM)
|
||||||
|
|
||||||
|
|
559
providers/implementations/encode_decode/ml_kem_codecs.c
Normal file
559
providers/implementations/encode_decode/ml_kem_codecs.c
Normal file
|
@ -0,0 +1,559 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||||
|
* this file except in compliance with the License. You can obtain a copy
|
||||||
|
* in the file LICENSE in the source distribution or at
|
||||||
|
* https://www.openssl.org/source/license.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <openssl/byteorder.h>
|
||||||
|
#include <openssl/proverr.h>
|
||||||
|
#include <openssl/x509.h>
|
||||||
|
#include <openssl/core_names.h>
|
||||||
|
#include "internal/encoder.h"
|
||||||
|
#include "ml_kem_codecs.h"
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* Tables describing supported ASN.1 input/output formats.
|
||||||
|
* For each parameter set we support a few PKCS#8 input formats, three
|
||||||
|
* corresponding to the "either or both" variants of:
|
||||||
|
*
|
||||||
|
* ML-KEM-PrivateKey ::= SEQUENCE {
|
||||||
|
* seed OCTET STRING SIZE (64) OPTIONAL,
|
||||||
|
* expandedKey [1] IMPLICIT OCTET STRING SIZE (1632 | 2400 | 3172) OPTIONAL }
|
||||||
|
* (WITH COMPONENTS {..., seed PRESENT } |
|
||||||
|
* WITH COMPONENTS {..., expandedKey PRESENT })
|
||||||
|
*
|
||||||
|
* and two more for historical OQS encodings.
|
||||||
|
*
|
||||||
|
* - OQS private key: OCTET STRING
|
||||||
|
* - OQS private + public key: OCTET STRING
|
||||||
|
* (The public key is ignored, just as with PKCS#8 v2.)
|
||||||
|
*
|
||||||
|
* An offset of zero means that particular field is absent.
|
||||||
|
*
|
||||||
|
* On output the PKCS8 info table order is important:
|
||||||
|
* - When we have a seed we'll use the first entry with a non-zero seed offset.
|
||||||
|
* - Otherwise, the first entry with a zero seed offset.
|
||||||
|
*
|
||||||
|
* As written, when possible, we prefer to output both the seed and private
|
||||||
|
* key, otherwise, just the private key ([1] IMPLICIT OCTET STRING form).
|
||||||
|
*
|
||||||
|
* The various lengths in the PKCS#8 tag/len fields could have been left
|
||||||
|
* zeroed, and filled in on the fly from the algorithm parameters, but that
|
||||||
|
* makes the code more complex, so a choice was made to embed them directly
|
||||||
|
* into the tables. Had they been zeroed, one table could cover all three
|
||||||
|
* ML-KEM parameter sets.
|
||||||
|
*/
|
||||||
|
#define NUM_PKCS8_FORMATS 5
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* ML-KEM-512:
|
||||||
|
* Public key bytes: 800 (0x0320)
|
||||||
|
* Private key bytes: 1632 (0x0660)
|
||||||
|
*/
|
||||||
|
static const ML_KEM_SPKI_INFO ml_kem_512_spki_info = {
|
||||||
|
{ 0x30, 0x82, 0x03, 0x32, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48,
|
||||||
|
0x01, 0x65, 0x03, 0x04, 0x04, 0x01, 0x03, 0x82, 0x03, 0x21, 0x00, }
|
||||||
|
};
|
||||||
|
static const ML_KEM_PKCS8_INFO ml_kem_512_pkcs8_info[NUM_PKCS8_FORMATS] = {
|
||||||
|
{ "seed-priv", 1706, 0x308206a6, 0x0440, 6, 0x81820660, 74, 0, },
|
||||||
|
{ "priv-only", 1640, 0x30820664, 0, 0, 0x81820660, 8, 0, },
|
||||||
|
{ "seed-only", 68, 0x30420440, 0x0440, 4, 0, 0, 0, },
|
||||||
|
{ "priv-oqs", 1636, 0x04820660, 0, 0, 0x04820660, 4, 0, },
|
||||||
|
{ "pair-oqs", 2436, 0x04820980, 0, 0, 0x04820980, 4, 1636, },
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* ML-KEM-768:
|
||||||
|
* Public key bytes: 1184 (0x04a0)
|
||||||
|
* Private key bytes: 2400 (0x0960)
|
||||||
|
*/
|
||||||
|
static const ML_KEM_SPKI_INFO ml_kem_768_spki_info = {
|
||||||
|
{ 0x30, 0x82, 0x04, 0xb2, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48,
|
||||||
|
0x01, 0x65, 0x03, 0x04, 0x04, 0x02, 0x03, 0x82, 0x04, 0xa1, 0x00, }
|
||||||
|
};
|
||||||
|
static const ML_KEM_PKCS8_INFO ml_kem_768_pkcs8_info[NUM_PKCS8_FORMATS] = {
|
||||||
|
{ "seed-priv", 2474, 0x308209a6, 0x0440, 6, 0x81820960, 74, 0, },
|
||||||
|
{ "priv-only", 2408, 0x30820964, 0, 0, 0x81820960, 8, 0, },
|
||||||
|
{ "seed-only", 68, 0x30420440, 0x0440, 4, 0, 0, 0, },
|
||||||
|
{ "priv-oqs", 2404, 0x04820960, 0, 0, 0x04820960, 4, 0, },
|
||||||
|
{ "pair-oqs", 3588, 0x04820e00, 0, 0, 0x04820e00, 4, 2404, },
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* ML-KEM-1024:
|
||||||
|
* Private key bytes: 3168 (0x0c60)
|
||||||
|
* Public key bytes: 1568 (0x0620)
|
||||||
|
*/
|
||||||
|
static const ML_KEM_SPKI_INFO ml_kem_1024_spki_info = {
|
||||||
|
{ 0x30, 0x82, 0x06, 0x32, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48,
|
||||||
|
0x01, 0x65, 0x03, 0x04, 0x04, 0x03, 0x03, 0x82, 0x06, 0x21, 0x00, }
|
||||||
|
};
|
||||||
|
static const ML_KEM_PKCS8_INFO ml_kem_1024_pkcs8_info[NUM_PKCS8_FORMATS] = {
|
||||||
|
{ "seed-priv", 3242, 0x30820ca6, 0x0440, 6, 0x81820c60, 74, 0, },
|
||||||
|
{ "priv-only", 3176, 0x30820c64, 0, 0, 0x81820c60, 8, 0, },
|
||||||
|
{ "seed-only", 68, 0x30420440, 0x0440, 4, 0, 0, 0, },
|
||||||
|
{ "priv-oqs", 3172, 0x04820c60, 0, 0, 0x04820c60, 4, 0, },
|
||||||
|
{ "pair-oqs", 4740, 0x04821280, 0, 0, 0x04821280, 4, 3172, },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Indices of slots in the `cinfo_map` table below */
|
||||||
|
#define ML_KEM_512_CINFO 0
|
||||||
|
#define ML_KEM_768_CINFO 1
|
||||||
|
#define ML_KEM_1024_CINFO 2
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Per-variant fixed parameters
|
||||||
|
*/
|
||||||
|
static const ML_KEM_CINFO cinfo_map[3] = {
|
||||||
|
{ &ml_kem_512_spki_info, ml_kem_512_pkcs8_info },
|
||||||
|
{ &ml_kem_768_spki_info, ml_kem_768_pkcs8_info },
|
||||||
|
{ &ml_kem_1024_spki_info, ml_kem_1024_pkcs8_info }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Retrieve the parameters of one of the ML-KEM variants */
|
||||||
|
static const ML_KEM_CINFO *ml_kem_get_cinfo(int evp_type)
|
||||||
|
{
|
||||||
|
switch (evp_type) {
|
||||||
|
case EVP_PKEY_ML_KEM_512:
|
||||||
|
return &cinfo_map[ML_KEM_512_CINFO];
|
||||||
|
case EVP_PKEY_ML_KEM_768:
|
||||||
|
return &cinfo_map[ML_KEM_768_CINFO];
|
||||||
|
case EVP_PKEY_ML_KEM_1024:
|
||||||
|
return &cinfo_map[ML_KEM_1024_CINFO];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vp8_pref_cmp(const void *va, const void *vb)
|
||||||
|
{
|
||||||
|
const ML_KEM_PKCS8_PREF *a = va;
|
||||||
|
const ML_KEM_PKCS8_PREF *b = vb;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Zeros sort last, otherwise the sort is in increasing order.
|
||||||
|
*
|
||||||
|
* The preferences are small enough to ensure the comparison is monotone as
|
||||||
|
* required. Some versions of qsort(3) have been known to crash when the
|
||||||
|
* comparison is not monotone.
|
||||||
|
*/
|
||||||
|
if (a->vp8_pref > 0 && b->vp8_pref > 0)
|
||||||
|
return a->vp8_pref - b->vp8_pref;
|
||||||
|
if (a->vp8_pref == 0)
|
||||||
|
return b->vp8_pref;
|
||||||
|
return -a->vp8_pref;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ML_KEM_PKCS8_PREF *vp8_order(const char *algorithm_name,
|
||||||
|
const ML_KEM_PKCS8_INFO *pkcs8_info,
|
||||||
|
const char *direction, const char *formats)
|
||||||
|
{
|
||||||
|
ML_KEM_PKCS8_PREF *ret;
|
||||||
|
int i, count = 0;
|
||||||
|
const char *fmt = formats, *end;
|
||||||
|
const char *sep = "\t ,";
|
||||||
|
|
||||||
|
if ((ret = OPENSSL_zalloc((NUM_PKCS8_FORMATS + 1) * sizeof(*ret))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
for (i = 0; i < NUM_PKCS8_FORMATS; ++i) {
|
||||||
|
ret[i].vp8_entry = &pkcs8_info[i];
|
||||||
|
ret[i].vp8_pref = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default to compile-time table order. */
|
||||||
|
if (formats == NULL)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Formats are case-insensitive, separated by spaces, tabs and/or commas */
|
||||||
|
do {
|
||||||
|
if (*(fmt += strspn(fmt, sep)) == '\0')
|
||||||
|
break;
|
||||||
|
end = fmt + strcspn(fmt, sep);
|
||||||
|
for (i = 0; i < NUM_PKCS8_FORMATS; ++i) {
|
||||||
|
if (ret[i].vp8_pref > 0
|
||||||
|
|| OPENSSL_strncasecmp(ret[i].vp8_entry->p8_name,
|
||||||
|
fmt, (end - fmt)) != 0)
|
||||||
|
continue;
|
||||||
|
ret[i].vp8_pref = ++count;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fmt = end;
|
||||||
|
} while (count < NUM_PKCS8_FORMATS);
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
OPENSSL_free(ret);
|
||||||
|
ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_KEM_NO_FORMAT,
|
||||||
|
"no %s private key %s formats are enabled",
|
||||||
|
algorithm_name, direction);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
qsort(ret, NUM_PKCS8_FORMATS, sizeof(*ret), vp8_pref_cmp);
|
||||||
|
ret[count].vp8_entry = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ML_KEM_KEY *
|
||||||
|
ossl_ml_kem_d2i_PUBKEY(const uint8_t *pubenc, int publen, int evp_type,
|
||||||
|
PROV_CTX *provctx, const char *propq)
|
||||||
|
{
|
||||||
|
OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
|
||||||
|
const ML_KEM_VINFO *v;
|
||||||
|
const ML_KEM_CINFO *c;
|
||||||
|
const ML_KEM_SPKI_INFO *vspki;
|
||||||
|
ML_KEM_KEY *ret;
|
||||||
|
|
||||||
|
if ((v = ossl_ml_kem_get_vinfo(evp_type)) == NULL
|
||||||
|
|| (c = ml_kem_get_cinfo(evp_type)) == NULL)
|
||||||
|
return NULL;
|
||||||
|
vspki = c->spki_info;
|
||||||
|
if (publen != ML_KEM_SPKI_OVERHEAD + (ossl_ssize_t) v->pubkey_bytes
|
||||||
|
|| memcmp(pubenc, vspki->asn1_prefix, ML_KEM_SPKI_OVERHEAD) != 0)
|
||||||
|
return NULL;
|
||||||
|
publen -= ML_KEM_SPKI_OVERHEAD;
|
||||||
|
pubenc += ML_KEM_SPKI_OVERHEAD;
|
||||||
|
|
||||||
|
if ((ret = ossl_ml_kem_key_new(libctx, propq, evp_type)) == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!ossl_ml_kem_parse_public_key(pubenc, (size_t) publen, ret)) {
|
||||||
|
ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
|
||||||
|
"errror parsing %s public key from input SPKI",
|
||||||
|
v->algorithm_name);
|
||||||
|
ossl_ml_kem_key_free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ML_KEM_KEY *
|
||||||
|
ossl_ml_kem_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
|
||||||
|
int evp_type, PROV_CTX *provctx,
|
||||||
|
const char *propq)
|
||||||
|
{
|
||||||
|
OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
|
||||||
|
const ML_KEM_VINFO *v;
|
||||||
|
const ML_KEM_CINFO *c;
|
||||||
|
ML_KEM_PKCS8_PREF *vp8_alloc = NULL, *vp8_slot;
|
||||||
|
const ML_KEM_PKCS8_INFO *vp8;
|
||||||
|
ML_KEM_KEY *key = NULL, *ret = NULL;
|
||||||
|
PKCS8_PRIV_KEY_INFO *p8inf = NULL;
|
||||||
|
const uint8_t *buf, *pos;
|
||||||
|
const X509_ALGOR *alg = NULL;
|
||||||
|
const char *formats;
|
||||||
|
int len, ptype;
|
||||||
|
uint32_t magic;
|
||||||
|
uint16_t seed_magic;
|
||||||
|
|
||||||
|
/* Which ML-KEM variant? */
|
||||||
|
if ((v = ossl_ml_kem_get_vinfo(evp_type)) == NULL
|
||||||
|
|| (c = ml_kem_get_cinfo(evp_type)) == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Extract the key OID and any parameters. */
|
||||||
|
if ((p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &prvenc, prvlen)) == NULL)
|
||||||
|
return 0;
|
||||||
|
/* Shortest prefix is 4 bytes: seq tag/len + octet string tag/len */
|
||||||
|
if (!PKCS8_pkey_get0(NULL, &buf, &len, &alg, p8inf))
|
||||||
|
goto end;
|
||||||
|
/* Bail out early if this is some other key type. */
|
||||||
|
if (OBJ_obj2nid(alg->algorithm) != evp_type)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* Get the list of enabled decoders. Their order is not important here. */
|
||||||
|
formats = ossl_prov_ctx_get_param(
|
||||||
|
provctx, OSSL_PKEY_PARAM_ML_KEM_INPUT_FORMATS, NULL);
|
||||||
|
vp8_slot = vp8_alloc = vp8_order(v->algorithm_name, c->pkcs8_info,
|
||||||
|
"input", formats);
|
||||||
|
if (vp8_alloc == NULL)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* Parameters must be absent. */
|
||||||
|
X509_ALGOR_get0(NULL, &ptype, NULL, alg);
|
||||||
|
if (ptype != V_ASN1_UNDEF) {
|
||||||
|
ERR_raise_data(ERR_LIB_PROV, PROV_R_UNEXPECTED_KEY_PARAMETERS,
|
||||||
|
"unexpected parameters with a PKCS#8 %s private key",
|
||||||
|
v->algorithm_name);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if ((ossl_ssize_t)len < (ossl_ssize_t)sizeof(magic))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* Find the matching p8 info slot, that also has the expected length. */
|
||||||
|
pos = OPENSSL_load_u32_be(&magic, buf);
|
||||||
|
for (vp8_slot = vp8_alloc; vp8_slot->vp8_entry != NULL; ++vp8_slot) {
|
||||||
|
if (magic == vp8_slot->vp8_entry->p8_magic
|
||||||
|
&& len == (ossl_ssize_t)vp8_slot->vp8_entry->p8_bytes)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((vp8 = vp8_slot->vp8_entry) == NULL)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
if (vp8->seed_offset > 0) {
|
||||||
|
/* Check |seed| tag/len, if not subsumed by |magic|. */
|
||||||
|
if (pos + sizeof(uint16_t) == buf + vp8->seed_offset) {
|
||||||
|
pos = OPENSSL_load_u16_be(&seed_magic, pos);
|
||||||
|
if (seed_magic != vp8->seed_magic)
|
||||||
|
goto end;
|
||||||
|
} else if (pos != buf + vp8->seed_offset) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
pos += ML_KEM_SEED_BYTES;
|
||||||
|
}
|
||||||
|
if (vp8->priv_offset > 0) {
|
||||||
|
/* Check |priv| tag/len */
|
||||||
|
if (pos + sizeof(uint32_t) == buf + vp8->priv_offset) {
|
||||||
|
pos = OPENSSL_load_u32_be(&magic, pos);
|
||||||
|
if (magic != vp8->priv_magic)
|
||||||
|
goto end;
|
||||||
|
} else if (pos != buf + vp8->priv_offset) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
pos += v->prvkey_bytes;
|
||||||
|
}
|
||||||
|
if (vp8->pub_offset > 0) {
|
||||||
|
if (pos != buf + vp8->pub_offset)
|
||||||
|
goto end;
|
||||||
|
pos += v->pubkey_bytes;
|
||||||
|
}
|
||||||
|
if (pos != buf + len)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Collect the seed and/or key into a "decoded" private key object,
|
||||||
|
* to be turned into a real key on provider "load" or "import".
|
||||||
|
*/
|
||||||
|
if ((key = ossl_ml_kem_key_new(libctx, propq, evp_type)) == NULL)
|
||||||
|
goto end;
|
||||||
|
key->retain_seed = ossl_prov_ctx_get_bool_param(
|
||||||
|
provctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1);
|
||||||
|
key->prefer_seed = ossl_prov_ctx_get_bool_param(
|
||||||
|
provctx, OSSL_PKEY_PARAM_ML_KEM_PREFER_SEED, 1);
|
||||||
|
if (vp8->seed_offset > 0) {
|
||||||
|
if (!ossl_ml_kem_set_seed(buf + vp8->seed_offset,
|
||||||
|
ML_KEM_SEED_BYTES, key)) {
|
||||||
|
ERR_raise_data(ERR_LIB_OSSL_DECODER, ERR_R_INTERNAL_ERROR,
|
||||||
|
"error storing %s private key seed",
|
||||||
|
v->algorithm_name);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vp8->priv_offset > 0) {
|
||||||
|
if ((key->encoded_dk = OPENSSL_malloc(v->prvkey_bytes)) == NULL) {
|
||||||
|
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
|
||||||
|
"error parsing %s private key",
|
||||||
|
v->algorithm_name);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
memcpy(key->encoded_dk, buf + vp8->priv_offset, v->prvkey_bytes);
|
||||||
|
}
|
||||||
|
/* Any OQS public key content is ignored */
|
||||||
|
ret = key;
|
||||||
|
|
||||||
|
end:
|
||||||
|
OPENSSL_free(vp8_alloc);
|
||||||
|
PKCS8_PRIV_KEY_INFO_free(p8inf);
|
||||||
|
if (ret == NULL)
|
||||||
|
ossl_ml_kem_key_free(key);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Same as ossl_ml_kem_encode_pubkey, but allocates the output buffer. */
|
||||||
|
int ossl_ml_kem_i2d_pubkey(const ML_KEM_KEY *key, unsigned char **out)
|
||||||
|
{
|
||||||
|
size_t publen;
|
||||||
|
|
||||||
|
if (!ossl_ml_kem_have_pubkey(key)) {
|
||||||
|
ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY,
|
||||||
|
"no %s public key data available",
|
||||||
|
key->vinfo->algorithm_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
publen = key->vinfo->pubkey_bytes;
|
||||||
|
|
||||||
|
if (out != NULL
|
||||||
|
&& (*out = OPENSSL_malloc(publen)) == NULL)
|
||||||
|
return 0;
|
||||||
|
if (!ossl_ml_kem_encode_public_key(*out, publen, key)) {
|
||||||
|
ERR_raise_data(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR,
|
||||||
|
"error encoding %s public key",
|
||||||
|
key->vinfo->algorithm_name);
|
||||||
|
OPENSSL_free(*out);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)publen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate and encode PKCS#8 private key payload. */
|
||||||
|
int ossl_ml_kem_i2d_prvkey(const ML_KEM_KEY *key, uint8_t **out,
|
||||||
|
PROV_CTX *provctx)
|
||||||
|
{
|
||||||
|
const ML_KEM_VINFO *v = key->vinfo;
|
||||||
|
const ML_KEM_CINFO *c;
|
||||||
|
ML_KEM_PKCS8_PREF *vp8_alloc, *vp8_slot;
|
||||||
|
const ML_KEM_PKCS8_INFO *vp8;
|
||||||
|
uint8_t *buf = NULL, *pos;
|
||||||
|
const char *formats;
|
||||||
|
int len = ML_KEM_SEED_BYTES;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* Not ours to handle */
|
||||||
|
if ((c = ml_kem_get_cinfo(v->evp_type)) == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!ossl_ml_kem_have_prvkey(key)) {
|
||||||
|
ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY,
|
||||||
|
"no %s private key data available",
|
||||||
|
key->vinfo->algorithm_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
formats = ossl_prov_ctx_get_param(
|
||||||
|
provctx, OSSL_PKEY_PARAM_ML_KEM_OUTPUT_FORMATS, NULL);
|
||||||
|
vp8_slot = vp8_alloc = vp8_order(v->algorithm_name, c->pkcs8_info,
|
||||||
|
"output", formats);
|
||||||
|
if (vp8_alloc == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* If we don't have a seed, skip seedful entries */
|
||||||
|
if (!ossl_ml_kem_have_seed(key))
|
||||||
|
while (vp8_slot->vp8_entry != NULL
|
||||||
|
&& vp8_slot->vp8_entry->seed_offset != 0)
|
||||||
|
++vp8_slot;
|
||||||
|
/* No matching table entries, give up */
|
||||||
|
if ((vp8 = vp8_slot->vp8_entry) == NULL) {
|
||||||
|
ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_KEM_NO_FORMAT,
|
||||||
|
"no matching enabled %s private key output formats",
|
||||||
|
v->algorithm_name);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
len = vp8->p8_bytes;
|
||||||
|
|
||||||
|
if (out == NULL) {
|
||||||
|
ret = len;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pos = buf = OPENSSL_malloc((size_t) len)) == NULL)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
pos = OPENSSL_store_u32_be(pos, vp8->p8_magic);
|
||||||
|
if (vp8->seed_offset != 0) {
|
||||||
|
/*
|
||||||
|
* Either the tag/len were already included in |magic| or they require
|
||||||
|
* us to write two bytes now.
|
||||||
|
*/
|
||||||
|
if (pos + sizeof(uint16_t) == buf + vp8->seed_offset)
|
||||||
|
pos = OPENSSL_store_u16_be(pos, vp8->seed_magic);
|
||||||
|
if (pos != buf + vp8->seed_offset
|
||||||
|
|| !ossl_ml_kem_encode_seed(pos, ML_KEM_SEED_BYTES, key)) {
|
||||||
|
ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
|
||||||
|
"error encoding %s private key",
|
||||||
|
v->algorithm_name);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
pos += ML_KEM_SEED_BYTES;
|
||||||
|
}
|
||||||
|
if (vp8->priv_offset != 0) {
|
||||||
|
if (pos + sizeof(uint32_t) == buf + vp8->priv_offset)
|
||||||
|
pos = OPENSSL_store_u32_be(pos, vp8->priv_magic);
|
||||||
|
if (pos != buf + vp8->priv_offset
|
||||||
|
|| !ossl_ml_kem_encode_private_key(pos, v->prvkey_bytes, key)) {
|
||||||
|
ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
|
||||||
|
"error encoding %s private key",
|
||||||
|
v->algorithm_name);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
pos += v->prvkey_bytes;
|
||||||
|
}
|
||||||
|
/* OQS form output with tacked-on public key */
|
||||||
|
if (vp8->pub_offset != 0) {
|
||||||
|
/* The OQS pubkey is never separately DER-wrapped */
|
||||||
|
if (pos != buf + vp8->pub_offset
|
||||||
|
|| !ossl_ml_kem_encode_public_key(pos, v->pubkey_bytes, key)) {
|
||||||
|
ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
|
||||||
|
"error encoding %s private key",
|
||||||
|
v->algorithm_name);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
pos += v->pubkey_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos == buf + len) {
|
||||||
|
*out = buf;
|
||||||
|
ret = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
OPENSSL_free(vp8_alloc);
|
||||||
|
if (ret == 0)
|
||||||
|
OPENSSL_free(buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ossl_ml_kem_key_to_text(BIO *out, const ML_KEM_KEY *key, int selection)
|
||||||
|
{
|
||||||
|
uint8_t seed[ML_KEM_SEED_BYTES], *prvenc = NULL, *pubenc = NULL;
|
||||||
|
size_t publen, prvlen;
|
||||||
|
const char *type_label = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (out == NULL || key == NULL) {
|
||||||
|
ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
type_label = key->vinfo->algorithm_name;
|
||||||
|
publen = key->vinfo->pubkey_bytes;
|
||||||
|
prvlen = key->vinfo->prvkey_bytes;
|
||||||
|
|
||||||
|
if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
|
||||||
|
&& (ossl_ml_kem_have_prvkey(key)
|
||||||
|
|| ossl_ml_kem_have_seed(key))) {
|
||||||
|
if (BIO_printf(out, "%s Private-Key:\n", type_label) <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ossl_ml_kem_have_seed(key)) {
|
||||||
|
if (!ossl_ml_kem_encode_seed(seed, sizeof(seed), key))
|
||||||
|
goto end;
|
||||||
|
if (!ossl_bio_print_labeled_buf(out, "seed:", seed, sizeof(seed)))
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (ossl_ml_kem_have_prvkey(key)) {
|
||||||
|
if ((prvenc = OPENSSL_malloc(prvlen)) == NULL)
|
||||||
|
return 0;
|
||||||
|
if (!ossl_ml_kem_encode_private_key(prvenc, prvlen, key))
|
||||||
|
goto end;
|
||||||
|
if (!ossl_bio_print_labeled_buf(out, "dk:", prvenc, prvlen))
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The public key is output regardless of the selection */
|
||||||
|
if (ossl_ml_kem_have_pubkey(key)) {
|
||||||
|
/* If we did not output private key bits, this is a public key */
|
||||||
|
if (ret == 0 && BIO_printf(out, "%s Public-Key:\n", type_label) <= 0)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
if ((pubenc = OPENSSL_malloc(key->vinfo->pubkey_bytes)) == NULL
|
||||||
|
|| !ossl_ml_kem_encode_public_key(pubenc, publen, key)
|
||||||
|
|| !ossl_bio_print_labeled_buf(out, "ek:", pubenc, publen))
|
||||||
|
goto end;
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we got here, and ret == 0, there was no key material */
|
||||||
|
if (ret == 0)
|
||||||
|
ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY,
|
||||||
|
"no %s key material available",
|
||||||
|
type_label);
|
||||||
|
|
||||||
|
end:
|
||||||
|
OPENSSL_free(pubenc);
|
||||||
|
OPENSSL_free(prvenc);
|
||||||
|
return ret;
|
||||||
|
}
|
91
providers/implementations/encode_decode/ml_kem_codecs.h
Normal file
91
providers/implementations/encode_decode/ml_kem_codecs.h
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||||
|
* this file except in compliance with the License. You can obtain a copy
|
||||||
|
* in the file LICENSE in the source distribution or at
|
||||||
|
* https://www.openssl.org/source/license.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PROV_ML_KEM_CODECS_H
|
||||||
|
# define PROV_ML_KEM_CODECS_H
|
||||||
|
# pragma once
|
||||||
|
|
||||||
|
# include <openssl/e_os2.h>
|
||||||
|
# include "crypto/ml_kem.h"
|
||||||
|
# include "prov/provider_ctx.h"
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* The DER ASN.1 encoding of ML-KEM (and ML-DSA) public keys prepends 22 bytes
|
||||||
|
* to the encoded public key:
|
||||||
|
*
|
||||||
|
* - 4 byte outer sequence tag and length
|
||||||
|
* - 2 byte algorithm sequence tag and length
|
||||||
|
* - 2 byte algorithm OID tag and length
|
||||||
|
* - 9 byte algorithm OID (from NIST CSOR OID arc)
|
||||||
|
* - 4 byte bit string tag and length
|
||||||
|
* - 1 bitstring lead byte
|
||||||
|
*/
|
||||||
|
# define ML_KEM_SPKI_OVERHEAD 22
|
||||||
|
typedef struct {
|
||||||
|
const uint8_t asn1_prefix[ML_KEM_SPKI_OVERHEAD];
|
||||||
|
} ML_KEM_SPKI_INFO;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* For each parameter set we support a few PKCS#8 input formats, three
|
||||||
|
* corresponding to the "either or both" variants of:
|
||||||
|
*
|
||||||
|
* ML-KEM-PrivateKey ::= SEQUENCE {
|
||||||
|
* seed OCTET STRING SIZE (64) OPTIONAL,
|
||||||
|
* expandedKey [1] IMPLICIT OCTET STRING SIZE (1632 | 2400 | 3172) OPTIONAL }
|
||||||
|
* (WITH COMPONENTS {..., seed PRESENT } |
|
||||||
|
* WITH COMPONENTS {..., expandedKey PRESENT })
|
||||||
|
*
|
||||||
|
* and two more for historical OQS encodings.
|
||||||
|
*
|
||||||
|
* - OQS private key: OCTET STRING
|
||||||
|
* - OQS private + public key: OCTET STRING
|
||||||
|
* (The public key is ignored, just as with PKCS#8 v2.)
|
||||||
|
*
|
||||||
|
* An offset of zero means that particular field is absent.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
const char *p8_name;
|
||||||
|
size_t p8_bytes;
|
||||||
|
uint32_t p8_magic;
|
||||||
|
uint16_t seed_magic;
|
||||||
|
size_t seed_offset;
|
||||||
|
uint32_t priv_magic;
|
||||||
|
size_t priv_offset;
|
||||||
|
size_t pub_offset;
|
||||||
|
} ML_KEM_PKCS8_INFO;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const ML_KEM_SPKI_INFO *spki_info;
|
||||||
|
const ML_KEM_PKCS8_INFO *pkcs8_info;
|
||||||
|
} ML_KEM_CINFO;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const ML_KEM_PKCS8_INFO *vp8_entry;
|
||||||
|
int vp8_pref;
|
||||||
|
} ML_KEM_PKCS8_PREF;
|
||||||
|
|
||||||
|
__owur
|
||||||
|
ML_KEM_KEY *ossl_ml_kem_d2i_PUBKEY(const uint8_t *pubenc, int publen,
|
||||||
|
int evp_type, PROV_CTX *provctx,
|
||||||
|
const char *propq);
|
||||||
|
__owur
|
||||||
|
ML_KEM_KEY *ossl_ml_kem_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
|
||||||
|
int evp_type, PROV_CTX *provctx,
|
||||||
|
const char *propq);
|
||||||
|
__owur
|
||||||
|
int ossl_ml_kem_key_to_text(BIO *out, const ML_KEM_KEY *key, int selection);
|
||||||
|
__owur
|
||||||
|
__owur
|
||||||
|
int ossl_ml_kem_i2d_pubkey(const ML_KEM_KEY *key, unsigned char **out);
|
||||||
|
__owur
|
||||||
|
__owur
|
||||||
|
int ossl_ml_kem_i2d_prvkey(const ML_KEM_KEY *key, unsigned char **out,
|
||||||
|
PROV_CTX *provctx);
|
||||||
|
|
||||||
|
#endif /* PROV_ML_KEM_CODECS_H */
|
|
@ -52,10 +52,9 @@ static const int minimal_selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
|
||||||
| OSSL_KEYMGMT_SELECT_PRIVATE_KEY;
|
| OSSL_KEYMGMT_SELECT_PRIVATE_KEY;
|
||||||
|
|
||||||
typedef struct ml_kem_gen_ctx_st {
|
typedef struct ml_kem_gen_ctx_st {
|
||||||
OSSL_LIB_CTX *libctx;
|
PROV_CTX *provctx;
|
||||||
char *propq;
|
char *propq;
|
||||||
int selection;
|
int selection;
|
||||||
int retain_seed;
|
|
||||||
int evp_type;
|
int evp_type;
|
||||||
uint8_t seedbuf[ML_KEM_SEED_BYTES];
|
uint8_t seedbuf[ML_KEM_SEED_BYTES];
|
||||||
uint8_t *seed;
|
uint8_t *seed;
|
||||||
|
@ -127,19 +126,19 @@ err:
|
||||||
}
|
}
|
||||||
#endif /* FIPS_MODULE */
|
#endif /* FIPS_MODULE */
|
||||||
|
|
||||||
static void *ml_kem_new(PROV_CTX *ctx, int evp_type)
|
static void *ml_kem_new(PROV_CTX *ctx, const char *propq, int evp_type)
|
||||||
{
|
{
|
||||||
OSSL_LIB_CTX *libctx = NULL;
|
ML_KEM_KEY *key;
|
||||||
int retain_seed = 1;
|
|
||||||
|
|
||||||
if (!ossl_prov_is_running())
|
if (!ossl_prov_is_running())
|
||||||
return NULL;
|
return NULL;
|
||||||
if (ctx != NULL) {
|
if ((key = ossl_ml_kem_key_new(PROV_LIBCTX_OF(ctx), propq, evp_type)) != NULL) {
|
||||||
libctx = PROV_LIBCTX_OF(ctx);
|
key->retain_seed = ossl_prov_ctx_get_bool_param(
|
||||||
retain_seed = ossl_prov_ctx_get_bool_param(
|
|
||||||
ctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1);
|
ctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1);
|
||||||
|
key->prefer_seed = ossl_prov_ctx_get_bool_param(
|
||||||
|
ctx, OSSL_PKEY_PARAM_ML_KEM_PREFER_SEED, 1);
|
||||||
}
|
}
|
||||||
return ossl_ml_kem_key_new(libctx, NULL, retain_seed, evp_type);
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ml_kem_has(const void *vkey, int selection)
|
static int ml_kem_has(const void *vkey, int selection)
|
||||||
|
@ -196,7 +195,7 @@ static int ml_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb,
|
||||||
if (!ossl_ml_kem_have_pubkey(key)) {
|
if (!ossl_ml_kem_have_pubkey(key)) {
|
||||||
/* Fail when no key material can be returned */
|
/* Fail when no key material can be returned */
|
||||||
if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0
|
if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0
|
||||||
|| !ossl_ml_kem_have_seed(key)) {
|
|| !ossl_ml_kem_decoded_key(key)) {
|
||||||
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
|
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -224,6 +223,11 @@ static int ml_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb,
|
||||||
if ((prvenc = OPENSSL_secure_zalloc(prvlen)) == NULL
|
if ((prvenc = OPENSSL_secure_zalloc(prvlen)) == NULL
|
||||||
|| !ossl_ml_kem_encode_private_key(prvenc, prvlen, key))
|
|| !ossl_ml_kem_encode_private_key(prvenc, prvlen, key))
|
||||||
goto err;
|
goto err;
|
||||||
|
} else if (ossl_ml_kem_have_dkenc(key)) {
|
||||||
|
prvlen = v->prvkey_bytes;
|
||||||
|
if ((prvenc = OPENSSL_secure_zalloc(prvlen)) == NULL)
|
||||||
|
goto err;
|
||||||
|
memcpy(prvenc, key->encoded_dk, prvlen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,10 +237,8 @@ static int ml_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb,
|
||||||
|
|
||||||
/* The (d, z) seed, when available and private keys are requested. */
|
/* The (d, z) seed, when available and private keys are requested. */
|
||||||
if (seedenc != NULL
|
if (seedenc != NULL
|
||||||
&& (!ossl_param_build_set_octet_string(
|
&& !ossl_param_build_set_octet_string(
|
||||||
tmpl, params, OSSL_PKEY_PARAM_ML_KEM_SEED, seedenc, seedlen)
|
tmpl, params, OSSL_PKEY_PARAM_ML_KEM_SEED, seedenc, seedlen))
|
||||||
|| (!key->retain_seed && !ossl_param_build_set_int(
|
|
||||||
tmpl, params, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 0))))
|
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* The private key in the FIPS 203 |dk| format, when requested. */
|
/* The private key in the FIPS 203 |dk| format, when requested. */
|
||||||
|
@ -269,7 +271,6 @@ err:
|
||||||
static const OSSL_PARAM *ml_kem_imexport_types(int selection)
|
static const OSSL_PARAM *ml_kem_imexport_types(int selection)
|
||||||
{
|
{
|
||||||
static const OSSL_PARAM key_types[] = {
|
static const OSSL_PARAM key_types[] = {
|
||||||
OSSL_PARAM_int(OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, NULL),
|
|
||||||
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, NULL, 0),
|
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, NULL, 0),
|
||||||
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
|
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
|
||||||
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
|
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
|
||||||
|
@ -295,32 +296,24 @@ static int ml_kem_key_fromdata(ML_KEM_KEY *key,
|
||||||
return 0;
|
return 0;
|
||||||
v = ossl_ml_kem_key_vinfo(key);
|
v = ossl_ml_kem_key_vinfo(key);
|
||||||
|
|
||||||
p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED);
|
|
||||||
if (p != NULL
|
|
||||||
&& !(OSSL_PARAM_get_int(p, &key->retain_seed)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When a seed is provided, the private and public keys will be ignored,
|
|
||||||
* after validating just their lengths. Comparing encodings or hashes
|
|
||||||
* when applicable is possible, but not currently implemented.
|
|
||||||
*/
|
|
||||||
p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_KEM_SEED);
|
|
||||||
if (p != NULL
|
|
||||||
&& OSSL_PARAM_get_octet_string_ptr(p, &seedenc, &seedlen) != 1)
|
|
||||||
return 0;
|
|
||||||
if (seedlen == ML_KEM_SEED_BYTES) {
|
|
||||||
ossl_ml_kem_set_seed((uint8_t *)seedenc, seedlen, key);
|
|
||||||
} else if (seedlen != 0) {
|
|
||||||
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When a private key is provided, without a seed, any public key also
|
* When a private key is provided, without a seed, any public key also
|
||||||
* provided will be ignored (apart from length), just as with the seed.
|
* provided will be ignored (apart from length), just as with the seed.
|
||||||
*/
|
*/
|
||||||
if (include_private) {
|
if (include_private) {
|
||||||
|
/*
|
||||||
|
* When a seed is provided, the private and public keys may be ignored,
|
||||||
|
* after validating just their lengths. Comparing encodings or hashes
|
||||||
|
* when applicable is possible, but not currently implemented.
|
||||||
|
*/
|
||||||
|
p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_KEM_SEED);
|
||||||
|
if (p != NULL
|
||||||
|
&& OSSL_PARAM_get_octet_string_ptr(p, &seedenc, &seedlen) != 1)
|
||||||
|
return 0;
|
||||||
|
if (seedlen != 0 && seedlen != ML_KEM_SEED_BYTES) {
|
||||||
|
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
|
p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
|
||||||
if (p != NULL
|
if (p != NULL
|
||||||
&& OSSL_PARAM_get_octet_string_ptr(p, &prvenc, &prvlen) != 1)
|
&& OSSL_PARAM_get_octet_string_ptr(p, &prvenc, &prvlen) != 1)
|
||||||
|
@ -347,8 +340,9 @@ static int ml_kem_key_fromdata(ML_KEM_KEY *key,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seedlen != 0)
|
if (seedlen != 0 && (prvlen == 0 || key->prefer_seed))
|
||||||
return ossl_ml_kem_genkey(NULL, 0, key);
|
return ossl_ml_kem_set_seed(seedenc, seedlen, key)
|
||||||
|
&& ossl_ml_kem_genkey(NULL, 0, key);
|
||||||
else if (prvlen != 0)
|
else if (prvlen != 0)
|
||||||
return ossl_ml_kem_parse_private_key(prvenc, prvlen, key);
|
return ossl_ml_kem_parse_private_key(prvenc, prvlen, key);
|
||||||
return ossl_ml_kem_parse_public_key(pubenc, publen, key);
|
return ossl_ml_kem_parse_public_key(pubenc, publen, key);
|
||||||
|
@ -401,23 +395,31 @@ static const OSSL_PARAM *ml_kem_gettable_params(void *provctx)
|
||||||
void *ml_kem_load(const void *reference, size_t reference_sz)
|
void *ml_kem_load(const void *reference, size_t reference_sz)
|
||||||
{
|
{
|
||||||
ML_KEM_KEY *key = NULL;
|
ML_KEM_KEY *key = NULL;
|
||||||
|
uint8_t *encoded_dk;
|
||||||
|
|
||||||
if (ossl_prov_is_running() && reference_sz == sizeof(key)) {
|
if (ossl_prov_is_running() && reference_sz == sizeof(key)) {
|
||||||
/* The contents of the reference is the address to our object */
|
/* The contents of the reference is the address to our object */
|
||||||
key = *(ML_KEM_KEY **)reference;
|
key = *(ML_KEM_KEY **)reference;
|
||||||
|
encoded_dk = key->encoded_dk;
|
||||||
|
key->encoded_dk = NULL;
|
||||||
/* We grabbed, so we detach it */
|
/* We grabbed, so we detach it */
|
||||||
*(ML_KEM_KEY **)reference = NULL;
|
*(ML_KEM_KEY **)reference = NULL;
|
||||||
if (ossl_ml_kem_have_pubkey(key))
|
|
||||||
return key;
|
|
||||||
/* Generate the key now, if it holds only a stashed seed. */
|
/* Generate the key now, if it holds only a stashed seed. */
|
||||||
if (ossl_ml_kem_have_seed(key)) {
|
if (ossl_ml_kem_have_seed(key) &&
|
||||||
if (!ossl_ml_kem_genkey(NULL, 0, key)) {
|
(encoded_dk == NULL || key->prefer_seed)) {
|
||||||
ossl_ml_kem_key_free(key);
|
if (!ossl_ml_kem_genkey(NULL, 0, key))
|
||||||
return NULL;
|
goto err;
|
||||||
}
|
} else if (encoded_dk != NULL) {
|
||||||
|
if (!ossl_ml_kem_parse_private_key(encoded_dk,
|
||||||
|
key->vinfo->prvkey_bytes, key))
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
|
OPENSSL_free(encoded_dk);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err:
|
||||||
|
ossl_ml_kem_key_free(key);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -581,11 +583,6 @@ static int ml_kem_gen_set_params(void *vgctx, const OSSL_PARAM params[])
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED);
|
|
||||||
if (p != NULL
|
|
||||||
&& !(OSSL_PARAM_get_int(p, &gctx->retain_seed)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,10 +602,7 @@ static void *ml_kem_gen_init(void *provctx, int selection,
|
||||||
|
|
||||||
gctx->selection = selection;
|
gctx->selection = selection;
|
||||||
gctx->evp_type = evp_type;
|
gctx->evp_type = evp_type;
|
||||||
gctx->retain_seed = ossl_prov_ctx_get_bool_param(
|
gctx->provctx = provctx;
|
||||||
provctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1);
|
|
||||||
if (provctx != NULL)
|
|
||||||
gctx->libctx = PROV_LIBCTX_OF(provctx);
|
|
||||||
if (ml_kem_gen_set_params(gctx, params))
|
if (ml_kem_gen_set_params(gctx, params))
|
||||||
return gctx;
|
return gctx;
|
||||||
|
|
||||||
|
@ -638,8 +632,7 @@ static void *ml_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg)
|
||||||
|| (gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) ==
|
|| (gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) ==
|
||||||
OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
|
OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
|
||||||
return NULL;
|
return NULL;
|
||||||
key = ossl_ml_kem_key_new(gctx->libctx, gctx->propq,
|
key = ml_kem_new(gctx->provctx, gctx->propq, gctx->evp_type);
|
||||||
gctx->retain_seed, gctx->evp_type);
|
|
||||||
if (key == NULL)
|
if (key == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -700,7 +693,7 @@ static void *ml_kem_dup(const void *vkey, int selection)
|
||||||
#define DECLARE_VARIANT(bits) \
|
#define DECLARE_VARIANT(bits) \
|
||||||
static void *ml_kem_##bits##_new(void *provctx) \
|
static void *ml_kem_##bits##_new(void *provctx) \
|
||||||
{ \
|
{ \
|
||||||
return ml_kem_new(provctx, EVP_PKEY_ML_KEM_##bits); \
|
return ml_kem_new(provctx, NULL, EVP_PKEY_ML_KEM_##bits); \
|
||||||
} \
|
} \
|
||||||
static void *ml_kem_##bits##_gen_init(void *provctx, int selection, \
|
static void *ml_kem_##bits##_gen_init(void *provctx, int selection, \
|
||||||
const OSSL_PARAM params[]) \
|
const OSSL_PARAM params[]) \
|
||||||
|
|
|
@ -137,8 +137,8 @@ static int sanity_test(void)
|
||||||
if (!TEST_true(EVP_RAND_CTX_set_params(privctx, params)))
|
if (!TEST_true(EVP_RAND_CTX_set_params(privctx, params)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
public_key = ossl_ml_kem_key_new(NULL, NULL, 0, alg[i]);
|
public_key = ossl_ml_kem_key_new(NULL, NULL, alg[i]);
|
||||||
private_key = ossl_ml_kem_key_new(NULL, NULL, 0, alg[i]);
|
private_key = ossl_ml_kem_key_new(NULL, NULL, alg[i]);
|
||||||
if (private_key == NULL || public_key == NULL
|
if (private_key == NULL || public_key == NULL
|
||||||
|| (v = ossl_ml_kem_key_vinfo(public_key)) == NULL)
|
|| (v = ossl_ml_kem_key_vinfo(public_key)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
@ -25,7 +25,7 @@ my @formats = qw(seed-priv priv-only seed-only priv-oqs pair-oqs);
|
||||||
plan skip_all => "ML-KEM isn't supported in this build"
|
plan skip_all => "ML-KEM isn't supported in this build"
|
||||||
if disabled("ml-kem");
|
if disabled("ml-kem");
|
||||||
|
|
||||||
plan tests => @algs * (16 + 10 * @formats);
|
plan tests => @algs * (18 + 10 * @formats);
|
||||||
my $seed = join ("", map {sprintf "%02x", $_} (0..63));
|
my $seed = join ("", map {sprintf "%02x", $_} (0..63));
|
||||||
my $ikme = join ("", map {sprintf "%02x", $_} (0..31));
|
my $ikme = join ("", map {sprintf "%02x", $_} (0..31));
|
||||||
|
|
||||||
|
@ -139,6 +139,15 @@ foreach my $alg (@algs) {
|
||||||
sprintf("seedfull via cli vs. conf key match: %s", $alg));
|
sprintf("seedfull via cli vs. conf key match: %s", $alg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 2 tests
|
||||||
|
# Test decoder seed non-preference via the command-line.
|
||||||
|
my $privpref = sprintf("privpref-%s.dec.cli.pem", $alg);
|
||||||
|
ok(run(app(['openssl', 'pkey', '-provparam', 'ml-kem.prefer_seed=no',
|
||||||
|
'-in', data_file($formats{'seed-priv'}), '-out', $privpref])));
|
||||||
|
ok(!compare(data_file($formats{'priv-only'}), $privpref),
|
||||||
|
sprintf("seed non-preference via provparam key match: %s", $alg));
|
||||||
|
|
||||||
|
# 10(5 * 2) tests
|
||||||
# Check text encoding
|
# Check text encoding
|
||||||
while (my ($f, $k) = each %formats) {
|
while (my ($f, $k) = each %formats) {
|
||||||
my $txt = sprintf("prv-%s-%s.txt", $alg,
|
my $txt = sprintf("prv-%s-%s.txt", $alg,
|
||||||
|
|
|
@ -419,6 +419,7 @@ my %params = (
|
||||||
|
|
||||||
# ML-KEM parameters
|
# ML-KEM parameters
|
||||||
'PKEY_PARAM_ML_KEM_SEED' => "seed",
|
'PKEY_PARAM_ML_KEM_SEED' => "seed",
|
||||||
|
'PKEY_PARAM_ML_KEM_PREFER_SEED' => "ml-kem.prefer_seed",
|
||||||
'PKEY_PARAM_ML_KEM_RETAIN_SEED' => "ml-kem.retain_seed",
|
'PKEY_PARAM_ML_KEM_RETAIN_SEED' => "ml-kem.retain_seed",
|
||||||
'PKEY_PARAM_ML_KEM_INPUT_FORMATS' => "ml-kem.input_formats",
|
'PKEY_PARAM_ML_KEM_INPUT_FORMATS' => "ml-kem.input_formats",
|
||||||
'PKEY_PARAM_ML_KEM_OUTPUT_FORMATS' => "ml-kem.output_formats",
|
'PKEY_PARAM_ML_KEM_OUTPUT_FORMATS' => "ml-kem.output_formats",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue