Skip to content

Commit

Permalink
export-object: Recover public key from algorithm specific attributes
Browse files Browse the repository at this point in the history
Some tokens such as SoftHSMv2 omits CKA_PUBLIC_KEY_INFO when it can be
derived from algorithm algorithm specific attributes, such as
CKA_MODULUS and CKA_PUBLIC_EXPONENT for RSA.  This adds support for it
if libtasn1 is available at compile time.

Signed-off-by: Daiki Ueno <[email protected]>
  • Loading branch information
ueno committed Sep 21, 2023
1 parent 65f0fcd commit 101373a
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 55 deletions.
2 changes: 2 additions & 0 deletions common/oid.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,4 +241,6 @@ static const unsigned char P11_OID_RESERVED_PURPOSE[] =
{ 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x99, 0x77, 0x06, 0x0a, 0x10 };
static const char P11_OID_RESERVED_PURPOSE_STR[] = "1.3.6.1.4.1.3319.6.10.16";

static const char P11_OID_PKIX1_RSA_STR[] = "1.2.840.113549.1.1.1";

#endif
4 changes: 4 additions & 0 deletions common/pkix.asn
Original file line number Diff line number Diff line change
Expand Up @@ -537,4 +537,8 @@ ProxyPolicy ::= SEQUENCE {
policyLanguage OBJECT IDENTIFIER,
policy OCTET STRING OPTIONAL }

RSAPublicKey ::= SEQUENCE {
modulus INTEGER,
publicExponent INTEGER }

END
278 changes: 225 additions & 53 deletions p11-kit/export-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#include "config.h"

#include "attrs.h"
#include "buffer.h"
#include "constants.h"
#include "debug.h"
Expand All @@ -44,7 +45,13 @@
#include "pem.h"
#include "tool.h"

#ifdef WITH_ASN1
#include "asn1.h"
#include "oid.h"
#endif

#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

Expand All @@ -59,85 +66,241 @@ int
p11_kit_export_object (int argc,
char *argv[]);

#ifdef WITH_ASN1

static void
export_attribute (P11KitIter *iter,
CK_ATTRIBUTE_TYPE type,
const char *label)
prepend_leading_zero (CK_ATTRIBUTE *attr)
{
p11_buffer buf;
CK_ATTRIBUTE attr = { type, NULL_PTR, 0 };
if (*((unsigned char *)attr->pValue) & 0x80) {
unsigned char *padded;

return_if_fail (attr->ulValueLen < ULONG_MAX);
padded = malloc (attr->ulValueLen + 1);
return_if_fail (padded);
memcpy (padded + 1, attr->pValue, attr->ulValueLen);
*padded = 0x00;
free (attr->pValue);
attr->pValue = padded;
attr->ulValueLen++;
}
}

