Skip to content

Commit

Permalink
tools/pkcs11-tool.c - Changes for PKCS11 v3.0 errata and v3.1
Browse files Browse the repository at this point in the history
https://docs.oasis-open.org/pkcs11/pkcs11-curr/v3.0/os/pkcs11-curr-v3.0-os.html

PKCS11 3.0 OASIS Standard (15 June 2020) said for Edwards and Montgomery
public keys:
"CKA_EC_POINT | byte array | DER-encoding of ANSI X9.62 ECPoint value Q"

https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/pkcs11-spec-v3.1.pdf

PKCS11 3.1 OASIS Standard (23 July 2023) says for Edwards public keys:
"CKA_EC_POINT | Byte array | Public key bytes in little endian order as
defined in RFC 8032"
and for Montgomery public keys:
"CKA_EC_POINT | Byte array | Public key bytes in little endian order as
defined in RFC 7748"

https://docs.oasis-open.org/pkcs11/pkcs11-curr/v3.0/errata01/csd01/pkcs11-curr-v3.0-errata01-csd01-public-review-metadata.html

announces v3.0 errata with comments ending on 07 June 2024.
This puts V3.0 in line with v3.1

In order to align OpenSC PKCS11 module and pkcs11-tool to v3.1  specs:

Pkcs11-tool will send EC_POINT for Edwards and Montgomery public keys
to a PKCS11 module as raw byte string. pkcs11-tool will accept an
EC_POINT from a pkcs11 module as raw byte string, OCTET STRING or BIT STRING.

Note: CKA_PUBLIC_KEY_INFO is SubjectPublicKeyInfo as defined
in RFC8410.

Additional tests when compiled by OpenSSL older versions and Libressl 3.8.4
have been added and a signautre operation using a Yubkey Ed25519
key verifies using OpenSSL 3.3.2 utilities.

Pkcs11-tool as input and output files use RFC 8410 formats as OpenSSL
formats are based on RFC 8410.

 On branch X25519-improvements-2
 Changes to be committed:
	modified:   tools/pkcs11-tool.c
  • Loading branch information
dengert committed Nov 30, 2024
1 parent 4e8070b commit 6ff93ba
Showing 1 changed file with 65 additions and 20 deletions.
85 changes: 65 additions & 20 deletions src/tools/pkcs11-tool.c
Original file line number Diff line number Diff line change
Expand Up @@ -4686,15 +4686,15 @@ static CK_RV write_object(CK_SESSION_HANDLE session)
n_privkey_attr++;
}

