Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Composite sigs update #549

Merged
merged 13 commits into from
Oct 31, 2024
13 changes: 13 additions & 0 deletions ALGORITHMS.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,23 @@ As standardization for these algorithms within TLS is not done, all TLS code poi
| mldsa44 | 0xfed0 |Yes| OQS_CODEPOINT_MLDSA44
| p256_mldsa44 | 0xfed3 |Yes| OQS_CODEPOINT_P256_MLDSA44
| rsa3072_mldsa44 | 0xfed4 |Yes| OQS_CODEPOINT_RSA3072_MLDSA44
| mldsa44_pss2048 | 0xfee1 |Yes| OQS_CODEPOINT_MLDSA44_pss2048
| mldsa44_rsa2048 | 0xfee2 |Yes| OQS_CODEPOINT_MLDSA44_rsa2048
| mldsa44_ed25519 | 0xfee3 |Yes| OQS_CODEPOINT_MLDSA44_ed25519
| mldsa44_p256 | 0xfee4 |Yes| OQS_CODEPOINT_MLDSA44_p256
| mldsa44_bp256 | 0xfee5 |Yes| OQS_CODEPOINT_MLDSA44_bp256
| mldsa65 | 0xfed1 |Yes| OQS_CODEPOINT_MLDSA65
| p384_mldsa65 | 0xfed5 |Yes| OQS_CODEPOINT_P384_MLDSA65
| mldsa65_pss3072 | 0xfee6 |Yes| OQS_CODEPOINT_MLDSA65_pss3072
| mldsa65_rsa3072 | 0xfee7 |Yes| OQS_CODEPOINT_MLDSA65_rsa3072
| mldsa65_p256 | 0xfee8 |Yes| OQS_CODEPOINT_MLDSA65_p256
| mldsa65_bp256 | 0xfee9 |Yes| OQS_CODEPOINT_MLDSA65_bp256
| mldsa65_ed25519 | 0xfeea |Yes| OQS_CODEPOINT_MLDSA65_ed25519
| mldsa87 | 0xfed2 |Yes| OQS_CODEPOINT_MLDSA87
| p521_mldsa87 | 0xfed6 |Yes| OQS_CODEPOINT_P521_MLDSA87
| mldsa87_p384 | 0xfeeb |Yes| OQS_CODEPOINT_MLDSA87_p384
| mldsa87_bp384 | 0xfeec |Yes| OQS_CODEPOINT_MLDSA87_bp384
| mldsa87_ed448 | 0xfeed |Yes| OQS_CODEPOINT_MLDSA87_ed448
| falcon512 | 0xfed7 |Yes| OQS_CODEPOINT_FALCON512
| p256_falcon512 | 0xfed8 |Yes| OQS_CODEPOINT_P256_FALCON512
| rsa3072_falcon512 | 0xfed9 |Yes| OQS_CODEPOINT_RSA3072_FALCON512
Expand Down
2 changes: 1 addition & 1 deletion STANDARDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ components, this provider implements the following standards:
- Hybrid post-quantum / traditional private keys:
- Simple concatenation of traditional and post-quantum components in plain binary / OCTET_STRING representations.

