2003-10-30 17:04:22 +00:00
|
|
|
/*
|
2005-02-04 20:29:35 +00:00
|
|
|
* card-openpgp.c: Support for OpenPGP card
|
2003-10-30 17:04:22 +00:00
|
|
|
*
|
|
|
|
* Copyright (C) 2003 Olaf Kirch <okir@suse.de>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
2011-01-09 10:17:16 +00:00
|
|
|
/*
|
|
|
|
* Specifications:
|
2018-06-10 09:58:51 +00:00
|
|
|
* (all available from: https://gnupg.org/ftp/specs/)
|
|
|
|
* https://gnupg.org/ftp/specs/openpgp-card-1.0.pdf (obsolete)
|
|
|
|
* https://gnupg.org/ftp/specs/openpgp-card-1.1.pdf
|
|
|
|
* https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-2.0.pdf
|
|
|
|
* https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-2.1.pdf
|
|
|
|
* https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-2.2.pdf
|
|
|
|
* https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.0.pdf
|
|
|
|
* https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.1.pdf
|
|
|
|
* https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.2.pdf
|
|
|
|
* https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.3.pdf
|
|
|
|
* https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.3.0.pdf
|
|
|
|
* https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.3.1.pdf
|
2019-12-25 13:59:08 +00:00
|
|
|
* https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.4.pdf
|
2011-01-09 10:17:16 +00:00
|
|
|
*/
|
|
|
|
|
2015-04-22 21:55:33 +00:00
|
|
|
#if HAVE_CONFIG_H
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "config.h"
|
2015-04-22 21:55:33 +00:00
|
|
|
#endif
|
2010-03-04 08:14:36 +00:00
|
|
|
|
2003-10-30 17:04:22 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
2012-06-14 02:05:16 +00:00
|
|
|
#include <time.h>
|
2003-10-30 17:04:22 +00:00
|
|
|
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "internal.h"
|
|
|
|
#include "asn1.h"
|
|
|
|
#include "cardctl.h"
|
2011-05-23 17:31:24 +00:00
|
|
|
#include "errors.h"
|
2012-06-14 02:05:16 +00:00
|
|
|
#ifdef ENABLE_OPENSSL
|
|
|
|
#include <openssl/sha.h>
|
|
|
|
#endif /* ENABLE_OPENSSL */
|
2010-03-04 08:14:36 +00:00
|
|
|
|
2020-01-26 09:56:03 +00:00
|
|
|
#include "card-openpgp.h"
|
|
|
|
|
|
|
|
|
2018-06-16 18:04:27 +00:00
|
|
|
static const char default_cardname[] = "OpenPGP card";
|
|
|
|
static const char default_cardname_v1[] = "OpenPGP card v1.x";
|
|
|
|
static const char default_cardname_v2[] = "OpenPGP card v2.x";
|
|
|
|
static const char default_cardname_v3[] = "OpenPGP card v3.x";
|
|
|
|
|
2020-01-26 09:56:03 +00:00
|
|
|
|
2018-08-22 15:01:51 +00:00
|
|
|
static const struct sc_atr_table pgp_atrs[] = {
|
2018-06-16 18:04:27 +00:00
|
|
|
{ "3b:fa:13:00:ff:81:31:80:45:00:31:c1:73:c0:01:00:00:90:00:b1", NULL, default_cardname_v1, SC_CARD_TYPE_OPENPGP_V1, 0, NULL },
|
|
|
|
{ "3b:da:18:ff:81:b1:fe:75:1f:03:00:31:c5:73:c0:01:40:00:90:00:0c", NULL, default_cardname_v2, SC_CARD_TYPE_OPENPGP_V2, 0, NULL },
|
2018-09-12 11:04:12 +00:00
|
|
|
{
|
|
|
|
"3b:da:11:ff:81:b1:fe:55:1f:03:00:31:84:73:80:01:80:00:90:00:e4",
|
2020-11-20 09:08:16 +00:00
|
|
|
"ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:ff:ff:ff",
|
2019-11-07 16:31:53 +00:00
|
|
|
"Gnuk v1.x.x (OpenPGP v2.0)", SC_CARD_TYPE_OPENPGP_GNUK, 0, NULL
|
2018-09-12 11:04:12 +00:00
|
|
|
},
|
2015-08-01 09:25:31 +00:00
|
|
|
{ "3b:fc:13:00:00:81:31:fe:15:59:75:62:69:6b:65:79:4e:45:4f:72:33:e1", NULL, "Yubikey NEO (OpenPGP v2.0)", SC_CARD_TYPE_OPENPGP_V2, 0, NULL },
|
2018-04-04 19:41:39 +00:00
|
|
|
{ "3b:f8:13:00:00:81:31:fe:15:59:75:62:69:6b:65:79:34:d4", NULL, "Yubikey 4 (OpenPGP v2.1)", SC_CARD_TYPE_OPENPGP_V2, 0, NULL },
|
2019-12-25 15:30:17 +00:00
|
|
|
{ "3b:fd:13:00:00:81:31:fe:15:80:73:c0:21:c0:57:59:75:62:69:4b:65:79:40", NULL, "Yubikey 5 (OpenPGP v3.4)", SC_CARD_TYPE_OPENPGP_V3, 0, NULL },
|
2018-06-16 18:04:27 +00:00
|
|
|
{ "3b:da:18:ff:81:b1:fe:75:1f:03:00:31:f5:73:c0:01:60:00:90:00:1c", NULL, default_cardname_v3, SC_CARD_TYPE_OPENPGP_V3, 0, NULL },
|
2005-09-07 08:33:55 +00:00
|
|
|
{ NULL, NULL, NULL, 0, 0, NULL }
|
2003-10-30 17:04:22 +00:00
|
|
|
};
|
|
|
|
|
2020-01-26 09:56:03 +00:00
|
|
|
|
2003-10-30 17:04:22 +00:00
|
|
|
static struct sc_card_operations *iso_ops;
|
|
|
|
static struct sc_card_operations pgp_ops;
|
|
|
|
static struct sc_card_driver pgp_drv = {
|
2005-02-23 10:39:33 +00:00
|
|
|
"OpenPGP card",
|
2003-10-30 17:04:22 +00:00
|
|
|
"openpgp",
|
2005-09-07 08:33:55 +00:00
|
|
|
&pgp_ops,
|
|
|
|
NULL, 0, NULL
|
2003-10-30 17:04:22 +00:00
|
|
|
};
|
|
|
|
|
2011-05-23 17:32:54 +00:00
|
|
|
|
2020-01-26 09:56:03 +00:00
|
|
|
static pgp_ec_curves_t ec_curves[] = {
|
2020-03-24 11:09:06 +00:00
|
|
|
{{{1, 2, 840, 10045, 3, 1, 7, -1}}, 256}, /* ansiX9p256r1 */
|
|
|
|
{{{1, 3, 132, 0, 34, -1}}, 384}, /* ansiX9p384r1 */
|
|
|
|
{{{1, 3, 132, 0, 35, -1}}, 521}, /* ansiX9p521r1 */
|
|
|
|
{{{1, 3, 36, 3, 3, 2, 8, 1, 1, 7, -1}}, 256}, /* brainpoolP256r1 */
|
|
|
|
{{{1, 3, 36, 3, 3, 2, 8, 1, 1, 11, -1}}, 384}, /* brainpoolP384r1 */
|
|
|
|
{{{1, 3, 36, 3, 3, 2, 8, 1, 1, 13, -1}}, 512}, /* brainpoolP512r1 */
|
|
|
|
{{{-1}}, 0} /* This entry must not be touched. */
|
2019-01-30 21:00:36 +00:00
|
|
|
};
|
|
|
|
|
2020-01-26 09:56:03 +00:00
|
|
|
static pgp_ec_curves_t ec_curves_gnuk[] = {
|
2020-03-24 11:09:06 +00:00
|
|
|
{{{1, 2, 840, 10045, 3, 1, 7, -1}}, 256}, /* ansiX9p256r1 */
|
|
|
|
{{{1, 3, 132, 0, 10, -1}}, 256}, /* secp256k1 */
|
|
|
|
/*{{{1, 3, 6, 1, 4, 1, 3029, 1, 5, 1, -1}}, 256}, //cv25519
|
|
|
|
{{{1, 3, 6, 1, 4, 1, 11591, 15, 1, -1}}, 256}, // ed25519 */
|
|
|
|
{{{-1}}, 0} /* This entry must not be touched. */
|
2019-11-07 16:31:53 +00:00
|
|
|
};
|
2011-05-23 17:32:54 +00:00
|
|
|
|
|
|
|
|
2003-10-31 16:01:00 +00:00
|
|
|
/*
|
|
|
|
* The OpenPGP card doesn't have a file system, instead everything
|
|
|
|
* is stored in data objects that are accessed through GET/PUT.
|
|
|
|
*
|
|
|
|
* However, much inside OpenSC's pkcs15 implementation is based on
|
|
|
|
* the assumption that we have a file system. So we fake one here.
|
|
|
|
*
|
|
|
|
* Selecting the MF causes us to select the OpenPGP AID.
|
|
|
|
*
|
|
|
|
* Everything else is mapped to "file" IDs.
|
|
|
|
*/
|
2011-05-23 17:32:49 +00:00
|
|
|
|
2011-05-23 17:33:04 +00:00
|
|
|
static int pgp_get_card_features(sc_card_t *card);
|
2011-05-23 17:31:31 +00:00
|
|
|
static int pgp_finish(sc_card_t *card);
|
2014-07-13 11:41:36 +00:00
|
|
|
static void pgp_iterate_blobs(pgp_blob_t *, int, void (*func)());
|
2011-05-23 17:30:56 +00:00
|
|
|
|
2014-07-13 11:41:36 +00:00
|
|
|
static int pgp_get_blob(sc_card_t *card, pgp_blob_t *blob,
|
|
|
|
unsigned int id, pgp_blob_t **ret);
|
2020-01-26 09:56:03 +00:00
|
|
|
static pgp_blob_t *pgp_new_blob(sc_card_t *, pgp_blob_t *, unsigned int, sc_file_t *);
|
2014-07-13 11:41:36 +00:00
|
|
|
static void pgp_free_blob(pgp_blob_t *);
|
2020-01-26 09:56:03 +00:00
|
|
|
static int pgp_get_pubkey(sc_card_t *, unsigned int, u8 *, size_t);
|
|
|
|
static int pgp_get_pubkey_pem(sc_card_t *, unsigned int, u8 *, size_t);
|
|
|
|
|
|
|
|
|
|
|
|
static pgp_do_info_t pgp1x_objects[] = { /* OpenPGP card spec 1.1 */
|
2011-05-23 17:32:54 +00:00
|
|
|
{ 0x004f, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x005b, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x005e, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
|
|
|
|
{ 0x0065, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
|
|
|
|
{ 0x006e, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
|
|
|
|
{ 0x0073, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x007a, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
|
|
|
|
{ 0x0081, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x0082, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x0093, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x00c0, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x00c1, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x00c2, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x00c3, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x00c4, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00c5, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00c6, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00c7, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00c8, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00c9, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00ca, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00cb, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00cc, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00cd, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00ce, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00cf, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00d0, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00e0, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00e1, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00e2, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
2012-05-25 16:56:12 +00:00
|
|
|
{ 0x0101, SIMPLE, READ_ALWAYS | WRITE_PIN2, sc_get_data, sc_put_data },
|
2011-05-23 17:32:54 +00:00
|
|
|
{ 0x0102, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
|
2012-05-25 16:56:12 +00:00
|
|
|
{ 0x0103, SIMPLE, READ_PIN2 | WRITE_PIN2, sc_get_data, sc_put_data },
|
2011-05-23 17:32:54 +00:00
|
|
|
{ 0x0104, SIMPLE, READ_PIN3 | WRITE_PIN3, sc_get_data, sc_put_data },
|
2011-06-01 11:33:23 +00:00
|
|
|
{ 0x3f00, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
2011-05-23 17:32:54 +00:00
|
|
|
{ 0x5f2d, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x5f35, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x5f50, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
|
|
|
|
{ 0x7f49, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
2014-11-09 08:58:40 +00:00
|
|
|
{ DO_AUTH, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
|
|
|
|
{ DO_AUTH_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
|
|
|
|
{ DO_SIGN, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
|
|
|
|
{ DO_SIGN_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
|
|
|
|
{ DO_ENCR, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
|
|
|
|
{ DO_ENCR_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
|
2011-05-23 17:32:54 +00:00
|
|
|
{ 0, 0, 0, NULL, NULL },
|
|
|
|
};
|
|
|
|
|
2020-01-26 09:56:03 +00:00
|
|
|
static pgp_do_info_t pgp34_objects[] = { /**** OpenPGP card spec 3.4 ****/
|
2019-12-25 13:59:08 +00:00
|
|
|
{ 0x00d9, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00da, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00db, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00dc, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00de, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00de, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
/* DO FA is CONSTRUCTED in spec; we treat it as SIMPLE for the time being */
|
|
|
|
{ 0x00fa, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
/* DO FB is CONSTRUCTED in spec; we treat it as SIMPLE for the time being */
|
|
|
|
{ 0x00fb, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
/* DO FC is CONSTRUCTED in spec; we treat it as SIMPLE for the time being */
|
|
|
|
{ 0x00fc, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
/**** OpenPGP card spec 3.3 ****/
|
2018-06-10 10:35:37 +00:00
|
|
|
{ 0x00f9, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
2019-12-25 13:59:08 +00:00
|
|
|
/**** OpenPGP card spec 3.0 - 3.2 ****/
|
2018-06-10 10:35:37 +00:00
|
|
|
{ 0x00d6, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00d7, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00d8, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
/* DO 7F66 is CONSTRUCTED in spec; we treat it as SIMPLE: no need to parse TLV */
|
|
|
|
{ 0x7f66, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, sc_put_data },
|
|
|
|
/* DO 7F74 is CONSTRUCTED in spec; we treat it as SIMPLE for the time being */
|
|
|
|
{ 0x7f74, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, sc_put_data },
|
2019-12-25 13:59:08 +00:00
|
|
|
/**** OpenPGP card spec 2.1 & 2.2 ****/
|
2018-06-09 15:18:42 +00:00
|
|
|
{ 0x00d5, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
2019-12-25 13:59:08 +00:00
|
|
|
/**** OpenPGP card spec 2.0 ****/
|
2011-05-23 17:32:54 +00:00
|
|
|
{ 0x004d, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x004f, SIMPLE, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
|
|
|
|
{ 0x005b, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x005e, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
|
|
|
|
{ 0x0065, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
|
|
|
|
{ 0x006e, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
|
|
|
|
{ 0x0073, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x007a, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
|
|
|
|
{ 0x0081, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x0082, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x0093, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x00c0, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x00c1, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00c2, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00c3, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00c4, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
|
|
|
|
{ 0x00c5, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00c6, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00c7, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00c8, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00c9, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00ca, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00cb, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00cc, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00cd, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00ce, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00cf, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00d0, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00d1, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00d2, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00d3, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x00f4, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
2012-05-25 16:56:12 +00:00
|
|
|
{ 0x0101, SIMPLE, READ_ALWAYS | WRITE_PIN2, sc_get_data, sc_put_data },
|
2011-05-23 17:32:54 +00:00
|
|
|
{ 0x0102, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
|
2012-05-25 16:56:12 +00:00
|
|
|
{ 0x0103, SIMPLE, READ_PIN2 | WRITE_PIN2, sc_get_data, sc_put_data },
|
2011-05-23 17:32:54 +00:00
|
|
|
{ 0x0104, SIMPLE, READ_PIN3 | WRITE_PIN3, sc_get_data, sc_put_data },
|
2011-06-01 11:33:23 +00:00
|
|
|
{ 0x3f00, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
2011-05-23 17:32:54 +00:00
|
|
|
{ 0x5f2d, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x5f35, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x5f48, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
|
|
|
|
{ 0x5f50, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
|
|
|
|
{ 0x5f52, SIMPLE, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
|
2018-06-10 18:11:34 +00:00
|
|
|
/* DO 7F21 is CONSTRUCTED in spec; we treat it as SIMPLE: no need to parse TLV */
|
2014-11-09 08:58:40 +00:00
|
|
|
{ DO_CERT, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
|
2011-05-23 17:32:54 +00:00
|
|
|
{ 0x7f48, CONSTRUCTED, READ_NEVER | WRITE_NEVER, NULL, NULL },
|
|
|
|
{ 0x7f49, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
|
2014-11-09 08:58:40 +00:00
|
|
|
{ DO_AUTH, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
|
2018-06-10 18:11:34 +00:00
|
|
|
/* The DOs 0xA401, 0xB601, 0xB801 are virtual DOs, they do not represent any real DO.
|
2012-06-21 08:22:44 +00:00
|
|
|
* However, their R/W access condition may block the process of importing key in pkcs15init.
|
2012-08-08 03:12:11 +00:00
|
|
|
* So we set their accesses condition as WRITE_PIN3 (writable). */
|
2014-11-09 08:58:40 +00:00
|
|
|
{ DO_AUTH_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
|
|
|
|
{ DO_SIGN, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
|
|
|
|
{ DO_SIGN_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
|
|
|
|
{ DO_ENCR, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
|
|
|
|
{ DO_ENCR_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
|
2011-05-23 17:32:54 +00:00
|
|
|
{ 0, 0, 0, NULL, NULL },
|
2003-10-31 16:01:00 +00:00
|
|
|
};
|
|
|
|
|
2020-01-26 09:56:03 +00:00
|
|
|
static pgp_do_info_t *pgp33_objects = pgp34_objects + 9;
|
|
|
|
static pgp_do_info_t *pgp30_objects = pgp34_objects + 10;
|
|
|
|
static pgp_do_info_t *pgp21_objects = pgp34_objects + 15;
|
|
|
|
static pgp_do_info_t *pgp20_objects = pgp34_objects + 16;
|
2003-10-30 17:04:22 +00:00
|
|
|
|
2018-06-10 11:09:13 +00:00
|
|
|
|
2018-06-16 13:54:17 +00:00
|
|
|
/**
|
|
|
|
* Internal: get OpenPGP application identifier from AID DO 004F
|
|
|
|
*/
|
2018-04-04 19:41:39 +00:00
|
|
|
static int
|
|
|
|
get_full_pgp_aid(sc_card_t *card, sc_file_t *file)
|
|
|
|
{
|
2018-06-16 17:34:39 +00:00
|
|
|
int r = SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
|
|
|
if (file != NULL) {
|
|
|
|
/* explicitly get the full aid */
|
|
|
|
r = sc_get_data(card, 0x004F, file->name, sizeof file->name);
|
|
|
|
file->namelen = MAX(r, 0);
|
|
|
|
}
|
2018-04-04 19:41:39 +00:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2018-06-16 13:54:17 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
|
|
|
* ABI: check if card's ATR matches one of driver's
|
2018-06-16 13:54:17 +00:00
|
|
|
* or if the OpenPGP application is present on the card.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2003-10-30 17:04:22 +00:00
|
|
|
static int
|
|
|
|
pgp_match_card(sc_card_t *card)
|
|
|
|
{
|
2005-02-04 20:29:35 +00:00
|
|
|
int i;
|
2003-10-30 17:04:22 +00:00
|
|
|
|
2018-06-16 13:57:43 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2005-02-10 10:07:13 +00:00
|
|
|
i = _sc_match_atr(card, pgp_atrs, &card->type);
|
2011-01-09 10:17:16 +00:00
|
|
|
if (i >= 0) {
|
|
|
|
card->name = pgp_atrs[i].name;
|
2018-04-04 19:41:39 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, 1);
|
2015-09-20 18:36:40 +00:00
|
|
|
}
|
|
|
|
else {
|
2015-07-28 22:26:19 +00:00
|
|
|
sc_path_t partial_aid;
|
2018-01-05 11:37:20 +00:00
|
|
|
sc_file_t *file = NULL;
|
2015-07-28 22:26:19 +00:00
|
|
|
|
|
|
|
/* select application "OpenPGP" */
|
|
|
|
sc_format_path("D276:0001:2401", &partial_aid);
|
|
|
|
partial_aid.type = SC_PATH_TYPE_DF_NAME;
|
2018-01-05 11:37:20 +00:00
|
|
|
/* OpenPGP card only supports selection *with* requested FCI */
|
|
|
|
i = iso_ops->select_file(card, &partial_aid, &file);
|
|
|
|
if (SC_SUCCESS == i) {
|
2018-01-05 10:08:14 +00:00
|
|
|
card->type = SC_CARD_TYPE_OPENPGP_BASE;
|
2018-06-16 18:04:27 +00:00
|
|
|
card->name = default_cardname;
|
|
|
|
|
2018-04-04 19:41:39 +00:00
|
|
|
if (file->namelen != 16)
|
|
|
|
(void) get_full_pgp_aid(card, file);
|
2018-01-05 10:08:14 +00:00
|
|
|
if (file->namelen == 16) {
|
2018-06-16 18:04:27 +00:00
|
|
|
unsigned char major = BCD2UCHAR(file->name[6]);
|
|
|
|
|
2018-01-05 10:08:14 +00:00
|
|
|
switch (major) {
|
|
|
|
case 1:
|
|
|
|
card->type = SC_CARD_TYPE_OPENPGP_V1;
|
2018-06-16 18:04:27 +00:00
|
|
|
card->name = default_cardname_v1;
|
2018-01-05 10:08:14 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
card->type = SC_CARD_TYPE_OPENPGP_V2;
|
2018-06-16 18:04:27 +00:00
|
|
|
card->name = default_cardname_v2;
|
2018-01-05 10:08:14 +00:00
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
card->type = SC_CARD_TYPE_OPENPGP_V3;
|
2018-06-16 18:04:27 +00:00
|
|
|
card->name = default_cardname_v3;
|
2018-01-05 10:08:14 +00:00
|
|
|
break;
|
2018-04-04 19:41:39 +00:00
|
|
|
default:
|
|
|
|
break;
|
2015-09-20 18:36:40 +00:00
|
|
|
}
|
2015-07-28 22:26:19 +00:00
|
|
|
}
|
2018-01-05 10:08:14 +00:00
|
|
|
sc_file_free(file);
|
2018-06-16 13:57:43 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, 1);
|
2015-07-28 22:26:19 +00:00
|
|
|
}
|
2011-01-09 10:17:16 +00:00
|
|
|
}
|
2018-06-16 13:57:43 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, 0);
|
2003-10-30 17:04:22 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: initialize driver & allocate private data.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2003-10-30 17:04:22 +00:00
|
|
|
static int
|
|
|
|
pgp_init(sc_card_t *card)
|
|
|
|
{
|
2012-06-14 02:17:28 +00:00
|
|
|
struct pgp_priv_data *priv;
|
2018-01-16 20:03:30 +00:00
|
|
|
sc_path_t path;
|
2003-10-31 16:01:00 +00:00
|
|
|
sc_file_t *file = NULL;
|
2020-01-26 09:56:03 +00:00
|
|
|
pgp_do_info_t *info;
|
2019-01-30 21:00:36 +00:00
|
|
|
int r, i;
|
2003-10-30 17:04:22 +00:00
|
|
|
|
2013-02-20 04:54:30 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
Do not cast the return value of malloc(3) and calloc(3)
From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety
" Casting and type safety
malloc returns a void pointer (void *), which indicates that it is a
pointer to a region of unknown data type. One may "cast" (see type
conversion) this pointer to a specific type, as in
int *ptr = (int*)malloc(10 * sizeof (int));
When using C, this is considered bad practice; it is redundant under the
C standard. Moreover, putting in a cast may mask failure to include the
header stdlib.h, in which the prototype for malloc is found. In the
absence of a prototype for malloc, the C compiler will assume that
malloc returns an int, and will issue a warning in a context such as the
above, provided the error is not masked by a cast. On certain
architectures and data models (such as LP64 on 64 bit systems, where
long and pointers are 64 bit and int is 32 bit), this error can actually
result in undefined behavior, as the implicitly declared malloc returns
a 32 bit value whereas the actually defined function returns a 64 bit
value. Depending on calling conventions and memory layout, this may
result in stack smashing.
The returned pointer need not be explicitly cast to a more specific
pointer type, since ANSI C defines an implicit conversion between the
void pointer type and other pointers to objects. An explicit cast of
malloc's return value is sometimes performed because malloc originally
returned a char *, but this cast is unnecessary in standard C
code.[4][5] Omitting the cast, however, creates an incompatibility with
C++, which does require it.
The lack of a specific pointer type returned from malloc is type-unsafe
behaviour: malloc allocates based on byte count but not on type. This
distinguishes it from the C++ new operator that returns a pointer whose
type relies on the operand. (see C Type Safety). "
See also
http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
|
|
|
priv = calloc (1, sizeof *priv);
|
2003-10-30 17:04:22 +00:00
|
|
|
if (!priv)
|
2018-06-16 13:57:43 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
|
2011-05-23 17:32:31 +00:00
|
|
|
card->drv_data = priv;
|
|
|
|
|
2003-10-30 17:04:22 +00:00
|
|
|
card->cla = 0x00;
|
|
|
|
|
2011-05-23 17:31:18 +00:00
|
|
|
/* select application "OpenPGP" */
|
2018-01-16 20:03:30 +00:00
|
|
|
sc_format_path("D276:0001:2401", &path);
|
|
|
|
path.type = SC_PATH_TYPE_DF_NAME;
|
|
|
|
if ((r = iso_ops->select_file(card, &path, &file)) < 0) {
|
2019-09-02 07:39:11 +00:00
|
|
|
sc_file_free(file);
|
2011-05-23 17:32:31 +00:00
|
|
|
pgp_finish(card);
|
2018-05-18 21:49:29 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD);
|
2011-05-23 17:31:55 +00:00
|
|
|
}
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2015-03-29 13:55:10 +00:00
|
|
|
/* defensive programming check */
|
|
|
|
if (!file) {
|
|
|
|
pgp_finish(card);
|
2018-05-18 21:49:29 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD);
|
2015-03-29 13:55:10 +00:00
|
|
|
}
|
|
|
|
|
2015-07-28 22:26:19 +00:00
|
|
|
if (file->namelen != 16) {
|
|
|
|
/* explicitly get the full aid */
|
2018-04-04 19:41:39 +00:00
|
|
|
r = get_full_pgp_aid(card, file);
|
2015-07-28 22:26:19 +00:00
|
|
|
if (r < 0) {
|
2019-10-01 09:00:06 +00:00
|
|
|
sc_file_free(file);
|
2015-07-28 22:26:19 +00:00
|
|
|
pgp_finish(card);
|
2018-06-16 13:57:43 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD);
|
2015-07-28 22:26:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:49 +00:00
|
|
|
/* read information from AID */
|
2015-03-29 13:55:10 +00:00
|
|
|
if (file->namelen == 16) {
|
2018-06-23 15:11:17 +00:00
|
|
|
static char card_name[SC_MAX_APDU_BUFFER_SIZE] = "OpenPGP card";
|
|
|
|
|
2018-06-10 09:58:51 +00:00
|
|
|
/* OpenPGP card spec 1.1, 2.x & 3.x, section 4.2.1 & 4.1.2.1 */
|
2011-05-23 17:32:49 +00:00
|
|
|
priv->bcd_version = bebytes2ushort(file->name + 6);
|
2018-06-16 18:04:27 +00:00
|
|
|
card->version.fw_major = card->version.hw_major = BCD2UCHAR(file->name[6]);
|
|
|
|
card->version.fw_minor = card->version.hw_minor = BCD2UCHAR(file->name[7]);
|
|
|
|
|
2018-06-23 15:11:17 +00:00
|
|
|
/* for "standard" cards, include detailed card version & serial no. in card name */
|
2018-06-16 18:04:27 +00:00
|
|
|
if (card->name == default_cardname_v1 ||
|
|
|
|
card->name == default_cardname_v2 ||
|
|
|
|
card->name == default_cardname_v3) {
|
2018-06-23 15:11:17 +00:00
|
|
|
snprintf(card_name, sizeof(card_name),
|
|
|
|
"OpenPGP card v%u.%u (%04X %08lX)",
|
|
|
|
card->version.hw_major, card->version.hw_minor,
|
|
|
|
bebytes2ushort(file->name + 8),
|
|
|
|
bebytes2ulong(file->name + 10));
|
|
|
|
}
|
|
|
|
else if (card->name != NULL) {
|
|
|
|
/* for other cards, append serial number to the card name */
|
|
|
|
snprintf(card_name, sizeof(card_name),
|
|
|
|
"%s (%04X %08lX)",
|
|
|
|
card->name,
|
|
|
|
bebytes2ushort(file->name + 8),
|
|
|
|
bebytes2ulong(file->name + 10));
|
2018-06-16 18:04:27 +00:00
|
|
|
}
|
2018-06-23 15:11:17 +00:00
|
|
|
card->name = card_name;
|
2015-10-12 19:40:18 +00:00
|
|
|
|
2018-06-23 15:11:17 +00:00
|
|
|
/* GPG compatibility: set card's serial number to manufacturer ID + serial number */
|
2011-05-23 17:32:00 +00:00
|
|
|
memcpy(card->serialnr.value, file->name + 8, 6);
|
|
|
|
card->serialnr.len = 6;
|
2018-01-05 10:08:14 +00:00
|
|
|
} else {
|
|
|
|
/* set detailed card version */
|
|
|
|
switch (card->type) {
|
|
|
|
case SC_CARD_TYPE_OPENPGP_V3:
|
|
|
|
priv->bcd_version = OPENPGP_CARD_3_0;
|
|
|
|
break;
|
|
|
|
case SC_CARD_TYPE_OPENPGP_GNUK:
|
|
|
|
case SC_CARD_TYPE_OPENPGP_V2:
|
|
|
|
priv->bcd_version = OPENPGP_CARD_2_0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
priv->bcd_version = OPENPGP_CARD_1_1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set pointer to correct list of card objects */
|
2018-06-09 15:18:42 +00:00
|
|
|
priv->pgp_objects = (priv->bcd_version < OPENPGP_CARD_2_0) ? pgp1x_objects
|
|
|
|
: (priv->bcd_version < OPENPGP_CARD_2_1) ? pgp20_objects
|
2018-06-10 10:35:37 +00:00
|
|
|
: (priv->bcd_version < OPENPGP_CARD_3_0) ? pgp21_objects
|
|
|
|
: (priv->bcd_version < OPENPGP_CARD_3_3) ? pgp30_objects
|
2019-12-25 13:59:08 +00:00
|
|
|
: (priv->bcd_version < OPENPGP_CARD_3_4) ? pgp33_objects
|
|
|
|
: pgp34_objects;
|
2011-05-23 17:32:00 +00:00
|
|
|
|
2011-05-31 16:34:41 +00:00
|
|
|
/* change file path to MF for re-use in MF */
|
2003-10-31 16:01:00 +00:00
|
|
|
sc_format_path("3f00", &file->path);
|
|
|
|
|
2011-05-31 16:34:41 +00:00
|
|
|
/* set up the root of our fake file tree */
|
2019-10-01 09:00:06 +00:00
|
|
|
/* Transfers ownership of the file to the priv->mf structure */
|
2011-05-31 16:34:41 +00:00
|
|
|
priv->mf = pgp_new_blob(card, NULL, 0x3f00, file);
|
|
|
|
if (!priv->mf) {
|
2019-10-01 09:00:06 +00:00
|
|
|
sc_file_free(file);
|
2011-05-31 16:34:41 +00:00
|
|
|
pgp_finish(card);
|
2015-09-20 19:31:46 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
|
2011-05-31 16:34:41 +00:00
|
|
|
}
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2011-05-23 17:32:36 +00:00
|
|
|
/* select MF */
|
2011-05-23 17:30:56 +00:00
|
|
|
priv->current = priv->mf;
|
2003-11-20 15:41:28 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* populate MF - add matching blobs listed in the pgp_objects table */
|
2011-05-23 17:32:54 +00:00
|
|
|
for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++) {
|
2018-06-16 14:03:41 +00:00
|
|
|
if (((info->access & READ_MASK) != READ_NEVER) && (info->get_fn != NULL)) {
|
|
|
|
pgp_blob_t *child = NULL;
|
|
|
|
|
2011-05-31 19:00:42 +00:00
|
|
|
child = pgp_new_blob(card, priv->mf, info->id, sc_file_new());
|
|
|
|
|
2011-05-23 17:32:54 +00:00
|
|
|
/* catch out of memory condition */
|
2011-06-01 11:41:25 +00:00
|
|
|
if (child == NULL) {
|
|
|
|
pgp_finish(card);
|
2015-09-20 19:31:46 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
|
2011-06-01 11:41:25 +00:00
|
|
|
}
|
2011-05-23 17:32:54 +00:00
|
|
|
}
|
2003-11-20 15:41:28 +00:00
|
|
|
}
|
2011-05-23 17:31:31 +00:00
|
|
|
|
2011-05-23 17:33:04 +00:00
|
|
|
/* get card_features from ATR & DOs */
|
|
|
|
pgp_get_card_features(card);
|
|
|
|
|
2018-09-30 15:49:18 +00:00
|
|
|
/* if algorithm attributes can be changed,
|
2019-01-30 21:00:36 +00:00
|
|
|
* add supported algorithms based on specification for pkcs15-init */
|
2018-09-30 15:49:18 +00:00
|
|
|
if ((priv->ext_caps & EXT_CAP_ALG_ATTR_CHANGEABLE) &&
|
|
|
|
(strcmp(card->ctx->app_name, "pkcs15-init") == 0)) {
|
2019-01-30 21:00:36 +00:00
|
|
|
unsigned long flags_rsa, flags_ecc, ext_flags;
|
|
|
|
|
2018-09-30 15:49:18 +00:00
|
|
|
/* OpenPGP card spec 1.1 & 2.x, section 7.2.9 & 7.2.10 / v3.x section 7.2.11 & 7.2.12 */
|
2019-01-30 21:00:36 +00:00
|
|
|
flags_rsa = SC_ALGORITHM_RSA_PAD_PKCS1|
|
|
|
|
SC_ALGORITHM_RSA_HASH_NONE|
|
|
|
|
SC_ALGORITHM_ONBOARD_KEY_GEN;
|
|
|
|
flags_ecc = SC_ALGORITHM_ECDSA_RAW|
|
|
|
|
SC_ALGORITHM_ECDH_CDH_RAW|
|
|
|
|
SC_ALGORITHM_ECDSA_HASH_NONE|
|
|
|
|
SC_ALGORITHM_ONBOARD_KEY_GEN;
|
|
|
|
ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE;
|
2018-08-31 12:38:14 +00:00
|
|
|
|
|
|
|
switch (card->type) {
|
|
|
|
case SC_CARD_TYPE_OPENPGP_V3:
|
|
|
|
/* RSA 1024 was removed for v3+ */
|
2019-01-30 21:00:36 +00:00
|
|
|
_sc_card_add_rsa_alg(card, 2048, flags_rsa, 0);
|
|
|
|
_sc_card_add_rsa_alg(card, 3072, flags_rsa, 0);
|
|
|
|
_sc_card_add_rsa_alg(card, 4096, flags_rsa, 0);
|
|
|
|
/* v3.0+ supports: [RFC 4880 & 6637] 0x12 = ECDH, 0x13 = ECDSA */
|
|
|
|
for (i=0; ec_curves[i].oid.value[0] >= 0; i++)
|
|
|
|
{
|
|
|
|
_sc_card_add_ec_alg(card, ec_curves[i].size, flags_ecc, ext_flags, &ec_curves[i].oid);
|
|
|
|
}
|
2018-08-31 12:38:14 +00:00
|
|
|
break;
|
|
|
|
case SC_CARD_TYPE_OPENPGP_GNUK:
|
2019-01-30 21:00:36 +00:00
|
|
|
_sc_card_add_rsa_alg(card, 2048, flags_rsa, 0);
|
2019-11-07 16:31:53 +00:00
|
|
|
/* Gnuk supports NIST, SECG and Curve25519 since version 1.2 */
|
|
|
|
for (i=0; ec_curves_gnuk[i].oid.value[0] >= 0; i++)
|
|
|
|
{
|
|
|
|
_sc_card_add_ec_alg(card, ec_curves_gnuk[i].size,
|
|
|
|
flags_ecc, ext_flags, &ec_curves_gnuk[i].oid);
|
|
|
|
}
|
2018-08-31 12:38:14 +00:00
|
|
|
break;
|
|
|
|
case SC_CARD_TYPE_OPENPGP_V2:
|
|
|
|
default:
|
2019-01-30 21:00:36 +00:00
|
|
|
_sc_card_add_rsa_alg(card, 1024, flags_rsa, 0);
|
|
|
|
_sc_card_add_rsa_alg(card, 2048, flags_rsa, 0);
|
|
|
|
_sc_card_add_rsa_alg(card, 3072, flags_rsa, 0);
|
|
|
|
_sc_card_add_rsa_alg(card, 4096, flags_rsa, 0);
|
2018-08-31 12:38:14 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-20 19:31:46 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
2011-05-23 17:33:04 +00:00
|
|
|
}
|
|
|
|
|
2018-06-16 13:54:17 +00:00
|
|
|
|
2018-04-22 12:38:23 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* Internal: parse historic bytes to get card capabilities.
|
2018-04-22 12:38:23 +00:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
pgp_parse_hist_bytes(sc_card_t *card, u8 *ctlv, size_t ctlv_len)
|
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2018-06-08 06:17:13 +00:00
|
|
|
const u8 *ptr;
|
|
|
|
|
|
|
|
/* IS07816-4 hist bytes: 3rd function table */
|
|
|
|
if ((ptr = sc_compacttlv_find_tag(ctlv, ctlv_len, 0x73, NULL)) != NULL) {
|
|
|
|
/* bit 0x40 in byte 3 of TL 0x73 means "extended Le/Lc" */
|
|
|
|
if (ptr[2] & 0x40) {
|
|
|
|
card->caps |= SC_CARD_CAP_APDU_EXT;
|
|
|
|
priv->ext_caps |= EXT_CAP_APDU_EXT;
|
2018-04-22 12:38:23 +00:00
|
|
|
}
|
2018-06-08 06:17:13 +00:00
|
|
|
/* bit 0x80 in byte 3 of TL 0x73 means "Command chaining" */
|
2018-12-22 20:25:38 +00:00
|
|
|
if (ptr[2] & 0x80) {
|
2018-06-08 06:17:13 +00:00
|
|
|
priv->ext_caps |= EXT_CAP_CHAINING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((priv->bcd_version >= OPENPGP_CARD_3_0) &&
|
|
|
|
((ptr = sc_compacttlv_find_tag(ctlv, ctlv_len, 0x31, NULL)) != NULL)) {
|
|
|
|
// ToDo ...
|
2018-04-22 12:38:23 +00:00
|
|
|
}
|
|
|
|
}
|
2011-05-23 17:31:39 +00:00
|
|
|
|
2018-06-16 13:54:17 +00:00
|
|
|
|
2018-10-06 13:32:27 +00:00
|
|
|
/**
|
|
|
|
* Internal: parse an algorithm attributes DO
|
|
|
|
**/
|
|
|
|
static int
|
2020-02-24 17:23:23 +00:00
|
|
|
pgp_parse_algo_attr_blob(sc_card_t *card, const pgp_blob_t *blob,
|
|
|
|
sc_cardctl_openpgp_keygen_info_t *key_info)
|
2018-10-06 13:32:27 +00:00
|
|
|
{
|
2019-01-30 21:00:36 +00:00
|
|
|
struct sc_object_id oid;
|
2020-03-24 11:09:06 +00:00
|
|
|
unsigned int j, r;
|
2019-01-30 21:00:36 +00:00
|
|
|
|
2020-02-24 17:23:23 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2018-10-06 13:32:27 +00:00
|
|
|
if (blob == NULL || blob->data == NULL || blob->len == 0 ||
|
2020-02-24 17:23:23 +00:00
|
|
|
blob->id < 0x00c1 || blob->id > 0x00c3 || key_info == NULL) {
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS);
|
|
|
|
}
|
2018-10-06 13:32:27 +00:00
|
|
|
|
|
|
|
key_info->key_id = blob->id - 0x00c0; /* attribute algorithm blobs are C1 - C3 */
|
|
|
|
|
|
|
|
switch (blob->data[0]) {
|
|
|
|
case SC_OPENPGP_KEYALGO_RSA:
|
2020-02-24 17:23:23 +00:00
|
|
|
if (blob->len < 5) {
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS);
|
|
|
|
}
|
2018-10-06 13:32:27 +00:00
|
|
|
|
|
|
|
key_info->algorithm = SC_OPENPGP_KEYALGO_RSA;
|
2019-01-30 21:00:36 +00:00
|
|
|
key_info->u.rsa.modulus_len = bebytes2ushort(blob->data + 1);
|
|
|
|
key_info->u.rsa.exponent_len = bebytes2ushort(blob->data + 3);
|
2018-10-06 13:32:27 +00:00
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
key_info->u.rsa.keyformat = (blob->len > 5)
|
2018-10-06 13:32:27 +00:00
|
|
|
? blob->data[5]
|
|
|
|
: SC_OPENPGP_KEYFORMAT_RSA_STD;
|
|
|
|
break;
|
2019-01-30 21:00:36 +00:00
|
|
|
case SC_OPENPGP_KEYALGO_ECDH:
|
|
|
|
case SC_OPENPGP_KEYALGO_ECDSA:
|
|
|
|
|
|
|
|
/* SC_OPENPGP_KEYALGO_ECDH || SC_OPENPGP_KEYALGO_ECDSA */
|
|
|
|
key_info->algorithm = blob->data[0];
|
|
|
|
|
2019-10-09 15:18:21 +00:00
|
|
|
/* last byte is only set if pubkey import is supported, empty otherwise*/
|
2020-03-19 12:31:31 +00:00
|
|
|
if (blob->data[blob->len-1] == SC_OPENPGP_KEYFORMAT_EC_STDPUB){
|
|
|
|
if (blob->len < 3)
|
|
|
|
return SC_ERROR_INCORRECT_PARAMETERS;
|
2019-10-09 15:18:21 +00:00
|
|
|
key_info->u.ec.oid_len = blob->len - 2;
|
|
|
|
key_info->u.ec.keyformat = SC_OPENPGP_KEYFORMAT_EC_STDPUB;
|
|
|
|
}
|
|
|
|
else {
|
2020-03-19 12:31:31 +00:00
|
|
|
if (blob->len < 2)
|
|
|
|
return SC_ERROR_INCORRECT_PARAMETERS;
|
2019-10-09 15:18:21 +00:00
|
|
|
key_info->u.ec.oid_len = blob->len - 1;
|
|
|
|
key_info->u.ec.keyformat = SC_OPENPGP_KEYFORMAT_EC_STD;
|
2019-01-30 21:00:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Create copy of oid from blob */
|
2020-03-19 12:31:31 +00:00
|
|
|
sc_init_oid(&oid);
|
2020-03-24 11:09:06 +00:00
|
|
|
r = sc_asn1_decode_object_id(&blob->data[1], key_info->u.ec.oid_len, &oid);
|
2019-01-30 21:00:36 +00:00
|
|
|
|
2020-03-24 11:09:06 +00:00
|
|
|
/* decoding failed, return sc_asn1_decode_object_id error code */
|
|
|
|
if (r > 0){
|
|
|
|
return r;
|
|
|
|
}
|
2019-01-30 21:00:36 +00:00
|
|
|
/* compare with list of supported ec_curves */
|
2020-02-24 17:23:23 +00:00
|
|
|
for (j = 0; ec_curves[j].oid.value[0] >= 0; j++) {
|
|
|
|
if (sc_compare_oid(&ec_curves[j].oid, &oid)) {
|
|
|
|
sc_log(card->ctx, "Matched EC oid %s (%d)",
|
|
|
|
sc_dump_oid(&oid), j);
|
2019-01-30 21:00:36 +00:00
|
|
|
key_info->u.ec.oid = ec_curves[j].oid;
|
|
|
|
key_info->u.ec.key_length = ec_curves[j].size;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-10-09 15:18:21 +00:00
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
break;
|
2018-10-06 13:32:27 +00:00
|
|
|
default:
|
2020-02-24 17:23:23 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
2018-10-06 13:32:27 +00:00
|
|
|
}
|
|
|
|
|
2020-02-24 17:23:23 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
2018-10-06 13:32:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
|
|
|
* Internal: get features of the card: capabilities, ...
|
|
|
|
*/
|
2011-05-23 17:33:04 +00:00
|
|
|
static int
|
|
|
|
pgp_get_card_features(sc_card_t *card)
|
|
|
|
{
|
2015-09-19 14:52:51 +00:00
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2015-10-17 16:58:29 +00:00
|
|
|
u8 *hist_bytes = card->reader->atr_info.hist_bytes;
|
|
|
|
size_t hist_bytes_len = card->reader->atr_info.hist_bytes_len;
|
2015-09-20 06:49:20 +00:00
|
|
|
size_t i;
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *blob, *blob6e, *blob73;
|
2011-05-23 17:33:04 +00:00
|
|
|
|
2020-02-24 17:23:23 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2018-04-22 12:38:23 +00:00
|
|
|
/* parse card capabilities from historical bytes in ATR */
|
2015-10-17 16:58:29 +00:00
|
|
|
if (hist_bytes_len > 0) {
|
2018-04-22 12:38:23 +00:00
|
|
|
/* category indicator 0x00, 0x10 or 0x80 => compact TLV (ISO) */
|
|
|
|
switch (hist_bytes[0]) {
|
|
|
|
case 0x00:
|
2018-07-03 07:36:21 +00:00
|
|
|
if (hist_bytes_len > 4) {
|
|
|
|
pgp_parse_hist_bytes(card, hist_bytes+1, hist_bytes_len-4);
|
|
|
|
}
|
2018-04-22 12:38:23 +00:00
|
|
|
break;
|
|
|
|
case 0x80:
|
2018-07-03 07:36:21 +00:00
|
|
|
if (hist_bytes_len > 1) {
|
|
|
|
pgp_parse_hist_bytes(card, hist_bytes+1, hist_bytes_len-1);
|
|
|
|
}
|
2018-04-22 12:38:23 +00:00
|
|
|
break;
|
|
|
|
case 0x10:
|
2018-07-03 07:36:21 +00:00
|
|
|
if (hist_bytes_len > 2) {
|
|
|
|
pgp_parse_hist_bytes(card, hist_bytes+2, hist_bytes_len-2);
|
|
|
|
}
|
2018-04-22 12:38:23 +00:00
|
|
|
break;
|
2012-05-26 20:27:58 +00:00
|
|
|
}
|
2011-05-23 17:31:39 +00:00
|
|
|
}
|
2015-09-20 06:49:20 +00:00
|
|
|
|
2018-06-10 20:06:53 +00:00
|
|
|
/* v1.1 does not support lifecycle via ACTIVATE & TERMINATE: set default */
|
|
|
|
priv->ext_caps &= ~EXT_CAP_LCS;
|
|
|
|
|
2012-05-26 20:27:58 +00:00
|
|
|
if (priv->bcd_version >= OPENPGP_CARD_2_0) {
|
2011-05-23 17:33:04 +00:00
|
|
|
/* get card capabilities from "historical bytes" DO */
|
|
|
|
if ((pgp_get_blob(card, priv->mf, 0x5f52, &blob) >= 0) &&
|
|
|
|
(blob->data != NULL) && (blob->data[0] == 0x00)) {
|
2015-09-20 06:49:20 +00:00
|
|
|
|
2018-12-22 22:01:10 +00:00
|
|
|
if (blob->len > 4) {
|
|
|
|
pgp_parse_hist_bytes(card, blob->data+1, blob->len-4);
|
2018-07-03 07:36:21 +00:00
|
|
|
}
|
2012-05-26 20:27:58 +00:00
|
|
|
|
|
|
|
/* get card status from historical bytes status indicator */
|
2018-06-10 20:06:53 +00:00
|
|
|
if ((blob->data[0] == 0x00) && (blob->len >= 4)) {
|
2012-05-26 20:27:58 +00:00
|
|
|
priv->state = blob->data[blob->len-3];
|
2018-06-10 20:06:53 +00:00
|
|
|
/* state not CARD_STATE_UNKNOWN => LCS supported */
|
|
|
|
if (priv->state != CARD_STATE_UNKNOWN)
|
|
|
|
priv->ext_caps |= EXT_CAP_LCS;
|
|
|
|
}
|
2011-05-23 17:33:04 +00:00
|
|
|
}
|
|
|
|
}
|
2011-05-23 17:31:39 +00:00
|
|
|
|
2018-01-17 20:11:16 +00:00
|
|
|
if (priv->bcd_version >= OPENPGP_CARD_3_1) {
|
|
|
|
card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO;
|
|
|
|
}
|
|
|
|
|
2020-01-26 11:42:00 +00:00
|
|
|
/* v1.1 & v2.x: special DOs are limited to 254 bytes */
|
|
|
|
priv->max_specialDO_size = 254;
|
|
|
|
|
2011-05-23 17:33:15 +00:00
|
|
|
if ((pgp_get_blob(card, priv->mf, 0x006e, &blob6e) >= 0) &&
|
|
|
|
(pgp_get_blob(card, blob6e, 0x0073, &blob73) >= 0)) {
|
|
|
|
|
|
|
|
/* get "extended capabilities" DO */
|
|
|
|
if ((pgp_get_blob(card, blob73, 0x00c0, &blob) >= 0) &&
|
|
|
|
(blob->data != NULL) && (blob->len > 0)) {
|
2018-06-10 09:58:51 +00:00
|
|
|
/* v2.0+: bit 0x04 in first byte means "algorithm attributes changeable" */
|
2013-02-20 04:54:30 +00:00
|
|
|
if ((blob->data[0] & 0x04) &&
|
2018-01-05 10:08:14 +00:00
|
|
|
(priv->bcd_version >= OPENPGP_CARD_2_0))
|
2012-05-26 20:27:58 +00:00
|
|
|
priv->ext_caps |= EXT_CAP_ALG_ATTR_CHANGEABLE;
|
|
|
|
/* bit 0x08 in first byte means "support for private use DOs" */
|
|
|
|
if (blob->data[0] & 0x08)
|
|
|
|
priv->ext_caps |= EXT_CAP_PRIVATE_DO;
|
|
|
|
/* bit 0x10 in first byte means "support for CHV status byte changeable" */
|
|
|
|
if (blob->data[0] & 0x10)
|
|
|
|
priv->ext_caps |= EXT_CAP_C4_CHANGEABLE;
|
|
|
|
/* bit 0x20 in first byte means "support for Key Import" */
|
|
|
|
if (blob->data[0] & 0x20)
|
|
|
|
priv->ext_caps |= EXT_CAP_KEY_IMPORT;
|
|
|
|
/* bit 0x40 in first byte means "support for Get Challenge" */
|
|
|
|
if (blob->data[0] & 0x40) {
|
2011-05-23 17:33:15 +00:00
|
|
|
card->caps |= SC_CARD_CAP_RNG;
|
2012-05-26 20:27:58 +00:00
|
|
|
priv->ext_caps |= EXT_CAP_GET_CHALLENGE;
|
|
|
|
}
|
2018-06-10 09:58:51 +00:00
|
|
|
/* v2.0+: bit 0x80 in first byte means "support Secure Messaging" */
|
2013-02-20 04:54:30 +00:00
|
|
|
if ((blob->data[0] & 0x80) &&
|
2018-01-05 10:08:14 +00:00
|
|
|
(priv->bcd_version >= OPENPGP_CARD_2_0))
|
2012-05-26 20:27:58 +00:00
|
|
|
priv->ext_caps |= EXT_CAP_SM;
|
2011-05-23 17:33:15 +00:00
|
|
|
|
|
|
|
if ((priv->bcd_version >= OPENPGP_CARD_2_0) && (blob->len >= 10)) {
|
2018-06-10 09:58:51 +00:00
|
|
|
/* v2.0+: max. challenge size is at bytes 3-4 */
|
2012-05-26 20:27:58 +00:00
|
|
|
priv->max_challenge_size = bebytes2ushort(blob->data + 2);
|
2018-06-10 09:58:51 +00:00
|
|
|
/* v2.0+: max. cert size it at bytes 5-6 */
|
2012-05-26 20:27:58 +00:00
|
|
|
priv->max_cert_size = bebytes2ushort(blob->data + 4);
|
2018-06-10 11:09:13 +00:00
|
|
|
|
2018-02-07 15:40:03 +00:00
|
|
|
if (priv->bcd_version < OPENPGP_CARD_3_0) {
|
2018-06-10 11:09:13 +00:00
|
|
|
/* v2.x: SM algorithm is at byte 2: 0 == 3DES */
|
2018-08-31 13:56:57 +00:00
|
|
|
priv->sm_algo = blob->data[1];
|
2018-06-10 11:09:13 +00:00
|
|
|
if ((priv->sm_algo == SM_ALGO_NONE) && (priv->ext_caps & EXT_CAP_SM))
|
|
|
|
priv->sm_algo = SM_ALGO_3DES;
|
|
|
|
|
2018-06-10 09:58:51 +00:00
|
|
|
/* v2.x: max. send/receive sizes are at bytes 7-8 resp. 9-10 */
|
2018-02-07 15:40:03 +00:00
|
|
|
card->max_send_size = bebytes2ushort(blob->data + 6);
|
|
|
|
card->max_recv_size = bebytes2ushort(blob->data + 8);
|
|
|
|
}
|
2018-06-10 11:09:13 +00:00
|
|
|
else {
|
|
|
|
/* v3.0+: SM algorithm is at byte 2: 0 == UNKNOWN */
|
2018-08-31 13:56:57 +00:00
|
|
|
priv->sm_algo = blob->data[1];
|
2018-06-10 11:09:13 +00:00
|
|
|
if ((priv->sm_algo == SM_ALGO_NONE) && (priv->ext_caps & EXT_CAP_SM))
|
|
|
|
priv->sm_algo = SM_ALGO_UNKNOWN;
|
2020-01-26 11:42:00 +00:00
|
|
|
|
|
|
|
/* v3.0+: max. size of special DOs is at bytes 7-8 */
|
|
|
|
priv->max_specialDO_size = bebytes2ushort(blob->data + 6);
|
2018-06-10 11:09:13 +00:00
|
|
|
}
|
2018-09-06 08:57:23 +00:00
|
|
|
if (priv->bcd_version >= OPENPGP_CARD_3_3 && (blob->len >= 10)) {
|
|
|
|
/* v3.3+: MSE for key numbers 2(DEC) and 3(AUT) supported */
|
|
|
|
if (blob->data[9])
|
|
|
|
priv->ext_caps |= EXT_CAP_MSE;
|
|
|
|
}
|
2011-05-23 17:33:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get max. PIN length from "CHV status bytes" DO */
|
|
|
|
if ((pgp_get_blob(card, blob73, 0x00c4, &blob) >= 0) &&
|
2015-07-03 16:21:38 +00:00
|
|
|
(blob->data != NULL) && (blob->len > 1)) {
|
2011-05-23 17:33:15 +00:00
|
|
|
/* 2nd byte in "CHV status bytes" DO means "max. PIN length" */
|
|
|
|
card->max_pin_len = blob->data[1];
|
|
|
|
}
|
2011-05-23 17:33:27 +00:00
|
|
|
|
2018-08-31 12:38:14 +00:00
|
|
|
/* get _current_ algorithms & key lengths from "algorithm attributes" DOs
|
|
|
|
*
|
|
|
|
* All available algorithms should be already provided by pgp_init. However, if another
|
|
|
|
* algorithm is found in the "algorithm attributes" DOs, it is supported by the card as
|
2019-02-02 15:09:16 +00:00
|
|
|
* well and therefore added
|
2018-08-31 12:38:14 +00:00
|
|
|
* see OpenPGP card spec 1.1 & 2.x section 4.3.3.6 / v3.x section 4.4.3.7 */
|
2015-09-20 06:49:20 +00:00
|
|
|
for (i = 0x00c1; i <= 0x00c3; i++) {
|
2018-10-06 15:28:15 +00:00
|
|
|
sc_cardctl_openpgp_keygen_info_t key_info;
|
2011-05-23 17:33:27 +00:00
|
|
|
|
2020-02-24 17:23:23 +00:00
|
|
|
sc_log(card->ctx, "Parsing algorithm attribues DO %zX" , i);
|
|
|
|
|
2018-06-24 08:15:36 +00:00
|
|
|
/* OpenPGP card spec 1.1 & 2.x section 4.3.3.6 / v3.x section 4.4.3.7 */
|
2018-10-06 15:28:15 +00:00
|
|
|
if ((pgp_get_blob(card, blob73, i, &blob) >= 0) &&
|
2020-02-24 17:23:23 +00:00
|
|
|
(pgp_parse_algo_attr_blob(card, blob, &key_info) >= 0)) {
|
2011-05-23 17:33:27 +00:00
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
/* RSA [RFC 4880] */
|
|
|
|
if (key_info.algorithm == SC_OPENPGP_KEYALGO_RSA){
|
|
|
|
/* OpenPGP card spec 1.1 & 2.x, section 7.2.9 & 7.2.10 /
|
|
|
|
* v3.x section 7.2.11 & 7.2.12 */
|
|
|
|
unsigned long flags = SC_ALGORITHM_RSA_PAD_PKCS1 |
|
|
|
|
SC_ALGORITHM_RSA_HASH_NONE |
|
|
|
|
SC_ALGORITHM_ONBOARD_KEY_GEN; /* key gen on card */
|
|
|
|
_sc_card_add_rsa_alg(card, key_info.u.rsa.modulus_len, flags, 0);
|
2020-02-24 17:23:23 +00:00
|
|
|
sc_log(card->ctx, "DO %zX: Added RSA algorithm, mod_len = %zu" , i, key_info.u.rsa.modulus_len);
|
2019-01-30 21:00:36 +00:00
|
|
|
}
|
|
|
|
/* v3.0+: ECC [RFC 4880 & 6637] */
|
|
|
|
else if (key_info.algorithm == SC_OPENPGP_KEYALGO_ECDH
|
|
|
|
|| key_info.algorithm == SC_OPENPGP_KEYALGO_ECDSA) {
|
|
|
|
|
|
|
|
unsigned long flags, ext_flags;
|
|
|
|
|
|
|
|
if (key_info.algorithm == SC_OPENPGP_KEYALGO_ECDH)
|
|
|
|
flags = SC_ALGORITHM_ECDH_CDH_RAW;
|
|
|
|
if (key_info.algorithm == SC_OPENPGP_KEYALGO_ECDSA)
|
|
|
|
flags = SC_ALGORITHM_ECDSA_RAW;
|
|
|
|
flags |= SC_ALGORITHM_ECDSA_HASH_NONE;
|
|
|
|
flags |= SC_ALGORITHM_ONBOARD_KEY_GEN;
|
|
|
|
ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE;
|
2019-02-02 15:09:16 +00:00
|
|
|
|
|
|
|
_sc_card_add_ec_alg(card, key_info.u.ec.key_length, flags, ext_flags,
|
2019-01-30 21:00:36 +00:00
|
|
|
&key_info.u.ec.oid);
|
2020-02-24 17:23:23 +00:00
|
|
|
sc_log(card->ctx, "DO %zX: Added EC algorithm (%d), mod_len = %d" , i, key_info.algorithm, key_info.u.ec.key_length);
|
2019-01-30 21:00:36 +00:00
|
|
|
}
|
2011-05-23 17:33:27 +00:00
|
|
|
}
|
|
|
|
}
|
2018-06-10 12:03:06 +00:00
|
|
|
|
|
|
|
if (priv->bcd_version >= OPENPGP_CARD_3_0) {
|
|
|
|
/* v3.0+: get length info from "extended length information" DO */
|
|
|
|
if ((pgp_get_blob(card, blob6e, 0x7f66, &blob) >= 0) &&
|
|
|
|
(blob->data != NULL) && (blob->len >= 8)) {
|
|
|
|
/* kludge: treat as SIMPLE DO and use appropriate offsets */
|
|
|
|
card->max_send_size = bebytes2ushort(blob->data + 2);
|
|
|
|
card->max_recv_size = bebytes2ushort(blob->data + 6);
|
|
|
|
}
|
|
|
|
}
|
2011-05-23 17:33:15 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:31:24 +00:00
|
|
|
return SC_SUCCESS;
|
2003-10-30 17:04:22 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: terminate driver & free private data.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2003-10-30 17:04:22 +00:00
|
|
|
static int
|
|
|
|
pgp_finish(sc_card_t *card)
|
|
|
|
{
|
2011-05-23 17:32:26 +00:00
|
|
|
if (card != NULL) {
|
2015-09-19 14:52:51 +00:00
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2003-10-30 17:04:22 +00:00
|
|
|
|
2011-05-23 17:32:26 +00:00
|
|
|
if (priv != NULL) {
|
|
|
|
/* delete fake file hierarchy */
|
|
|
|
pgp_iterate_blobs(priv->mf, 99, pgp_free_blob);
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2011-05-23 17:32:26 +00:00
|
|
|
/* delete private data */
|
|
|
|
free(priv);
|
|
|
|
}
|
|
|
|
card->drv_data = NULL;
|
|
|
|
}
|
2011-05-23 17:31:24 +00:00
|
|
|
return SC_SUCCESS;
|
2003-10-30 17:04:22 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
|
|
|
* Internal: fill a blob's data.
|
|
|
|
*/
|
2003-11-20 15:41:28 +00:00
|
|
|
static int
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_set_blob(pgp_blob_t *blob, const u8 *data, size_t len)
|
2003-11-20 15:41:28 +00:00
|
|
|
{
|
|
|
|
if (blob->data)
|
|
|
|
free(blob->data);
|
2011-05-23 17:31:09 +00:00
|
|
|
blob->data = NULL;
|
|
|
|
blob->len = 0;
|
2003-11-20 15:41:28 +00:00
|
|
|
blob->status = 0;
|
|
|
|
|
2011-05-23 17:31:09 +00:00
|
|
|
if (len > 0) {
|
2012-06-08 09:00:18 +00:00
|
|
|
void *tmp = calloc(len, 1);
|
2011-05-23 17:31:09 +00:00
|
|
|
|
|
|
|
if (tmp == NULL)
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
blob->data = tmp;
|
2015-08-08 06:17:12 +00:00
|
|
|
blob->len = (unsigned int)len;
|
2012-06-08 09:00:18 +00:00
|
|
|
if (data != NULL)
|
|
|
|
memcpy(blob->data, data, len);
|
2011-05-23 17:31:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (blob->file)
|
|
|
|
blob->file->size = len;
|
|
|
|
|
2011-05-23 17:31:24 +00:00
|
|
|
return SC_SUCCESS;
|
2003-11-20 15:41:28 +00:00
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2012-06-01 18:00:13 +00:00
|
|
|
/**
|
2015-09-19 14:52:51 +00:00
|
|
|
* Internal: implement Access Control List for emulated file.
|
2012-06-01 18:00:13 +00:00
|
|
|
* The Access Control is derived from the DO access permission.
|
|
|
|
**/
|
2012-06-11 16:48:37 +00:00
|
|
|
static void
|
2020-01-26 09:56:03 +00:00
|
|
|
pgp_attach_acl(sc_card_t *card, sc_file_t *file, pgp_do_info_t *info)
|
2012-06-01 18:00:13 +00:00
|
|
|
{
|
|
|
|
unsigned int method = SC_AC_NONE;
|
2012-06-21 08:22:44 +00:00
|
|
|
unsigned long key_ref = SC_AC_KEY_REF_NONE;
|
2012-06-01 18:00:13 +00:00
|
|
|
|
|
|
|
/* Write access */
|
2012-06-02 06:13:55 +00:00
|
|
|
switch (info->access & WRITE_MASK) {
|
|
|
|
case WRITE_NEVER:
|
2012-06-01 18:00:13 +00:00
|
|
|
method = SC_AC_NEVER;
|
2012-06-02 06:13:55 +00:00
|
|
|
break;
|
|
|
|
case WRITE_PIN1:
|
2012-06-01 18:00:13 +00:00
|
|
|
method = SC_AC_CHV;
|
2012-06-02 06:13:55 +00:00
|
|
|
key_ref = 0x01;
|
|
|
|
break;
|
|
|
|
case WRITE_PIN2:
|
2012-06-01 18:00:13 +00:00
|
|
|
method = SC_AC_CHV;
|
2012-06-03 04:01:19 +00:00
|
|
|
key_ref = 0x02;
|
2012-06-02 06:13:55 +00:00
|
|
|
break;
|
|
|
|
case WRITE_PIN3:
|
2012-06-01 18:00:13 +00:00
|
|
|
method = SC_AC_CHV;
|
2012-06-03 04:01:19 +00:00
|
|
|
key_ref = 0x03;
|
2012-06-02 06:13:55 +00:00
|
|
|
break;
|
2012-06-01 18:00:13 +00:00
|
|
|
}
|
|
|
|
|
2012-06-21 08:22:44 +00:00
|
|
|
if (method != SC_AC_NONE || key_ref != SC_AC_KEY_REF_NONE) {
|
2012-06-01 18:00:13 +00:00
|
|
|
sc_file_add_acl_entry(file, SC_AC_OP_WRITE, method, key_ref);
|
|
|
|
sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, method, key_ref);
|
|
|
|
sc_file_add_acl_entry(file, SC_AC_OP_DELETE, method, key_ref);
|
|
|
|
sc_file_add_acl_entry(file, SC_AC_OP_CREATE, method, key_ref);
|
|
|
|
}
|
2012-06-21 08:22:44 +00:00
|
|
|
else {
|
|
|
|
/* When SC_AC_OP_DELETE is absent, we need to provide
|
|
|
|
* SC_AC_OP_DELETE_SELF for sc_pkcs15init_delete_by_path() */
|
|
|
|
sc_file_add_acl_entry(file, SC_AC_OP_DELETE_SELF, method, key_ref);
|
|
|
|
}
|
2012-06-01 18:00:13 +00:00
|
|
|
|
|
|
|
method = SC_AC_NONE;
|
2012-06-21 08:22:44 +00:00
|
|
|
key_ref = SC_AC_KEY_REF_NONE;
|
2012-06-01 18:00:13 +00:00
|
|
|
/* Read access */
|
2012-06-02 06:13:55 +00:00
|
|
|
switch (info->access & READ_MASK) {
|
|
|
|
case READ_NEVER:
|
2012-06-01 18:00:13 +00:00
|
|
|
method = SC_AC_NEVER;
|
2012-06-02 06:13:55 +00:00
|
|
|
break;
|
|
|
|
case READ_PIN1:
|
2012-06-01 18:00:13 +00:00
|
|
|
method = SC_AC_CHV;
|
2012-06-02 06:13:55 +00:00
|
|
|
key_ref = 0x01;
|
|
|
|
break;
|
|
|
|
case READ_PIN2:
|
2012-06-01 18:00:13 +00:00
|
|
|
method = SC_AC_CHV;
|
2012-06-03 04:01:19 +00:00
|
|
|
key_ref = 0x02;
|
2012-06-02 06:13:55 +00:00
|
|
|
break;
|
|
|
|
case READ_PIN3:
|
2012-06-01 18:00:13 +00:00
|
|
|
method = SC_AC_CHV;
|
2012-06-03 04:01:19 +00:00
|
|
|
key_ref = 0x03;
|
2012-06-02 06:13:55 +00:00
|
|
|
break;
|
2012-06-01 18:00:13 +00:00
|
|
|
}
|
2012-06-02 06:13:55 +00:00
|
|
|
|
2012-06-21 08:22:44 +00:00
|
|
|
if (method != SC_AC_NONE || key_ref != SC_AC_KEY_REF_NONE) {
|
2012-06-02 09:51:10 +00:00
|
|
|
sc_file_add_acl_entry(file, SC_AC_OP_READ, method, key_ref);
|
2012-06-01 18:00:13 +00:00
|
|
|
}
|
|
|
|
}
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal: append a blob to the list of children of a given parent blob.
|
|
|
|
*/
|
2014-07-13 11:41:36 +00:00
|
|
|
static pgp_blob_t *
|
|
|
|
pgp_new_blob(sc_card_t *card, pgp_blob_t *parent, unsigned int file_id,
|
2011-05-31 19:00:42 +00:00
|
|
|
sc_file_t *file)
|
2003-10-31 16:01:00 +00:00
|
|
|
{
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *blob = NULL;
|
2011-05-31 19:00:42 +00:00
|
|
|
|
|
|
|
if (file == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2014-07-13 11:41:36 +00:00
|
|
|
if ((blob = calloc(1, sizeof(pgp_blob_t))) != NULL) {
|
2015-09-19 14:52:51 +00:00
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2020-01-26 09:56:03 +00:00
|
|
|
pgp_do_info_t *info;
|
2011-05-31 19:00:42 +00:00
|
|
|
|
|
|
|
blob->file = file;
|
|
|
|
|
|
|
|
blob->file->type = SC_FILE_TYPE_WORKING_EF; /* default */
|
|
|
|
blob->file->ef_structure = SC_FILE_EF_TRANSPARENT;
|
|
|
|
blob->file->id = file_id;
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2011-05-23 17:31:31 +00:00
|
|
|
blob->id = file_id;
|
2011-05-31 19:00:42 +00:00
|
|
|
blob->parent = parent;
|
|
|
|
|
|
|
|
if (parent != NULL) {
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t **p;
|
2011-05-31 19:00:42 +00:00
|
|
|
|
|
|
|
/* set file's path = parent's path + file's id */
|
|
|
|
blob->file->path = parent->file->path;
|
|
|
|
sc_append_file_id(&blob->file->path, file_id);
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2011-05-31 19:00:42 +00:00
|
|
|
/* append blob to list of parent's children */
|
|
|
|
for (p = &parent->files; *p != NULL; p = &(*p)->next)
|
|
|
|
;
|
|
|
|
*p = blob;
|
|
|
|
}
|
|
|
|
else {
|
2018-06-16 19:07:37 +00:00
|
|
|
char path[10] = "0000"; /* long enough */
|
2011-05-31 19:00:42 +00:00
|
|
|
|
|
|
|
/* no parent: set file's path = file's id */
|
2018-06-16 19:07:37 +00:00
|
|
|
if (4 != snprintf(path, sizeof(path), "%04X", file_id & 0xFFFF)) {
|
|
|
|
free(blob);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc_format_path(path, &blob->file->path);
|
2011-05-31 19:00:42 +00:00
|
|
|
}
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2011-05-31 19:00:42 +00:00
|
|
|
/* find matching DO info: set file type depending on it */
|
|
|
|
for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++) {
|
|
|
|
if (info->id == file_id) {
|
|
|
|
blob->info = info;
|
|
|
|
blob->file->type = blob->info->type;
|
2012-06-01 18:00:13 +00:00
|
|
|
pgp_attach_acl(card, blob->file, info);
|
2011-05-31 19:00:42 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-05-23 17:31:31 +00:00
|
|
|
}
|
2003-10-31 16:01:00 +00:00
|
|
|
|
|
|
|
return blob;
|
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
|
|
|
* Internal: free a blob including its content.
|
|
|
|
*/
|
2011-05-23 17:30:56 +00:00
|
|
|
static void
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_free_blob(pgp_blob_t *blob)
|
2011-05-23 17:30:56 +00:00
|
|
|
{
|
|
|
|
if (blob) {
|
2011-06-02 08:10:48 +00:00
|
|
|
if (blob->parent) {
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t **p;
|
2011-06-02 08:10:48 +00:00
|
|
|
|
|
|
|
/* remove blob from list of parent's children */
|
|
|
|
for (p = &blob->parent->files; *p != NULL && *p != blob; p = &(*p)->next)
|
|
|
|
;
|
|
|
|
if (*p == blob)
|
|
|
|
*p = blob->next;
|
|
|
|
}
|
|
|
|
|
2017-01-05 19:21:44 +00:00
|
|
|
sc_file_free(blob->file);
|
2011-05-23 17:30:56 +00:00
|
|
|
if (blob->data)
|
|
|
|
free(blob->data);
|
|
|
|
free(blob);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
|
|
|
* Internal: iterate through the blob tree, calling a function for each blob.
|
|
|
|
*/
|
2011-05-23 17:30:56 +00:00
|
|
|
static void
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_iterate_blobs(pgp_blob_t *blob, int level, void (*func)())
|
2011-05-23 17:30:56 +00:00
|
|
|
{
|
|
|
|
if (blob) {
|
|
|
|
if (level > 0) {
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *child = blob->files;
|
2011-05-23 17:30:56 +00:00
|
|
|
|
|
|
|
while (child != NULL) {
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *next = child->next;
|
2011-05-23 17:30:56 +00:00
|
|
|
|
|
|
|
pgp_iterate_blobs(child, level-1, func);
|
|
|
|
child = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func(blob);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
|
|
|
* Internal: read a blob's contents from card.
|
|
|
|
*/
|
2003-11-20 15:41:28 +00:00
|
|
|
static int
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_read_blob(sc_card_t *card, pgp_blob_t *blob)
|
2003-11-20 15:41:28 +00:00
|
|
|
{
|
2015-09-19 14:52:51 +00:00
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2013-04-11 04:47:51 +00:00
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
if (blob->data != NULL)
|
2011-05-23 17:31:24 +00:00
|
|
|
return SC_SUCCESS;
|
2003-11-20 15:41:28 +00:00
|
|
|
if (blob->info == NULL)
|
|
|
|
return blob->status;
|
|
|
|
|
2011-05-23 17:32:59 +00:00
|
|
|
if (blob->info->get_fn) { /* readable, top-level DO */
|
2018-11-17 12:41:24 +00:00
|
|
|
u8 buffer[MAX_OPENPGP_DO_SIZE];
|
2015-08-01 09:25:31 +00:00
|
|
|
size_t buf_len = sizeof(buffer);
|
2013-05-08 09:51:21 +00:00
|
|
|
int r = SC_SUCCESS;
|
2013-04-05 10:18:50 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* buffer length for certificate */
|
2013-04-11 04:47:51 +00:00
|
|
|
if (blob->id == DO_CERT && priv->max_cert_size > 0) {
|
|
|
|
buf_len = MIN(priv->max_cert_size, sizeof(buffer));
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* buffer length for Gnuk pubkey */
|
2013-04-05 10:18:50 +00:00
|
|
|
if (card->type == SC_CARD_TYPE_OPENPGP_GNUK &&
|
2015-09-14 19:48:48 +00:00
|
|
|
(blob->id == DO_AUTH ||
|
|
|
|
blob->id == DO_SIGN ||
|
|
|
|
blob->id == DO_ENCR ||
|
|
|
|
blob->id == DO_AUTH_SYM ||
|
|
|
|
blob->id == DO_SIGN_SYM ||
|
|
|
|
blob->id == DO_ENCR_SYM)) {
|
2018-10-13 19:36:48 +00:00
|
|
|
buf_len = MIN(MAXLEN_RESP_PUBKEY_GNUK, sizeof(buffer));
|
2013-04-05 10:18:50 +00:00
|
|
|
}
|
|
|
|
|
2013-05-08 09:51:21 +00:00
|
|
|
r = blob->info->get_fn(card, blob->id, buffer, buf_len);
|
2003-11-20 15:41:28 +00:00
|
|
|
|
2011-05-23 17:32:59 +00:00
|
|
|
if (r < 0) { /* an error occurred */
|
|
|
|
blob->status = r;
|
|
|
|
return r;
|
|
|
|
}
|
2003-11-20 15:41:28 +00:00
|
|
|
|
2011-05-23 17:32:59 +00:00
|
|
|
return pgp_set_blob(blob, buffer, r);
|
|
|
|
}
|
2012-06-02 09:51:51 +00:00
|
|
|
else { /* un-readable DO or part of a constructed DO */
|
2011-05-23 17:32:59 +00:00
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
2003-11-20 15:41:28 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2018-06-16 13:54:17 +00:00
|
|
|
/**
|
2015-09-19 14:52:51 +00:00
|
|
|
* Internal: enumerate contents of a data blob.
|
2011-05-23 17:30:49 +00:00
|
|
|
* The OpenPGP card has a TLV encoding according ASN.1 BER-encoding rules.
|
2003-10-30 17:04:22 +00:00
|
|
|
*/
|
2003-10-31 16:01:00 +00:00
|
|
|
static int
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_enumerate_blob(sc_card_t *card, pgp_blob_t *blob)
|
2003-10-31 16:01:00 +00:00
|
|
|
{
|
2011-05-23 17:30:49 +00:00
|
|
|
const u8 *in;
|
2003-11-20 15:41:28 +00:00
|
|
|
int r;
|
|
|
|
|
|
|
|
if (blob->files != NULL)
|
2011-05-23 17:31:24 +00:00
|
|
|
return SC_SUCCESS;
|
2003-11-20 15:41:28 +00:00
|
|
|
|
|
|
|
if ((r = pgp_read_blob(card, blob)) < 0)
|
|
|
|
return r;
|
2003-10-31 16:01:00 +00:00
|
|
|
|
|
|
|
in = blob->data;
|
|
|
|
|
2011-05-27 08:49:43 +00:00
|
|
|
while ((int) blob->len > (in - blob->data)) {
|
2011-05-23 17:30:49 +00:00
|
|
|
unsigned int cla, tag, tmptag;
|
|
|
|
size_t len;
|
|
|
|
const u8 *data = in;
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *new;
|
2011-05-23 17:30:49 +00:00
|
|
|
|
2018-05-25 12:54:47 +00:00
|
|
|
if (!in)
|
|
|
|
return SC_ERROR_OBJECT_NOT_VALID;
|
|
|
|
|
2011-05-23 17:30:49 +00:00
|
|
|
r = sc_asn1_read_tag(&data, blob->len - (in - blob->data),
|
|
|
|
&cla, &tag, &len);
|
2018-03-19 14:25:14 +00:00
|
|
|
if (r < 0 || data == NULL) {
|
2019-02-02 15:09:16 +00:00
|
|
|
sc_log(card->ctx,
|
2011-05-23 17:30:49 +00:00
|
|
|
"Unexpected end of contents\n");
|
|
|
|
return SC_ERROR_OBJECT_NOT_VALID;
|
|
|
|
}
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2018-05-25 12:54:47 +00:00
|
|
|
if (data + len > blob->data + blob->len)
|
|
|
|
return SC_ERROR_OBJECT_NOT_VALID;
|
|
|
|
|
2011-05-23 17:30:49 +00:00
|
|
|
/* undo ASN1's split of tag & class */
|
|
|
|
for (tmptag = tag; tmptag > 0x0FF; tmptag >>= 8) {
|
|
|
|
cla <<= 8;
|
2003-10-31 16:01:00 +00:00
|
|
|
}
|
2011-05-23 17:30:49 +00:00
|
|
|
tag |= cla;
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2015-08-01 09:25:31 +00:00
|
|
|
/* Awful hack for composite DOs that have
|
|
|
|
* a TLV with the DO's id encompassing the
|
|
|
|
* entire blob. Example: Yubikey Neo */
|
|
|
|
if (tag == blob->id) {
|
|
|
|
in = data;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-05-31 19:00:42 +00:00
|
|
|
/* create fake file system hierarchy by
|
|
|
|
* using constructed DOs as DF */
|
|
|
|
if ((new = pgp_new_blob(card, blob, tag, sc_file_new())) == NULL)
|
2011-05-23 17:30:49 +00:00
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
pgp_set_blob(new, data, len);
|
|
|
|
in = data + len;
|
2003-10-31 16:01:00 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:31:24 +00:00
|
|
|
return SC_SUCCESS;
|
2003-10-31 16:01:00 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
|
|
|
* Internal: find a blob by ID below a given parent, filling its contents when necessary.
|
|
|
|
*/
|
2003-10-31 16:01:00 +00:00
|
|
|
static int
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_get_blob(sc_card_t *card, pgp_blob_t *blob, unsigned int id,
|
|
|
|
pgp_blob_t **ret)
|
2003-10-31 16:01:00 +00:00
|
|
|
{
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *child;
|
2003-10-31 16:01:00 +00:00
|
|
|
int r;
|
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
if ((r = pgp_enumerate_blob(card, blob)) < 0)
|
|
|
|
return r;
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
for (child = blob->files; child; child = child->next) {
|
2011-05-23 17:33:33 +00:00
|
|
|
if (child->id == id) {
|
|
|
|
(void) pgp_read_blob(card, child);
|
|
|
|
*ret = child;
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
2003-10-31 16:01:00 +00:00
|
|
|
}
|
|
|
|
|
2013-03-25 04:58:38 +00:00
|
|
|
/* This part is for "NOT FOUND" cases */
|
|
|
|
|
|
|
|
/* Special case:
|
|
|
|
* Gnuk does not have default value for children of DO 65 (DOs 5B, 5F2D, 5F35)
|
|
|
|
* So, if these blob was not found, we create it. */
|
2014-11-11 15:16:15 +00:00
|
|
|
if (blob->id == DO_CARDHOLDER && (id == DO_NAME || id == DO_LANG_PREF || id == DO_SEX)) {
|
2013-03-25 04:58:38 +00:00
|
|
|
sc_log(card->ctx, "Create blob %X under %X", id, blob->id);
|
|
|
|
child = pgp_new_blob(card, blob, id, sc_file_new());
|
|
|
|
if (child) {
|
|
|
|
pgp_set_blob(child, NULL, 0);
|
|
|
|
*ret = child;
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
else
|
2017-03-14 19:02:30 +00:00
|
|
|
sc_log(card->ctx,
|
|
|
|
"Not enough memory to create blob for DO %X",
|
|
|
|
id);
|
2013-03-25 04:58:38 +00:00
|
|
|
}
|
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
return SC_ERROR_FILE_NOT_FOUND;
|
2003-10-31 16:01:00 +00:00
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal: search recursively for a blob by ID below a given root.
|
|
|
|
*/
|
2012-06-01 15:45:12 +00:00
|
|
|
static int
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_seek_blob(sc_card_t *card, pgp_blob_t *root, unsigned int id,
|
|
|
|
pgp_blob_t **ret)
|
2012-06-01 15:45:12 +00:00
|
|
|
{
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *child;
|
2012-06-01 15:45:12 +00:00
|
|
|
int r;
|
|
|
|
|
|
|
|
if ((r = pgp_get_blob(card, root, id, ret)) == 0)
|
2015-09-19 14:52:51 +00:00
|
|
|
/* the sought blob is right under root */
|
2012-06-01 15:45:12 +00:00
|
|
|
return r;
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* not found, seek deeper */
|
2012-06-01 15:45:12 +00:00
|
|
|
for (child = root->files; child; child = child->next) {
|
|
|
|
/* The DO of SIMPLE type or the DO holding certificate
|
|
|
|
* does not contain children */
|
2018-01-05 10:08:14 +00:00
|
|
|
if ((child->info && child->info->type == SIMPLE) || child->id == DO_CERT)
|
2012-06-01 15:45:12 +00:00
|
|
|
continue;
|
|
|
|
r = pgp_seek_blob(card, child, id, ret);
|
|
|
|
if (r == 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SC_ERROR_FILE_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal: find a blob by tag - pgp_seek_blob with optimizations.
|
|
|
|
*/
|
2014-07-13 11:41:36 +00:00
|
|
|
static pgp_blob_t *
|
2012-06-02 06:51:09 +00:00
|
|
|
pgp_find_blob(sc_card_t *card, unsigned int tag)
|
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *blob = NULL;
|
2012-06-02 06:51:09 +00:00
|
|
|
int r;
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* check if current selected blob is which we want to test */
|
2012-06-02 06:51:09 +00:00
|
|
|
if (priv->current->id == tag) {
|
|
|
|
return priv->current;
|
|
|
|
}
|
2015-09-19 14:52:51 +00:00
|
|
|
/* look for the blob representing the DO */
|
2012-06-02 06:51:09 +00:00
|
|
|
r = pgp_seek_blob(card, priv->mf, tag, &blob);
|
|
|
|
if (r < 0) {
|
|
|
|
sc_log(card->ctx, "Failed to seek the blob representing the tag %04X. Error %d.", tag, r);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return blob;
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal: get info for a specific tag.
|
|
|
|
*/
|
2020-01-26 09:56:03 +00:00
|
|
|
static pgp_do_info_t *
|
2012-06-14 02:05:16 +00:00
|
|
|
pgp_get_info_by_tag(sc_card_t *card, unsigned int tag)
|
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2020-01-26 09:56:03 +00:00
|
|
|
pgp_do_info_t *info;
|
2012-06-14 02:05:16 +00:00
|
|
|
|
|
|
|
for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++)
|
|
|
|
if (tag == info->id)
|
|
|
|
return info;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2012-06-01 15:57:36 +00:00
|
|
|
/**
|
2015-09-19 14:52:51 +00:00
|
|
|
* Internal: strip out the parts of PKCS15 file layout in the path.
|
|
|
|
* Get the reduced version which is understood by the OpenPGP card driver.
|
2012-06-01 15:57:36 +00:00
|
|
|
* Return the index whose preceding part will be ignored.
|
|
|
|
**/
|
2015-09-19 14:52:51 +00:00
|
|
|
static unsigned int
|
|
|
|
pgp_strip_path(sc_card_t *card, const sc_path_t *path)
|
2012-06-01 15:57:36 +00:00
|
|
|
{
|
|
|
|
unsigned int start_point = 0;
|
|
|
|
/* start_point will move through the path string */
|
2015-01-28 06:39:35 +00:00
|
|
|
if (path->len == 0)
|
2012-06-01 15:57:36 +00:00
|
|
|
return 0;
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* ignore 3F00 (MF) at the beginning */
|
2012-06-01 15:57:36 +00:00
|
|
|
start_point = (memcmp(path->value, "\x3f\x00", 2) == 0) ? 2 : 0;
|
2015-09-19 14:52:51 +00:00
|
|
|
/* strip path of PKCS15-App DF (5015) */
|
2012-06-01 15:57:36 +00:00
|
|
|
start_point += (memcmp(path->value + start_point, "\x50\x15", 2) == 0) ? 2 : 0;
|
|
|
|
return start_point;
|
|
|
|
}
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: ISO 7816-4 SELECT FILE - search given file & make it the currently selected one.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2003-10-31 16:01:00 +00:00
|
|
|
static int
|
|
|
|
pgp_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **ret)
|
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *blob;
|
2012-06-01 15:57:36 +00:00
|
|
|
unsigned int path_start = 0;
|
2003-10-31 16:01:00 +00:00
|
|
|
unsigned int n;
|
2012-07-05 09:49:01 +00:00
|
|
|
sc_path_t dummy_path;
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2003-10-31 16:01:00 +00:00
|
|
|
if (path->type == SC_PATH_TYPE_DF_NAME)
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, iso_ops->select_file(card, path, ret));
|
|
|
|
|
2003-10-31 16:01:00 +00:00
|
|
|
if (path->len < 2 || (path->len & 1))
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"invalid path length");
|
|
|
|
|
2012-06-01 15:57:36 +00:00
|
|
|
if (path->type == SC_PATH_TYPE_FILE_ID && path->len != 2)
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"invalid path type");
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* Due to pkcs15init implementation, sometimes a file at path "11001101"
|
|
|
|
* need to be written (one use case is when importing key&cert from p12 file).
|
2012-07-05 09:49:01 +00:00
|
|
|
* This file does not exist in OpenPGP but pkcs15 requires that
|
2015-09-19 14:52:51 +00:00
|
|
|
* writing this file must be successful.
|
2012-07-05 09:49:01 +00:00
|
|
|
* So, we pretend that selecting & writing this file is successful.
|
|
|
|
* The "11001101"is defined in sc_pkcs15emu_get_df() function, pkcs15-sync.c file. */
|
|
|
|
sc_format_path("11001101", &dummy_path);
|
|
|
|
if (sc_compare_path(path, &dummy_path)) {
|
2012-07-17 13:24:42 +00:00
|
|
|
if (ret != NULL) {
|
|
|
|
*ret = sc_file_new();
|
|
|
|
/* One use case of this dummy file is after writing certificate in pkcs15init.
|
|
|
|
* So we set its size to be the same as max certificate size the card supports. */
|
|
|
|
(*ret)->size = priv->max_cert_size;
|
|
|
|
}
|
2012-07-05 09:49:01 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2018-04-14 17:38:34 +00:00
|
|
|
/* ignore explicitly mentioned MF at the path's beginning */
|
2012-06-01 15:57:36 +00:00
|
|
|
path_start = pgp_strip_path(card, path);
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2011-05-23 17:32:43 +00:00
|
|
|
/* starting with the MF ... */
|
2011-05-23 17:30:56 +00:00
|
|
|
blob = priv->mf;
|
2011-05-23 17:32:43 +00:00
|
|
|
/* ... recurse through the tree following the path */
|
|
|
|
for (n = path_start; n < path->len; n += 2) {
|
|
|
|
unsigned int id = bebytes2ushort(path->value + n);
|
|
|
|
int r = pgp_get_blob(card, blob, id, &blob);
|
|
|
|
|
2018-04-14 17:38:34 +00:00
|
|
|
/* This file ID is referred when importing key&certificate via pkcs15init, like above.
|
2012-07-17 13:24:42 +00:00
|
|
|
* We pretend to successfully find this inexistent file. */
|
|
|
|
if (id == 0x4402 || id == 0x5f48) {
|
|
|
|
if (ret == NULL)
|
|
|
|
/* No need to return file */
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
|
|
|
|
|
|
|
/* Else, need to return file */
|
|
|
|
*ret = sc_file_new();
|
|
|
|
(*ret)->size = priv->max_cert_size;
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:43 +00:00
|
|
|
if (r < 0) { /* failure */
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2003-10-31 16:01:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:43 +00:00
|
|
|
/* success: select file = set "current" pointer to blob found */
|
2003-10-31 16:01:00 +00:00
|
|
|
priv->current = blob;
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
sc_file_dup(ret, blob->file);
|
2011-05-23 17:32:21 +00:00
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
2003-10-31 16:01:00 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: ISO 7816-4 LIST FILES - enumerate all files in current DF.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2003-10-31 16:01:00 +00:00
|
|
|
static int
|
|
|
|
pgp_list_files(sc_card_t *card, u8 *buf, size_t buflen)
|
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *blob;
|
2003-10-31 16:01:00 +00:00
|
|
|
unsigned int k;
|
|
|
|
int r;
|
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2011-05-23 17:32:36 +00:00
|
|
|
/* jump to selected file */
|
2003-11-20 15:41:28 +00:00
|
|
|
blob = priv->current;
|
2011-05-23 17:32:36 +00:00
|
|
|
|
2003-10-31 16:01:00 +00:00
|
|
|
if (blob->file->type != SC_FILE_TYPE_DF)
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_OBJECT_NOT_VALID,
|
|
|
|
"invalid file type");
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
if ((r = pgp_enumerate_blob(card, blob)) < 0)
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2011-06-01 11:17:26 +00:00
|
|
|
for (k = 0, blob = blob->files; blob != NULL; blob = blob->next) {
|
|
|
|
if (blob->info != NULL && (blob->info->access & READ_MASK) != READ_NEVER) {
|
|
|
|
if (k + 2 > buflen)
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL);
|
|
|
|
|
|
|
|
ushort2bebytes(buf + k, blob->id);
|
|
|
|
k += 2;
|
|
|
|
}
|
2003-10-31 16:01:00 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, k);
|
2003-10-31 16:01:00 +00:00
|
|
|
}
|
|
|
|
|
2018-06-16 13:54:17 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* ABI: ISO 7816-4 GET CHALLENGE - generate random byte sequence.
|
|
|
|
*/
|
2018-01-18 11:33:34 +00:00
|
|
|
static int
|
|
|
|
pgp_get_challenge(struct sc_card *card, u8 *rnd, size_t len)
|
|
|
|
{
|
2018-01-23 12:53:23 +00:00
|
|
|
struct pgp_priv_data *priv;
|
2018-01-18 11:33:34 +00:00
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2018-01-23 12:53:23 +00:00
|
|
|
priv = DRVDATA(card);
|
2018-01-18 11:33:34 +00:00
|
|
|
if (0 == (priv->ext_caps & EXT_CAP_GET_CHALLENGE)) {
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (priv->max_challenge_size > 0 && len > priv->max_challenge_size) {
|
2018-01-23 12:53:23 +00:00
|
|
|
len = priv->max_challenge_size;
|
2018-01-18 11:33:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, iso_ops->get_challenge(card, rnd, len));
|
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: ISO 7816-4 READ BINARY - read data from currently selected EF.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2003-10-31 16:01:00 +00:00
|
|
|
static int
|
|
|
|
pgp_read_binary(sc_card_t *card, unsigned int idx,
|
|
|
|
u8 *buf, size_t count, unsigned long flags)
|
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *blob;
|
2003-11-20 15:41:28 +00:00
|
|
|
int r;
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2011-05-23 17:32:36 +00:00
|
|
|
/* jump to selected file */
|
|
|
|
blob = priv->current;
|
|
|
|
|
|
|
|
if (blob == NULL)
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND);
|
2003-10-31 16:01:00 +00:00
|
|
|
|
|
|
|
if (blob->file->type != SC_FILE_TYPE_WORKING_EF)
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND);
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
if ((r = pgp_read_blob(card, blob)) < 0)
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2003-11-20 15:41:28 +00:00
|
|
|
|
2003-10-31 16:01:00 +00:00
|
|
|
if (idx > blob->len)
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS);
|
2003-10-31 16:01:00 +00:00
|
|
|
|
|
|
|
if (idx + count > blob->len)
|
|
|
|
count = blob->len - idx;
|
|
|
|
memcpy(buf, blob->data + idx, count);
|
2011-05-23 17:32:21 +00:00
|
|
|
|
2015-08-08 06:17:12 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, (int)count);
|
2003-10-31 16:01:00 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* Internal: get public key from card - as DF + sub-wEFs.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2003-10-31 16:01:00 +00:00
|
|
|
static int
|
|
|
|
pgp_get_pubkey(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len)
|
|
|
|
{
|
|
|
|
sc_apdu_t apdu;
|
2015-09-14 20:01:00 +00:00
|
|
|
u8 apdu_case = (card->type == SC_CARD_TYPE_OPENPGP_GNUK)
|
|
|
|
? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_4;
|
2003-10-31 16:01:00 +00:00
|
|
|
u8 idbuf[2];
|
|
|
|
int r;
|
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
sc_log(card->ctx, "called, tag=%04x\n", tag);
|
2003-11-20 15:41:28 +00:00
|
|
|
|
2013-02-20 04:54:30 +00:00
|
|
|
sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x81, 0);
|
2003-10-31 16:01:00 +00:00
|
|
|
apdu.lc = 2;
|
2011-05-23 17:33:50 +00:00
|
|
|
apdu.data = ushort2bebytes(idbuf, tag);
|
2003-10-31 16:01:00 +00:00
|
|
|
apdu.datalen = 2;
|
2011-05-23 17:31:45 +00:00
|
|
|
apdu.le = ((buf_len >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : buf_len;
|
2003-10-31 16:01:00 +00:00
|
|
|
apdu.resp = buf;
|
|
|
|
apdu.resplen = buf_len;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
|
|
|
|
|
2003-10-31 16:01:00 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Card returned error");
|
2003-10-31 16:01:00 +00:00
|
|
|
|
2015-08-08 06:17:12 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen);
|
2003-10-31 16:01:00 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* Internal: get public key from card - as one wEF.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2003-11-20 15:41:28 +00:00
|
|
|
static int
|
|
|
|
pgp_get_pubkey_pem(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len)
|
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2020-01-21 15:43:56 +00:00
|
|
|
pgp_blob_t *blob, *mod_blob, *exp_blob, *pubkey_blob, *blob6e, *blob73, *aa_blob;
|
|
|
|
sc_pkcs15_pubkey_t p15pubkey;
|
|
|
|
sc_cardctl_openpgp_keygen_info_t key_info;
|
|
|
|
unsigned int aa_tag = 0;
|
|
|
|
u8 *data = NULL;
|
|
|
|
size_t len = 0;
|
2003-11-20 15:41:28 +00:00
|
|
|
int r;
|
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
sc_log(card->ctx, "called, tag=%04x\n", tag);
|
2020-01-21 15:43:56 +00:00
|
|
|
memset(&p15pubkey, 0, sizeof(p15pubkey));
|
2012-06-02 09:51:51 +00:00
|
|
|
|
2011-05-23 17:30:56 +00:00
|
|
|
if ((r = pgp_get_blob(card, priv->mf, tag & 0xFFFE, &blob)) < 0
|
2019-01-30 21:00:36 +00:00
|
|
|
|| (r = pgp_get_blob(card, blob, 0x7F49, &blob)) < 0)
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "error getting elements");
|
2003-11-20 15:41:28 +00:00
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
/* RSA */
|
2019-03-05 23:31:38 +00:00
|
|
|
if ((r = pgp_get_blob(card, blob, 0x0081, &mod_blob)) >= 0
|
|
|
|
&& (r = pgp_get_blob(card, blob, 0x0082, &exp_blob)) >= 0
|
|
|
|
&& (r = pgp_read_blob(card, mod_blob)) >= 0
|
|
|
|
&& (r = pgp_read_blob(card, exp_blob)) >= 0) {
|
2019-01-30 21:00:36 +00:00
|
|
|
|
2020-01-21 15:43:56 +00:00
|
|
|
p15pubkey.algorithm = SC_ALGORITHM_RSA;
|
|
|
|
p15pubkey.u.rsa.modulus.data = mod_blob->data;
|
|
|
|
p15pubkey.u.rsa.modulus.len = mod_blob->len;
|
|
|
|
p15pubkey.u.rsa.exponent.data = exp_blob->data;
|
|
|
|
p15pubkey.u.rsa.exponent.len = exp_blob->len;
|
|
|
|
r = sc_pkcs15_encode_pubkey(card->ctx, &p15pubkey, &data, &len);
|
2019-01-30 21:00:36 +00:00
|
|
|
}
|
|
|
|
/* ECC */
|
2019-03-05 23:31:38 +00:00
|
|
|
else if ((r = pgp_get_blob(card, blob, 0x0086, &pubkey_blob)) >= 0
|
|
|
|
&& (r = pgp_read_blob(card, pubkey_blob)) >= 0) {
|
2019-01-30 21:00:36 +00:00
|
|
|
|
2020-01-21 15:43:56 +00:00
|
|
|
p15pubkey.algorithm = SC_ALGORITHM_EC;
|
|
|
|
p15pubkey.u.ec.ecpointQ.value = pubkey_blob->data;
|
|
|
|
p15pubkey.u.ec.ecpointQ.len = pubkey_blob->len;
|
|
|
|
|
|
|
|
switch(tag & 0xFFFE) {
|
|
|
|
case DO_SIGN: aa_tag = 0x00C1; break;
|
|
|
|
case DO_ENCR: aa_tag = 0x00C2; break;
|
|
|
|
case DO_AUTH: aa_tag = 0x00C3; break;
|
|
|
|
default: r = SC_ERROR_INCORRECT_PARAMETERS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get EC parameters from Algorithm Attribute if present */
|
2019-01-30 21:00:36 +00:00
|
|
|
|
2020-01-21 15:43:56 +00:00
|
|
|
if (aa_tag && ((r = pgp_get_blob(card, priv->mf, 0x006e, &blob6e)) >= 0) &&
|
|
|
|
((r = pgp_get_blob(card, blob6e, 0x0073, &blob73)) >= 0) &&
|
|
|
|
((r = pgp_get_blob(card, blob73, aa_tag, &aa_blob)) >= 0) &&
|
2020-02-24 17:23:23 +00:00
|
|
|
((r = pgp_parse_algo_attr_blob(card, aa_blob, &key_info)) >= 0)) {
|
2019-01-30 21:00:36 +00:00
|
|
|
|
2020-01-21 15:43:56 +00:00
|
|
|
if ((r = sc_encode_oid(card->ctx, &key_info.u.ec.oid,
|
|
|
|
&p15pubkey.u.ec.params.der.value,
|
|
|
|
&p15pubkey.u.ec.params.der.len)) == 0) {
|
|
|
|
p15pubkey.u.ec.params.type = 1;
|
|
|
|
r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, &p15pubkey, &data, &len);
|
2020-02-24 17:23:23 +00:00
|
|
|
} else {
|
|
|
|
sc_log(card->ctx, "Unable to encode EC curve OID from algorithm info");
|
2020-01-21 15:43:56 +00:00
|
|
|
}
|
2020-02-24 17:23:23 +00:00
|
|
|
} else {
|
2020-01-21 15:43:56 +00:00
|
|
|
sc_log(card->ctx, "Unable to find Algorithm Attribute for EC curve OID");
|
2020-02-24 17:23:23 +00:00
|
|
|
}
|
|
|
|
} else {
|
2019-01-30 21:00:36 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "error getting elements");
|
2020-02-24 17:23:23 +00:00
|
|
|
}
|
2003-11-20 15:41:28 +00:00
|
|
|
|
2020-01-21 15:43:56 +00:00
|
|
|
/* clean up anything we may have set in p15pubkey that can not be freed */
|
|
|
|
if (p15pubkey.algorithm == SC_ALGORITHM_RSA) {
|
|
|
|
p15pubkey.u.rsa.modulus.data = NULL;
|
|
|
|
p15pubkey.u.rsa.modulus.len = 0;
|
|
|
|
p15pubkey.u.rsa.exponent.data = NULL;
|
|
|
|
p15pubkey.u.rsa.exponent.len = 0;
|
|
|
|
} else
|
|
|
|
if (p15pubkey.algorithm == SC_ALGORITHM_EC) {
|
|
|
|
p15pubkey.u.ec.ecpointQ.value = NULL;
|
|
|
|
p15pubkey.u.ec.ecpointQ.len = 0;
|
|
|
|
/* p15pubkey.u.ec.params.der and named_curve will be freed by sc_pkcs15_erase_pubkey */
|
|
|
|
}
|
|
|
|
sc_pkcs15_erase_pubkey(&p15pubkey);
|
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "public key encoding failed");
|
2003-11-20 15:41:28 +00:00
|
|
|
|
|
|
|
if (len > buf_len)
|
|
|
|
len = buf_len;
|
|
|
|
memcpy(buf, data, len);
|
|
|
|
free(data);
|
2011-05-23 17:32:21 +00:00
|
|
|
|
2015-08-08 06:17:12 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, (int)len);
|
2003-11-20 15:41:28 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: ISO 7816-4 GET DATA - get contents of a DO.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2003-10-30 17:04:22 +00:00
|
|
|
static int
|
|
|
|
pgp_get_data(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len)
|
|
|
|
{
|
|
|
|
sc_apdu_t apdu;
|
|
|
|
int r;
|
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2011-05-23 17:31:50 +00:00
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0xCA, tag >> 8, tag);
|
|
|
|
apdu.le = ((buf_len >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : buf_len;
|
2003-10-30 17:04:22 +00:00
|
|
|
apdu.resp = buf;
|
|
|
|
apdu.resplen = buf_len;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
|
|
|
|
|
2003-10-30 17:04:22 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2013-03-25 04:58:38 +00:00
|
|
|
|
2018-10-13 19:36:48 +00:00
|
|
|
/* Gnuk returns an error instead of empty data if there is no certificate or private DO.
|
2013-03-25 04:58:38 +00:00
|
|
|
* So, for this case, we ignore error and consider success */
|
2018-10-13 19:36:48 +00:00
|
|
|
if (card->type == SC_CARD_TYPE_OPENPGP_GNUK &&
|
|
|
|
(tag == DO_CERT ||
|
|
|
|
tag == DO_PRIV1 ||
|
|
|
|
tag == DO_PRIV2 ||
|
|
|
|
tag == DO_PRIV3 ||
|
|
|
|
tag == DO_PRIV4)) {
|
|
|
|
if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) {
|
|
|
|
r = SC_SUCCESS;
|
|
|
|
apdu.resplen = 0;
|
|
|
|
}
|
2013-03-25 04:58:38 +00:00
|
|
|
}
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Card returned error");
|
2003-10-30 17:04:22 +00:00
|
|
|
|
2015-08-08 06:17:12 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen);
|
2003-10-30 17:04:22 +00:00
|
|
|
}
|
|
|
|
|
2013-04-11 04:47:51 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
|
|
|
* Internal: write certificate for Gnuk.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
gnuk_write_certificate(sc_card_t *card, const u8 *buf, size_t length)
|
2003-10-30 17:04:22 +00:00
|
|
|
{
|
2013-04-11 04:47:51 +00:00
|
|
|
size_t i = 0;
|
2012-06-01 15:40:39 +00:00
|
|
|
sc_apdu_t apdu;
|
2013-04-11 04:47:51 +00:00
|
|
|
int r = SC_SUCCESS;
|
|
|
|
|
2015-09-14 20:27:43 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
2013-04-11 04:47:51 +00:00
|
|
|
|
|
|
|
/* If null data is passed, delete certificate */
|
|
|
|
if (buf == NULL || length == 0) {
|
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xD6, 0x85, 0);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
|
|
|
|
/* Check response */
|
2019-01-29 10:30:22 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2));
|
2013-04-11 04:47:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Ref: gnuk_put_binary_libusb.py and gnuk_token.py in Gnuk source tree */
|
|
|
|
/* Split data to segments of 256 bytes. Send each segment via command chaining,
|
|
|
|
* with particular P1 byte for each segment */
|
2015-09-14 20:27:43 +00:00
|
|
|
for (i = 0; i*256 < length; i++) {
|
|
|
|
u8 *part = (u8 *)buf + i*256;
|
|
|
|
size_t plen = MIN(length - i*256, 256);
|
|
|
|
u8 roundbuf[256]; /* space to build APDU data with even length for Gnuk */
|
2013-04-11 04:47:51 +00:00
|
|
|
|
2017-03-14 19:02:30 +00:00
|
|
|
sc_log(card->ctx,
|
|
|
|
"Write part %"SC_FORMAT_LEN_SIZE_T"u from offset 0x%"SC_FORMAT_LEN_SIZE_T"X, len %"SC_FORMAT_LEN_SIZE_T"u",
|
|
|
|
i+1, i*256, plen);
|
2013-04-11 04:47:51 +00:00
|
|
|
|
2015-09-14 20:27:43 +00:00
|
|
|
/* 1st chunk: P1 = 0x85, further chunks: P1 = chunk no */
|
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, (i == 0) ? 0x85 : i, 0);
|
2013-04-11 04:47:51 +00:00
|
|
|
apdu.flags |= SC_APDU_FLAGS_CHAINING;
|
2015-09-14 20:27:43 +00:00
|
|
|
apdu.data = part;
|
|
|
|
apdu.datalen = apdu.lc = plen;
|
2013-04-11 09:18:31 +00:00
|
|
|
|
|
|
|
/* If the last part has odd length, we add zero padding to make it even.
|
|
|
|
* Gnuk does not allow data with odd length */
|
|
|
|
if (plen < 256 && (plen % 2) != 0) {
|
|
|
|
memcpy(roundbuf, part, plen);
|
2015-09-14 20:27:43 +00:00
|
|
|
roundbuf[plen++] = 0;
|
2013-04-11 09:18:31 +00:00
|
|
|
apdu.data = roundbuf;
|
|
|
|
apdu.datalen = apdu.lc = plen;
|
|
|
|
}
|
2013-04-11 04:47:51 +00:00
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
|
|
|
|
/* Check response */
|
2013-04-16 09:02:17 +00:00
|
|
|
LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "UPDATE BINARY returned error");
|
2013-04-11 04:47:51 +00:00
|
|
|
}
|
2015-09-14 20:27:43 +00:00
|
|
|
|
2015-08-08 06:17:12 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, (int)length);
|
2013-04-11 04:47:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
|
|
|
* Internal: use PUT DATA command to write.
|
|
|
|
*/
|
2013-04-11 04:47:51 +00:00
|
|
|
static int
|
|
|
|
pgp_put_data_plain(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len)
|
|
|
|
{
|
2012-06-01 15:40:39 +00:00
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2013-04-11 04:47:51 +00:00
|
|
|
sc_apdu_t apdu;
|
2012-05-30 10:31:39 +00:00
|
|
|
u8 ins = 0xDA;
|
|
|
|
u8 p1 = tag >> 8;
|
2012-06-07 17:31:02 +00:00
|
|
|
u8 p2 = tag & 0xFF;
|
2015-09-14 20:01:00 +00:00
|
|
|
u8 apdu_case = (card->type == SC_CARD_TYPE_OPENPGP_GNUK)
|
|
|
|
? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_3;
|
2012-06-01 15:40:39 +00:00
|
|
|
int r;
|
|
|
|
|
2015-09-14 20:59:19 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
2012-05-30 10:13:36 +00:00
|
|
|
|
2018-06-16 13:54:17 +00:00
|
|
|
/* Extended Header list (DO 004D) needs a variant of PUT DATA command */
|
2012-05-30 10:31:39 +00:00
|
|
|
if (tag == 0x004D) {
|
|
|
|
ins = 0xDB;
|
|
|
|
p1 = 0x3F;
|
|
|
|
p2 = 0xFF;
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* build APDU */
|
2012-06-07 17:31:02 +00:00
|
|
|
if (buf != NULL && buf_len > 0) {
|
2013-02-20 04:54:30 +00:00
|
|
|
sc_format_apdu(card, &apdu, apdu_case, ins, p1, p2);
|
2012-06-07 17:31:02 +00:00
|
|
|
|
|
|
|
/* if card/reader does not support extended APDUs, but chaining, then set it */
|
|
|
|
if (((card->caps & SC_CARD_CAP_APDU_EXT) == 0) && (priv->ext_caps & EXT_CAP_CHAINING))
|
|
|
|
apdu.flags |= SC_APDU_FLAGS_CHAINING;
|
|
|
|
|
2013-02-20 04:54:30 +00:00
|
|
|
apdu.data = (u8 *)buf;
|
2012-06-07 17:31:02 +00:00
|
|
|
apdu.datalen = buf_len;
|
|
|
|
apdu.lc = buf_len;
|
2012-06-01 15:40:39 +00:00
|
|
|
}
|
2012-06-01 17:57:26 +00:00
|
|
|
else {
|
2013-04-11 04:47:51 +00:00
|
|
|
/* This case is to empty DO */
|
2012-06-07 17:31:02 +00:00
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, ins, p1, p2);
|
2012-06-01 17:57:26 +00:00
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* send APDU to card */
|
2012-06-01 15:40:39 +00:00
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2015-09-14 20:59:19 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
|
2015-09-19 14:52:51 +00:00
|
|
|
/* check response */
|
2012-06-01 15:40:39 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2018-10-28 10:35:32 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Card returned error");
|
2013-04-11 04:47:51 +00:00
|
|
|
|
2015-09-14 20:59:19 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, (int)buf_len);
|
2013-04-11 04:47:51 +00:00
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: ISO 7816-4 PUT DATA - write contents of a DO.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2013-04-11 04:47:51 +00:00
|
|
|
static int
|
|
|
|
pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len)
|
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *affected_blob = NULL;
|
2020-01-26 09:56:03 +00:00
|
|
|
pgp_do_info_t *dinfo = NULL;
|
2013-04-11 04:47:51 +00:00
|
|
|
int r;
|
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2021-01-31 18:30:56 +00:00
|
|
|
/* Check if there is a blob for the given tag */
|
|
|
|
affected_blob = pgp_find_blob(card, tag);
|
2013-04-11 04:47:51 +00:00
|
|
|
|
|
|
|
/* Non-readable DOs have no represented blob, we have to check from pgp_get_info_by_tag */
|
|
|
|
if (affected_blob == NULL)
|
|
|
|
dinfo = pgp_get_info_by_tag(card, tag);
|
|
|
|
else
|
|
|
|
dinfo = affected_blob->info;
|
|
|
|
|
2021-01-31 18:30:56 +00:00
|
|
|
/* Make sure the DO exists and is writeable */
|
2013-04-11 04:47:51 +00:00
|
|
|
if (dinfo == NULL) {
|
|
|
|
sc_log(card->ctx, "The DO %04X does not exist.", tag);
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
|
|
|
|
}
|
|
|
|
else if ((dinfo->access & WRITE_MASK) == WRITE_NEVER) {
|
|
|
|
sc_log(card->ctx, "DO %04X is not writable.", tag);
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ALLOWED);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check data size.
|
|
|
|
* We won't check other DOs than 7F21 (certificate), because their capacity
|
2015-09-19 14:52:51 +00:00
|
|
|
* is hard-coded and may change in various version of the card.
|
|
|
|
* If we check here, the driver may be stuck to a limit version number of card.
|
2013-04-11 04:47:51 +00:00
|
|
|
* 7F21 size is soft-coded, so we can check it. */
|
|
|
|
if (tag == DO_CERT && buf_len > priv->max_cert_size) {
|
2017-03-14 19:02:30 +00:00
|
|
|
sc_log(card->ctx,
|
|
|
|
"Data size %"SC_FORMAT_LEN_SIZE_T"u exceeds DO size limit %"SC_FORMAT_LEN_SIZE_T"u.",
|
|
|
|
buf_len, priv->max_cert_size);
|
2013-04-11 04:47:51 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tag == DO_CERT && card->type == SC_CARD_TYPE_OPENPGP_GNUK) {
|
|
|
|
/* Gnuk need a special way to write certificate. */
|
|
|
|
r = gnuk_write_certificate(card, buf, buf_len);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
r = pgp_put_data_plain(card, tag, buf, buf_len);
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* instruct more in case of error */
|
2012-06-01 15:40:39 +00:00
|
|
|
if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) {
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Please verify PIN first.");
|
|
|
|
}
|
2012-06-07 17:31:02 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "PUT DATA returned error");
|
2012-06-01 15:40:39 +00:00
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
if (affected_blob) {
|
2015-09-19 14:52:51 +00:00
|
|
|
/* update the corresponding file */
|
2012-06-14 02:05:16 +00:00
|
|
|
sc_log(card->ctx, "Updating the corresponding blob data");
|
|
|
|
r = pgp_set_blob(affected_blob, buf, buf_len);
|
|
|
|
if (r < 0)
|
|
|
|
sc_log(card->ctx, "Failed to update blob %04X. Error %d.", affected_blob->id, r);
|
|
|
|
/* pgp_set_blob()'s failures do not impact pgp_put_data()'s result */
|
|
|
|
}
|
2012-06-01 15:40:39 +00:00
|
|
|
|
2015-08-08 06:17:12 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, (int)buf_len);
|
2003-10-30 17:04:22 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: ISO 7816-9 PIN CMD - verify/change/unblock a PIN.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2003-10-30 17:04:22 +00:00
|
|
|
static int
|
|
|
|
pgp_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
|
|
|
|
{
|
2016-08-13 23:46:55 +00:00
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2003-10-30 17:04:22 +00:00
|
|
|
if (data->pin_type != SC_AC_CHV)
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"invalid PIN type");
|
2003-10-30 17:04:22 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* In general, the PIN Reference is extracted from the key-id,
|
|
|
|
* for example, CHV0 -> Ref=0, CHV1 -> Ref=1.
|
2020-02-28 09:53:59 +00:00
|
|
|
* However, in the case of OpenPGP, the PIN Ref to compose APDU
|
2012-06-01 15:41:17 +00:00
|
|
|
* must be 81, 82, 83.
|
|
|
|
* So, if we receive Ref=1, Ref=2, we must convert to 81, 82...
|
2015-09-19 14:52:51 +00:00
|
|
|
* In OpenPGP v1, the PINs are named CHV1, CHV2, CHV3.
|
|
|
|
* In v2, they are named PW1, PW3 (PW1 operates in 2 modes).
|
2016-08-13 23:46:55 +00:00
|
|
|
*
|
|
|
|
* The PIN references (P2 in APDU) for "VERIFY" are the same in both versions:
|
|
|
|
* 81 (CHV1 or PW1), 82 (CHV2 or PW1-mode 2), 83 (CHV3 or PW3),
|
|
|
|
* On the other hand from version 2.0 "CHANGE REFERENCE DATA" and
|
|
|
|
* "RESET RETRY COUNTER" don't support PW1-mode 2 (82) and need this
|
|
|
|
* value changed to PW1 (81).
|
|
|
|
* Both of these commands also differ between card versions in that
|
|
|
|
* v1 cards can use only implicit old PIN or CHV3 test for both commands
|
|
|
|
* whereas v2 can use both implicit (for PW3) and explicit
|
|
|
|
* (for special "Resetting Code") PIN test for "RESET RETRY COUNTER"
|
|
|
|
* and only explicit test for "CHANGE REFERENCE DATA".
|
2012-06-01 15:41:17 +00:00
|
|
|
*
|
|
|
|
* Note that if this function is called from sc_pkcs15_verify_pin() in pkcs15-pin.c,
|
|
|
|
* the Ref is already 81, 82, 83.
|
|
|
|
*/
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* convert the PIN Reference if needed */
|
2012-06-01 15:41:17 +00:00
|
|
|
data->pin_reference |= 0x80;
|
2016-08-13 23:46:55 +00:00
|
|
|
|
|
|
|
/* check version-dependent constraints */
|
|
|
|
if (data->cmd == SC_PIN_CMD_CHANGE || data->cmd == SC_PIN_CMD_UNBLOCK) {
|
|
|
|
if (priv->bcd_version >= OPENPGP_CARD_2_0) {
|
|
|
|
if (data->pin_reference == 0x82)
|
|
|
|
data->pin_reference = 0x81;
|
|
|
|
|
|
|
|
if (data->cmd == SC_PIN_CMD_CHANGE) {
|
|
|
|
if (data->pin1.len == 0 &&
|
|
|
|
!(data->flags & SC_PIN_CMD_USE_PINPAD))
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
2019-01-01 17:42:51 +00:00
|
|
|
"v2 cards don't support implicit old PIN for PIN change");
|
2016-08-13 23:46:55 +00:00
|
|
|
|
|
|
|
data->flags &= ~SC_PIN_CMD_IMPLICIT_CHANGE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (data->pin1.len != 0) {
|
|
|
|
sc_log(card->ctx,
|
|
|
|
"v1 cards don't support explicit old or CHV3 PIN, PIN ignored.");
|
|
|
|
sc_log(card->ctx,
|
|
|
|
"please make sure that you have verified the relevant PIN first.");
|
|
|
|
data->pin1.len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->flags |= SC_PIN_CMD_IMPLICIT_CHANGE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->cmd == SC_PIN_CMD_UNBLOCK && data->pin2.len == 0 &&
|
|
|
|
!(data->flags & SC_PIN_CMD_USE_PINPAD))
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
2019-01-01 17:42:51 +00:00
|
|
|
"new PIN must be provided for unblock operation");
|
2016-08-13 23:46:55 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* ensure pin_reference is 81, 82, 83 */
|
2019-01-01 18:22:29 +00:00
|
|
|
if (data->pin_reference < 0x81 || data->pin_reference > 0x83)
|
2012-06-01 15:41:17 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
2019-01-01 18:22:29 +00:00
|
|
|
"Invalid key ID; must be 1, 2, or 3");
|
2018-10-27 15:31:54 +00:00
|
|
|
|
|
|
|
/* emulate SC_PIN_CMD_GET_INFO command for cards not supporting it */
|
|
|
|
if (data->cmd == SC_PIN_CMD_GET_INFO && (card->caps & SC_CARD_CAP_ISO7816_PIN_INFO) == 0) {
|
|
|
|
u8 c4data[10];
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = sc_get_data(card, 0x00c4, c4data, sizeof(c4data));
|
|
|
|
LOG_TEST_RET(card->ctx, r, "reading CHV status bytes failed");
|
|
|
|
|
|
|
|
if (r != 7)
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_OBJECT_NOT_VALID,
|
|
|
|
"CHV status bytes have unexpected length");
|
|
|
|
|
|
|
|
data->pin1.tries_left = c4data[4 + (data->pin_reference & 0x0F)];
|
|
|
|
data->pin1.max_tries = 3;
|
|
|
|
data->pin1.logged_in = SC_PIN_STATE_UNKNOWN;
|
|
|
|
if (tries_left != NULL)
|
|
|
|
*tries_left = data->pin1.tries_left;
|
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data, tries_left));
|
2003-10-30 17:04:22 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2018-06-16 13:54:17 +00:00
|
|
|
/**
|
|
|
|
* ABI: ISO 7816-8 LOGOUT - reset all access rights gained.
|
|
|
|
*/
|
2018-01-17 20:11:16 +00:00
|
|
|
int pgp_logout(struct sc_card *card)
|
|
|
|
{
|
|
|
|
int r = SC_SUCCESS;
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
|
|
|
if (priv->bcd_version >= OPENPGP_CARD_3_1) {
|
|
|
|
unsigned char pin_reference;
|
|
|
|
for (pin_reference = 0x81; pin_reference <= 0x83; pin_reference++) {
|
|
|
|
int tmp = iso7816_logout(card, pin_reference);
|
|
|
|
if (r == SC_SUCCESS) {
|
|
|
|
r = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sc_path_t path;
|
|
|
|
sc_file_t *file = NULL;
|
|
|
|
|
|
|
|
/* select application "OpenPGP" */
|
|
|
|
sc_format_path("D276:0001:2401", &path);
|
|
|
|
path.type = SC_PATH_TYPE_DF_NAME;
|
|
|
|
r = iso_ops->select_file(card, &path, &file);
|
|
|
|
sc_file_free(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: ISO 7816-8 SET SECURITY ENVIRONMENT.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2003-10-30 17:04:22 +00:00
|
|
|
static int
|
|
|
|
pgp_set_security_env(sc_card_t *card,
|
2003-11-20 15:41:28 +00:00
|
|
|
const sc_security_env_t *env, int se_num)
|
2003-10-30 17:04:22 +00:00
|
|
|
{
|
2003-11-20 15:41:28 +00:00
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
if ((env->flags & SC_SEC_ENV_ALG_PRESENT)
|
|
|
|
&& (env->algorithm != SC_ALGORITHM_RSA)
|
|
|
|
&& (priv->bcd_version < OPENPGP_CARD_3_0))
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"only RSA algorithm supported");
|
|
|
|
|
|
|
|
if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || (env->key_ref_len != 1))
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"exactly one key reference required");
|
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT)
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"passing file references not supported");
|
2003-11-20 15:41:28 +00:00
|
|
|
|
2012-07-17 13:24:42 +00:00
|
|
|
sc_log(card->ctx, "Key ref %d", env->key_ref[0]);
|
2003-11-20 15:41:28 +00:00
|
|
|
switch (env->operation) {
|
|
|
|
case SC_SEC_OPERATION_SIGN:
|
2012-07-17 13:24:42 +00:00
|
|
|
sc_log(card->ctx, "Operation: Sign.");
|
2011-05-23 17:32:21 +00:00
|
|
|
if (env->key_ref[0] != 0x00 && env->key_ref[0] != 0x02) {
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED,
|
2003-11-20 15:41:28 +00:00
|
|
|
"Key reference not compatible with "
|
2011-05-23 17:32:21 +00:00
|
|
|
"requested usage");
|
2003-11-20 15:41:28 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SC_SEC_OPERATION_DECIPHER:
|
2012-07-17 13:24:42 +00:00
|
|
|
sc_log(card->ctx, "Operation: Decipher.");
|
2015-09-19 14:52:51 +00:00
|
|
|
/* we allow key ref 2 (auth key) to be used for deciphering */
|
2012-07-17 13:24:42 +00:00
|
|
|
if (env->key_ref[0] != 0x01 && env->key_ref[0] != 0x02) {
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED,
|
2003-11-20 15:41:28 +00:00
|
|
|
"Key reference not compatible with "
|
2011-05-23 17:32:21 +00:00
|
|
|
"requested usage");
|
2003-11-20 15:41:28 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"invalid operation");
|
2003-11-20 15:41:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
priv->sec_env = *env;
|
2011-05-23 17:32:21 +00:00
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
2003-10-30 17:04:22 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2019-02-02 15:09:16 +00:00
|
|
|
/**
|
2018-09-06 08:57:23 +00:00
|
|
|
* set MANAGE SECURITY ENVIRONMENT as documented in 7.2.18 since OpenPGP Card v3.3
|
|
|
|
*
|
|
|
|
* "This optional command (announced in Extended Capabilities) assigns a specific key to a
|
|
|
|
* command. The DEC-key (Key-Ref 2) can be assigned to the command INTERNAL AUTHENTICATE
|
|
|
|
* and the AUT-Key (Key.Ref 3) can be linked to the command PSO:DECIPHER also."
|
|
|
|
*
|
2019-02-02 15:09:16 +00:00
|
|
|
* key: Key-Ref to change (2 for DEC-Key or 3 for AUT-Key)
|
2018-09-06 08:57:23 +00:00
|
|
|
* p2: Usage to set (0xb8 for PSO:DECIPHER or 0xa4 for INTERNAL AUTHENTICATE)
|
|
|
|
**/
|
|
|
|
static int
|
|
|
|
pgp_set_MSE(sc_card_t *card, int key, u8 p2)
|
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
|
|
|
sc_apdu_t apdu;
|
|
|
|
u8 apdu_case = SC_APDU_CASE_3;
|
|
|
|
u8 apdu_data[3];
|
|
|
|
int r;
|
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
|
|
|
// check if MSE is supported
|
|
|
|
if (!(priv->ext_caps & EXT_CAP_MSE))
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
|
|
|
|
|
|
|
// create apdu
|
|
|
|
sc_format_apdu(card, &apdu, apdu_case, 0x22, 0x41, p2);
|
|
|
|
apdu.lc = 3;
|
|
|
|
apdu_data[0] = 0x83;
|
|
|
|
apdu_data[1] = 0x01;
|
|
|
|
apdu_data[2] = key;
|
|
|
|
apdu.data = apdu_data;
|
|
|
|
apdu.datalen = 3;
|
|
|
|
|
|
|
|
// transmit apdu
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
|
|
|
|
|
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
|
LOG_TEST_RET(card->ctx, r, "Card returned error");
|
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: ISO 7816-8 COMPUTE DIGITAL SIGNATURE.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2003-10-30 17:04:22 +00:00
|
|
|
static int
|
|
|
|
pgp_compute_signature(sc_card_t *card, const u8 *data,
|
|
|
|
size_t data_len, u8 * out, size_t outlen)
|
|
|
|
{
|
2003-11-20 15:41:28 +00:00
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
|
|
|
sc_security_env_t *env = &priv->sec_env;
|
|
|
|
sc_apdu_t apdu;
|
2015-09-14 20:01:00 +00:00
|
|
|
u8 apdu_case = (card->type == SC_CARD_TYPE_OPENPGP_GNUK)
|
|
|
|
? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_4;
|
2003-11-20 15:41:28 +00:00
|
|
|
int r;
|
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
if (env->operation != SC_SEC_OPERATION_SIGN)
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"invalid operation");
|
2003-11-20 15:41:28 +00:00
|
|
|
|
|
|
|
switch (env->key_ref[0]) {
|
|
|
|
case 0x00: /* signature key */
|
|
|
|
/* PSO SIGNATURE */
|
2013-02-20 04:54:30 +00:00
|
|
|
sc_format_apdu(card, &apdu, apdu_case, 0x2A, 0x9E, 0x9A);
|
2003-11-20 15:41:28 +00:00
|
|
|
break;
|
|
|
|
case 0x02: /* authentication key */
|
|
|
|
/* INTERNAL AUTHENTICATE */
|
2013-02-20 04:54:30 +00:00
|
|
|
sc_format_apdu(card, &apdu, apdu_case, 0x88, 0, 0);
|
2003-11-20 15:41:28 +00:00
|
|
|
break;
|
|
|
|
case 0x01:
|
|
|
|
default:
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"invalid key reference");
|
2003-11-20 15:41:28 +00:00
|
|
|
}
|
|
|
|
|
2015-08-01 09:25:31 +00:00
|
|
|
/* if card/reader does not support extended APDUs, but chaining, then set it */
|
|
|
|
if (((card->caps & SC_CARD_CAP_APDU_EXT) == 0) && (priv->ext_caps & EXT_CAP_CHAINING))
|
|
|
|
apdu.flags |= SC_APDU_FLAGS_CHAINING;
|
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
apdu.lc = data_len;
|
2013-02-20 04:54:30 +00:00
|
|
|
apdu.data = (u8 *)data;
|
2003-11-20 15:41:28 +00:00
|
|
|
apdu.datalen = data_len;
|
2011-05-23 17:31:50 +00:00
|
|
|
apdu.le = ((outlen >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : outlen;
|
2006-05-05 10:06:38 +00:00
|
|
|
apdu.resp = out;
|
2003-11-20 15:41:28 +00:00
|
|
|
apdu.resplen = outlen;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
|
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Card returned error");
|
2003-11-20 15:41:28 +00:00
|
|
|
|
2015-08-08 06:17:12 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen);
|
2003-11-20 15:41:28 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: ISO 7816-8 DECIPHER - perform deciphering operation.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2003-11-20 15:41:28 +00:00
|
|
|
static int
|
|
|
|
pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen,
|
|
|
|
u8 *out, size_t outlen)
|
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
|
|
|
sc_security_env_t *env = &priv->sec_env;
|
|
|
|
sc_apdu_t apdu;
|
2013-02-20 04:54:30 +00:00
|
|
|
u8 apdu_case = SC_APDU_CASE_4;
|
2003-11-20 15:41:28 +00:00
|
|
|
u8 *temp = NULL;
|
|
|
|
int r;
|
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2018-06-10 09:58:51 +00:00
|
|
|
/* padding according to OpenPGP card spec 1.1 & 2.x section 7.2.9 / 3.x section 7.2.11 */
|
Do not cast the return value of malloc(3) and calloc(3)
From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety
" Casting and type safety
malloc returns a void pointer (void *), which indicates that it is a
pointer to a region of unknown data type. One may "cast" (see type
conversion) this pointer to a specific type, as in
int *ptr = (int*)malloc(10 * sizeof (int));
When using C, this is considered bad practice; it is redundant under the
C standard. Moreover, putting in a cast may mask failure to include the
header stdlib.h, in which the prototype for malloc is found. In the
absence of a prototype for malloc, the C compiler will assume that
malloc returns an int, and will issue a warning in a context such as the
above, provided the error is not masked by a cast. On certain
architectures and data models (such as LP64 on 64 bit systems, where
long and pointers are 64 bit and int is 32 bit), this error can actually
result in undefined behavior, as the implicitly declared malloc returns
a 32 bit value whereas the actually defined function returns a 64 bit
value. Depending on calling conventions and memory layout, this may
result in stack smashing.
The returned pointer need not be explicitly cast to a more specific
pointer type, since ANSI C defines an implicit conversion between the
void pointer type and other pointers to objects. An explicit cast of
malloc's return value is sometimes performed because malloc originally
returned a char *, but this cast is unnecessary in standard C
code.[4][5] Omitting the cast, however, creates an incompatibility with
C++, which does require it.
The lack of a specific pointer type returned from malloc is type-unsafe
behaviour: malloc allocates based on byte count but not on type. This
distinguishes it from the C++ new operator that returns a pointer whose
type relies on the operand. (see C Type Safety). "
See also
http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
|
|
|
if (!(temp = malloc(inlen + 1)))
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
|
2019-01-30 21:00:36 +00:00
|
|
|
/* padding byte: 0xa6 = ECC; 0x00 = RSA; 0x02 = AES */
|
|
|
|
(env->algorithm == SC_ALGORITHM_EC) ? (temp[0] = 0xa6) : (temp[0] = 0x00);
|
2005-12-05 21:39:58 +00:00
|
|
|
memcpy(temp + 1, in, inlen);
|
|
|
|
in = temp;
|
|
|
|
inlen += 1;
|
2003-11-20 15:41:28 +00:00
|
|
|
|
2005-12-05 21:39:58 +00:00
|
|
|
if (env->operation != SC_SEC_OPERATION_DECIPHER) {
|
|
|
|
free(temp);
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"invalid operation");
|
2005-12-05 21:39:58 +00:00
|
|
|
}
|
2003-11-20 15:41:28 +00:00
|
|
|
|
|
|
|
switch (env->key_ref[0]) {
|
|
|
|
case 0x01: /* Decryption key */
|
2012-07-17 13:24:42 +00:00
|
|
|
case 0x02: /* authentication key */
|
2003-11-20 15:41:28 +00:00
|
|
|
/* PSO DECIPHER */
|
2013-02-20 04:54:30 +00:00
|
|
|
sc_format_apdu(card, &apdu, apdu_case, 0x2A, 0x80, 0x86);
|
2003-11-20 15:41:28 +00:00
|
|
|
break;
|
|
|
|
case 0x00: /* signature key */
|
|
|
|
default:
|
2005-12-05 21:39:58 +00:00
|
|
|
free(temp);
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"invalid key reference");
|
2003-11-20 15:41:28 +00:00
|
|
|
}
|
|
|
|
|
2013-02-20 04:54:30 +00:00
|
|
|
/* Gnuk only supports short APDU, so we need to use command chaining */
|
|
|
|
if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) {
|
|
|
|
apdu.flags |= SC_APDU_FLAGS_CHAINING;
|
|
|
|
}
|
2015-08-01 09:25:31 +00:00
|
|
|
/* if card/reader does not support extended APDUs, but chaining, then set it */
|
|
|
|
if (((card->caps & SC_CARD_CAP_APDU_EXT) == 0) && (priv->ext_caps & EXT_CAP_CHAINING))
|
|
|
|
apdu.flags |= SC_APDU_FLAGS_CHAINING;
|
2013-02-20 04:54:30 +00:00
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
apdu.lc = inlen;
|
2013-02-20 04:54:30 +00:00
|
|
|
apdu.data = (u8 *)in;
|
2003-11-20 15:41:28 +00:00
|
|
|
apdu.datalen = inlen;
|
2011-05-23 17:31:50 +00:00
|
|
|
apdu.le = ((outlen >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : outlen;
|
2003-11-20 15:41:28 +00:00
|
|
|
apdu.resp = out;
|
|
|
|
apdu.resplen = outlen;
|
|
|
|
|
2018-09-06 08:57:23 +00:00
|
|
|
/* For OpenPGP Card >=v3.3, key slot 3 instead of 2 can be used for deciphering,
|
|
|
|
* but this has to be set via MSE beforehand on every usage (slot 2 is used by default)
|
|
|
|
* see section 7.2.18 of the specification of OpenPGP Card v3.3 */
|
|
|
|
if (priv->bcd_version >= OPENPGP_CARD_3_3 && env->key_ref[0] == 0x02){
|
|
|
|
pgp_set_MSE(card, 3, 0xb8);
|
|
|
|
}
|
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2005-12-05 21:39:58 +00:00
|
|
|
free(temp);
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
|
2005-12-05 21:39:58 +00:00
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Card returned error");
|
2003-11-20 15:41:28 +00:00
|
|
|
|
2018-09-06 08:57:23 +00:00
|
|
|
/* For OpenPGP Card >=v3.3, use key slot 2 for deciphering again (set to default) */
|
|
|
|
if (priv->bcd_version >= OPENPGP_CARD_3_3 && env->key_ref[0] == 0x02){
|
|
|
|
pgp_set_MSE(card, 2, 0xb8);
|
|
|
|
}
|
|
|
|
|
2015-08-08 06:17:12 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen);
|
2003-10-30 17:04:22 +00:00
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
#ifdef ENABLE_OPENSSL
|
2012-06-02 10:09:12 +00:00
|
|
|
/**
|
2015-09-19 14:52:51 +00:00
|
|
|
* Internal: update algorithm attribute for new key size (before generating key).
|
2012-06-02 10:09:12 +00:00
|
|
|
**/
|
|
|
|
static int
|
2012-06-14 02:05:16 +00:00
|
|
|
pgp_update_new_algo_attr(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info)
|
2012-06-02 10:09:12 +00:00
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *algo_blob;
|
2018-09-29 17:25:36 +00:00
|
|
|
const unsigned int tag = 0x00C0 | key_info->key_id;
|
2019-01-30 21:00:36 +00:00
|
|
|
u8 *data;
|
|
|
|
int data_len;
|
2012-06-14 02:05:16 +00:00
|
|
|
int r = SC_SUCCESS;
|
2019-01-30 21:00:36 +00:00
|
|
|
unsigned int i;
|
2012-06-14 02:05:16 +00:00
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
2018-09-30 09:34:10 +00:00
|
|
|
|
2018-09-30 09:57:24 +00:00
|
|
|
r = pgp_seek_blob(card, priv->mf, tag, &algo_blob);
|
2012-06-14 02:05:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Cannot get old algorithm attributes");
|
2019-01-30 21:00:36 +00:00
|
|
|
|
2019-08-22 11:29:17 +00:00
|
|
|
if (priv->ext_caps & EXT_CAP_ALG_ATTR_CHANGEABLE) {
|
|
|
|
/* ECDSA and ECDH */
|
|
|
|
if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|
|
|
|
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA){
|
|
|
|
data_len = key_info->u.ec.oid_len+1;
|
|
|
|
data = malloc(data_len);
|
|
|
|
if (!data)
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
|
|
|
|
data[0] = key_info->algorithm;
|
|
|
|
/* oid.value is type int, therefore we need to loop over the values */
|
|
|
|
for (i=0; i < key_info->u.ec.oid_len; i++){
|
|
|
|
data[i+1] = key_info->u.ec.oid.value[i];
|
|
|
|
}
|
2019-01-30 21:00:36 +00:00
|
|
|
}
|
2012-06-14 02:05:16 +00:00
|
|
|
|
2019-08-22 11:29:17 +00:00
|
|
|
/* RSA */
|
|
|
|
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){
|
|
|
|
|
|
|
|
/* We can not rely on previous key attributes anymore, as it might be ECC */
|
|
|
|
if (key_info->u.rsa.exponent_len == 0 || key_info->u.rsa.modulus_len == 0)
|
|
|
|
LOG_FUNC_RETURN(card->ctx,SC_ERROR_INVALID_ARGUMENTS);
|
|
|
|
|
|
|
|
data_len = 6;
|
|
|
|
data = malloc(data_len);
|
|
|
|
if (!data)
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
|
|
|
|
data[0] = key_info->algorithm;
|
|
|
|
ushort2bebytes(data+1, key_info->u.rsa.modulus_len);
|
2020-08-29 21:22:34 +00:00
|
|
|
/* OpenPGP Card only accepts 32bit as exponent length field,
|
2019-08-22 11:29:17 +00:00
|
|
|
* although you can import keys with smaller exponent;
|
|
|
|
* thus we don't change rsa.exponent_len, but ignore it here */
|
|
|
|
ushort2bebytes(data+3, SC_OPENPGP_MAX_EXP_BITS);
|
|
|
|
/* Import-Format of private key (e,p,q) */
|
|
|
|
data[5] = SC_OPENPGP_KEYFORMAT_RSA_STD;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sc_log(card->ctx, "Unknown algorithm id");
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
|
|
|
}
|
2019-01-30 21:00:36 +00:00
|
|
|
|
2019-08-22 11:29:17 +00:00
|
|
|
pgp_set_blob(algo_blob, data, data_len);
|
|
|
|
free(data);
|
|
|
|
r = pgp_put_data(card, tag, algo_blob->data, data_len);
|
|
|
|
/* Note: Don't use pgp_set_blob to set data, because it won't touch the real DO */
|
|
|
|
LOG_TEST_RET(card->ctx, r, "Cannot set new algorithm attributes");
|
|
|
|
} else {
|
|
|
|
sc_cardctl_openpgp_keygen_info_t old_key_info;
|
|
|
|
|
2020-02-24 17:23:23 +00:00
|
|
|
if (pgp_parse_algo_attr_blob(card, algo_blob, &old_key_info) != SC_SUCCESS
|
2019-08-22 11:29:17 +00:00
|
|
|
|| old_key_info.algorithm != key_info->algorithm)
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_NO_CARD_SUPPORT,
|
|
|
|
"Requested algorithm not supported");
|
|
|
|
/* FIXME check whether the static parameters match the requested ones. */
|
|
|
|
}
|
2019-01-30 21:00:36 +00:00
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
/**
|
2015-09-19 14:52:51 +00:00
|
|
|
* Internal: store creation time of key.
|
2012-06-18 02:43:25 +00:00
|
|
|
* Pass non-zero outtime to use predefined time.
|
|
|
|
* Pass zero/null outtime to calculate current time. outtime then will be output.
|
|
|
|
* Pass null outtime to not receive output.
|
2012-06-14 02:05:16 +00:00
|
|
|
**/
|
2015-09-19 14:52:51 +00:00
|
|
|
static int
|
|
|
|
pgp_store_creationtime(sc_card_t *card, u8 key_id, time_t *outtime)
|
2012-06-14 02:05:16 +00:00
|
|
|
{
|
2012-06-02 10:09:12 +00:00
|
|
|
int r;
|
2012-06-18 02:43:25 +00:00
|
|
|
time_t createtime = 0;
|
|
|
|
const size_t timestrlen = 64;
|
2012-07-17 13:24:42 +00:00
|
|
|
char timestring[65];
|
2012-06-14 02:05:16 +00:00
|
|
|
u8 buf[4];
|
2019-01-14 09:01:14 +00:00
|
|
|
struct tm tm;
|
2012-06-02 10:09:12 +00:00
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
2019-01-01 18:22:29 +00:00
|
|
|
|
|
|
|
if (key_id < 1 || key_id > 3)
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"Invalid key ID; must be 1, 2, or 3");
|
2012-06-14 02:05:16 +00:00
|
|
|
|
2012-06-18 02:43:25 +00:00
|
|
|
if (outtime != NULL && *outtime != 0)
|
|
|
|
createtime = *outtime;
|
|
|
|
else if (outtime != NULL)
|
2015-09-19 14:52:51 +00:00
|
|
|
/* set output */
|
2012-06-18 02:43:25 +00:00
|
|
|
*outtime = createtime = time(NULL);
|
|
|
|
|
2019-01-14 09:01:14 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (0 != gmtime_s(&tm, &createtime))
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
|
|
|
|
#else
|
|
|
|
if (NULL == gmtime_r(&createtime, &tm))
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
|
|
|
|
#endif
|
|
|
|
strftime(timestring, timestrlen, "%c %Z", &tm);
|
2012-06-18 02:43:25 +00:00
|
|
|
sc_log(card->ctx, "Creation time %s.", timestring);
|
2012-06-14 02:05:16 +00:00
|
|
|
/* Code borrowed from GnuPG */
|
2015-08-08 06:17:12 +00:00
|
|
|
ulong2bebytes(buf, (unsigned long)createtime);
|
2012-06-14 02:05:16 +00:00
|
|
|
r = pgp_put_data(card, 0x00CD + key_id, buf, 4);
|
|
|
|
LOG_TEST_RET(card->ctx, r, "Cannot write to DO");
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
/**
|
2019-01-30 21:00:36 +00:00
|
|
|
* Internal: calculate and store PGP fingerprints.
|
2012-06-14 02:05:16 +00:00
|
|
|
* Reference: GnuPG, app-openpgp.c.
|
|
|
|
**/
|
|
|
|
static int
|
|
|
|
pgp_calculate_and_store_fingerprint(sc_card_t *card, time_t ctime,
|
2012-06-18 02:43:25 +00:00
|
|
|
sc_cardctl_openpgp_keygen_info_t *key_info)
|
2012-06-14 02:05:16 +00:00
|
|
|
{
|
|
|
|
u8 fingerprint[SHA_DIGEST_LENGTH];
|
2015-09-19 14:52:51 +00:00
|
|
|
u8 *fp_buffer = NULL; /* fingerprint buffer, not hashed */
|
2012-06-14 02:05:16 +00:00
|
|
|
size_t fp_buffer_len;
|
2015-09-19 14:52:51 +00:00
|
|
|
u8 *p; /* use this pointer to set fp_buffer content */
|
2012-06-14 02:05:16 +00:00
|
|
|
size_t pk_packet_len;
|
2018-09-30 09:57:24 +00:00
|
|
|
unsigned int tag = 0x00C6 + key_info->key_id;
|
2018-10-28 11:08:33 +00:00
|
|
|
pgp_blob_t *fpseq_blob = NULL;
|
|
|
|
u8 *newdata = NULL;
|
2012-06-14 02:05:16 +00:00
|
|
|
int r;
|
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
/* constructing public-key packet length */
|
|
|
|
/* RSA */
|
|
|
|
if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA) {
|
2018-09-30 09:34:10 +00:00
|
|
|
|
2019-02-02 15:09:16 +00:00
|
|
|
if (key_info->u.rsa.modulus == NULL
|
2019-01-30 21:00:36 +00:00
|
|
|
|| key_info->u.rsa.exponent == NULL
|
|
|
|
|| (key_info->u.rsa.modulus_len) == 0
|
|
|
|
|| (key_info->u.rsa.exponent_len) == 0) {
|
2012-06-18 02:43:25 +00:00
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
sc_log(card->ctx, "Null data (modulus or exponent)");
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* https://tools.ietf.org/html/rfc4880 page 41, 72 */
|
|
|
|
pk_packet_len = 1 /* version number */
|
|
|
|
+ 4 /* creation time */
|
|
|
|
+ 1 /* algorithm */
|
|
|
|
+ 2 /* algorithm-specific fields: RSA modulus+exponent */
|
|
|
|
+ (BYTES4BITS(key_info->u.rsa.modulus_len))
|
|
|
|
+ 2
|
|
|
|
+ (BYTES4BITS(key_info->u.rsa.exponent_len));
|
|
|
|
|
|
|
|
}
|
|
|
|
/* ECC */
|
|
|
|
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|
|
|
|
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA) {
|
|
|
|
if (key_info->u.ec.ecpoint == NULL || (key_info->u.ec.ecpoint_len) == 0) {
|
|
|
|
sc_log(card->ctx, "Error: ecpoint required!");
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
|
|
|
|
}
|
|
|
|
|
2019-02-02 15:09:16 +00:00
|
|
|
/* https://tools.ietf.org/html/rfc4880 page 41, 72
|
2019-01-30 21:00:36 +00:00
|
|
|
* and https://tools.ietf.org/html/rfc6637 section 9 (page 8 and 9) */
|
|
|
|
pk_packet_len = 1 /* version number */
|
|
|
|
+ 4 /* creation time */
|
|
|
|
+ 1 /* algorithm */
|
|
|
|
+ 1 /* oid len */
|
|
|
|
+ (key_info->u.ec.oid_len) /* oid */
|
|
|
|
+ (key_info->u.ec.ecpoint_len); /* ecpoint */
|
|
|
|
|
|
|
|
/* KDF parameters for ECDH */
|
|
|
|
if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH) {
|
|
|
|
/* https://tools.ietf.org/html/rfc6637#section-8 */
|
|
|
|
pk_packet_len += 1 /* number of bytes */
|
|
|
|
+ 1 /* version number */
|
|
|
|
+ 1 /* KDF algo */
|
|
|
|
+ 1; /* KEK algo */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
2019-02-11 19:50:12 +00:00
|
|
|
sc_log(card->ctx, "pk_packet_len is %"SC_FORMAT_LEN_SIZE_T"u", pk_packet_len);
|
2012-06-14 02:05:16 +00:00
|
|
|
|
|
|
|
fp_buffer_len = 3 + pk_packet_len;
|
|
|
|
p = fp_buffer = calloc(fp_buffer_len, 1);
|
2018-10-28 11:08:33 +00:00
|
|
|
if (p == NULL)
|
2012-06-14 02:05:16 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
/* constructing public-key packet */
|
2012-06-14 02:05:16 +00:00
|
|
|
p[0] = 0x99; /* http://tools.ietf.org/html/rfc4880 page 71 */
|
2015-08-08 06:17:12 +00:00
|
|
|
ushort2bebytes(++p, (unsigned short)pk_packet_len);
|
2015-09-19 14:52:51 +00:00
|
|
|
/* start pk_packet */
|
2012-06-14 02:05:16 +00:00
|
|
|
p += 2;
|
|
|
|
*p = 4; /* Version 4 key */
|
2015-08-08 06:17:12 +00:00
|
|
|
ulong2bebytes(++p, (unsigned long)ctime); /* Creation time */
|
2012-06-14 02:05:16 +00:00
|
|
|
p += 4;
|
2019-01-30 21:00:36 +00:00
|
|
|
|
|
|
|
/* RSA */
|
|
|
|
if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA) {
|
|
|
|
*p = 1; /* Algorithm ID, RSA */
|
|
|
|
p += 1;
|
|
|
|
ushort2bebytes(p, (unsigned short)key_info->u.rsa.modulus_len);
|
|
|
|
p += 2;
|
|
|
|
memcpy(p, key_info->u.rsa.modulus, (BYTES4BITS(key_info->u.rsa.modulus_len)));
|
|
|
|
p += (key_info->u.rsa.modulus_len >> 3);
|
|
|
|
ushort2bebytes(++p, (unsigned short)key_info->u.rsa.exponent_len);
|
|
|
|
p += 2;
|
|
|
|
memcpy(p, key_info->u.rsa.exponent, (BYTES4BITS(key_info->u.rsa.exponent_len)));
|
|
|
|
}
|
|
|
|
/* ECC */
|
|
|
|
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|
|
|
|
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA) {
|
|
|
|
/* Algorithm ID, see https://tools.ietf.org/html/rfc6637#section-5 */
|
|
|
|
*p = key_info->algorithm + 6;
|
|
|
|
p += 1;
|
|
|
|
*p = key_info->u.ec.oid_len;
|
|
|
|
p += 1;
|
|
|
|
memcpy(p, key_info->u.ec.oid.value, key_info->u.ec.oid_len);
|
|
|
|
p += key_info->u.ec.oid_len;
|
|
|
|
memcpy(p, key_info->u.ec.ecpoint, key_info->u.ec.ecpoint_len);
|
|
|
|
|
|
|
|
/* KDF parameters for ECDH */
|
|
|
|
if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH) {
|
2019-02-02 15:09:16 +00:00
|
|
|
/* https://tools.ietf.org/html/rfc6637#section-8
|
2019-01-30 21:00:36 +00:00
|
|
|
* This is copied from GnuPG's ecdh_params() function in app-openpgp.c */
|
|
|
|
p += key_info->u.ec.ecpoint_len;
|
|
|
|
*p = 0x03; /* number of bytes following */
|
|
|
|
p += 1;
|
|
|
|
*p = 0x01; /* version of this format */
|
|
|
|
p += 1;
|
|
|
|
if (key_info->u.ec.ecpoint_len <= 256){ /* ec bit size <= 256 */
|
|
|
|
*p = 0x08; /* KDF algo */
|
|
|
|
*(p+1) = 0x07; /* KEK algo */
|
|
|
|
}
|
|
|
|
else if (key_info->u.ec.ecpoint_len <= 384) { /* ec bit size <= 384 */
|
|
|
|
*p = 0x09; /* KDF algo */
|
|
|
|
*(p+1) = 0x08; /* KEK algo */
|
|
|
|
}
|
2019-05-22 13:49:58 +00:00
|
|
|
else { /* ec bit size = 512 or 521*/
|
2019-01-30 21:00:36 +00:00
|
|
|
*p = 0x0a; /* KDF algo */
|
|
|
|
*(p+1) = 0x09; /* KEK algo */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
p = NULL;
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* hash with SHA-1 */
|
2012-06-14 02:05:16 +00:00
|
|
|
SHA1(fp_buffer, fp_buffer_len, fingerprint);
|
|
|
|
free(fp_buffer);
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* store to DO */
|
2018-09-30 09:57:24 +00:00
|
|
|
sc_log(card->ctx, "Writing to DO %04X.", tag);
|
|
|
|
r = pgp_put_data(card, tag, fingerprint, SHA_DIGEST_LENGTH);
|
2019-01-01 17:42:51 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Cannot write to DO");
|
2012-06-14 02:05:16 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* update the blob containing fingerprints (00C5) */
|
2018-09-30 09:57:24 +00:00
|
|
|
sc_log(card->ctx, "Updating fingerprint blob 00C5.");
|
2012-06-14 02:05:16 +00:00
|
|
|
fpseq_blob = pgp_find_blob(card, 0x00C5);
|
2018-10-28 11:08:33 +00:00
|
|
|
if (fpseq_blob == NULL)
|
|
|
|
LOG_TEST_GOTO_ERR(card->ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot find blob 00C5");
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* save the fingerprints sequence */
|
2012-06-14 02:05:16 +00:00
|
|
|
newdata = malloc(fpseq_blob->len);
|
2018-10-28 11:08:33 +00:00
|
|
|
if (newdata == NULL)
|
|
|
|
LOG_TEST_GOTO_ERR(card->ctx, SC_ERROR_OUT_OF_MEMORY,
|
|
|
|
"Not enough memory to update fingerprint blob 00C5");
|
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
memcpy(newdata, fpseq_blob->data, fpseq_blob->len);
|
2015-09-19 14:52:51 +00:00
|
|
|
/* move p to the portion holding the fingerprint of the current key */
|
2018-09-29 17:25:36 +00:00
|
|
|
p = newdata + 20 * (key_info->key_id - 1);
|
2015-09-19 14:52:51 +00:00
|
|
|
/* copy new fingerprint value */
|
2012-06-14 02:05:16 +00:00
|
|
|
memcpy(p, fingerprint, 20);
|
2015-09-19 14:52:51 +00:00
|
|
|
/* set blob's data */
|
2012-06-14 02:05:16 +00:00
|
|
|
pgp_set_blob(fpseq_blob, newdata, fpseq_blob->len);
|
|
|
|
free(newdata);
|
|
|
|
|
2018-10-28 11:08:33 +00:00
|
|
|
err:
|
2012-06-14 02:05:16 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
/**
|
2015-09-19 14:52:51 +00:00
|
|
|
* Internal: update pubkey blob.
|
2012-06-18 02:43:25 +00:00
|
|
|
* Note that modulus_len, exponent_len is measured in bit.
|
2012-06-14 02:05:16 +00:00
|
|
|
**/
|
|
|
|
static int
|
2019-01-30 21:00:36 +00:00
|
|
|
pgp_update_pubkey_blob(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info)
|
2012-06-14 02:05:16 +00:00
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *pk_blob;
|
2019-01-01 18:22:29 +00:00
|
|
|
unsigned int blob_id = 0;
|
2020-01-21 15:43:56 +00:00
|
|
|
sc_pkcs15_pubkey_t p15pubkey;
|
2012-06-18 02:43:25 +00:00
|
|
|
u8 *data = NULL;
|
|
|
|
size_t len;
|
2012-06-14 02:05:16 +00:00
|
|
|
int r;
|
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
if (key_info->key_id == SC_OPENPGP_KEY_SIGN)
|
2014-11-09 08:58:40 +00:00
|
|
|
blob_id = DO_SIGN_SYM;
|
2019-01-30 21:00:36 +00:00
|
|
|
else if (key_info->key_id == SC_OPENPGP_KEY_ENCR)
|
2014-11-09 08:58:40 +00:00
|
|
|
blob_id = DO_ENCR_SYM;
|
2019-01-30 21:00:36 +00:00
|
|
|
else if (key_info->key_id == SC_OPENPGP_KEY_AUTH)
|
2014-11-09 08:58:40 +00:00
|
|
|
blob_id = DO_AUTH_SYM;
|
2012-06-14 02:05:16 +00:00
|
|
|
else {
|
2019-01-01 18:22:29 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"Invalid key ID; must be 1, 2, or 3");
|
2012-06-14 02:05:16 +00:00
|
|
|
}
|
|
|
|
|
2018-09-30 09:57:24 +00:00
|
|
|
sc_log(card->ctx, "Retrieving blob %04X.", blob_id);
|
2012-06-14 02:05:16 +00:00
|
|
|
r = pgp_get_blob(card, priv->mf, blob_id, &pk_blob);
|
2019-01-01 17:42:51 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Cannot get the blob");
|
2012-06-14 02:05:16 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* encode pubkey */
|
2019-01-30 21:00:36 +00:00
|
|
|
/* RSA */
|
|
|
|
if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){
|
2020-01-21 15:43:56 +00:00
|
|
|
memset(&p15pubkey, 0, sizeof(p15pubkey));
|
|
|
|
p15pubkey.algorithm = SC_ALGORITHM_RSA;
|
|
|
|
p15pubkey.u.rsa.modulus.data = key_info->u.rsa.modulus;
|
|
|
|
p15pubkey.u.rsa.modulus.len = BYTES4BITS(key_info->u.rsa.modulus_len);
|
|
|
|
p15pubkey.u.rsa.exponent.data = key_info->u.rsa.exponent;
|
|
|
|
p15pubkey.u.rsa.exponent.len = BYTES4BITS(key_info->u.rsa.exponent_len);
|
2019-01-30 21:00:36 +00:00
|
|
|
}
|
|
|
|
/* ECC */
|
|
|
|
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|
|
|
|
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA){
|
2020-01-21 15:43:56 +00:00
|
|
|
memset(&p15pubkey, 0, sizeof(p15pubkey));
|
|
|
|
p15pubkey.algorithm = SC_ALGORITHM_EC;
|
|
|
|
p15pubkey.u.ec.ecpointQ.value = key_info->u.ec.ecpoint;
|
|
|
|
p15pubkey.u.ec.ecpointQ.len = key_info->u.ec.ecpoint_len;
|
2019-01-30 21:00:36 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
|
2012-06-18 02:43:25 +00:00
|
|
|
|
2020-01-21 15:43:56 +00:00
|
|
|
r = sc_pkcs15_encode_pubkey(card->ctx, &p15pubkey, &data, &len);
|
2019-01-01 17:42:51 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Cannot encode pubkey");
|
2012-06-18 02:43:25 +00:00
|
|
|
|
2018-09-30 09:57:24 +00:00
|
|
|
sc_log(card->ctx, "Updating blob %04X's content.", blob_id);
|
2012-06-18 02:43:25 +00:00
|
|
|
r = pgp_set_blob(pk_blob, data, len);
|
2019-01-01 17:42:51 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Cannot update blob content");
|
2012-06-14 02:05:16 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
/**
|
2015-09-19 14:52:51 +00:00
|
|
|
* Internal: parse response data and set output
|
2012-06-14 02:05:16 +00:00
|
|
|
**/
|
|
|
|
static int
|
|
|
|
pgp_parse_and_set_pubkey_output(sc_card_t *card, u8* data, size_t data_len,
|
2012-06-18 02:43:25 +00:00
|
|
|
sc_cardctl_openpgp_keygen_info_t *key_info)
|
2012-06-14 02:05:16 +00:00
|
|
|
{
|
2012-06-18 02:43:25 +00:00
|
|
|
time_t ctime = 0;
|
2012-06-14 02:05:16 +00:00
|
|
|
u8 *in = data;
|
|
|
|
int r;
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* store creation time */
|
2018-09-29 17:25:36 +00:00
|
|
|
r = pgp_store_creationtime(card, key_info->key_id, &ctime);
|
2012-06-14 02:05:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Cannot store creation time");
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* parse response. Ref: pgp_enumerate_blob() */
|
2014-02-25 08:07:09 +00:00
|
|
|
while (data_len > (size_t) (in - data)) {
|
2012-06-02 10:09:12 +00:00
|
|
|
unsigned int cla, tag, tmptag;
|
|
|
|
size_t len;
|
|
|
|
u8 *part = in;
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* parse TLV structure */
|
2019-01-30 21:00:36 +00:00
|
|
|
r = sc_asn1_read_tag((const u8**)&part, data_len - (in - data), &cla, &tag, &len);
|
2018-03-19 14:25:14 +00:00
|
|
|
if (part == NULL)
|
|
|
|
r = SC_ERROR_ASN1_OBJECT_NOT_FOUND;
|
2019-01-01 17:42:51 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Unexpected end of contents");
|
2015-09-19 14:52:51 +00:00
|
|
|
/* undo ASN1's split of tag & class */
|
2012-06-02 10:09:12 +00:00
|
|
|
for (tmptag = tag; tmptag > 0x0FF; tmptag >>= 8) {
|
|
|
|
cla <<= 8;
|
|
|
|
}
|
|
|
|
tag |= cla;
|
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
/* RSA modulus */
|
2012-06-14 02:05:16 +00:00
|
|
|
if (tag == 0x0081) {
|
2019-01-30 21:00:36 +00:00
|
|
|
if ((BYTES4BITS(key_info->u.rsa.modulus_len) < len) /* modulus_len is in bits */
|
|
|
|
|| key_info->u.rsa.modulus == NULL) {
|
|
|
|
|
|
|
|
free(key_info->u.rsa.modulus);
|
|
|
|
key_info->u.rsa.modulus = malloc(len);
|
|
|
|
if (key_info->u.rsa.modulus == NULL)
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
|
2012-06-02 10:09:12 +00:00
|
|
|
}
|
2019-01-30 21:00:36 +00:00
|
|
|
|
|
|
|
/* set values */
|
|
|
|
memcpy(key_info->u.rsa.modulus, part, len);
|
|
|
|
key_info->u.rsa.modulus_len = len * 8; /* store length in bits */
|
2012-06-02 10:09:12 +00:00
|
|
|
}
|
2019-01-30 21:00:36 +00:00
|
|
|
/* RSA public exponent */
|
2012-06-02 10:09:12 +00:00
|
|
|
else if (tag == 0x0082) {
|
2019-01-30 21:00:36 +00:00
|
|
|
if ((BYTES4BITS(key_info->u.rsa.exponent_len) < len) /* exponent_len is in bits */
|
|
|
|
|| key_info->u.rsa.exponent == NULL) {
|
|
|
|
|
|
|
|
free(key_info->u.rsa.exponent);
|
|
|
|
key_info->u.rsa.exponent = malloc(len);
|
|
|
|
if (key_info->u.rsa.exponent == NULL)
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set values */
|
|
|
|
memcpy(key_info->u.rsa.exponent, part, len);
|
|
|
|
key_info->u.rsa.exponent_len = len * 8; /* store length in bits */
|
|
|
|
}
|
|
|
|
/* ECC public key */
|
|
|
|
else if (tag == 0x0086) {
|
2015-09-19 14:52:51 +00:00
|
|
|
/* set the output data */
|
2019-01-30 21:00:36 +00:00
|
|
|
/* len is ecpoint length + format byte
|
|
|
|
* see section 7.2.14 of 3.3.1 specs */
|
|
|
|
if ((key_info->u.ec.ecpoint_len) != (len - 1)
|
|
|
|
|| key_info->u.ec.ecpoint == NULL) {
|
|
|
|
free(key_info->u.ec.ecpoint);
|
|
|
|
key_info->u.ec.ecpoint = malloc(len);
|
|
|
|
if (key_info->u.ec.ecpoint == NULL)
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
|
2012-06-02 10:09:12 +00:00
|
|
|
}
|
2019-01-30 21:00:36 +00:00
|
|
|
memcpy(key_info->u.ec.ecpoint, part + 1, len - 1);
|
2012-06-02 10:09:12 +00:00
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* go to next part to parse */
|
2012-06-14 02:05:16 +00:00
|
|
|
/* This will be different from pgp_enumerate_blob() a bit */
|
|
|
|
in = part + ((tag != 0x7F49) ? len : 0);
|
2012-06-02 10:09:12 +00:00
|
|
|
}
|
2012-06-14 02:05:16 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* calculate and store fingerprint */
|
2012-06-14 02:05:16 +00:00
|
|
|
sc_log(card->ctx, "Calculate and store fingerprint");
|
2019-01-30 21:00:36 +00:00
|
|
|
r = pgp_calculate_and_store_fingerprint(card, ctime, key_info);
|
2019-01-01 17:42:51 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Cannot store fingerprint");
|
2019-01-30 21:00:36 +00:00
|
|
|
/* update pubkey blobs (B601, B801, A401) */
|
2012-06-14 02:05:16 +00:00
|
|
|
sc_log(card->ctx, "Update blobs holding pubkey info.");
|
2019-01-30 21:00:36 +00:00
|
|
|
r = pgp_update_pubkey_blob(card, key_info);
|
2012-06-02 10:09:12 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2012-06-02 10:09:12 +00:00
|
|
|
/**
|
2015-09-19 14:52:51 +00:00
|
|
|
* Internal: update card->algorithms
|
2012-06-14 02:05:16 +00:00
|
|
|
*/
|
2015-09-19 14:52:51 +00:00
|
|
|
static int
|
|
|
|
pgp_update_card_algorithms(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info)
|
2012-06-14 02:05:16 +00:00
|
|
|
{
|
|
|
|
sc_algorithm_info_t *algo;
|
2018-09-29 17:25:36 +00:00
|
|
|
u8 id = key_info->key_id;
|
2012-06-14 02:05:16 +00:00
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2019-11-07 16:31:53 +00:00
|
|
|
/* protect incompatible cards against non-RSA */
|
2019-01-30 21:00:36 +00:00
|
|
|
if (key_info->algorithm != SC_OPENPGP_KEYALGO_RSA
|
2019-11-07 16:31:53 +00:00
|
|
|
&& card->type < SC_CARD_TYPE_OPENPGP_V3
|
|
|
|
&& card->type != SC_CARD_TYPE_OPENPGP_GNUK)
|
2018-09-30 09:34:10 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
if (id > card->algorithm_count) {
|
2017-03-14 19:02:30 +00:00
|
|
|
sc_log(card->ctx,
|
|
|
|
"This key ID %u is out of the card's algorithm list.",
|
|
|
|
(unsigned int)id);
|
2012-06-14 02:05:16 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* get the algorithm corresponding to the key ID */
|
2012-06-14 02:05:16 +00:00
|
|
|
algo = card->algorithms + (id - 1);
|
2019-01-30 21:00:36 +00:00
|
|
|
/* update new key attribute */
|
|
|
|
if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA) {
|
|
|
|
algo->algorithm = SC_ALGORITHM_RSA;
|
|
|
|
algo->key_length = (unsigned int)key_info->u.rsa.modulus_len;
|
|
|
|
}
|
|
|
|
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|
|
|
|
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA) {
|
|
|
|
algo->algorithm = SC_ALGORITHM_EC;
|
|
|
|
algo->key_length = (unsigned int)((key_info->u.ec.ecpoint_len));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
|
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
/**
|
2015-09-19 14:52:51 +00:00
|
|
|
* ABI (card ctl): GENERATE ASYMMETRIC KEY PAIR
|
2012-06-02 10:09:12 +00:00
|
|
|
**/
|
2015-09-19 14:52:51 +00:00
|
|
|
static int
|
|
|
|
pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info)
|
2012-06-02 10:09:12 +00:00
|
|
|
{
|
|
|
|
sc_apdu_t apdu;
|
2015-09-19 14:52:51 +00:00
|
|
|
/* temporary variables to hold APDU params */
|
2012-06-02 10:09:12 +00:00
|
|
|
u8 apdu_case;
|
2019-01-01 17:09:49 +00:00
|
|
|
u8 apdu_data[2] = { 0x00, 0x00 };
|
2012-06-13 11:31:33 +00:00
|
|
|
size_t apdu_le;
|
2013-04-05 10:18:50 +00:00
|
|
|
size_t resplen = 0;
|
2012-06-02 10:09:12 +00:00
|
|
|
int r = SC_SUCCESS;
|
|
|
|
|
2012-06-17 08:49:00 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2019-11-07 16:31:53 +00:00
|
|
|
/* protect incompatible cards against non-RSA */
|
2019-01-30 21:00:36 +00:00
|
|
|
if (key_info->algorithm != SC_OPENPGP_KEYALGO_RSA
|
2019-11-07 16:31:53 +00:00
|
|
|
&& card->type < SC_CARD_TYPE_OPENPGP_V3
|
|
|
|
&& card->type != SC_CARD_TYPE_OPENPGP_GNUK)
|
2018-09-30 09:34:10 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* set Control Reference Template for key */
|
2018-09-29 17:25:36 +00:00
|
|
|
if (key_info->key_id == SC_OPENPGP_KEY_SIGN)
|
2019-01-01 17:09:49 +00:00
|
|
|
ushort2bebytes(apdu_data, DO_SIGN);
|
2018-09-29 17:25:36 +00:00
|
|
|
else if (key_info->key_id == SC_OPENPGP_KEY_ENCR)
|
2019-01-01 17:09:49 +00:00
|
|
|
ushort2bebytes(apdu_data, DO_ENCR);
|
2018-09-29 17:25:36 +00:00
|
|
|
else if (key_info->key_id == SC_OPENPGP_KEY_AUTH)
|
2019-01-01 17:09:49 +00:00
|
|
|
ushort2bebytes(apdu_data, DO_AUTH);
|
2012-06-02 10:09:12 +00:00
|
|
|
else {
|
2019-01-01 18:22:29 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"Invalid key ID; must be 1, 2, or 3");
|
2012-06-02 10:09:12 +00:00
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* set attributes for new-generated key */
|
2012-06-14 02:05:16 +00:00
|
|
|
r = pgp_update_new_algo_attr(card, key_info);
|
|
|
|
LOG_TEST_RET(card->ctx, r, "Cannot set attributes for new-generated key");
|
2012-06-02 10:09:12 +00:00
|
|
|
|
2012-06-13 11:31:33 +00:00
|
|
|
/* Test whether we will need extended APDU. 1900 is an
|
2012-06-14 02:17:28 +00:00
|
|
|
* arbitrary modulus length which for sure fits into a short APDU.
|
|
|
|
* This idea is borrowed from GnuPG code. */
|
2013-02-20 04:54:30 +00:00
|
|
|
if (card->caps & SC_CARD_CAP_APDU_EXT
|
2019-01-30 21:00:36 +00:00
|
|
|
&& key_info->u.rsa.modulus_len > 1900
|
2015-07-03 16:21:38 +00:00
|
|
|
&& card->type != SC_CARD_TYPE_OPENPGP_GNUK) {
|
2012-06-13 11:31:33 +00:00
|
|
|
/* We won't store to apdu variable yet, because it will be reset in
|
|
|
|
* sc_format_apdu() */
|
|
|
|
apdu_le = card->max_recv_size;
|
2012-06-02 10:09:12 +00:00
|
|
|
apdu_case = SC_APDU_CASE_4_EXT;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
apdu_case = SC_APDU_CASE_4_SHORT;
|
2013-04-05 10:18:50 +00:00
|
|
|
apdu_le = 256;
|
|
|
|
resplen = MAXLEN_RESP_PUBKEY;
|
|
|
|
}
|
|
|
|
if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) {
|
|
|
|
resplen = MAXLEN_RESP_PUBKEY_GNUK;
|
2012-06-02 10:09:12 +00:00
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* prepare APDU */
|
2013-04-05 10:18:50 +00:00
|
|
|
sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x80, 0);
|
2012-06-13 11:31:33 +00:00
|
|
|
apdu.data = apdu_data;
|
2019-01-01 17:09:49 +00:00
|
|
|
apdu.datalen = sizeof(apdu_data);
|
|
|
|
apdu.lc = sizeof(apdu_data);
|
2012-06-13 11:31:33 +00:00
|
|
|
apdu.le = apdu_le;
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* buffer to receive response */
|
2013-04-05 10:18:50 +00:00
|
|
|
apdu.resplen = (resplen > 0) ? resplen : apdu_le;
|
|
|
|
apdu.resp = calloc(apdu.resplen, 1);
|
2012-06-02 10:09:12 +00:00
|
|
|
if (apdu.resp == NULL) {
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* send */
|
2012-06-13 11:31:33 +00:00
|
|
|
sc_log(card->ctx, "Waiting for the card to generate key...");
|
2012-06-02 10:09:12 +00:00
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2012-06-13 11:31:33 +00:00
|
|
|
sc_log(card->ctx, "Card has done key generation.");
|
2018-10-28 10:43:53 +00:00
|
|
|
LOG_TEST_GOTO_ERR(card->ctx, r, "APDU transmit failed");
|
2012-06-02 10:09:12 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* check response */
|
2012-06-02 10:09:12 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2015-09-19 14:52:51 +00:00
|
|
|
/* instruct more in case of error */
|
2012-06-02 10:09:12 +00:00
|
|
|
if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) {
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Please verify PIN first.");
|
2018-10-28 10:43:53 +00:00
|
|
|
goto err;
|
2012-06-02 10:09:12 +00:00
|
|
|
}
|
2018-10-28 10:43:53 +00:00
|
|
|
LOG_TEST_GOTO_ERR(card->ctx, r, "Card returned error");
|
2012-06-02 10:09:12 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* parse response data and set output */
|
2012-06-14 02:05:16 +00:00
|
|
|
pgp_parse_and_set_pubkey_output(card, apdu.resp, apdu.resplen, key_info);
|
|
|
|
pgp_update_card_algorithms(card, key_info);
|
2012-06-02 10:09:12 +00:00
|
|
|
|
2018-10-28 10:43:53 +00:00
|
|
|
err:
|
2012-06-02 10:09:12 +00:00
|
|
|
free(apdu.resp);
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
}
|
2012-06-18 02:43:25 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2012-06-18 02:43:25 +00:00
|
|
|
/**
|
2015-09-19 14:52:51 +00:00
|
|
|
* Internal: build TLV.
|
2015-10-09 21:11:03 +00:00
|
|
|
*
|
|
|
|
* FIXME use `sc_asn1_put_tag` or similar instead
|
|
|
|
*
|
2012-06-18 02:43:25 +00:00
|
|
|
* @param[in] data The data ("value") part to build TLV.
|
|
|
|
* @param[in] len Data length
|
|
|
|
* @param[out] out The buffer of overall TLV. This buffer should be freed later.
|
|
|
|
* @param[out] outlen The length of buffer out.
|
|
|
|
**/
|
|
|
|
static int
|
|
|
|
pgp_build_tlv(sc_context_t *ctx, unsigned int tag, u8 *data, size_t len, u8 **out, size_t *outlen)
|
|
|
|
{
|
|
|
|
u8 highest_order = 0;
|
|
|
|
int r;
|
2015-09-19 16:14:44 +00:00
|
|
|
|
2012-07-29 10:56:03 +00:00
|
|
|
r = sc_asn1_write_element(ctx, tag, data, len, out, outlen);
|
2012-06-18 02:43:25 +00:00
|
|
|
LOG_TEST_RET(ctx, r, "Failed to write ASN.1 element");
|
2015-09-19 16:14:44 +00:00
|
|
|
|
2012-07-29 10:56:03 +00:00
|
|
|
/* Restore class bits stripped by sc_asn1_write_element */
|
2015-09-19 14:52:51 +00:00
|
|
|
/* determine the leftmost byte of tag, which contains class bits */
|
2015-09-19 16:14:44 +00:00
|
|
|
while ((tag >> 8*highest_order) != 0) {
|
2012-06-18 02:43:25 +00:00
|
|
|
highest_order++;
|
|
|
|
}
|
|
|
|
highest_order--;
|
2015-09-19 16:14:44 +00:00
|
|
|
|
|
|
|
/* restore class bits in output */
|
|
|
|
if (highest_order < 4)
|
|
|
|
*out[0] |= (tag >> 8*highest_order);
|
|
|
|
|
2012-06-18 02:43:25 +00:00
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2012-06-18 02:43:25 +00:00
|
|
|
/**
|
2015-09-19 14:52:51 +00:00
|
|
|
* Internal: set Tag & Length components for TLV, store them in buffer.
|
2015-10-09 21:11:03 +00:00
|
|
|
*
|
|
|
|
* FIXME use `sc_asn1_put_tag` or similar instead
|
|
|
|
*
|
2012-06-18 02:43:25 +00:00
|
|
|
* Return the total length of Tag + Length.
|
|
|
|
* Note that the Value components is not counted.
|
|
|
|
* Ref: add_tlv() of GnuPG code.
|
|
|
|
**/
|
|
|
|
static size_t
|
|
|
|
set_taglength_tlv(u8 *buffer, unsigned int tag, size_t length)
|
|
|
|
{
|
|
|
|
u8 *p = buffer;
|
|
|
|
|
|
|
|
assert(tag <= 0xffff);
|
|
|
|
if (tag > 0xff)
|
|
|
|
*p++ = (tag >> 8) & 0xFF;
|
|
|
|
*p++ = tag;
|
|
|
|
if (length < 128)
|
2015-08-08 06:17:12 +00:00
|
|
|
*p++ = (u8)length;
|
2012-06-18 02:43:25 +00:00
|
|
|
else if (length < 256) {
|
|
|
|
*p++ = 0x81;
|
2015-08-08 06:17:12 +00:00
|
|
|
*p++ = (u8)length;
|
2012-06-18 02:43:25 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (length > 0xffff)
|
|
|
|
length = 0xffff;
|
|
|
|
*p++ = 0x82;
|
|
|
|
*p++ = (length >> 8) & 0xFF;
|
|
|
|
*p++ = length & 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
return p - buffer;
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2012-06-18 02:43:25 +00:00
|
|
|
/**
|
2019-01-30 21:00:36 +00:00
|
|
|
* Internal: build Extended Header list (sec 4.3.3.9 - OpenPGP card spec v.3)
|
2012-06-18 02:43:25 +00:00
|
|
|
**/
|
|
|
|
static int
|
|
|
|
pgp_build_extended_header_list(sc_card_t *card, sc_cardctl_openpgp_keystore_info_t *key_info,
|
|
|
|
u8 **result, size_t *resultlen)
|
|
|
|
{
|
|
|
|
sc_context_t *ctx = card->ctx;
|
2015-09-19 14:52:51 +00:00
|
|
|
/* Cardholder private key template (7F48) part */
|
2012-06-18 02:43:25 +00:00
|
|
|
const size_t max_prtem_len = 7*(1 + 3); /* 7 components */
|
|
|
|
/* 1 for tag name (91, 92... 97)
|
|
|
|
* 3 for storing length */
|
2012-07-17 13:24:42 +00:00
|
|
|
u8 pritemplate[7*(1 + 3)];
|
2012-06-18 02:43:25 +00:00
|
|
|
size_t tpl_len = 0; /* Actual size of pritemplate */
|
|
|
|
/* Concatenation of key data */
|
|
|
|
u8 kdata[3 + 256 + 256 + 512]; /* Exponent is stored in 3 bytes
|
|
|
|
* With maximum 4096-bit key,
|
|
|
|
* p and q can be stored in 256 bytes (2048 bits).
|
|
|
|
* Maximum 4096-bit modulus is stored in 512 bytes */
|
|
|
|
size_t kdata_len = 0; /* Actual size of kdata */
|
|
|
|
u8 *tlvblock = NULL;
|
|
|
|
size_t tlvlen = 0;
|
|
|
|
u8 *tlv_5f48 = NULL;
|
|
|
|
size_t tlvlen_5f48 = 0;
|
|
|
|
u8 *tlv_7f48 = NULL;
|
|
|
|
size_t tlvlen_7f48 = 0;
|
|
|
|
u8 *data = NULL;
|
|
|
|
size_t len = 0;
|
|
|
|
u8 *p = NULL;
|
2019-01-30 21:00:36 +00:00
|
|
|
u8 *components[4];
|
|
|
|
size_t componentlens[4];
|
|
|
|
unsigned int componenttags[4];
|
|
|
|
char *componentnames[4];
|
|
|
|
size_t comp_to_add;
|
2012-06-18 02:43:25 +00:00
|
|
|
u8 i;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
LOG_FUNC_CALLED(ctx);
|
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
/* RSA */
|
|
|
|
if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){
|
|
|
|
|
|
|
|
components[0] = key_info->u.rsa.e;
|
|
|
|
components[1] = key_info->u.rsa.p;
|
|
|
|
components[2] = key_info->u.rsa.q;
|
|
|
|
componentlens[0] = key_info->u.rsa.e_len;
|
|
|
|
componentlens[1] = key_info->u.rsa.p_len;
|
|
|
|
componentlens[2] = key_info->u.rsa.q_len;
|
|
|
|
componenttags[0] = 0x91;
|
|
|
|
componenttags[1] = 0x92;
|
|
|
|
componenttags[2] = 0x93;
|
|
|
|
componentnames[0] = "public exponent";
|
|
|
|
componentnames[1] = "prime p";
|
|
|
|
componentnames[2] = "prime q";
|
|
|
|
comp_to_add = 3;
|
2019-02-02 15:09:16 +00:00
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
/* The maximum exponent length is 32 bit, as set on card
|
|
|
|
* we use this variable to check against actual exponent_len */
|
|
|
|
size_t max_e_len_bytes = BYTES4BITS(SC_OPENPGP_MAX_EXP_BITS);
|
|
|
|
size_t e_len_bytes = BYTES4BITS(key_info->u.rsa.e_len);
|
|
|
|
|
|
|
|
if (key_info->u.rsa.keyformat == SC_OPENPGP_KEYFORMAT_RSA_STDN
|
|
|
|
|| key_info->u.rsa.keyformat == SC_OPENPGP_KEYFORMAT_RSA_CRTN){
|
|
|
|
components[3] = key_info->u.rsa.n;
|
|
|
|
componentlens[3] = key_info->u.rsa.n_len;
|
|
|
|
componenttags[3] = 0x97;
|
|
|
|
componentnames[3] = "modulus";
|
|
|
|
comp_to_add = 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* validate */
|
|
|
|
if (comp_to_add == 4 && (key_info->u.rsa.n == NULL || key_info->u.rsa.n_len == 0)){
|
|
|
|
sc_log(ctx, "Error: Modulus required!");
|
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cardholder private key template's data part */
|
|
|
|
memset(pritemplate, 0, max_prtem_len);
|
|
|
|
|
|
|
|
/* maximum 32 bit exponent length allowed on OpenPGP Card */
|
|
|
|
assert(key_info->u.rsa.e_len <= SC_OPENPGP_MAX_EXP_BITS);
|
|
|
|
|
|
|
|
/* We need to right justify the exponent with allowed exponent length,
|
|
|
|
* e.g. from '01 00 01' to '00 01 00 01' */
|
|
|
|
if (key_info->u.rsa.e_len < SC_OPENPGP_MAX_EXP_BITS) {
|
|
|
|
/* create new buffer */
|
|
|
|
p = calloc(max_e_len_bytes, 1);
|
|
|
|
if (!p)
|
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
|
|
|
|
memcpy(p + (max_e_len_bytes - e_len_bytes), key_info->u.rsa.e, e_len_bytes);
|
|
|
|
/* set key_info->u.rsa.e to new buffer */
|
|
|
|
free(key_info->u.rsa.e);
|
|
|
|
key_info->u.rsa.e = p;
|
|
|
|
components[0] = p;
|
|
|
|
key_info->u.rsa.e_len = SC_OPENPGP_MAX_EXP_BITS; /* we store info in bits */
|
|
|
|
componentlens[0] = max_e_len_bytes; /* ... but in bytes for header list */
|
|
|
|
}
|
2012-06-18 02:43:25 +00:00
|
|
|
}
|
2019-01-30 21:00:36 +00:00
|
|
|
/* ECC */
|
|
|
|
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|
|
|
|
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA){
|
|
|
|
components[0] = key_info->u.ec.privateD;
|
|
|
|
componentlens[0] = key_info->u.ec.privateD_len;
|
|
|
|
componenttags[0] = 0x92;
|
|
|
|
componentnames[0] = "private key";
|
|
|
|
comp_to_add = 1;
|
|
|
|
|
2019-10-09 15:18:21 +00:00
|
|
|
/* import public key as well */
|
|
|
|
if (key_info->u.ec.keyformat == SC_OPENPGP_KEYFORMAT_EC_STDPUB){
|
|
|
|
components[1] = key_info->u.ec.ecpointQ;
|
|
|
|
componentlens[1] = key_info->u.ec.ecpointQ_len;
|
|
|
|
componenttags[1] = 0x99;
|
|
|
|
componentnames[1] = "public key";
|
|
|
|
comp_to_add = 2;
|
|
|
|
}
|
2019-01-30 21:00:36 +00:00
|
|
|
|
|
|
|
/* validate */
|
2019-10-09 13:02:36 +00:00
|
|
|
if ((key_info->u.ec.ecpointQ == NULL || key_info->u.ec.ecpointQ_len == 0)){
|
|
|
|
sc_log(ctx, "Error: ecpointQ required!");
|
2019-01-30 21:00:36 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cardholder private key template's data part */
|
|
|
|
memset(pritemplate, 0, max_prtem_len);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
2012-06-18 02:43:25 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* start from beginning of pritemplate */
|
2012-06-18 02:43:25 +00:00
|
|
|
p = pritemplate;
|
|
|
|
|
|
|
|
for (i = 0; i < comp_to_add; i++) {
|
|
|
|
sc_log(ctx, "Set Tag+Length for %s (%X).", componentnames[i], componenttags[i]);
|
|
|
|
len = set_taglength_tlv(p, componenttags[i], componentlens[i]);
|
|
|
|
tpl_len += len;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* <-- kdata_len --><-- Copy here -->
|
|
|
|
* kdata |===============|___________________
|
|
|
|
*/
|
|
|
|
memcpy(kdata + kdata_len, components[i], componentlens[i]);
|
|
|
|
kdata_len += componentlens[i];
|
|
|
|
|
|
|
|
/* Move p to next part and build */
|
|
|
|
p += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Components for CRT format */
|
|
|
|
|
|
|
|
/* TLV block for 7F48 */
|
|
|
|
r = pgp_build_tlv(ctx, 0x7F48, pritemplate, tpl_len, &tlv_7f48, &tlvlen_7f48);
|
2019-01-01 17:42:51 +00:00
|
|
|
LOG_TEST_RET(ctx, r, "Failed to build TLV for 7F48");
|
2012-06-18 02:43:25 +00:00
|
|
|
tlv_7f48[0] |= 0x7F;
|
|
|
|
r = pgp_build_tlv(ctx, 0x5f48, kdata, kdata_len, &tlv_5f48, &tlvlen_5f48);
|
2018-10-28 11:04:19 +00:00
|
|
|
LOG_TEST_GOTO_ERR(ctx, r, "Failed to build TLV for 5F48");
|
2012-06-18 02:43:25 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* data part's length for Extended Header list */
|
2012-06-18 02:43:25 +00:00
|
|
|
len = 2 + tlvlen_7f48 + tlvlen_5f48;
|
2015-09-19 14:52:51 +00:00
|
|
|
/* set data part content */
|
2012-06-18 02:43:25 +00:00
|
|
|
data = calloc(len, 1);
|
2018-10-28 11:04:19 +00:00
|
|
|
if (data == NULL)
|
2019-01-01 17:42:51 +00:00
|
|
|
LOG_TEST_GOTO_ERR(ctx, SC_ERROR_NOT_ENOUGH_MEMORY, "Not enough memory");
|
2018-10-28 11:04:19 +00:00
|
|
|
|
2018-09-29 17:25:36 +00:00
|
|
|
switch (key_info->key_id) {
|
2019-01-01 16:51:12 +00:00
|
|
|
case SC_OPENPGP_KEY_SIGN:
|
|
|
|
ushort2bebytes(data, DO_SIGN);
|
|
|
|
break;
|
|
|
|
case SC_OPENPGP_KEY_ENCR:
|
|
|
|
ushort2bebytes(data, DO_ENCR);
|
|
|
|
break;
|
|
|
|
case SC_OPENPGP_KEY_AUTH:
|
|
|
|
ushort2bebytes(data, DO_AUTH);
|
|
|
|
break;
|
|
|
|
default:
|
2019-01-01 18:22:29 +00:00
|
|
|
LOG_TEST_GOTO_ERR(ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"Invalid key ID; must be 1, 2, or 3");
|
2012-06-18 02:43:25 +00:00
|
|
|
}
|
2019-01-30 21:00:36 +00:00
|
|
|
memcpy(data + 2, tlv_7f48, tlvlen_7f48);
|
2012-06-18 02:43:25 +00:00
|
|
|
memcpy(data + 2 + tlvlen_7f48, tlv_5f48, tlvlen_5f48);
|
|
|
|
r = pgp_build_tlv(ctx, 0x4D, data, len, &tlvblock, &tlvlen);
|
2018-10-28 11:04:19 +00:00
|
|
|
LOG_TEST_GOTO_ERR(ctx, r, "Cannot build TLV for Extended Header list");
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* set output */
|
2012-06-18 02:43:25 +00:00
|
|
|
if (result != NULL) {
|
|
|
|
*result = tlvblock;
|
|
|
|
*resultlen = tlvlen;
|
2012-08-23 22:10:01 +00:00
|
|
|
} else {
|
|
|
|
free(tlvblock);
|
2012-06-18 02:43:25 +00:00
|
|
|
}
|
|
|
|
|
2018-10-28 11:04:19 +00:00
|
|
|
err:
|
2012-06-18 02:43:25 +00:00
|
|
|
free(data);
|
|
|
|
free(tlv_5f48);
|
2015-10-09 21:11:03 +00:00
|
|
|
free(tlv_7f48);
|
2012-06-18 02:43:25 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, r);
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2012-06-18 02:43:25 +00:00
|
|
|
/**
|
2015-09-19 14:52:51 +00:00
|
|
|
* ABI (card ctl): store key
|
2012-06-18 02:43:25 +00:00
|
|
|
**/
|
2015-09-19 14:52:51 +00:00
|
|
|
static int
|
|
|
|
pgp_store_key(sc_card_t *card, sc_cardctl_openpgp_keystore_info_t *key_info)
|
2012-06-18 02:43:25 +00:00
|
|
|
{
|
|
|
|
sc_cardctl_openpgp_keygen_info_t pubkey;
|
2018-03-13 20:56:32 +00:00
|
|
|
u8 *data = NULL;
|
|
|
|
size_t len = 0;
|
2012-06-18 02:43:25 +00:00
|
|
|
int r;
|
|
|
|
|
2018-10-28 10:57:32 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
2012-06-18 02:43:25 +00:00
|
|
|
|
2019-11-07 16:31:53 +00:00
|
|
|
/* protect incompatible cards against non-RSA */
|
2019-01-30 21:00:36 +00:00
|
|
|
if (key_info->algorithm != SC_OPENPGP_KEYALGO_RSA
|
2019-11-07 16:31:53 +00:00
|
|
|
&& card->type < SC_CARD_TYPE_OPENPGP_V3
|
|
|
|
&& card->type != SC_CARD_TYPE_OPENPGP_GNUK)
|
2018-10-28 10:57:32 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
2018-09-30 09:34:10 +00:00
|
|
|
|
2012-06-18 02:43:25 +00:00
|
|
|
/* Validate */
|
2019-01-01 18:22:29 +00:00
|
|
|
if (key_info->key_id < 1 || key_info->key_id > 3)
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"Invalid key ID; must be 1, 2, or 3");
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* set algorithm attributes */
|
2019-01-30 21:00:36 +00:00
|
|
|
/* RSA */
|
|
|
|
if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){
|
2019-10-08 13:43:40 +00:00
|
|
|
/* we just support standard key format */
|
|
|
|
switch (key_info->u.rsa.keyformat) {
|
|
|
|
case SC_OPENPGP_KEYFORMAT_RSA_STD:
|
|
|
|
case SC_OPENPGP_KEYFORMAT_RSA_STDN:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SC_OPENPGP_KEYFORMAT_RSA_CRT:
|
|
|
|
case SC_OPENPGP_KEYFORMAT_RSA_CRTN:
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
|
|
|
|
|
|
|
default:
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
|
|
|
|
}
|
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
/* we only support exponent of maximum 32 bits */
|
|
|
|
if (key_info->u.rsa.e_len > SC_OPENPGP_MAX_EXP_BITS) {
|
|
|
|
sc_log(card->ctx,
|
|
|
|
"Exponent %"SC_FORMAT_LEN_SIZE_T"u-bit (>32) is not supported.",
|
|
|
|
key_info->u.rsa.e_len);
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&pubkey, 0, sizeof(pubkey));
|
|
|
|
pubkey.key_id = key_info->key_id;
|
|
|
|
pubkey.algorithm = key_info->algorithm;
|
|
|
|
if (key_info->u.rsa.n && key_info->u.rsa.n_len
|
|
|
|
&& key_info->u.rsa.e && key_info->u.rsa.e_len) {
|
|
|
|
pubkey.u.rsa.modulus = key_info->u.rsa.n;
|
|
|
|
pubkey.u.rsa.modulus_len = key_info->u.rsa.n_len;
|
|
|
|
pubkey.u.rsa.exponent = key_info->u.rsa.e;
|
|
|
|
pubkey.u.rsa.exponent_len = key_info->u.rsa.e_len;
|
|
|
|
}
|
|
|
|
else
|
2019-02-02 15:09:16 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx,SC_ERROR_INVALID_ARGUMENTS);
|
2019-01-30 21:00:36 +00:00
|
|
|
}
|
|
|
|
/* ECC */
|
|
|
|
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|
|
|
|
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA){
|
|
|
|
memset(&pubkey, 0, sizeof(pubkey));
|
|
|
|
pubkey.key_id = key_info->key_id;
|
|
|
|
pubkey.algorithm = key_info->algorithm;
|
2019-10-09 13:02:36 +00:00
|
|
|
if (key_info->u.ec.ecpointQ && key_info->u.ec.ecpointQ_len){
|
|
|
|
pubkey.u.ec.ecpoint = key_info->u.ec.ecpointQ;
|
|
|
|
pubkey.u.ec.ecpoint_len = key_info->u.ec.ecpointQ_len;
|
2019-10-08 15:11:00 +00:00
|
|
|
pubkey.u.ec.oid = key_info->u.ec.oid;
|
|
|
|
pubkey.u.ec.oid_len = key_info->u.ec.oid_len;
|
2019-01-30 21:00:36 +00:00
|
|
|
}
|
|
|
|
else
|
2019-02-02 15:09:16 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx,SC_ERROR_INVALID_ARGUMENTS);
|
2012-06-18 02:43:25 +00:00
|
|
|
}
|
2019-01-30 21:00:36 +00:00
|
|
|
else
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
|
|
|
|
|
2012-06-18 02:43:25 +00:00
|
|
|
r = pgp_update_new_algo_attr(card, &pubkey);
|
|
|
|
LOG_TEST_RET(card->ctx, r, "Failed to update new algorithm attributes");
|
2018-10-28 10:57:32 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* build Extended Header list */
|
2012-06-18 02:43:25 +00:00
|
|
|
r = pgp_build_extended_header_list(card, key_info, &data, &len);
|
2018-10-28 10:57:32 +00:00
|
|
|
LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to build Extended Header list");
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* write to DO */
|
2012-06-18 02:43:25 +00:00
|
|
|
r = pgp_put_data(card, 0x4D, data, len);
|
2018-10-28 10:57:32 +00:00
|
|
|
LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to write to DO 004D");
|
2012-06-18 02:43:25 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* store creation time */
|
2018-09-29 17:25:36 +00:00
|
|
|
r = pgp_store_creationtime(card, key_info->key_id, &key_info->creationtime);
|
2012-06-18 02:43:25 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Cannot store creation time");
|
|
|
|
|
2018-10-28 10:57:32 +00:00
|
|
|
/* calculate and store fingerprint */
|
2012-06-18 02:43:25 +00:00
|
|
|
sc_log(card->ctx, "Calculate and store fingerprint");
|
2019-01-30 21:00:36 +00:00
|
|
|
r = pgp_calculate_and_store_fingerprint(card, key_info->creationtime, &pubkey);
|
2019-01-01 17:42:51 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Cannot store fingerprint");
|
2015-09-19 14:52:51 +00:00
|
|
|
/* update pubkey blobs (B601,B801, A401) */
|
2012-06-18 02:43:25 +00:00
|
|
|
sc_log(card->ctx, "Update blobs holding pubkey info.");
|
2019-01-30 21:00:36 +00:00
|
|
|
r = pgp_update_pubkey_blob(card, &pubkey);
|
2012-06-18 02:43:25 +00:00
|
|
|
|
2019-01-30 21:00:36 +00:00
|
|
|
sc_log(card->ctx, "Update card algorithms");
|
2012-07-05 09:49:01 +00:00
|
|
|
pgp_update_card_algorithms(card, &pubkey);
|
|
|
|
|
2018-10-28 10:57:32 +00:00
|
|
|
err:
|
2018-12-11 20:36:40 +00:00
|
|
|
free(data);
|
2018-10-28 10:57:32 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2012-06-18 02:43:25 +00:00
|
|
|
}
|
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
#endif /* ENABLE_OPENSSL */
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
2013-03-04 04:28:08 +00:00
|
|
|
/**
|
2015-09-19 14:52:51 +00:00
|
|
|
* ABI (card ctl): erase card
|
2013-03-04 04:28:08 +00:00
|
|
|
**/
|
2015-09-19 14:52:51 +00:00
|
|
|
static int
|
|
|
|
pgp_erase_card(sc_card_t *card)
|
2013-03-04 04:28:08 +00:00
|
|
|
{
|
2014-07-13 09:37:59 +00:00
|
|
|
/* Special series of commands to erase OpenPGP card,
|
|
|
|
* according to https://www.crypto-stick.com/en/faq
|
|
|
|
* (How to reset a Crypto Stick? question).
|
|
|
|
* Gnuk is known not to support this feature. */
|
2018-01-09 13:55:48 +00:00
|
|
|
const char *apdu_hex[] = {
|
2015-09-19 15:40:57 +00:00
|
|
|
/* block PIN1 */
|
|
|
|
"00:20:00:81:08:40:40:40:40:40:40:40:40",
|
|
|
|
"00:20:00:81:08:40:40:40:40:40:40:40:40",
|
|
|
|
"00:20:00:81:08:40:40:40:40:40:40:40:40",
|
|
|
|
"00:20:00:81:08:40:40:40:40:40:40:40:40",
|
|
|
|
/* block PIN3 */
|
|
|
|
"00:20:00:83:08:40:40:40:40:40:40:40:40",
|
|
|
|
"00:20:00:83:08:40:40:40:40:40:40:40:40",
|
|
|
|
"00:20:00:83:08:40:40:40:40:40:40:40:40",
|
|
|
|
"00:20:00:83:08:40:40:40:40:40:40:40:40",
|
|
|
|
/* TERMINATE */
|
|
|
|
"00:e6:00:00",
|
|
|
|
NULL
|
2013-03-04 04:28:08 +00:00
|
|
|
};
|
2018-01-09 13:55:48 +00:00
|
|
|
sc_apdu_t apdu;
|
2015-09-19 15:40:57 +00:00
|
|
|
int i;
|
|
|
|
int r = SC_SUCCESS;
|
2018-01-09 13:55:48 +00:00
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2013-03-04 04:28:08 +00:00
|
|
|
|
2015-09-19 15:40:57 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
2013-03-04 04:28:08 +00:00
|
|
|
|
2018-06-10 20:06:53 +00:00
|
|
|
if ((priv->ext_caps & EXT_CAP_LCS) == 0) {
|
2018-01-09 13:55:48 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_NO_CARD_SUPPORT,
|
|
|
|
"Card does not offer life cycle management");
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (priv->state) {
|
|
|
|
case CARD_STATE_ACTIVATED:
|
|
|
|
/* iterate over the commands above */
|
|
|
|
for (i = 0; apdu_hex[i] != NULL; i++) {
|
|
|
|
u8 apdu_bin[25]; /* large enough to convert apdu_hex */
|
|
|
|
size_t apdu_bin_len = sizeof(apdu_bin);
|
|
|
|
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
|
|
|
|
|
/* convert hex array to bin array */
|
|
|
|
r = sc_hex_to_bin(apdu_hex[i], apdu_bin, &apdu_bin_len);
|
|
|
|
LOG_TEST_RET(card->ctx, r, "Failed to convert APDU bytes");
|
|
|
|
|
|
|
|
/* build APDU from binary array */
|
|
|
|
r = sc_bytes2apdu(card->ctx, apdu_bin, apdu_bin_len, &apdu);
|
2019-01-01 18:02:03 +00:00
|
|
|
if (r)
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL,
|
|
|
|
"Failed to build APDU");
|
2015-09-19 15:40:57 +00:00
|
|
|
|
2018-01-09 13:55:48 +00:00
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
2013-03-04 04:28:08 +00:00
|
|
|
|
2018-01-09 13:55:48 +00:00
|
|
|
/* send APDU to card */
|
|
|
|
sc_log(card->ctx, "Sending APDU%d %s", i, apdu_hex[i]);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
LOG_TEST_RET(card->ctx, r, "Transmitting APDU failed");
|
|
|
|
}
|
|
|
|
/* fall through */
|
|
|
|
case CARD_STATE_INITIALIZATION:
|
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x44, 0, 0);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
LOG_TEST_RET(card->ctx, r, "Transmitting APDU failed");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_NO_CARD_SUPPORT,
|
|
|
|
"Card does not offer life cycle management");
|
2013-03-04 04:28:08 +00:00
|
|
|
}
|
2015-09-19 15:40:57 +00:00
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2013-03-04 04:28:08 +00:00
|
|
|
}
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
|
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: ISO 7816-9 CARD CTL - perform special card-specific operations.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pgp_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
|
2011-05-23 17:32:00 +00:00
|
|
|
{
|
2012-06-02 10:09:12 +00:00
|
|
|
int r;
|
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2011-05-23 17:32:00 +00:00
|
|
|
switch(cmd) {
|
|
|
|
case SC_CARDCTL_GET_SERIALNR:
|
|
|
|
memmove((sc_serial_number_t *) ptr, &card->serialnr, sizeof(card->serialnr));
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
2011-05-23 17:32:00 +00:00
|
|
|
break;
|
2012-06-02 10:09:12 +00:00
|
|
|
|
2012-06-14 02:05:16 +00:00
|
|
|
#ifdef ENABLE_OPENSSL
|
2012-06-02 10:09:12 +00:00
|
|
|
case SC_CARDCTL_OPENPGP_GENERATE_KEY:
|
|
|
|
r = pgp_gen_key(card, (sc_cardctl_openpgp_keygen_info_t *) ptr);
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2012-06-18 02:43:25 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SC_CARDCTL_OPENPGP_STORE_KEY:
|
|
|
|
r = pgp_store_key(card, (sc_cardctl_openpgp_keystore_info_t *) ptr);
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
break;
|
2012-06-14 02:05:16 +00:00
|
|
|
#endif /* ENABLE_OPENSSL */
|
2013-03-04 04:28:08 +00:00
|
|
|
case SC_CARDCTL_ERASE_CARD:
|
|
|
|
r = pgp_erase_card(card);
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
break;
|
2011-05-23 17:32:00 +00:00
|
|
|
}
|
|
|
|
|
2011-05-23 17:32:21 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
2011-05-23 17:32:00 +00:00
|
|
|
}
|
|
|
|
|
2013-04-12 08:33:31 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* Internal: delete key (GnuK only).
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2013-04-12 08:33:31 +00:00
|
|
|
static int
|
|
|
|
gnuk_delete_key(sc_card_t *card, u8 key_id)
|
|
|
|
{
|
|
|
|
sc_context_t *ctx = card->ctx;
|
|
|
|
int r = SC_SUCCESS;
|
2019-01-01 16:58:06 +00:00
|
|
|
u8 data[4] = { 0x4D, 0x02, 0x00, 0x00 };
|
2013-04-12 08:33:31 +00:00
|
|
|
|
|
|
|
LOG_FUNC_CALLED(ctx);
|
|
|
|
|
2019-01-01 18:22:29 +00:00
|
|
|
if (key_id < 1 || key_id > 3)
|
|
|
|
LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS,
|
|
|
|
"Invalid key ID; must be 1, 2, or 3");
|
2013-04-16 03:19:34 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* delete fingerprint */
|
2013-04-12 08:33:31 +00:00
|
|
|
sc_log(ctx, "Delete fingerprints");
|
|
|
|
r = pgp_put_data(card, 0xC6 + key_id, NULL, 0);
|
|
|
|
LOG_TEST_RET(ctx, r, "Failed to delete fingerprints");
|
2015-09-19 14:52:51 +00:00
|
|
|
/* delete creation time */
|
2013-04-12 08:33:31 +00:00
|
|
|
sc_log(ctx, "Delete creation time");
|
|
|
|
r = pgp_put_data(card, 0xCD + key_id, NULL, 0);
|
|
|
|
LOG_TEST_RET(ctx, r, "Failed to delete creation time");
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* rewrite Extended Header List */
|
2013-04-12 08:33:31 +00:00
|
|
|
sc_log(ctx, "Rewrite Extended Header List");
|
|
|
|
|
2019-01-01 16:58:06 +00:00
|
|
|
if (key_id == SC_OPENPGP_KEY_SIGN)
|
|
|
|
ushort2bebytes(data+2, DO_SIGN);
|
|
|
|
else if (key_id == SC_OPENPGP_KEY_ENCR)
|
|
|
|
ushort2bebytes(data+2, DO_ENCR);
|
|
|
|
else if (key_id == SC_OPENPGP_KEY_AUTH)
|
|
|
|
ushort2bebytes(data+2, DO_AUTH);
|
2013-04-12 08:33:31 +00:00
|
|
|
|
2019-01-01 16:58:06 +00:00
|
|
|
r = pgp_put_data(card, 0x4D, data, sizeof(data));
|
2013-04-12 08:33:31 +00:00
|
|
|
|
|
|
|
LOG_FUNC_RETURN(ctx, r);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: ISO 7816-9 DELETE FILE - delete EF or DF given.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2012-06-07 18:31:44 +00:00
|
|
|
static int
|
|
|
|
pgp_delete_file(sc_card_t *card, const sc_path_t *path)
|
2012-06-01 15:59:20 +00:00
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *blob;
|
2012-06-01 15:59:20 +00:00
|
|
|
sc_file_t *file;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/* sc_pkcs15init_delete_by_path() sets the path type to SC_PATH_TYPE_FILE_ID */
|
2012-06-01 15:59:20 +00:00
|
|
|
r = pgp_select_file(card, path, &file);
|
2019-01-01 17:42:51 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Cannot select file");
|
2012-06-01 15:59:20 +00:00
|
|
|
|
2012-06-07 18:31:44 +00:00
|
|
|
/* save "current" blob */
|
|
|
|
blob = priv->current;
|
2012-06-01 15:59:20 +00:00
|
|
|
|
2018-06-16 13:54:17 +00:00
|
|
|
/* don't try to delete MF */
|
2012-06-07 18:31:44 +00:00
|
|
|
if (blob == priv->mf)
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
|
|
|
|
2013-04-12 08:33:31 +00:00
|
|
|
if (card->type != SC_CARD_TYPE_OPENPGP_GNUK &&
|
2015-07-03 16:21:38 +00:00
|
|
|
(file->id == DO_SIGN_SYM || file->id == DO_ENCR_SYM || file->id == DO_AUTH_SYM)) {
|
2015-09-19 14:52:51 +00:00
|
|
|
/* These tags are just symbolic. We don't really delete them. */
|
2012-06-21 08:22:44 +00:00
|
|
|
r = SC_SUCCESS;
|
|
|
|
}
|
2014-11-09 08:58:40 +00:00
|
|
|
else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == DO_SIGN_SYM) {
|
2013-04-12 08:33:31 +00:00
|
|
|
r = gnuk_delete_key(card, 1);
|
|
|
|
}
|
2014-11-09 08:58:40 +00:00
|
|
|
else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == DO_ENCR_SYM) {
|
2013-04-12 08:33:31 +00:00
|
|
|
r = gnuk_delete_key(card, 2);
|
|
|
|
}
|
2014-11-09 08:58:40 +00:00
|
|
|
else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == DO_AUTH_SYM) {
|
2013-04-12 08:33:31 +00:00
|
|
|
r = gnuk_delete_key(card, 3);
|
|
|
|
}
|
2012-06-21 08:22:44 +00:00
|
|
|
else {
|
|
|
|
/* call pgp_put_data() with zero-sized NULL-buffer to zap the DO contents */
|
|
|
|
r = pgp_put_data(card, file->id, NULL, 0);
|
|
|
|
}
|
2012-06-07 18:31:44 +00:00
|
|
|
|
|
|
|
/* set "current" blob to parent */
|
|
|
|
priv->current = blob->parent;
|
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2012-06-01 15:59:20 +00:00
|
|
|
}
|
2011-05-23 17:32:10 +00:00
|
|
|
|
2012-06-07 18:31:44 +00:00
|
|
|
|
2015-09-19 14:52:51 +00:00
|
|
|
/**
|
2018-06-16 13:54:17 +00:00
|
|
|
* ABI: ISO 7816-4 UPDATE BINARY - update data in current EF.
|
2015-09-19 14:52:51 +00:00
|
|
|
*/
|
2012-06-07 17:55:21 +00:00
|
|
|
static int
|
|
|
|
pgp_update_binary(sc_card_t *card, unsigned int idx,
|
|
|
|
const u8 *buf, size_t count, unsigned long flags)
|
2012-05-29 09:20:08 +00:00
|
|
|
{
|
|
|
|
struct pgp_priv_data *priv = DRVDATA(card);
|
2014-07-13 11:41:36 +00:00
|
|
|
pgp_blob_t *blob = priv->current;
|
2012-07-05 09:49:01 +00:00
|
|
|
int r = SC_SUCCESS;
|
2012-05-29 09:20:08 +00:00
|
|
|
|
2012-06-07 17:55:21 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2012-05-29 09:20:08 +00:00
|
|
|
/* We will use PUT DATA to write to DO.
|
2012-06-08 08:18:46 +00:00
|
|
|
* As PUT DATA does not support idx, we don't either */
|
|
|
|
if (idx > 0)
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS);
|
2012-05-29 09:20:08 +00:00
|
|
|
|
2012-07-05 09:49:01 +00:00
|
|
|
/* When a dummy file, e.g "11001101", is selected, the current blob
|
|
|
|
* is set to NULL. We don't really put data to dummy file. */
|
|
|
|
if (blob != NULL) {
|
|
|
|
r = pgp_put_data(card, blob->id, buf, count);
|
|
|
|
}
|
2012-06-07 17:55:21 +00:00
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2012-05-29 09:20:08 +00:00
|
|
|
}
|
|
|
|
|
2012-06-07 17:55:21 +00:00
|
|
|
|
2018-06-16 13:54:17 +00:00
|
|
|
/**
|
|
|
|
* ABI: card reader lock obtained - re-select card applet if necessary.
|
|
|
|
*/
|
2018-01-16 20:03:30 +00:00
|
|
|
static int pgp_card_reader_lock_obtained(sc_card_t *card, int was_reset)
|
|
|
|
{
|
2018-01-19 22:55:35 +00:00
|
|
|
struct pgp_priv_data *priv = DRVDATA(card); /* may be null during initialization */
|
2018-01-16 20:03:30 +00:00
|
|
|
int r = SC_SUCCESS;
|
|
|
|
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
|
2018-01-19 22:55:35 +00:00
|
|
|
if (card->flags & SC_CARD_FLAG_KEEP_ALIVE
|
|
|
|
&& was_reset <= 0
|
|
|
|
&& priv != NULL && priv->mf && priv->mf->file) {
|
|
|
|
/* check whether applet is still selected */
|
|
|
|
unsigned char aid[16];
|
|
|
|
|
|
|
|
r = sc_get_data(card, 0x004F, aid, sizeof aid);
|
|
|
|
if ((size_t) r != priv->mf->file->namelen
|
|
|
|
|| 0 != memcmp(aid, priv->mf->file->name, r)) {
|
|
|
|
/* reselect is required */
|
|
|
|
was_reset = 1;
|
|
|
|
}
|
2018-02-07 15:41:52 +00:00
|
|
|
r = SC_SUCCESS;
|
2018-01-19 22:55:35 +00:00
|
|
|
}
|
|
|
|
|
2018-01-16 20:03:30 +00:00
|
|
|
if (was_reset > 0) {
|
|
|
|
sc_file_t *file = NULL;
|
|
|
|
sc_path_t path;
|
|
|
|
/* select application "OpenPGP" */
|
|
|
|
sc_format_path("D276:0001:2401", &path);
|
|
|
|
path.type = SC_PATH_TYPE_DF_NAME;
|
|
|
|
r = iso_ops->select_file(card, &path, &file);
|
|
|
|
sc_file_free(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-06-16 13:54:17 +00:00
|
|
|
/**
|
|
|
|
* API: integrate OpenPGP driver into OpenSC's driver list.
|
|
|
|
*/
|
2018-01-16 20:03:30 +00:00
|
|
|
struct sc_card_driver *
|
|
|
|
sc_get_openpgp_driver(void)
|
2003-10-30 17:04:22 +00:00
|
|
|
{
|
|
|
|
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
|
|
|
|
|
|
|
|
iso_ops = iso_drv->ops;
|
|
|
|
|
|
|
|
pgp_ops = *iso_ops;
|
2003-10-31 16:01:00 +00:00
|
|
|
pgp_ops.match_card = pgp_match_card;
|
|
|
|
pgp_ops.init = pgp_init;
|
|
|
|
pgp_ops.finish = pgp_finish;
|
|
|
|
pgp_ops.select_file = pgp_select_file;
|
|
|
|
pgp_ops.list_files = pgp_list_files;
|
2018-01-18 11:33:34 +00:00
|
|
|
pgp_ops.get_challenge = pgp_get_challenge;
|
2003-10-31 16:01:00 +00:00
|
|
|
pgp_ops.read_binary = pgp_read_binary;
|
2020-01-31 13:47:45 +00:00
|
|
|
pgp_ops.write_binary = NULL;
|
2003-10-31 16:01:00 +00:00
|
|
|
pgp_ops.pin_cmd = pgp_pin_cmd;
|
2018-01-17 20:11:16 +00:00
|
|
|
pgp_ops.logout = pgp_logout;
|
2003-10-31 16:01:00 +00:00
|
|
|
pgp_ops.get_data = pgp_get_data;
|
|
|
|
pgp_ops.put_data = pgp_put_data;
|
|
|
|
pgp_ops.set_security_env= pgp_set_security_env;
|
|
|
|
pgp_ops.compute_signature= pgp_compute_signature;
|
2003-11-20 15:41:28 +00:00
|
|
|
pgp_ops.decipher = pgp_decipher;
|
2011-05-23 17:32:00 +00:00
|
|
|
pgp_ops.card_ctl = pgp_card_ctl;
|
2012-06-01 15:59:20 +00:00
|
|
|
pgp_ops.delete_file = pgp_delete_file;
|
2012-06-07 17:55:21 +00:00
|
|
|
pgp_ops.update_binary = pgp_update_binary;
|
2018-01-16 20:03:30 +00:00
|
|
|
pgp_ops.card_reader_lock_obtained = pgp_card_reader_lock_obtained;
|
2003-10-30 17:04:22 +00:00
|
|
|
|
2006-06-17 12:24:04 +00:00
|
|
|
return &pgp_drv;
|
2003-10-30 17:04:22 +00:00
|
|
|
}
|