diff --git a/common/oid.h b/common/oid.h index 96a223a4..eff31365 100644 --- a/common/oid.h +++ b/common/oid.h @@ -241,4 +241,7 @@ 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"; +static const char P11_OID_PKIX1_EC_STR[] = "1.2.840.10045.2.1"; + #endif diff --git a/common/pkix.asn b/common/pkix.asn index cd2d2c6b..6791f82f 100644 --- a/common/pkix.asn +++ b/common/pkix.asn @@ -537,4 +537,11 @@ ProxyPolicy ::= SEQUENCE { policyLanguage OBJECT IDENTIFIER, policy OCTET STRING OPTIONAL } +RSAPublicKey ::= SEQUENCE { + modulus INTEGER, + publicExponent INTEGER } + +ECParameters ::= CHOICE { + namedCurve OBJECT IDENTIFIER } + END diff --git a/p11-kit/Makefile.am b/p11-kit/Makefile.am index b5195bee..5dde29f0 100644 --- a/p11-kit/Makefile.am +++ b/p11-kit/Makefile.am @@ -283,6 +283,11 @@ p11_kit_p11_kit_LDADD = \ $(LTLIBINTL) \ $(NULL) +if WITH_ASN1 +p11_kit_p11_kit_CFLAGS += $(LIBTASN1_CFLAGS) +p11_kit_p11_kit_LDADD += libp11-asn1.la $(LIBTASN1_LIBS) +endif + if WITH_BASH_COMPLETION bashcomp_DATA += bash-completion/p11-kit endif @@ -312,6 +317,11 @@ p11_kit_p11_kit_testable_CFLAGS = \ $(COMMON_CFLAGS) \ $(NULL) +if WITH_ASN1 +p11_kit_p11_kit_testable_CFLAGS += $(LIBTASN1_CFLAGS) +p11_kit_p11_kit_testable_LDADD += libp11-asn1.la $(LIBTASN1_LIBS) +endif + private_PROGRAMS += p11-kit/p11-kit-remote p11_kit_p11_kit_remote_SOURCES = \ diff --git a/p11-kit/export-object.c b/p11-kit/export-object.c index b68600f5..53311c88 100644 --- a/p11-kit/export-object.c +++ b/p11-kit/export-object.c @@ -36,6 +36,7 @@ #include "config.h" +#include "attrs.h" #include "buffer.h" #include "constants.h" #include "debug.h" @@ -44,7 +45,13 @@ #include "pem.h" #include "tool.h" +#ifdef WITH_ASN1 +#include "asn1.h" +#include "oid.h" +#endif + #include +#include #include #include @@ -59,85 +66,333 @@ 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"); + return_val_if_fail (asn, false); + + 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); - if (!p11_pem_write (attr.pValue, attr.ulValueLen, label, &buf)) { + asn = p11_asn1_create (defs, "PKIX1.SubjectPublicKeyInfo"); + return_val_if_fail (asn, false); + + 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 (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; +} + +static bool +export_pubkey_ec (P11KitIter *iter, p11_buffer *buf) +{ + CK_ATTRIBUTE template[] = { + { CKA_EC_PARAMS, }, + { CKA_EC_POINT, }, + }; + CK_ATTRIBUTE *attrs = NULL; + const CK_ATTRIBUTE *ec_params, *ec_point; + asn1_node asn = NULL; + p11_dict *defs = NULL; + int result; + unsigned char *der = NULL; + size_t 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; + } + + ec_params = p11_attrs_find_valid (attrs, CKA_EC_PARAMS); + ec_point = p11_attrs_find_valid (attrs, CKA_EC_POINT); + + if (!ec_params || !ec_point) { + p11_message (_("failed to retrieve attributes")); + goto cleanup; + } + + defs = p11_asn1_defs_load (); + return_val_if_fail (defs, false); + + asn = p11_asn1_create (defs, "PKIX1.SubjectPublicKeyInfo"); + return_val_if_fail (asn, false); + + result = asn1_write_value (asn, "algorithm.algorithm", + P11_OID_PKIX1_EC_STR, 1); + if (result != ASN1_SUCCESS) { + p11_message (_("unable to write value")); + goto cleanup; + } + + result = asn1_write_value (asn, "algorithm.parameters", + ec_params->pValue, ec_params->ulValueLen); + if (result != ASN1_SUCCESS) { + p11_message (_("unable to write value")); goto cleanup; } + /* Strip 2 octes of DER header for OCTET STRING */ + return_val_if_fail (ec_point->ulValueLen > 2, false); + result = asn1_write_value (asn, "subjectPublicKey", + ((unsigned char *)ec_point->pValue) + 2, + (ec_point->ulValueLen - 2) * 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 (der, n_der, "PUBLIC KEY", buf)) { + p11_message (_("failed to convert DER to PEM")); + goto cleanup; + } + + ok = true; + cleanup: - p11_buffer_uninit (&buf); - free (attr.pValue); + free (der); + p11_asn1_free (asn); + p11_dict_free (defs); + p11_attrs_free (attrs); + return ok; } -static void -export_certificate (P11KitIter *iter) +#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; + case CKK_EC: + if (!export_pubkey_ec (iter, buf)) { + p11_message (_("unable to extract %s public key"), "EC"); + 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_attrs_free (attrs); + return ok; +} + +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, "CERTIFICATE", 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) { @@ -163,28 +418,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) diff --git a/p11-kit/meson.build b/p11-kit/meson.build index 79190cc9..31d0ea3d 100644 --- a/p11-kit/meson.build +++ b/p11-kit/meson.build @@ -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_deps + libffi_deps + dlopen_deps, link_with: [libp11_kit, libp11_kit_internal], install: true) @@ -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_deps + libffi_deps + dlopen_deps, link_whole: libp11_kit_testable) endif