From 6a378210741db521c39d61628d7ca157e1647e91 Mon Sep 17 00:00:00 2001 From: Zoltan Fridrich Date: Fri, 5 Jan 2024 15:07:52 +0100 Subject: [PATCH] Add option to specify CKA_ID in generate-keypair and import-object Signed-off-by: Zoltan Fridrich --- common/Makefile.am | 4 ++ common/hex.c | 61 ++++++++++++++++- common/hex.h | 9 ++- common/meson.build | 1 + common/test-hex.c | 124 ++++++++++++++++++++++++++++++++++ doc/manual/p11-kit.xml | 12 +++- p11-kit/generate-keypair.c | 46 ++++++++++++- p11-kit/import-object.c | 73 ++++++++++++++++++-- p11-kit/test-import-public.sh | 12 ++-- 9 files changed, 321 insertions(+), 21 deletions(-) create mode 100644 common/test-hex.c diff --git a/common/Makefile.am b/common/Makefile.am index da180c621..91b6dc5e4 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -76,6 +76,7 @@ c_tests += \ test-hash \ test-dict \ test-array \ + test-hex \ test-constants \ test-attrs \ test-buffer \ @@ -93,6 +94,9 @@ test_argv_LDADD = $(common_LIBS) test_array_SOURCES = common/test-array.c test_array_LDADD = $(common_LIBS) +test_hex_SOURCES = common/test-hex.c +test_hex_LDADD = $(common_LIBS) + test_attrs_SOURCES = common/test-attrs.c test_attrs_LDADD = $(common_LIBS) diff --git a/common/hex.c b/common/hex.c index 4cecf4b4d..319cad45c 100644 --- a/common/hex.c +++ b/common/hex.c @@ -34,9 +34,12 @@ */ #include "config.h" + +#include "debug.h" #include "hex.h" -#include + #include +#include static const char HEXC_LOWER[] = "0123456789abcdef"; @@ -48,6 +51,8 @@ hex_encode (const unsigned char *data, size_t i; size_t o; + return_val_if_fail (data != NULL, NULL); + if ((SIZE_MAX - 1) / 3 < n_data) return NULL; result = malloc (n_data * 3 + 1); @@ -64,3 +69,57 @@ hex_encode (const unsigned char *data, result[o] = 0; return result; } + +unsigned char * +hex_decode (const char *hex, + size_t *bin_len) +{ + int i, j; + size_t bin_len_, hex_len; + unsigned char *bin, c; + bool with_separator; + + return_val_if_fail (hex != NULL, NULL); + return_val_if_fail (bin_len != NULL, NULL); + + hex_len = strlen (hex); + if (hex_len == 0) + return NULL; + + with_separator = hex_len > 2 && hex[2] == ':'; + if (with_separator) + for (i = 5; i < hex_len; i += 3) + if (hex[i] != ':') + return NULL; + + if (SIZE_MAX - 1 < hex_len || + (with_separator && (hex_len + 1) % 3 != 0) || + (!with_separator && hex_len % 2 != 0)) + return NULL; + + bin_len_ = with_separator ? (hex_len + 1) / 3 : hex_len / 2; + bin = calloc (bin_len_, 1); + if (bin == NULL) + return NULL; + + for (i = 0; i < bin_len_; ++i) { + for (j = 0; j < 2; ++j) { + c = with_separator ? hex[i * 3 + j] : hex[i * 2 + j]; + if ('0' <= c && c <= '9') + bin[i] |= c - '0'; + else if ('a' <= c && c <= 'f') + bin[i] |= c - 'a' + 10; + else if ('A' <= c && c <= 'F') + bin[i] |= c - 'A' + 10; + else { + free (bin); + return NULL; + } + if (j == 0) + bin[i] <<= 4; + } + } + + *bin_len = bin_len_; + return bin; +} diff --git a/common/hex.h b/common/hex.h index 2d7232860..43303b116 100644 --- a/common/hex.h +++ b/common/hex.h @@ -38,7 +38,12 @@ #include -char *hex_encode (const unsigned char *data, - size_t n_data); +char * +hex_encode (const unsigned char *data, + size_t n_data); + +unsigned char * +hex_decode (const char *hex, + size_t *bin_len); #endif /* P11_HEX_H */ diff --git a/common/meson.build b/common/meson.build index 44892d661..9757b2bac 100644 --- a/common/meson.build +++ b/common/meson.build @@ -150,6 +150,7 @@ if get_option('test') 'test-hash', 'test-dict', 'test-array', + 'test-hex', 'test-constants', 'test-attrs', 'test-buffer', diff --git a/common/test-hex.c b/common/test-hex.c new file mode 100644 index 000000000..d8048dab7 --- /dev/null +++ b/common/test-hex.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024, Red Hat Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Zoltan Fridrich + */ + +#include "config.h" + +#include +#include +#include + +#include "hex.h" +#include "test.h" + +#define assert_mem_eq(a1, a2, n) \ + do { const char *__s1 = (a1); \ + const char *__s2 = (a2); \ + if (__s1 && __s2 && memcmp (__s1, __s2, n) == 0) ; else \ + p11_test_fail (__FILE__, __LINE__, __FUNCTION__, "assertion failed"); \ + } while (0) + +static void +assert_encode_eq (const char *out, + const char *in, + size_t in_len) +{ + char *hex = hex_encode ((const unsigned char *)in, in_len); + assert_str_eq (out, hex); + free (hex); +} + +static void +assert_decode_eq (const char *out, + size_t out_len, + const char *in) +{ + size_t bin_len = 0; + char *bin = (char *)hex_decode (in, &bin_len); + assert_num_eq (out_len, bin_len); + assert_mem_eq (out, bin, bin_len); + free (bin); +} + +static void +assert_decode_fail (const char *in) +{ + size_t i; + assert_ptr_eq (NULL, hex_decode (in, &i)); +} + +static void +test_encode (void) +{ + assert_encode_eq ("", "", 0); + assert_encode_eq ("3a", "\x3a", 1); + assert_encode_eq ("3a:bc:f6:9a", "\x3a\xbc\xf6\x9a", 4); +} + +static void +test_decode (void) +{ + assert_decode_eq ("\x3a", 1, "3a"); + assert_decode_eq ("\x3a\xbc\xf6\x9a", 4, "3abcf69a"); + assert_decode_eq ("\x3a\xbc\xf6\x9a", 4, "3AbCf69a"); + assert_decode_eq ("\x3a\xbc\xf6\x9a", 4, "3ABCF69A"); + assert_decode_eq ("\x3a\xbc\xf6\x9a", 4, "3a:bc:f6:9a"); + assert_decode_eq ("\x3a\xbc\xf6\x9a", 4, "3a:Bc:F6:9A"); + assert_decode_eq ("\x3a\xbc\xf6\x9a", 4, "3a:bc:f6:9a"); + assert_decode_fail (""); + assert_decode_fail ("3"); + assert_decode_fail (":a"); + assert_decode_fail ("a:"); + assert_decode_fail ("3ab"); + assert_decode_fail ("3a:"); + assert_decode_fail (":3a"); + assert_decode_fail ("3a:b"); + assert_decode_fail ("3:ab"); + assert_decode_fail ("3a:bc:f6::9a"); + assert_decode_fail ("3a:bc:f69a"); + assert_decode_fail ("3a:bc:f6::9"); + assert_decode_fail ("3a:bc:f69aa"); + assert_decode_fail ("3a$bc:f6:9a"); + assert_decode_fail ("3a:bc:f6$9a"); +} + +int +main (int argc, + char *argv[]) +{ + p11_test (test_encode, "/hex/encode"); + p11_test (test_decode, "/hex/decode"); + return p11_test_run (argc, argv); +} diff --git a/doc/manual/p11-kit.xml b/doc/manual/p11-kit.xml index 4d78976b5..137bee1fd 100644 --- a/doc/manual/p11-kit.xml +++ b/doc/manual/p11-kit.xml @@ -176,7 +176,7 @@ $ pkg-config p11-kit-1 --variable p11_module_path Import object into PKCS#11 token. -$ p11-kit import-object --file=file.pem [--label=label] pkcs11:token +$ p11-kit import-object --file=file.pem [--label=label] [--id=object_id] pkcs11:token Takes either an X.509 certificate or a public key in the form of a PEM file @@ -199,6 +199,10 @@ $ p11-kit import-object --file=file.pem [--label=label] pkcs11:token Assigns label to the imported object. + + + Assigns ID to the imported object. The ID should be specified in hexadecimal format without '0x' prefix. + Authenticate to the token before enumerating objects. The PIN value is read from either the pin-value attribute in the URI or from the terminal. @@ -276,7 +280,7 @@ $ pkg-config p11-kit-1 --variable p11_module_path Generate key-pair on a PKCS#11 token. -$ p11-kit generate-keypair --type=algorithm {--bits=n|--curve=name} [--label=label] pkcs11:token +$ p11-kit generate-keypair --type=algorithm {--bits=n|--curve=name} [--label=label] [--id=object_id] pkcs11:token Generate private-public key-pair of given type on the first @@ -311,6 +315,10 @@ $ p11-kit generate-keypair --type=algorithm {--bits=n|--curve=name} &l Assigns label to the generated key-pair objects. + + + Assigns ID to the generated key-pair objects. The ID should be specified in hexadecimal format without '0x' prefix. + Authenticate to the token before enumerating objects. The PIN value is read from either the pin-value attribute in the URI or from the terminal. diff --git a/p11-kit/generate-keypair.c b/p11-kit/generate-keypair.c index bcb94fa01..847d775db 100644 --- a/p11-kit/generate-keypair.c +++ b/p11-kit/generate-keypair.c @@ -39,6 +39,7 @@ #include "attrs.h" #include "compat.h" #include "debug.h" +#include "hex.h" #include "iter.h" #include "message.h" #include "options.h" @@ -160,6 +161,7 @@ check_args (CK_MECHANISM_TYPE type, static bool get_templates (const char *label, + const char *id, CK_MECHANISM_TYPE type, CK_ULONG bits, const uint8_t *ec_params, @@ -207,6 +209,36 @@ get_templates (const char *label, priv = tmp; } + if (id != NULL) { + size_t bin_len = 0; + unsigned char *bin = NULL; + CK_ATTRIBUTE attr_id = { CKA_ID, NULL, 0 }; + + bin = hex_decode (id, &bin_len); + if (bin == NULL) { + p11_message (_("failed to decode hex value: %s"), id); + goto error; + } + + attr_id.pValue = (void *)bin; + attr_id.ulValueLen = bin_len; + + tmp = p11_attrs_build (pub, &attr_id, NULL); + if (tmp == NULL) { + free (bin); + p11_message (_("failed to allocate memory")); + goto error; + } + pub = tmp; + tmp = p11_attrs_build (priv, &attr_id, NULL); + free (bin); + if (tmp == NULL) { + p11_message (_("failed to allocate memory")); + goto error; + } + priv = tmp; + } + switch (type) { #ifdef P11_KIT_TESTABLE case CKM_MOCK_GENERATE: @@ -254,6 +286,7 @@ get_templates (const char *label, static int generate_keypair (p11_tool *tool, const char *label, + const char *id, CK_MECHANISM mechanism, CK_ULONG bits, const uint8_t *ec_params, @@ -267,7 +300,7 @@ generate_keypair (p11_tool *tool, CK_ATTRIBUTE *pubkey = NULL, *privkey = NULL; CK_OBJECT_HANDLE pubkey_obj, privkey_obj; - if (!get_templates (label, mechanism.mechanism, bits, + if (!get_templates (label, id, mechanism.mechanism, bits, ec_params, ec_params_len, &pubkey, &privkey)) { p11_message (_("failed to create key templates")); return 1; @@ -318,7 +351,8 @@ p11_kit_generate_keypair (int argc, char *argv[]) { int opt, ret = 2; - char *label = NULL; + const char *label = NULL; + const char *id = NULL; CK_ULONG bits = 0; const uint8_t *ec_params = NULL; size_t ec_params_len = 0; @@ -332,6 +366,7 @@ p11_kit_generate_keypair (int argc, opt_quiet = 'q', opt_help = 'h', opt_label = 'L', + opt_id = CHAR_MAX + 3, opt_type = 't', opt_bits = 'b', opt_curve = 'c', @@ -344,6 +379,7 @@ p11_kit_generate_keypair (int argc, { "quiet", no_argument, NULL, opt_quiet }, { "help", no_argument, NULL, opt_help }, { "label", required_argument, NULL, opt_label }, + { "id", required_argument, NULL, opt_id }, { "type", required_argument, NULL, opt_type }, { "bits", required_argument, NULL, opt_bits }, { "curve", required_argument, NULL, opt_curve }, @@ -356,6 +392,7 @@ p11_kit_generate_keypair (int argc, { 0, "usage: p11-kit generate-keypair [--label=