if (!p11_buffer_init (&buf, 0)) {
p11_message (_("failed to initialize buffer"));
return;
static bool
export_pubkey_rsa (P11KitIter *iter, p11_buffer *buf)
{
CK_ATTRIBUTE template[] = {
{ CKA_MODULUS, },
{ CKA_PUBLIC_EXPONENT, },
};
CK_ATTRIBUTE *attrs = NULL;
CK_ATTRIBUTE *modulus, *public_exponent;
asn1_node asn = NULL;
p11_dict *defs = NULL;
int result;
unsigned char *spk = NULL, *der = NULL;
const unsigned char null[] = { 0x05, 0x00 };
size_t n_spk, n_der;
bool ok = false;

attrs = p11_attrs_buildn (NULL, template, 2);
return_val_if_fail (attrs, false);

if (p11_kit_iter_load_attributes (iter, attrs, p11_attrs_count (attrs)) != CKR_OK) {
p11_message (_("failed to retrieve attributes"));
goto cleanup;
}

if (p11_kit_iter_get_attributes (iter, &attr, 1) != CKR_OK) {
p11_message (_("failed to retrieve attribute length of an object"));
modulus = p11_attrs_find_valid (attrs, CKA_MODULUS);
public_exponent = p11_attrs_find_valid (attrs, CKA_PUBLIC_EXPONENT);

if (!modulus || !public_exponent) {
p11_message (_("failed to retrieve attributes"));
goto cleanup;
}

attr.pValue = malloc (attr.ulValueLen);
if (attr.pValue == NULL) {
p11_message (_("failed to allocate memory"));
defs = p11_asn1_defs_load ();
return_val_if_fail (defs, false);

asn = p11_asn1_create (defs, "PKIX1.RSAPublicKey");
prepend_leading_zero (modulus);
result = asn1_write_value (asn, "modulus", modulus->pValue, modulus->ulValueLen);
if (result != ASN1_SUCCESS) {
p11_message (_("unable to write value"));
goto cleanup;
}
prepend_leading_zero (public_exponent);
result = asn1_write_value (asn, "publicExponent", public_exponent->pValue, public_exponent->ulValueLen);
if (result != ASN1_SUCCESS) {
p11_message (_("unable to write value"));
goto cleanup;
}

if (p11_kit_iter_get_attributes (iter, &attr, 1) != CKR_OK) {
p11_message (_("failed to retrieve attribute of an object"));
spk = p11_asn1_encode (asn, &n_spk);
if (!spk) {
p11_message (_("unable to encode %s"), "subjectPublicKey");
goto cleanup;
}
p11_asn1_free (asn);

asn = p11_asn1_create (defs, "PKIX1.SubjectPublicKeyInfo");

result = asn1_write_value (asn, "algorithm.algorithm",
P11_OID_PKIX1_RSA_STR, 1);
if (result != ASN1_SUCCESS) {
p11_message (_("unable to write value"));
goto cleanup;
}

result = asn1_write_value (asn, "algorithm.parameters",
null, sizeof (null));
if (result != ASN1_SUCCESS) {
p11_message (_("unable to write value"));
goto cleanup;
}

result = asn1_write_value (asn, "subjectPublicKey", spk, n_spk * 8);
if (result != ASN1_SUCCESS) {
p11_message (_("unable to write value"));
goto cleanup;
}

der = p11_asn1_encode (asn, &n_der);
if (!der) {
p11_message (_("unable to encode %s"), "SubjectPublicKeyInfo");
goto cleanup;
}

if (!p11_pem_write (attr.pValue, attr.ulValueLen, label, &buf)) {
if (!p11_pem_write (der, n_der, "PUBLIC KEY", buf)) {
p11_message (_("failed to convert DER to PEM"));
goto cleanup;
}

if (fwrite (buf.data, 1, buf.len, stdout) != buf.len) {
p11_message (_("failed to write PEM data to stdout"));
ok = true;

cleanup:
free (der);
free (spk);
p11_asn1_free (asn);
p11_dict_free (defs);
p11_attrs_free (attrs);
return ok;
}

#endif /* WITH_ASN1 */

static bool
export_pubkey (P11KitIter *iter, p11_buffer *buf)
{
CK_ATTRIBUTE template[] = {
{ CKA_PUBLIC_KEY_INFO, },
{ CKA_KEY_TYPE, },
};
CK_ATTRIBUTE *attrs = NULL, *attr;
bool ok = false;

attrs = p11_attrs_buildn (NULL, template, 2);
return_val_if_fail (attrs, false);

if (p11_kit_iter_load_attributes (iter, attrs, p11_attrs_count (attrs)) != CKR_OK) {
p11_message (_("failed to retrieve attributes"));
goto cleanup;
}

attr = p11_attrs_find_valid (attrs, CKA_PUBLIC_KEY_INFO);
if (attr) {
if (!p11_pem_write (attr->pValue, attr->ulValueLen, "PUBLIC KEY", buf)) {
p11_message (_("failed to convert DER to PEM"));
goto cleanup;
}
} else {
#ifdef WITH_ASN1
CK_KEY_TYPE type;

if (!p11_attrs_find_ulong (attrs, CKA_KEY_TYPE, &type)) {
p11_message (_("unable to determine key type"));
goto cleanup;
}

switch (type) {
case CKK_RSA:
if (!export_pubkey_rsa (iter, buf)) {
p11_message (_("unable to extract %s public key"), "RSA");
goto cleanup;
}
break;
default:
p11_message (_("unsupported key type"));
goto cleanup;
}
#else /* WITH_ASN1 */
p11_message (_("ASN.1 support is not compiled in"));
goto cleanup;
#endif /* !WITH_ASN1 */
}

ok = true;

cleanup:
p11_buffer_uninit (&buf);
free (attr.pValue);
p11_attrs_free (attrs);
return ok;
}

static void
export_certificate (P11KitIter *iter)
static bool
export_certificate (P11KitIter *iter, p11_buffer *buf)
{
const char *type_str;
CK_CERTIFICATE_TYPE cert_type;
CK_ATTRIBUTE attr = { CKA_CERTIFICATE_TYPE, &cert_type, sizeof (cert_type) };
CK_ATTRIBUTE template[] = {
{ CKA_CERTIFICATE_TYPE, },
{ CKA_VALUE, },
};
CK_ATTRIBUTE *attrs = NULL, *attr;
bool ok = false;

if (p11_kit_iter_get_attributes (iter, &attr, 1) != CKR_OK) {
p11_message (_("failed to retrieve attribute of an object"));
return;
attrs = p11_attrs_buildn (NULL, template, 2);
return_val_if_fail (attrs, false);

if (p11_kit_iter_load_attributes (iter, attrs, p11_attrs_count (attrs)) != CKR_OK) {
p11_message (_("failed to retrieve attributes"));
goto cleanup;
}

switch (cert_type) {
case CKC_X_509:
export_attribute (iter, CKA_VALUE, "CERTIFICATE");
break;
default:
type_str = p11_constant_nick (p11_constant_certs, cert_type);
if (type_str == NULL)
type_str = "(unknown)";
p11_message (_("unsupported certificate type: %s"), type_str);
break;
if (!p11_attrs_find_ulong (attrs, CKA_CERTIFICATE_TYPE, &cert_type) ||
cert_type != CKC_X_509) {
p11_message (_("unrecognized certificate type"));
goto cleanup;
}

attr = p11_attrs_find_valid (attrs, CKA_VALUE);
if (!attr) {
p11_message (_("no valid certificate value"));
goto cleanup;
}

if (!p11_pem_write (attr->pValue, attr->ulValueLen, "PUBLIC KEY", buf)) {
p11_message (_("failed to convert DER to PEM"));
goto cleanup;
}

ok = true;

cleanup:
p11_attrs_free (attrs);
return ok;
}

static int
export_object (const char *token_str)
{
int ret = 1;
CK_RV rv;
CK_FUNCTION_LIST **modules = NULL;
P11KitUri *uri = NULL;
P11KitIter *iter = NULL;
CK_OBJECT_CLASS klass;
CK_ATTRIBUTE attr = { CKA_CLASS, &klass, sizeof (klass) };
p11_buffer buf;

if (!p11_buffer_init (&buf, 0))
return_val_if_reached (1);

uri = p11_kit_uri_new ();
if (uri == NULL) {
Expand All @@ -163,28 +326,37 @@ export_object (const char *token_str)
}

p11_kit_iter_begin (iter, modules);
while (p11_kit_iter_next (iter) == CKR_OK) {
rv = p11_kit_iter_get_attributes (iter, &attr, 1);
if (rv != CKR_OK) {
p11_message (_("failed to retrieve attribute of an object"));
if (p11_kit_iter_next (iter) != CKR_OK) {
p11_message (_("no matching object"));
goto cleanup;
}

if (p11_kit_iter_get_attributes (iter, &attr, 1) != CKR_OK) {
p11_message (_("failed to retrieve attribute of an object"));
goto cleanup;
}

switch (klass) {
case CKO_CERTIFICATE:
if (!export_certificate (iter, &buf))
goto cleanup;
}
break;
case CKO_PUBLIC_KEY:
if (!export_pubkey (iter, &buf))
goto cleanup;
default:
break;
}

switch (klass) {
case CKO_CERTIFICATE:
export_certificate (iter);
break;
case CKO_PUBLIC_KEY:
export_attribute (iter, CKA_PUBLIC_KEY_INFO, "PUBLIC KEY");
break;
default:
break;
}
if (fwrite (buf.data, 1, buf.len, stdout) != buf.len) {
p11_message (_("failed to write PEM data to stdout"));
goto cleanup;
}

ret = 0;

cleanup:
p11_buffer_uninit (&buf);
p11_kit_iter_free (iter);
p11_kit_uri_free (uri);
if (modules != NULL)
Expand Down
4 changes: 2 additions & 2 deletions p11-kit/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ p11_kit_sources = [
executable('p11-kit',
p11_kit_sources,
c_args: common_c_args + libp11_kit_internal_c_args,
dependencies: [libp11_tool_dep] + libffi_deps + dlopen_deps,
dependencies: [libp11_tool_dep, libp11_asn1_dep] + libffi_deps + dlopen_deps,
link_with: [libp11_kit, libp11_kit_internal],
install: true)

Expand All @@ -235,7 +235,7 @@ if get_option('test')
c_args: common_c_args + libp11_kit_internal_c_args + [
'-DP11_KIT_TESTABLE'
],
dependencies: [libp11_tool_dep] + libffi_deps + dlopen_deps,
dependencies: [libp11_tool_dep, libp11_asn1_dep] + libffi_deps + dlopen_deps,
link_whole: libp11_kit_testable)
endif

Expand Down

0 comments on commit 101373a

Please sign in to comment.