* https://www.openssl.org/source/license.html
*/
-#include <internal/common.h>
-#include <internal/constant_time.h>
-#include <internal/sha3.h>
-#include <crypto/ml_kem.h>
+#include <string.h>
#include <openssl/rand.h>
#include <openssl/byteorder.h>
+#include <openssl/proverr.h>
+#include <openssl/x509.h>
+#include "crypto/ctype.h"
+#include "crypto/ml_kem.h"
+#include "internal/common.h"
+#include "internal/constant_time.h"
+#include "internal/sha3.h"
+#include "internal/encoder.h"
#if defined(OPENSSL_CONSTANT_TIME_VALIDATION)
#include <valgrind/memcheck.h>
#endif
+/*
+ * Indices of slots in the vinfo tables below
+ */
+#define ML_KEM_512_VINFO 0
+#define ML_KEM_768_VINFO 1
+#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
*/
static const ML_KEM_VINFO vinfo_map[3] = {
{
"ML-KEM-512",
+ &ml_kem_512_spki_info,
+ ml_kem_512_pkcs8_info,
PRVKEY_BYTES(512),
sizeof(struct prvkey_512_alloc),
PUBKEY_BYTES(512),
},
{
"ML-KEM-768",
+ &ml_kem_768_spki_info,
+ ml_kem_768_pkcs8_info,
PRVKEY_BYTES(768),
sizeof(struct prvkey_768_alloc),
PUBKEY_BYTES(768),
},
{
"ML-KEM-1024",
+ &ml_kem_1024_spki_info,
+ ml_kem_1024_pkcs8_info,
PRVKEY_BYTES(1024),
sizeof(struct prvkey_1024_alloc),
PUBKEY_BYTES(1024),
*
* Note also that the input public key is assumed to hold a precomputed matrix
* |A| (our key->m, with the public key holding an expanded (16-bit per scalar
- * coefficient) key->t vector.
+ * coefficient) key->t vector).
*
* Caller passes storage in |tmp| for for two temporary vectors.
*/
{
switch (evp_type) {
case EVP_PKEY_ML_KEM_512:
- return &vinfo_map[0];
+ return &vinfo_map[ML_KEM_512_VINFO];
case EVP_PKEY_ML_KEM_768:
- return &vinfo_map[1];
+ return &vinfo_map[ML_KEM_768_VINFO];
case EVP_PKEY_ML_KEM_1024:
- return &vinfo_map[2];
+ return &vinfo_map[ML_KEM_1024_VINFO];
}
return NULL;
}
*/
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
+ * is 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
*/
#include "internal/deprecated.h"
-#include <openssl/asn1t.h>
#include <openssl/byteorder.h>
#include <openssl/core_dispatch.h>
#include <openssl/core_names.h>
#include "internal/nelem.h"
#include "ml_dsa_codecs.h"
-#ifndef OPENSSL_NO_ML_KEM
-typedef struct {
- ASN1_OBJECT *oid;
-} BARE_ALGOR;
-
-typedef struct {
- BARE_ALGOR algor;
- ASN1_BIT_STRING *pubkey;
-} BARE_PUBKEY;
-
-ASN1_SEQUENCE(BARE_ALGOR) = {
- ASN1_SIMPLE(BARE_ALGOR, oid, ASN1_OBJECT),
-} static_ASN1_SEQUENCE_END(BARE_ALGOR)
-
-ASN1_SEQUENCE(BARE_PUBKEY) = {
- ASN1_EMBED(BARE_PUBKEY, algor, BARE_ALGOR),
- ASN1_SIMPLE(BARE_PUBKEY, pubkey, ASN1_BIT_STRING)
-} static_ASN1_SEQUENCE_END(BARE_PUBKEY)
-#endif
-
struct der2key_ctx_st; /* Forward declaration */
typedef int check_key_fn(void *, struct der2key_ctx_st *ctx);
typedef void adjust_key_fn(void *, struct der2key_ctx_st *ctx);
/* ---------------------------------------------------------------------- */
#ifndef OPENSSL_NO_ML_KEM
-
-/* For lengths between 256 and 65535 the DER encoding is 4 bytes longer */
-#define ML_KEM_OCTET_STRING_OVERHEAD 4
-
-/*-
- * 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
-
-/*
- * The accepted private key format is either the 64-bytes (d, z) seed pair, or
- * else the optionally DER wrapped octet-string encoding (one byte tag, three
- * byte length) of the FIPS 203 expanded |dk| key, optionally concatenated with
- * the public |ek|. We don't need to worry about whether the public key is or
- * isn't appended, those are just ignored bytes at the end of an input buffer.
- */
-typedef enum {
- NONE_FMT, /* Unknown or unsupported format */
- SEED_FMT, /* Just the seed, no DER wrapping */
- LONG_FMT, /* Key(s), no octet-string DER wrapping */
- ASN1_FMT /* Key(s), octet-string DER wrapped */
-} ML_KEM_PRIV_FMT;
-
static void *
ml_kem_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx)
{
- const ML_KEM_VINFO *vinfo;
- ML_KEM_KEY *key = NULL, *ret = NULL;
+ ML_KEM_KEY *key;
OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
- PKCS8_PRIV_KEY_INFO *p8inf = NULL;
- const unsigned char *p;
- const X509_ALGOR *alg = NULL;
- ML_KEM_PRIV_FMT fmt;
- int plen, ptype, privlen, pairlen;
- uint16_t keylen;
- int retain;
-
- /* Extract the key OID and any parameters (we want none of those) */
- if ((p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, der, der_len)) == NULL)
- return 0;
- if (!PKCS8_pkey_get0(NULL, &p, &plen, &alg, p8inf))
- goto end;
-
- /* Bail out early if this is some other key type. */
- if (OBJ_obj2nid(alg->algorithm) != ctx->desc->evp_type)
- goto end;
- vinfo = ossl_ml_kem_get_vinfo(ctx->desc->evp_type);
-
- /* 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",
- vinfo->algorithm_name);
- goto end;
- }
- privlen = (int) vinfo->prvkey_bytes;
- pairlen = (int) (vinfo->prvkey_bytes + vinfo->pubkey_bytes);
-
- if (plen == ML_KEM_SEED_BYTES)
- fmt = SEED_FMT;
- else if (plen == privlen || plen == pairlen)
- fmt = LONG_FMT;
- else if (plen == ML_KEM_OCTET_STRING_OVERHEAD + privlen
- || plen == ML_KEM_OCTET_STRING_OVERHEAD + pairlen)
- fmt = ASN1_FMT;
- else
- fmt = NONE_FMT;
-
- switch (fmt) {
- case NONE_FMT:
- ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH,
- "unexpected PKCS#8 private key length: %d", plen);
- break;
- case SEED_FMT:
- retain = ossl_prov_ctx_get_bool_param(
+ int retain_seed = ossl_prov_ctx_get_bool_param(
ctx->provctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1);
- if ((key = ossl_ml_kem_key_new(libctx, ctx->propq, retain,
- ctx->desc->evp_type)) != NULL)
- ret = ossl_ml_kem_set_seed(p, plen, key);
- else
- ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
- "error storing %s private key seed",
- vinfo->algorithm_name);
- break;
- case ASN1_FMT:
- if (*p++ != V_ASN1_OCTET_STRING || *p++ != 0x82)
- break;
- p = OPENSSL_load_u16_be(&keylen, p);
- if (keylen != (plen -= ML_KEM_OCTET_STRING_OVERHEAD))
- break;
- /* fallthrough */
- case LONG_FMT:
- /* Check public key consistency if provided? */
- if ((key = ossl_ml_kem_key_new(libctx, ctx->propq, 1,
- ctx->desc->evp_type)) != NULL
- && ossl_ml_kem_parse_private_key(p, vinfo->prvkey_bytes, key))
- ret = key;
- else
- ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
- "error parsing %s private key",
- vinfo->algorithm_name);
- break;
- }
+ const char *formats = ossl_prov_ctx_get_param(
+ ctx->provctx, OSSL_PKEY_PARAM_ML_KEM_INPUT_FORMATS, NULL);
- end:
- PKCS8_PRIV_KEY_INFO_free(p8inf);
- if (ret == NULL)
- ossl_ml_kem_key_free(key);
- return ret;
+ key = ossl_ml_kem_d2i_PKCS8(*der, der_len, retain_seed, formats,
+ ctx->desc->evp_type, libctx, ctx->propq);
+ if (key != NULL)
+ *der += der_len;
+ return key;
}
static ossl_inline void *
ml_kem_d2i_PUBKEY(const uint8_t **der, long der_len,
struct der2key_ctx_st *ctx)
{
- const ML_KEM_VINFO *vinfo = ossl_ml_kem_get_vinfo(ctx->desc->evp_type);
- OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
- ML_KEM_KEY *ret = NULL;
- BARE_PUBKEY *spki;
- const uint8_t *end = *der;
-
- /*
- * Not our NID! Is there a sensible error to report here? Why was this
- * decoder called?
- */
- if (vinfo == NULL)
- return NULL;
-
- /*
- * Check that we have the right OID, the bit string has no "bits left" and
- * that we consume all the input exactly.
- */
- if (der_len != ML_KEM_SPKI_OVERHEAD + (long) vinfo->pubkey_bytes) {
- ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
- "unexpected %s public key length: %ld != %ld",
- vinfo->algorithm_name, der_len,
- ML_KEM_SPKI_OVERHEAD + (long) vinfo->pubkey_bytes);
- return NULL;
- }
-
- if ((spki = OPENSSL_zalloc(sizeof(*spki))) == NULL)
- return NULL;
+ ML_KEM_KEY *key;
- /* The spki storage is freed on error */
- if (ASN1_item_d2i_ex((ASN1_VALUE **)&spki, &end, der_len,
- ASN1_ITEM_rptr(BARE_PUBKEY), NULL, NULL) == NULL) {
- ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
- "malformed %s public key ASN.1 encoding",
- vinfo->algorithm_name);
- return NULL;
- }
-
- /* The spki structure now owns some memory */
- if ((spki->pubkey->flags & 0x7) != 0 || end != *der + der_len) {
- ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
- "malformed %s public key ASN.1 encoding",
- vinfo->algorithm_name);
- goto end;
- }
- if (OBJ_cmp(OBJ_nid2obj(ctx->desc->evp_type), spki->algor.oid) != 0) {
- ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
- "unexpected algorithm OID for an %s public key",
- vinfo->algorithm_name);
- goto end;
- }
-
- ret = ossl_ml_kem_key_new(libctx, ctx->propq, 1, ctx->desc->evp_type);
- if (ret == NULL
- || !ossl_ml_kem_parse_public_key(spki->pubkey->data,
- spki->pubkey->length, ret)) {
- ossl_ml_kem_key_free(ret);
- ret = NULL;
- ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
- "failed to parse %s public key from the input data",
- vinfo->algorithm_name);
- }
-
- end:
- ASN1_OBJECT_free(spki->algor.oid);
- ASN1_BIT_STRING_free(spki->pubkey);
- OPENSSL_free(spki);
- return ret;
+ key = ossl_ml_kem_d2i_PUBKEY(*der, der_len, ctx->desc->evp_type,
+ PROV_LIBCTX_OF(ctx->provctx), ctx->propq);
+ if (key != NULL)
+ *der += der_len;
+ return key;
}
-
# define ml_kem_512_evp_type EVP_PKEY_ML_KEM_512
# define ml_kem_512_d2i_private_key NULL
# define ml_kem_512_d2i_public_key NULL