else if ((type == CKK_EC) || (type == CKK_EC_EDWARDS) || (type = CKK_EC_MONTGOMERY)) {
else if ((type == CKK_EC) ||(type == CKK_EC_EDWARDS) || (type == CKK_EC_MONTGOMERY)) {
FILL_ATTR(privkey_templ[n_privkey_attr], CKA_KEY_TYPE, &type, sizeof(type));
n_privkey_attr++;
FILL_ATTR(privkey_templ[n_privkey_attr], CKA_EC_PARAMS, gost.param_oid.value, gost.param_oid.len);
n_privkey_attr++;
FILL_ATTR(privkey_templ[n_privkey_attr], CKA_VALUE, gost.private.value, gost.private.len);
n_privkey_attr++;

} else if (type == CKK_GOSTR3410) {

FILL_ATTR(privkey_templ[n_privkey_attr], CKA_KEY_TYPE, &type, sizeof(type));
n_privkey_attr++;
FILL_ATTR(privkey_templ[n_privkey_attr], CKA_GOSTR3410_PARAMS, gost.param_oid.value, gost.param_oid.len);
Expand Down Expand Up @@ -4769,6 +4769,7 @@ static CK_RV write_object(CK_SESSION_HANDLE session)
n_pubkey_attr++;
}
#if !defined(OPENSSL_NO_EC)

else if ((type == CKK_EC) || (type == CKK_EC_EDWARDS) || (type == CKK_EC_MONTGOMERY)) {

FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_KEY_TYPE, &type, sizeof(type));
Expand Down Expand Up @@ -6331,13 +6332,14 @@ static int read_object(CK_SESSION_HANDLE session)
/* If module supports CKA_PUBLIC_KEY_INFO which is DER of SPKI
* return whatever the module provides including ED448 and X448
*/
if (opt_public_key_info)
if (opt_public_key_info) {
value = getPUBLIC_KEY_INFO(session, obj, &len);
/* softhsm2 may return length 0 and varattr may allocate memory treat as invalid */
if (value && len == 0) {
p11_warn("getPUBLIC_KEY_INFO returned a value of length 0", 0);
free(value);
value = NULL;
/* softhsm2 may return length 0 and varattr may allocate memory treat as invalid */
if (value && len == 0) {
p11_warn("getPUBLIC_KEY_INFO returned a value of length 0", 0);
free(value);
value = NULL;
}
}
if (value == NULL) { /* Do the old way */
#ifdef ENABLE_OPENSSL
Expand Down Expand Up @@ -6525,22 +6527,25 @@ static int read_object(CK_SESSION_HANDLE session)
if (!i2d_PUBKEY_bio(pout, pkey))
util_fatal("cannot convert EC public key to DER");
#endif
/* only if compiled with a version of or OpenSSL or libressl */
/* do more tests for the other 3 as needed */
#ifdef EVP_PKEY_ED25519
} else if (type == CKK_EC_EDWARDS || type == CKK_EC_MONTGOMERY) {
EVP_PKEY *key = NULL;
CK_BYTE *params = NULL;
const unsigned char *a;
ASN1_OCTET_STRING *os;
int raw_pk = 0;

if ((params = getEC_PARAMS(session, obj, &len))) {
ASN1_PRINTABLESTRING *curve = NULL;
ASN1_OBJECT *obj = NULL;

a = params;
if (d2i_ASN1_PRINTABLESTRING(&curve, &a, (long)len) != NULL) {
if (strcmp((char *)curve->data, "edwards25519")) {
util_fatal("Unknown curve name, expected edwards25519, got %s",
curve->data);
if (strcmp((char *)curve->data, "edwards25519") &&
strcmp((char *)curve->data, "curve25519")) {
util_fatal("Unknown curve name \"%si\"", curve->data);
}
ASN1_PRINTABLESTRING_free(curve);
} else if (d2i_ASN1_OBJECT(&obj, &a, (long)len) != NULL) {
Expand All @@ -6559,29 +6564,66 @@ static int read_object(CK_SESSION_HANDLE session)
}

value = getEC_POINT(session, obj, &len);
/* PKCS#11-compliant modules should return ASN1_OCTET_STRING */
/* No, should be in BIT STRING accept both */
/* PKCS11 3.0 errta and 3.1 say Edwards and Montgomery
* return raw byte strings, convert to OCTET string for OpenSSL
* Will asccept as OCT STRING
*/
a = value;
os = d2i_ASN1_OCTET_STRING(NULL, &a, (long)len);
if (!os) {
os = d2i_ASN1_BIT_STRING(NULL, &a, (long)len);
len = BYTES4BITS(len);
if (os)
len = BYTES4BITS(len);
}
if (!os) {
util_fatal("cannot decode EC_POINT");
size_t buflen = 0;
unsigned char * buf = NULL;
unsigned char *in = value;

if (sc_pkcs15_encode_pubkey_eddsa_raw_to_os(NULL,
in, (size_t) len,
&buf, &buflen) != 0) {
util_fatal("cannot obtain EC POINT");
}
a = buf;
if ((os = d2i_ASN1_OCTET_STRING(NULL, &a,
(long) buflen)) == NULL) {
util_fatal("cannot obtain EC POINT");
}
free(buf);
}
if (os->length != 32) {
util_fatal("Invalid length of EC_POINT value");
if (!os) {
util_fatal("cannot decode EC_POINT");
}
key = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL,

if (type == CKK_EC_EDWARDS && os->length == BYTES4BITS(255))
raw_pk = EVP_PKEY_ED25519;
#if defined(EVP_PKEY_ED448)
else if (type == CKK_EC_EDWARDS && os->length == BYTES4BITS(448))
raw_pk = EVP_PKEY_ED448;
#endif /* EVP_PKEY_ED448 */
#if defined(EVP_PKEY_X25519)
else if (type == CKK_EC_MONTGOMERY && os->length == BYTES4BITS(255))
raw_pk = EVP_PKEY_X25519;
#endif /*EVP_PKEY_X25519 */
#if defined(EVP_PKEY_X448)
else if (type == CKK_EC_MONTGOMERY && os->length == BYTES4BITS(448))
raw_pk = EVP_PKEY_X448;
#endif /* EVP_PKEY_X448 */
else
util_fatal("Invalid or not supported CKK_EC_EDWARDS or CKK_EC_MONTGOMERY public key");

key = EVP_PKEY_new_raw_public_key(raw_pk, NULL,
(const uint8_t *)os->data,
os->length);
ASN1_STRING_free(os);
if (key == NULL) {
util_fatal("out of memory");
}
/* Note, that we write PEM here as there is no "native"
* representation of EdDSA public keys to use */
* in RFC 8410 /OpenSSL format
* representation of EdDSA public keys to use
*/
if (!PEM_write_bio_PUBKEY(pout, key)) {
util_fatal("cannot convert EdDSA public key to PEM");
}
Expand Down Expand Up @@ -8721,7 +8763,10 @@ static void test_ec(CK_SLOT_ID slot, CK_SESSION_HANDLE session)
return;
}
getEC_POINT(session, pub_key, &ec_point_len);
/* TODO only looking at length of encoded EC_POINT. May be in BIT STRING or OCTET STRING */
/* TODO if this routine us expanded to test EDDSA keys the following may be needed.
* a per 3.0 errata and 3.1 Edwards and Montgomery EC_POINT is just a byte string.
* Accept either BIT STRING, OCTET STRING or raw byte string.
*/
if (ec_point_len < 5 || ec_point_len > 10000) {
printf("ERR: GetAttribute(pubkey, CKA_EC_POINT) doesn't seem to work\n");
return;
Expand Down

0 comments on commit 6ff93ba

Please sign in to comment.