- For composite signatures, the implementation follows [this IETF draft](https://datatracker.ietf.org/doc/draft-ounsworth-pq-composite-sigs/) (version 13). Note that only those PQ algorithms denoted with the marker "composite" in [oqs-template/generate.yml](oqs-template/generate.yml) are provided with composite variants and not all.
- For composite signatures, the implementation follows [this IETF draft](https://datatracker.ietf.org/doc/draft-ietf-lamps-pq-composite-sigs/) (version 02). Note that only those PQ algorithms denoted with the marker "composite" in [oqs-template/generate.yml](oqs-template/generate.yml) are provided with composite variants and not all.

Note: Please heed the [documentation on the enablement of KEM encoders](CONFIGURE.md#oqs_kem_encoders) via PKCS#8 and X.509.
3 changes: 3 additions & 0 deletions oqs-template/ALGORITHMS.md/ids.fragment
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
{%- for classical_alg in variant['mix_with'] %}
| {{ classical_alg['name'] }}_{{variant['name']}} | {{ classical_alg['code_point'] }} | {%- if variant['enable'] -%} Yes {%- else -%} No {%- endif -%} | OQS_CODEPOINT_{{ classical_alg['name']|upper }}_{{ variant['name']|upper }}
{%- endfor %}
{%- for composite_alg in variant['composite'] %}
| {{variant['name']}}_{{ composite_alg['name'] }} | {{ composite_alg['code_point'] }} | {%- if variant['enable'] -%} Yes {%- else -%} No {%- endif -%} | OQS_CODEPOINT_{{ variant['name']|upper }}_{{ composite_alg['name'] }}
{%- endfor %}
{%- endfor %}
{%- endfor %}

7 changes: 7 additions & 0 deletions oqs-template/generate_oid_nid_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ def gen_sig_table(oqslibdocdir):
claimed_nist_level,
hybrid['code_point'],
hybrid['oid']])
for composite in variant['composite']:
table.append([variant['name'] + ' **composite with** ' + composite['name'],
liboqs_sigs[sig['family']]['spec-version'],
str(liboqs_sigs[sig['family']]['nist-round']),
claimed_nist_level,
composite['code_point'],
composite['oid']])
except KeyError as ke:
# Non-existant NIDs mean this alg is not supported any more
pass
Expand Down
371 changes: 192 additions & 179 deletions oqs-template/oqs-sig-info.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@


/** \brief List of composite signature algorithms. */
const char *kCompositeSignatureAlgorithms[] = {
{% for sig in config['sigs'] %}
{%- for variant in sig['variants'] %}
{%- for composite_alg in variant['composite'] -%}
"{{variant['name']}}_{{ composite_alg['name'] }}",
{%- endfor -%}
{%- endfor %}
{%- endfor %}
NULL,
};

121 changes: 97 additions & 24 deletions oqsprov/oqs_encode_key2any.c
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,9 @@ static int oqsx_pki_priv_to_der(const void *vxkey, unsigned char **pder) {
OPENSSL_malloc(oqsxkey->numkeys * sizeof(ASN1_OCTET_STRING *));
unsigned char **temp =
OPENSSL_malloc(oqsxkey->numkeys * sizeof(unsigned char *));
size_t *templen = OPENSSL_malloc(oqsxkey->numkeys * sizeof(size_t));
unsigned char *ed_internal;
size_t *templen = OPENSSL_malloc(oqsxkey->numkeys * sizeof(size_t)),
ed_internallen;
PKCS8_PRIV_KEY_INFO *p8inf_internal = NULL;
sk = sk_ASN1_TYPE_new_null();
int i;
Expand Down Expand Up @@ -789,7 +791,7 @@ static int oqsx_pki_priv_to_der(const void *vxkey, unsigned char **pder) {
memcpy(buf, oqsxkey->comp_privkey[i],
buflen); // buflen for classical (RSA)
// might be different from
// oqsxkey->privkeylen_cmp[
// oqsxkey->privkeylen_cmp
}

if (nid == EVP_PKEY_EC) { // add the curve OID with the ECPubkey OID
Expand All @@ -800,30 +802,87 @@ static int oqsx_pki_priv_to_der(const void *vxkey, unsigned char **pder) {
version = V_ASN1_UNDEF;
pval = NULL;
}
if (!PKCS8_pkey_set0(p8inf_internal, OBJ_nid2obj(nid), 0, version,
pval, buf, buflen)) {
for (int j = 0; j <= i; j++) {
OPENSSL_cleanse(aString[j]->data, aString[j]->length);
ASN1_OCTET_STRING_free(aString[j]);
OPENSSL_cleanse(aType[j]->value.sequence->data,
aType[j]->value.sequence->length);
OPENSSL_clear_free(temp[j], templen[j]);
if (nid == EVP_PKEY_ED25519 || nid == EVP_PKEY_ED448) {
baentsch marked this conversation as resolved.
Show resolved Hide resolved
oct.data = buf;
oct.length = buflen;
oct.flags = 0;
ed_internal = NULL;

ed_internallen = i2d_ASN1_OCTET_STRING(&oct, &ed_internal);
if (ed_internallen < 0) {
for (int j = 0; j <= i; j++) {
OPENSSL_cleanse(aString[j]->data, aString[j]->length);
ASN1_OCTET_STRING_free(aString[j]);
OPENSSL_cleanse(aType[j]->value.sequence->data,
aType[j]->value.sequence->length);
OPENSSL_clear_free(temp[j], templen[j]);
}

sk_ASN1_TYPE_pop_free(sk, &ASN1_TYPE_free);
OPENSSL_free(name);
OPENSSL_free(aType);
OPENSSL_free(aString);
OPENSSL_free(temp);
OPENSSL_free(templen);
OPENSSL_cleanse(buf,
buflen); // buf is part of p8inf_internal so
// we cant free now, we cleanse it
// to remove pkey from memory
PKCS8_PRIV_KEY_INFO_free(
p8inf_internal); // this also free buf
return -1;
}

sk_ASN1_TYPE_pop_free(sk, &ASN1_TYPE_free);
OPENSSL_free(name);
OPENSSL_free(aType);
OPENSSL_free(aString);
OPENSSL_free(temp);
OPENSSL_free(templen);
OPENSSL_cleanse(buf,
buflen); // buf is part of p8inf_internal so we
// cant free now, we cleanse it to
// remove pkey from memory
PKCS8_PRIV_KEY_INFO_free(p8inf_internal); // this also free buf
return -1;
}
if (!PKCS8_pkey_set0(p8inf_internal, OBJ_nid2obj(nid), 0,
version, pval, ed_internal,
ed_internallen)) {
for (int j = 0; j <= i; j++) {
OPENSSL_cleanse(aString[j]->data, aString[j]->length);
ASN1_OCTET_STRING_free(aString[j]);
OPENSSL_cleanse(aType[j]->value.sequence->data,
aType[j]->value.sequence->length);
OPENSSL_clear_free(temp[j], templen[j]);
}

sk_ASN1_TYPE_pop_free(sk, &ASN1_TYPE_free);
OPENSSL_free(name);
OPENSSL_free(aType);
OPENSSL_free(aString);
OPENSSL_free(temp);
OPENSSL_free(templen);
OPENSSL_secure_clear_free(buf, buflen);
OPENSSL_cleanse(ed_internal, ed_internallen);
PKCS8_PRIV_KEY_INFO_free(
p8inf_internal); // this also free ed_internal
return -1;
}

} else {
if (!PKCS8_pkey_set0(p8inf_internal, OBJ_nid2obj(nid), 0,
version, pval, buf, buflen)) {
for (int j = 0; j <= i; j++) {
OPENSSL_cleanse(aString[j]->data, aString[j]->length);
ASN1_OCTET_STRING_free(aString[j]);
OPENSSL_cleanse(aType[j]->value.sequence->data,
aType[j]->value.sequence->length);
OPENSSL_clear_free(temp[j], templen[j]);
}

sk_ASN1_TYPE_pop_free(sk, &ASN1_TYPE_free);
OPENSSL_free(name);
OPENSSL_free(aType);
OPENSSL_free(aString);
OPENSSL_free(temp);
OPENSSL_free(templen);
OPENSSL_cleanse(buf,
buflen); // buf is part of p8inf_internal so
// we cant free now, we cleanse it
// to remove pkey from memory
PKCS8_PRIV_KEY_INFO_free(
p8inf_internal); // this also free buf
return -1;
}
}
templen[i] =
i2d_PKCS8_PRIV_KEY_INFO(p8inf_internal,
&temp[i]); // create the privkey info
Expand Down Expand Up @@ -853,12 +912,26 @@ static int oqsx_pki_priv_to_der(const void *vxkey, unsigned char **pder) {
buflen); // buf is part of p8inf_internal so we
// cant free now, we cleanse it to
// remove pkey from memory
PKCS8_PRIV_KEY_INFO_free(p8inf_internal); // this also free buf
if (nid == EVP_PKEY_ED25519 || nid == EVP_PKEY_ED448) {
OPENSSL_cleanse(ed_internal, ed_internallen);
OPENSSL_secure_free(
buf); // in this case the ed_internal is
// freed from the pkcs8_free instead
// of buf, so we need to free buf here
}
PKCS8_PRIV_KEY_INFO_free(
p8inf_internal); // this also free buf or ed_internal
return -1;
}
OPENSSL_free(name);

OPENSSL_cleanse(buf, buflen);
if (nid == EVP_PKEY_ED25519 || nid == EVP_PKEY_ED448) {
OPENSSL_cleanse(ed_internal, ed_internallen);
OPENSSL_secure_free(buf); // in this case the ed_internal is
// freed from the pkcs8_free instead
// of buf, so we need to free buf here
}
PKCS8_PRIV_KEY_INFO_free(p8inf_internal);
}
keybloblen = i2d_ASN1_SEQUENCE_ANY(sk, pder);
Expand Down
2 changes: 1 addition & 1 deletion oqsprov/oqs_prov.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ char *get_oqsname_fromtls(char *tlsname);
char *get_oqsname(int nid);
char *get_cmpname(int nid, int index);
int get_oqsalg_idx(int nid);
int get_composite_idx(int idx);
int get_composite_idx(char *name);

/* Workaround for not functioning EC PARAM initialization
* TBD, check https://github.com/openssl/openssl/issues/16989
Expand Down
4 changes: 2 additions & 2 deletions oqsprov/oqs_sig.c
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ static int oqs_sig_sign(void *vpoqs_sigctx, unsigned char *sig, size_t *siglen,
unsigned char *buf;
int i;
int nid = OBJ_sn2nid(oqsxkey->tls_name);
int comp_idx = get_composite_idx(get_oqsalg_idx(nid));
int comp_idx = get_composite_idx(oqsxkey->tls_name);
if (comp_idx == -1)
goto endsign;
const unsigned char *oid_prefix = composite_OID_prefix[comp_idx - 1];
Expand Down Expand Up @@ -799,7 +799,7 @@ static int oqs_sig_verify(void *vpoqs_sigctx, const unsigned char *sig,
CompositeSignature *compsig;
int i;
int nid = OBJ_sn2nid(oqsxkey->tls_name);
int comp_idx = get_composite_idx(get_oqsalg_idx(nid));
int comp_idx = get_composite_idx(oqsxkey->tls_name);
if (comp_idx == -1)
goto endverify;
unsigned char *buf;
Expand Down
17 changes: 12 additions & 5 deletions oqsprov/oqsprov.c
Original file line number Diff line number Diff line change
Expand Up @@ -997,13 +997,20 @@ static const OSSL_ALGORITHM oqsprovider_decoder[] = {
};

// get the last number on the composite OID
int get_composite_idx(int idx) {
char *s;
int get_composite_idx(char *name) {
char *s = NULL;
int i, len, ret = -1, count = 0;

if (2 * idx > OQS_OID_CNT)
return 0;
s = (char *)oqs_oid_alg_list[idx * 2];
for (i = 1; i <= OQS_OID_CNT; i += 2) {
if (!strcmp((char *)oqs_oid_alg_list[i], name)) {
s = (char *)oqs_oid_alg_list[i - 1];
break;
}
}
if (s == NULL) {
return ret;
}

len = strlen(s);

for (i = 0; i < len; i++) {
Expand Down
13 changes: 13 additions & 0 deletions oqsprov/oqsprov_keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,19 @@ OQSX_KEY *oqsx_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf,
key_diff = nids_sig[6].length_private_key - buflen;
}

// removing extra OTECT STRING from ED25519 and ED448 keys
if ((keytype == EVP_PKEY_ED25519) ||
(keytype == EVP_PKEY_ED448)) {
ASN1_OCTET_STRING *ed_octet = NULL;
ed_octet = d2i_ASN1_OCTET_STRING(&ed_octet, &buf, buflen);
aux += ed_octet->length;
memcpy(concat_key + plen - 1 - aux, ed_octet->data,
ed_octet->length);
nid = 1; // setting to non zero value so the key is not
baentsch marked this conversation as resolved.
Show resolved Hide resolved
// copied again
ASN1_OCTET_STRING_free(ed_octet);
}

if (!nid) {
aux += buflen;
memcpy(concat_key + plen - 1 - aux, buf,
Expand Down
14 changes: 14 additions & 0 deletions test/oqs_test_evp_pkey_params.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ const char *kHybridSignatureAlgorithms[] = {
};
///// OQS_TEMPLATE_FRAGMENT_HYBRID_SIG_ALGS_END

///// OQS_TEMPLATE_FRAGMENT_COMPOSITE_SIG_ALGS_START

/** \brief List of composite signature algorithms. */
const char *kCompositeSignatureAlgorithms[] = {
baentsch marked this conversation as resolved.
Show resolved Hide resolved
"mldsa44_pss2048", "mldsa44_rsa2048",
"mldsa44_ed25519", "mldsa44_p256",
"mldsa44_bp256", "mldsa65_pss3072",
"mldsa65_rsa3072", "mldsa65_p256",
"mldsa65_bp256", "mldsa65_ed25519",
"mldsa87_p384", "mldsa87_bp384",
"mldsa87_ed448", NULL,
};
///// OQS_TEMPLATE_FRAGMENT_COMPOSITE_SIG_ALGS_END

///// OQS_TEMPLATE_FRAGMENT_HYBRID_KEM_ALGS_START

/** \brief List of hybrid KEMs. */
Expand Down
Loading