diff --git a/src/libopensc/card-myeid.c b/src/libopensc/card-myeid.c index 87be4dac..35146266 100644 --- a/src/libopensc/card-myeid.c +++ b/src/libopensc/card-myeid.c @@ -678,6 +678,10 @@ static int myeid_set_security_env_ec(sc_card_t *card, const sc_security_env_t *e apdu.p1 = 0x41; apdu.p2 = 0xB6; break; + case SC_SEC_OPERATION_DERIVE: + apdu.p1 = 0x41; + apdu.p2 = 0xA4; + break; default: return SC_ERROR_INVALID_ARGUMENTS; } @@ -920,20 +924,43 @@ int myeid_ecdh_derive(struct sc_card *card, const u8* pubkey, size_t pubkey_len, u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; + size_t ext_len_bytes; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x86, 0x00, 0x00); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); - /* Fill in "Data objects in dynamic authentication template (tag 0x7C) structure */ - sbuf[0] = 0x7C; - sbuf[1] = pubkey_len + 4; - sbuf[2] = 0x85; - sbuf[3] = pubkey_len; - memcpy(&sbuf[4], pubkey, pubkey_len); + /* Fill in "Data objects in dynamic authentication template" (tag 0x7C) structure + * + * TODO: encode the structure using OpenSC's ASN1-functions. + * + * Size of the structure depends on key length. With 521 bit keys two bytes are needed for defining length of a point. + */ - apdu.lc = pubkey_len + 4; + sbuf[0] = 0x7C; + ext_len_bytes = 0; + + if (pubkey_len > 127) + { + sbuf[1] = 0x81; + sbuf[2] = (u8) (pubkey_len + 3); + sbuf[3] = 0x85; + sbuf[4] = 0x81; + sbuf[5] = (u8) (pubkey_len); + ext_len_bytes = 2; + } + else + { + sbuf[1] = pubkey_len + 2; + sbuf[2] = 0x85; + sbuf[3] = pubkey_len; + } + + memcpy(&sbuf[4 + ext_len_bytes], pubkey, pubkey_len); + + apdu.lc = pubkey_len + 4 + ext_len_bytes; + apdu.le = 0; apdu.datalen = apdu.lc; apdu.data = sbuf; @@ -952,7 +979,7 @@ int myeid_ecdh_derive(struct sc_card *card, const u8* pubkey, size_t pubkey_len, memcpy(out, rbuf, apdu.resplen); - LOG_FUNC_RETURN(card->ctx, r); + LOG_FUNC_RETURN(card->ctx, apdu.resplen); } diff --git a/src/pkcs15init/pkcs15-myeid.c b/src/pkcs15init/pkcs15-myeid.c index d3e98850..cb610010 100644 --- a/src/pkcs15init/pkcs15-myeid.c +++ b/src/pkcs15init/pkcs15-myeid.c @@ -30,10 +30,12 @@ #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" +#include "libopensc/asn1.h" #undef KEEP_AC_NONE_FOR_INIT_APPLET #define MYEID_MAX_PINS 14 +#define MYEID_MAX_RSA_KEY_LEN 2048 unsigned char MYEID_DEFAULT_PUBKEY[] = {0x01, 0x00, 0x01}; #define MYEID_DEFAULT_PUBKEY_LEN sizeof(MYEID_DEFAULT_PUBKEY) @@ -580,8 +582,11 @@ myeid_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_cardctl_myeid_gen_store_key_info args; struct sc_file *file = NULL; int r; + unsigned int cla,tag; + size_t taglen; size_t keybits = key_info->modulus_length; - unsigned char raw_pubkey[256]; + u8 raw_pubkey[MYEID_MAX_RSA_KEY_LEN / 8]; + u8* dataptr; LOG_FUNC_CALLED(ctx); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA && object->type != SC_PKCS15_TYPE_PRKEY_EC) @@ -633,7 +638,7 @@ myeid_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, args.key_type = SC_CARDCTL_MYEID_KEY_EC; } - /* Generate RSA key */ + /* Generate the key */ r = sc_card_ctl(card, SC_CARDCTL_MYEID_GENERATE_STORE_KEY, &args); LOG_TEST_RET(ctx, r, "Card control 'MYEID_GENERATE_STORE_KEY' failed"); @@ -683,13 +688,23 @@ myeid_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, r = sc_card_ctl(card, SC_CARDCTL_MYEID_GETDATA, &data_obj); LOG_TEST_RET(ctx, r, "Cannot get EC public key: 'MYEID_GETDATA' failed"); + dataptr = data_obj.Data; + r = sc_asn1_read_tag((const u8 **)&dataptr, data_obj.DataLen, &cla, &tag, &taglen); + LOG_TEST_RET(ctx, r, "Invalid EC public key data. Cannot parse DER structure."); + + if (taglen == 0) + LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + if (pubkey->u.ec.ecpointQ.value) free(pubkey->u.ec.ecpointQ.value); - pubkey->u.ec.ecpointQ.value = malloc(data_obj.DataLen - 2); - if (pubkey->u.ec.ecpointQ.value == NULL) + + pubkey->u.ec.ecpointQ.value = malloc(taglen); + + if (pubkey->u.ec.ecpointQ.value == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); - memcpy(pubkey->u.ec.ecpointQ.value, data_obj.Data + 2, data_obj.DataLen - 2); - pubkey->u.ec.ecpointQ.len = data_obj.DataLen - 2; + + memcpy(pubkey->u.ec.ecpointQ.value, dataptr, taglen); + pubkey->u.ec.ecpointQ.len = taglen; if (pubkey->u.ec.params.named_curve) free(pubkey->u.ec.params.named_curve); diff --git a/src/tools/pkcs11-tool.c b/src/tools/pkcs11-tool.c index 7c0b7027..6bd885b8 100644 --- a/src/tools/pkcs11-tool.c +++ b/src/tools/pkcs11-tool.c @@ -2817,19 +2817,16 @@ static void show_object(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) } -static void -derive_key(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) +static CK_OBJECT_HANDLE +derive_ec_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, CK_MECHANISM_TYPE mech_mech) { - unsigned char *value = NULL; - CK_ULONG value_len = 0; +#if defined(ENABLE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) CK_MECHANISM mech; CK_OBJECT_CLASS newkey_class= CKO_SECRET_KEY; CK_KEY_TYPE newkey_type = CKK_GENERIC_SECRET; CK_BBOOL true = TRUE; CK_BBOOL false = FALSE; CK_OBJECT_HANDLE newkey = 0; - CK_RV rv; - int fd, r; CK_ATTRIBUTE newkey_template[] = { {CKA_TOKEN, &false, sizeof(false)}, /* session only object */ {CKA_CLASS, &newkey_class, sizeof(newkey_class)}, @@ -2837,83 +2834,96 @@ derive_key(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) {CKA_ENCRYPT, &true, sizeof(true)}, {CKA_DECRYPT, &true, sizeof(true)} }; -#if defined(ENABLE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) CK_ECDH1_DERIVE_PARAMS ecdh_parms; + CK_RV rv; + BIO *bio_in = NULL; + EC_KEY *eckey = NULL; + const EC_GROUP *ecgroup = NULL; + const EC_POINT *ecpoint = NULL; unsigned char buf[512]; -#endif /* ENABLE_OPENSSL etc */ - - if (!opt_mechanism_used) - if (!find_mechanism(slot, CKF_DERIVE|CKF_HW, NULL, 0, &opt_mechanism)) - util_fatal("Derive mechanism not supported"); + size_t buf_size = 0; + int len; - printf("Using derive algorithm 0x%8.8lx %s\n", opt_mechanism, p11_mechanism_to_name(opt_mechanism)); + printf("Using derive algorithm 0x%8.8lx %s\n", opt_mechanism, p11_mechanism_to_name(mech_mech)); memset(&mech, 0, sizeof(mech)); - mech.mechanism = opt_mechanism; + mech.mechanism = mech_mech; - switch(opt_mechanism) { -#if defined(ENABLE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) - case CKM_ECDH1_COFACTOR_DERIVE: - case CKM_ECDH1_DERIVE: - /* Use OpenSSL to read the other public key, and get the raw verion */ - { - int len; - BIO *bio_in = NULL; - const EC_KEY *eckey = NULL; - const EC_GROUP *ecgroup = NULL; - const EC_POINT * ecpoint = NULL; + /* Use OpenSSL to read the other public key, and get the raw version */ + bio_in = BIO_new(BIO_s_file()); + if (BIO_read_filename(bio_in, opt_input) <= 0) + util_fatal("Cannot open %s: %m", opt_input); - bio_in = BIO_new(BIO_s_file()); - if (BIO_read_filename(bio_in, opt_input) <= 0) - util_fatal("Cannot open %s: %m", opt_input); + eckey = d2i_EC_PUBKEY_bio(bio_in, NULL); + if (!eckey) + util_fatal("Cannot read EC key from %s", opt_input); - eckey = d2i_EC_PUBKEY_bio(bio_in, NULL); - if (!eckey) - util_fatal("Cannot read EC key from %s", opt_input); + ecpoint = EC_KEY_get0_public_key(eckey); + ecgroup = EC_KEY_get0_group(eckey); + if (!ecpoint || !ecgroup) + util_fatal("Failed to parse other EC key from %s", opt_input); - ecpoint = EC_KEY_get0_public_key(eckey); - ecgroup = EC_KEY_get0_group(eckey); - if (!ecpoint || !ecgroup) - util_fatal("Failed to parse other EC key from %s", opt_input); + buf_size = sizeof(buf); + len = EC_POINT_point2oct(ecgroup, ecpoint, POINT_CONVERSION_UNCOMPRESSED, buf, buf_size, NULL); - len = EC_POINT_point2oct(ecgroup, ecpoint, POINT_CONVERSION_UNCOMPRESSED, buf, sizeof(buf),NULL); + BIO_free(bio_in); + EC_KEY_free(eckey); - memset(&ecdh_parms, 0, sizeof(ecdh_parms)); - ecdh_parms.kdf = CKD_NULL; - ecdh_parms.ulSharedDataLen = 0; - ecdh_parms.pSharedData = NULL; - ecdh_parms.ulPublicDataLen = len; /* TODO drop header */ - ecdh_parms.pPublicData = buf; /* Cheat to test */ - mech.pParameter = &ecdh_parms; - mech.ulParameterLen = sizeof(ecdh_parms); - } - break; -#endif /* ENABLE_OPENSSL && !OPENSSL_NO_EC && !OPENSSL_NO_ECDSA */ - /* TODO add RSA but do not have card to test */ - default: - util_fatal("mechanism not supported for derive"); - break; - } + memset(&ecdh_parms, 0, sizeof(ecdh_parms)); + ecdh_parms.kdf = CKD_NULL; + ecdh_parms.ulSharedDataLen = 0; + ecdh_parms.pSharedData = NULL; + ecdh_parms.ulPublicDataLen = len; + ecdh_parms.pPublicData = buf; + mech.pParameter = &ecdh_parms; + mech.ulParameterLen = sizeof(ecdh_parms); rv = p11->C_DeriveKey(session, &mech, key, newkey_template, 5, &newkey); if (rv != CKR_OK) p11_fatal("C_DeriveKey", rv); - /*TODO get the key value and write to stdout or file */ - value = getVALUE(session, newkey, &value_len); + return newkey; +#else + util_fatal("Derive EC key not supported"); +#endif /* ENABLE_OPENSSL && !OPENSSL_NO_EC && !OPENSSL_NO_ECDSA */ +} + + +static void +derive_key(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) +{ + CK_BYTE *value = NULL; + CK_ULONG value_len = 0; + CK_OBJECT_HANDLE derived_key = 0; + int rv, fd; + + if (!opt_mechanism_used) + if (!find_mechanism(slot, CKF_DERIVE|CKF_HW, NULL, 0, &opt_mechanism)) + util_fatal("Derive mechanism not supported"); + + switch(opt_mechanism) { + case CKM_ECDH1_COFACTOR_DERIVE: + case CKM_ECDH1_DERIVE: + derived_key= derive_ec_key(session, key, opt_mechanism); + break; + default: + util_fatal("mechanism not supported for derive"); + break; + } + + value = getVALUE(session, derived_key, &value_len); if (value && value_len > 0) { - if (opt_output == NULL) { - fd = 1; - } - else { + fd = STDOUT_FILENO; + if (opt_output) { fd = open(opt_output, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, S_IRUSR|S_IWUSR); if (fd < 0) util_fatal("failed to open %s: %m", opt_output); } - r = write(fd, value, value_len); - if (r < 0) + rv = write(fd, value, value_len); + if (rv < 0) util_fatal("Failed to write to %s: %m", opt_output); - if (fd != 1) + + if (opt_output) close(fd); } }