From ea594e3e50037e10eaabe864016233ce3c4554c5 Mon Sep 17 00:00:00 2001 From: jey Date: Tue, 20 Nov 2001 22:21:58 +0000 Subject: [PATCH] - added very partial SSH support - rearranged some functions - added several new functions - fixed handling of SW's git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@47 c6295689-39f2-0310-b995-f0e70906c6a9 --- src/libopensc/defaults.c | 9 + src/libopensc/opensc-pkcs15.h | 15 +- src/libopensc/opensc.h | 22 +- src/libopensc/pkcs15-pin.c | 44 ++-- src/libopensc/pkcs15-prkey.c | 22 ++ src/libopensc/pkcs15-sec.c | 56 +++++ src/libopensc/pkcs15.c | 12 +- src/libopensc/pkcs15.h | 15 +- src/libopensc/sc.c | 401 ++++++++++++---------------------- src/libopensc/sec.c | 260 +++++++++++++++++++--- src/openssh/opensc-ssh.c | 315 ++++++++++++++++++++++++++ src/pam/README | 12 +- src/pam/pam_pkcs15.c | 88 +++----- src/signer/Makefile | 8 +- src/signer/signer.c | 333 ++++++++++++++++++++++++++++ src/tests/hst-test.c | 203 ++++++++--------- src/tests/pintest.c | 52 +++-- src/tools/opensc-tool.c | 195 +++++++++++++---- 18 files changed, 1490 insertions(+), 572 deletions(-) create mode 100644 src/libopensc/pkcs15-sec.c create mode 100644 src/openssh/opensc-ssh.c create mode 100644 src/signer/signer.c diff --git a/src/libopensc/defaults.c b/src/libopensc/defaults.c index 5914ad2b..1b69e50a 100644 --- a/src/libopensc/defaults.c +++ b/src/libopensc/defaults.c @@ -129,7 +129,16 @@ static int fineid_pkcs15_defaults(void *arg) return 0; } +static int multiflex_defaults(void *arg) +{ + struct sc_card *card = (struct sc_card *) arg; + + card->class = 0xC0; + return 0; +} + const struct sc_defaults sc_card_table[] = { { "3B:9F:94:40:1E:00:67:11:43:46:49:53:45:10:52:66:FF:81:90:00", fineid_defaults, fineid_pkcs15_defaults }, + { "3B:19:14:55:90:01:02:02:00:05:04:B0", multiflex_defaults, NULL }, { NULL, NULL, NULL } }; diff --git a/src/libopensc/opensc-pkcs15.h b/src/libopensc/opensc-pkcs15.h index 95f434b5..57a313be 100644 --- a/src/libopensc/opensc-pkcs15.h +++ b/src/libopensc/opensc-pkcs15.h @@ -158,6 +158,11 @@ struct sc_pkcs15_card { int sc_pkcs15_init(struct sc_card *card, struct sc_pkcs15_card **pkcs15_card); int sc_pkcs15_destroy(struct sc_pkcs15_card *card); + +int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, + const struct sc_pkcs15_prkey_info *prkey, + const u8 * in, int inlen, u8 *out, int outlen); + void sc_pkcs15_print_card(const struct sc_pkcs15_card *card); void sc_pkcs15_print_cert_info(const struct sc_pkcs15_cert_info *cert); @@ -169,6 +174,9 @@ void sc_pkcs15_free_certificate(struct sc_pkcs15_cert *cert); void sc_pkcs15_print_prkey_info(const struct sc_pkcs15_prkey_info *prkey); int sc_pkcs15_enum_private_keys(struct sc_pkcs15_card *card); +int sc_pkcs15_find_prkey_by_id(struct sc_pkcs15_card *card, + const struct sc_pkcs15_id *id, + struct sc_pkcs15_prkey_info **out); void sc_pkcs15_print_pin_info(const struct sc_pkcs15_pin_info *pin); int sc_pkcs15_enum_pins(struct sc_pkcs15_card *card); @@ -179,13 +187,14 @@ int sc_pkcs15_change_pin(struct sc_pkcs15_card *card, struct sc_pkcs15_pin_info *pin, char *oldpincode, int oldpinlen, char *newpincode, int newpinlen); +int sc_pkcs15_find_pin_by_auth_id(struct sc_pkcs15_card *card, + const struct sc_pkcs15_id *id, + struct sc_pkcs15_pin_info **out); int sc_pkcs15_compare_id(const struct sc_pkcs15_id *id1, const struct sc_pkcs15_id *id2); void sc_pkcs15_print_id(const struct sc_pkcs15_id *id); - -int sc_sec_ask_pin_code(struct sc_pkcs15_pin_info *pin, - char *out, int outlen, const char *prompt); +int sc_pkcs15_hex_string_to_id(const char *in, struct sc_pkcs15_id *out); int sc_pkcs15_parse_common_object_attr(struct sc_pkcs15_common_obj_attr *attr, const u8 * buf, int buflen); diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index c56185f8..7638bece 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -21,6 +21,7 @@ #ifndef _SC_H #define _SC_H +#include #include #define SC_ERROR_MIN -1000 @@ -46,6 +47,9 @@ #define SC_ERROR_RESOURCE_MANAGER -1019 #define SC_ERROR_CARD_REMOVED -1020 #define SC_ERROR_INVALID_PIN_LENGTH -1021 +#define SC_ERROR_UNKNOWN_SMARTCARD -1022 +#define SC_ERROR_UNKNOWN_REPLY -1023 +#define SC_ERROR_OBJECT_NOT_FOUND -1024 #define SC_APDU_CASE_NONE 0 #define SC_APDU_CASE_1 1 @@ -126,6 +130,7 @@ struct sc_card { int atr_len; const struct sc_defaults *defaults; + pthread_mutex_t mutex; }; struct sc_context { @@ -142,11 +147,13 @@ struct sc_apdu { int datalen; /* length of C-APDU */ u8 *resp; /* R-APDU */ int resplen; /* length of R-APDU */ + int no_response; /* No response required */ + + int sw1, sw2; }; struct sc_security_env { int algorithm_ref; - struct sc_path app_df_path; struct sc_path key_file_id; /* signature=1 ==> digital signing, signature=0 ==> authentication */ int signature; @@ -187,6 +194,7 @@ int sc_wait_for_card(struct sc_context *ctx, int reader, int timeout); int sc_lock(struct sc_card *card); int sc_unlock(struct sc_card *card); + /* ISO 7816-4 related functions */ int sc_select_file(struct sc_card *card, struct sc_file *file, const struct sc_path *path, int pathtype); @@ -201,6 +209,12 @@ int sc_decipher(struct sc_card *card, const u8 * crgram, int crgram_len, u8 * out, int outlen); int sc_compute_signature(struct sc_card *card, const u8 * data, int data_len, u8 * out, int outlen); +int sc_verify(struct sc_card *card, int ref, const u8 *buf, int buflen, + int *tries_left); +int sc_change_reference_data(struct sc_card *card, int ref, const u8 *old, + int oldlen, const u8 *new, int newlen); +int sc_reset_retry_counter(struct sc_card *card, int ref, const u8 *puk, + int puklen, const u8 *new, int newlen); /* ISO 7816-9 */ int sc_create_file(struct sc_card *card, const struct sc_file *file); @@ -209,10 +223,14 @@ int sc_delete_file(struct sc_card *card, int file_id); /* Possibly only on Setec cards */ int sc_list_files(struct sc_card *card, u8 * buf, int buflen); -int sc_file_valid(const struct sc_file *file); const char *sc_strerror(int error); + +/* Internal use only */ +int sc_file_valid(const struct sc_file *file); void sc_hex_dump(const u8 *buf, int len); void sc_print_binary(const u8 *buf, int len); +int sc_hex_to_bin(const char *in, u8 *out, int *outlen); +int _sc_sw_to_errorcode(int sw1, int sw2); extern int sc_debug; extern const char *sc_version; diff --git a/src/libopensc/pkcs15-pin.c b/src/libopensc/pkcs15-pin.c index ec91d41a..f8554183 100644 --- a/src/libopensc/pkcs15-pin.c +++ b/src/libopensc/pkcs15-pin.c @@ -169,10 +169,8 @@ int sc_pkcs15_verify_pin(struct sc_pkcs15_card *p15card, { int r; struct sc_file file; - struct sc_apdu apdu; struct sc_card *card; char pinbuf[SC_MAX_PIN_SIZE]; - char resp[MAX_BUFFER_SIZE]; assert(p15card != NULL); if (pin->magic != SC_PKCS15_PIN_MAGIC) @@ -185,30 +183,15 @@ int sc_pkcs15_verify_pin(struct sc_pkcs15_card *p15card, if (r) return r; - sc_format_apdu(p15card->card, &apdu, SC_APDU_CASE_3_SHORT, - 0x20, 0, pin->auth_id.value[0]); - apdu.lc = pin->stored_length; - apdu.data = pinbuf; - apdu.datalen = pin->stored_length; - apdu.resp = resp; - apdu.resplen = 2; memset(pinbuf, pin->pad_char, pin->stored_length); - memcpy(pinbuf, pincode, pinlen); - r = sc_transmit_apdu(card, &apdu); + r = sc_verify(card, pin->auth_id.value[0], + pinbuf, pin->stored_length, &pin->tries_left); memset(pinbuf, 0, pinlen); - if (r) return r; - if (apdu.resplen == 2) { - if (apdu.resp[0] == 0x90 && apdu.resp[1] == 0x00) - return 0; - if (apdu.resp[0] == 0x63 && (apdu.resp[1] & 0xF0) == 0xC0) { - pin->tries_left = apdu.resp[1] & 0x0F; - return SC_ERROR_PIN_CODE_INCORRECT; - } - } - return -1; + + return 0; } int sc_pkcs15_change_pin(struct sc_pkcs15_card *p15card, @@ -263,3 +246,22 @@ int sc_pkcs15_change_pin(struct sc_pkcs15_card *p15card, } return -1; } + +int sc_pkcs15_find_pin_by_auth_id(struct sc_pkcs15_card *card, + const struct sc_pkcs15_id *id, + struct sc_pkcs15_pin_info **pin_out) +{ + int r, i; + + r = sc_pkcs15_enum_pins(card); + if (r < 0) + return r; + for (i = 0; i < card->pin_count; i++) { + struct sc_pkcs15_pin_info *pin = &card->pin_info[i]; + if (sc_pkcs15_compare_id(&pin->auth_id, id) == 1) { + *pin_out = pin; + return 0; + } + } + return SC_ERROR_OBJECT_NOT_FOUND; +} diff --git a/src/libopensc/pkcs15-prkey.c b/src/libopensc/pkcs15-prkey.c index c0fc9756..3e81b2b6 100644 --- a/src/libopensc/pkcs15-prkey.c +++ b/src/libopensc/pkcs15-prkey.c @@ -39,6 +39,9 @@ void sc_pkcs15_print_prkey_info(const struct sc_pkcs15_prkey_info *prkey) for (i = 0; i < prkey->file_id.len; i++) printf("%02X", prkey->file_id.value[i]); printf("\n"); + printf("\tAuth ID : "); + sc_pkcs15_print_id(&prkey->com_attr.auth_id); + printf("\n"); printf("\tID : "); sc_pkcs15_print_id(&prkey->id); printf("\n"); @@ -146,3 +149,22 @@ int sc_pkcs15_enum_private_keys(struct sc_pkcs15_card *card) } return card->prkey_count; } + +int sc_pkcs15_find_prkey_by_id(struct sc_pkcs15_card *card, + const struct sc_pkcs15_id *id, + struct sc_pkcs15_prkey_info **key_out) +{ + int r, i; + + r = sc_pkcs15_enum_private_keys(card); + if (r < 0) + return r; + for (i = 0; i < card->prkey_count; i++) { + struct sc_pkcs15_prkey_info *key = &card->prkey_info[i]; + if (sc_pkcs15_compare_id(&key->id, id) == 1) { + *key_out = key; + return 0; + } + } + return SC_ERROR_OBJECT_NOT_FOUND; +} diff --git a/src/libopensc/pkcs15-sec.c b/src/libopensc/pkcs15-sec.c new file mode 100644 index 00000000..8a9e4d00 --- /dev/null +++ b/src/libopensc/pkcs15-sec.c @@ -0,0 +1,56 @@ +/* + * sc-pkcs15-sec.c: PKCS#15 cryptography functions + * + * Copyright (C) 2001 Juha Yrjölä + * + * 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 + */ + +#include "sc.h" +#include "sc-pkcs15.h" +#include +#include +#include +#include + +int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, + const struct sc_pkcs15_prkey_info *prkey, + const u8 * in, int inlen, u8 *out, int outlen) +{ + int r; + struct sc_security_env senv; + + + senv.algorithm_ref = 0x02; + senv.key_file_id = prkey->file_id; + senv.signature = 0; + senv.key_ref = prkey->key_reference; + + r = sc_select_file(p15card->card, &p15card->file_app, + &p15card->file_app.path, SC_SELECT_FILE_BY_PATH); + if (r) + return r; + r = sc_restore_security_env(p15card->card, 0); /* empty SE */ + if (r) + return r; + r = sc_set_security_env(p15card->card, &senv); + if (r) + return r; + r = sc_decipher(p15card->card, in, inlen, out, outlen); + if (r) + return r; + + return 0; +} diff --git a/src/libopensc/pkcs15.c b/src/libopensc/pkcs15.c index 502efc5b..6a62660a 100644 --- a/src/libopensc/pkcs15.c +++ b/src/libopensc/pkcs15.c @@ -229,8 +229,10 @@ int sc_pkcs15_init(struct sc_card *card, return SC_ERROR_OUT_OF_MEMORY; memset(p15card, 0, sizeof(struct sc_pkcs15_card)); p15card->card = card; - - if (card->defaults != NULL && card->defaults->pkcs15_defaults_func != NULL) { + + if (card->defaults != NULL && card->defaults->pkcs15_defaults_func == NULL) + return SC_ERROR_NOT_SUPPORTED; + if (card->defaults != NULL) { card->defaults->pkcs15_defaults_func(p15card); err = sc_select_file(card, &p15card->file_tokeninfo, &p15card->file_tokeninfo.path, @@ -369,3 +371,9 @@ void sc_pkcs15_print_id(const struct sc_pkcs15_id *id) for (i = 0; i < id->len; i++) printf("%02X", id->value[i]); } + +int sc_pkcs15_hex_string_to_id(const char *in, struct sc_pkcs15_id *out) +{ + out->len = sizeof(out->value); + return sc_hex_to_bin(in, out->value, &out->len); +} diff --git a/src/libopensc/pkcs15.h b/src/libopensc/pkcs15.h index 95f434b5..57a313be 100644 --- a/src/libopensc/pkcs15.h +++ b/src/libopensc/pkcs15.h @@ -158,6 +158,11 @@ struct sc_pkcs15_card { int sc_pkcs15_init(struct sc_card *card, struct sc_pkcs15_card **pkcs15_card); int sc_pkcs15_destroy(struct sc_pkcs15_card *card); + +int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, + const struct sc_pkcs15_prkey_info *prkey, + const u8 * in, int inlen, u8 *out, int outlen); + void sc_pkcs15_print_card(const struct sc_pkcs15_card *card); void sc_pkcs15_print_cert_info(const struct sc_pkcs15_cert_info *cert); @@ -169,6 +174,9 @@ void sc_pkcs15_free_certificate(struct sc_pkcs15_cert *cert); void sc_pkcs15_print_prkey_info(const struct sc_pkcs15_prkey_info *prkey); int sc_pkcs15_enum_private_keys(struct sc_pkcs15_card *card); +int sc_pkcs15_find_prkey_by_id(struct sc_pkcs15_card *card, + const struct sc_pkcs15_id *id, + struct sc_pkcs15_prkey_info **out); void sc_pkcs15_print_pin_info(const struct sc_pkcs15_pin_info *pin); int sc_pkcs15_enum_pins(struct sc_pkcs15_card *card); @@ -179,13 +187,14 @@ int sc_pkcs15_change_pin(struct sc_pkcs15_card *card, struct sc_pkcs15_pin_info *pin, char *oldpincode, int oldpinlen, char *newpincode, int newpinlen); +int sc_pkcs15_find_pin_by_auth_id(struct sc_pkcs15_card *card, + const struct sc_pkcs15_id *id, + struct sc_pkcs15_pin_info **out); int sc_pkcs15_compare_id(const struct sc_pkcs15_id *id1, const struct sc_pkcs15_id *id2); void sc_pkcs15_print_id(const struct sc_pkcs15_id *id); - -int sc_sec_ask_pin_code(struct sc_pkcs15_pin_info *pin, - char *out, int outlen, const char *prompt); +int sc_pkcs15_hex_string_to_id(const char *in, struct sc_pkcs15_id *out); int sc_pkcs15_parse_common_object_attr(struct sc_pkcs15_common_obj_attr *attr, const u8 * buf, int buflen); diff --git a/src/libopensc/sc.c b/src/libopensc/sc.c index 4df81e58..c6f93108 100644 --- a/src/libopensc/sc.c +++ b/src/libopensc/sc.c @@ -29,18 +29,18 @@ const char *sc_version = LIBSC_VERSION; int sc_debug = 1; -static int convert_sw_to_errorcode(u8 * sw) +int _sc_sw_to_errorcode(int sw1, int sw2) { - switch (sw[0]) { + switch (sw1) { case 0x69: - switch (sw[1]) { + switch (sw2) { case 0x82: return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; default: - return SC_ERROR_UNKNOWN; + return SC_ERROR_UNKNOWN_REPLY; } case 0x6A: - switch (sw[1]) { + switch (sw2) { case 0x81: return SC_ERROR_NOT_SUPPORTED; case 0x82: @@ -50,15 +50,18 @@ static int convert_sw_to_errorcode(u8 * sw) case 0x87: return SC_ERROR_INVALID_ARGUMENTS; default: - return SC_ERROR_UNKNOWN; + return SC_ERROR_UNKNOWN_REPLY; } case 0x6D: return SC_ERROR_NOT_SUPPORTED; + case 0x6E: + return SC_ERROR_UNKNOWN_SMARTCARD; case 0x90: - if (sw[1] == 0) + if (sw2 == 0) return 0; + return SC_ERROR_UNKNOWN_REPLY; } - return SC_ERROR_UNKNOWN; + return SC_ERROR_UNKNOWN_REPLY; } void sc_hex_dump(const u8 *buf, int count) @@ -90,6 +93,35 @@ void sc_print_binary(const u8 *buf, int count) fflush(stdout); } +int sc_hex_to_bin(const char *in, u8 *out, int *outlen) +{ + int c = 0, err = 0, left; + + assert(in != NULL && out != NULL && outlen != NULL); + left = *outlen; + + while (*in) { + int byte; + + if (sscanf(in, "%02X", &byte) != 1) { + err = SC_ERROR_INVALID_ARGUMENTS; + break; + } + in += 2; + if (*in == ':') + in++; + if (left <= 0) { + err = SC_ERROR_BUFFER_TOO_SMALL; + break; + } + *out++ = byte; + left--; + c++; + } + *outlen = c; + return err; +} + int sc_check_apdu(const struct sc_apdu *apdu) { switch (apdu->cse) { @@ -170,7 +202,7 @@ static int sc_transceive_t0(struct sc_card *card, struct sc_apdu *apdu) sRecvPci.cbPciLength = 0; dwSendLength = data - s; - dwRecvLength = apdu->resplen; + dwRecvLength = apdu->resplen + 2; if (sc_debug > 2) { printf("Sending: "); sc_hex_dump(s, dwSendLength); @@ -192,8 +224,14 @@ static int sc_transceive_t0(struct sc_card *card, struct sc_apdu *apdu) } } } + if (dwRecvLength < 2) + return SC_ERROR_UNKNOWN_RESPONSE; + apdu->sw1 = r[dwRecvLength-2]; + apdu->sw2 = r[dwRecvLength-1]; + dwRecvLength -= 2; apdu->resplen = dwRecvLength; - memcpy(apdu->resp, r, dwRecvLength); + if (dwRecvLength) + memcpy(apdu->resp, r, dwRecvLength); return 0; } @@ -209,20 +247,25 @@ int sc_transmit_apdu(struct sc_card *card, struct sc_apdu *apdu) if (r) return r; if (sc_debug > 2) { - printf("Received: "); - sc_hex_dump(apdu->resp, apdu->resplen); + printf("Received (SW1=%02X SW2=%02X)", apdu->sw1, apdu->sw2); + if (apdu->resplen) { + printf(": "); + sc_hex_dump(apdu->resp, apdu->resplen); + } else + printf("\n"); } - if (apdu->resp[0] == 0x61 && apdu->resplen == 2) { + if (apdu->sw1 == 0x61 && apdu->resplen == 0) { struct sc_apdu rspapdu; BYTE rsp[MAX_BUFFER_SIZE]; - rspapdu.cla = apdu->cla; - rspapdu.cse = SC_APDU_CASE_2_SHORT; - rspapdu.ins = 0xC0; - rspapdu.p1 = rspapdu.p2 = 0; - rspapdu.le = apdu->resp[1]; + if (apdu->no_response) + return 0; + + sc_format_apdu(card, &rspapdu, SC_APDU_CASE_2_SHORT, + 0xC0, 0, 0); + rspapdu.le = apdu->sw2; rspapdu.resp = rsp; - rspapdu.resplen = apdu->resp[1] + 2; + rspapdu.resplen = apdu->sw2; r = sc_transceive_t0(card, &rspapdu); if (r) { fprintf(stderr, "Error %d when getting response\n", @@ -230,11 +273,15 @@ int sc_transmit_apdu(struct sc_card *card, struct sc_apdu *apdu) return r; } if (sc_debug > 2) { - printf("Response: "); + printf("Response (SW1=%02X SW2=%02X): ", rspapdu.sw1, + rspapdu.sw2); sc_hex_dump(rspapdu.resp, rspapdu.resplen); } + /* FIXME: Check apdu->resplen */ memcpy(apdu->resp, rspapdu.resp, rspapdu.resplen); apdu->resplen = rspapdu.resplen; + apdu->sw1 = rspapdu.sw1; + apdu->sw2 = rspapdu.sw2; } return 0; } @@ -333,7 +380,7 @@ int sc_select_file(struct sc_card *card, { struct sc_context *ctx; struct sc_apdu apdu; - char buf[MAX_BUFFER_SIZE], cmd[15]; + char buf[MAX_BUFFER_SIZE]; u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; int r, pathlen; @@ -345,12 +392,10 @@ int sc_select_file(struct sc_card *card, memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; - apdu.cse = SC_APDU_CASE_3_SHORT; - apdu.cla = card->class; - apdu.ins = 0xA4; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0, 0); apdu.resp = buf; - apdu.resplen = 2; - memcpy(cmd, "\x00\xA4", 2); + apdu.resplen = 0; + switch (pathtype) { case SC_SELECT_FILE_BY_FILE_ID: apdu.p1 = 0; @@ -376,47 +421,37 @@ int sc_select_file(struct sc_card *card, apdu.lc = pathlen; apdu.data = path; apdu.datalen = pathlen; -#if 0 - /* FIXME: Some smarter way to do this... */ - if (file == NULL || sc_file_valid(file)) { - r = sc_transceive_t0(card, &apdu); /* we don't need the response */ - if (apdu.resplen == 2 && apdu.resp[0] == 0x61) { - apdu.resp[0] = 0x90; - apdu.resp[1] = 0; - } - } else { -#endif - if (file != NULL) - memset(file, 0, sizeof(*file)); - r = sc_transmit_apdu(card, &apdu); -#if 0 - } -#endif - if (r) - return r; - if (apdu.resplen < 2) - return SC_ERROR_UNKNOWN_RESPONSE; - switch (apdu.resp[0]) { - case 0x6F: - break; - case 0x90: - case 0x00: /* proprietary coding */ - return 0; - default: - return convert_sw_to_errorcode(apdu.resp); - } - if (file == NULL) - return 0; - if (pathtype == SC_SELECT_FILE_BY_PATH) { + + if (file != NULL) { + memset(file, 0, sizeof(*file)); memcpy(&file->path.value, path, pathlen); file->path.len = pathlen; } - if (apdu.resp[0] == 0x6F) { -// int l1 = apdu.resplen - 2, l2 = apdu.resp[1]; - int l1 = apdu.resplen, l2 = apdu.resp[1]; - int len = l1 > l2 ? l2 : l1; + if (file == NULL || sc_file_valid(file)) + apdu.no_response = 1; + r = sc_transmit_apdu(card, &apdu); + if (r) + return r; + + if (apdu.no_response) { + if (apdu.sw1 == 0x61) + return 0; + return _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); + } - process_fci(file, apdu.resp + 2, len); + r = _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); + if (r) + return r; + + switch (apdu.resp[0]) { + case 0x6F: + if (file != NULL && apdu.resp[1] <= apdu.resplen) + process_fci(file, apdu.resp+2, apdu.resp[1]); + break; + case 0x00: /* proprietary coding */ + return 0; + default: + return SC_ERROR_UNKNOWN_RESPONSE; } return 0; } @@ -426,14 +461,12 @@ int sc_read_binary(struct sc_card *card, { #define RB_BUF_SIZE 250 struct sc_apdu apdu; - struct sc_context *ctx; u8 recvbuf[MAX_BUFFER_SIZE]; int r; assert(card != NULL && buf != NULL); - ctx = card->context; - - memset(&apdu, 0, sizeof(apdu)); + if (count == 0) + return 0; if (count > RB_BUF_SIZE) { int bytes_read = 0; unsigned char *p = buf; @@ -452,38 +485,25 @@ int sc_read_binary(struct sc_card *card, } return bytes_read; } - apdu.cse = SC_APDU_CASE_2_SHORT; - apdu.cla = 0; - apdu.ins = 0xB0; - apdu.p1 = (idx >> 8) & 0x7f; - apdu.p2 = idx & 0xFF; + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, + (idx >> 8) & 0x7F, idx & 0xFF); apdu.le = count; - apdu.resplen = count + 2; + apdu.resplen = count; apdu.resp = recvbuf; r = sc_transmit_apdu(card, &apdu); if (r) return r; - if (apdu.resplen == 2) { - return convert_sw_to_errorcode(apdu.resp); - } - if (apdu.resplen == count + 2) - apdu.resplen = count; + if (apdu.resplen == 0) + return _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); memcpy(buf, recvbuf, apdu.resplen); - if (sc_debug > 3) { - FILE *file = fopen("sc_recv", "w"); - if (file != NULL) { - fwrite(buf, apdu.resplen, 1, file); - fclose(file); - } - } + return apdu.resplen; } -int sc_format_apdu(struct sc_card *card, - struct sc_apdu *apdu, - unsigned char cse, - unsigned char ins, unsigned char p1, unsigned char p2) +int sc_format_apdu(struct sc_card *card, struct sc_apdu *apdu, + unsigned char cse, unsigned char ins, + unsigned char p1, unsigned char p2) { assert(card != NULL && apdu != NULL); memset(apdu, 0, sizeof(*apdu)); @@ -492,6 +512,8 @@ int sc_format_apdu(struct sc_card *card, apdu->ins = ins; apdu->p1 = p1; apdu->p2 = p2; + apdu->no_response = 0; + return 0; } @@ -691,6 +713,7 @@ int sc_connect_card(struct sc_context *ctx, } else { card->class = 0; /* FIXME */ } + pthread_mutex_init(&card->mutex, NULL); *card_out = card; return 0; @@ -700,6 +723,7 @@ int sc_disconnect_card(struct sc_card *card) { assert(card != NULL); SCardDisconnect(card->pcsc_card, SCARD_LEAVE_CARD); + pthread_mutex_destroy(&card->mutex); return 0; } @@ -727,7 +751,10 @@ const char *sc_strerror(int error) "Card not present", "Error with Resource Manager", "Card removed", - "Invalid PIN length" + "Invalid PIN length", + "Unknown SmartCard", + "Unknown reply from SmartCard", + "Requested object not found", }; int nr_errors = sizeof(errors) / sizeof(errors[0]); @@ -740,159 +767,10 @@ const char *sc_strerror(int error) return errors[error]; } -int sc_set_security_env(struct sc_card *card, - const struct sc_security_env *env) -{ - struct sc_apdu apdu; - u8 recv[MAX_BUFFER_SIZE], send[MAX_BUFFER_SIZE]; - u8 *p; - int r; - struct sc_file file; - - assert(card != NULL && env != NULL); - r = sc_select_file(card, &file, &env->app_df_path, - SC_SELECT_FILE_BY_PATH); - if (r) - return r; - apdu.cla = card->class; - apdu.cse = SC_APDU_CASE_3_SHORT; - apdu.ins = 0x22; - if (env->signature) { - apdu.p1 = 0x81; - apdu.p2 = 0xB6; - } else { - apdu.p1 = 0x41; - apdu.p2 = 0xB8; - } - apdu.le = 0; - p = send; - *p++ = 0x80; /* algorithm reference */ - *p++ = 1; - *p++ = env->algorithm_ref; - *p++ = 0x81; - *p++ = env->key_file_id.len; - memcpy(p, env->key_file_id.value, env->key_file_id.len); - p += env->key_file_id.len; - *p++ = 0x84; - *p++ = 1; - *p++ = env->key_ref; - r = p - send; - apdu.lc = r; - apdu.datalen = r; - apdu.data = send; - apdu.resplen = 2; - apdu.resp = recv; - r = sc_transmit_apdu(card, &apdu); - if (r) - return r; - if (apdu.resp[0] != 0x90) { - if (sc_debug) - fprintf(stderr, "Set sec env: SWs=%02X%02X\n", - apdu.resp[0], apdu.resp[1]); - return -1; - } - return 0; -} - -int sc_restore_security_env(struct sc_card *card, int num) -{ - struct sc_apdu apdu; - int r; - u8 rbuf[2]; - - assert(card != NULL); - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0xF3, num); - apdu.resp = rbuf; - apdu.resplen = 2; - r = sc_transmit_apdu(card, &apdu); - if (r) - return r; - if (apdu.resplen != 2) - return -1; - return convert_sw_to_errorcode(apdu.resp); -} - -int sc_decipher(struct sc_card *card, - const u8 * crgram, int crgram_len, u8 * out, int outlen) -{ - int r; - struct sc_apdu apdu; - u8 recv[MAX_BUFFER_SIZE]; - u8 send[MAX_BUFFER_SIZE], *p; - - assert(card != NULL && crgram != NULL && out != NULL); - if (crgram_len > 255) - return SC_ERROR_INVALID_ARGUMENTS; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x80, - 0x86); - apdu.resp = recv; - apdu.resplen = 2; - - send[0] = 0; /* padding indicator byte */ ; - memcpy(send + 1, crgram, crgram_len); - apdu.data = send; - apdu.lc = crgram_len + 1; - apdu.datalen = crgram_len + 1; - r = sc_transmit_apdu(card, &apdu); - if (r) - return r; - p = apdu.resp + apdu.resplen - 2; - if (p[0] == 0x90 && p[1] == 0x00) { /* FIXME */ - int l1 = apdu.resplen - 2, l2 = outlen; - int len = l1 > l2 ? l2 : l1; - - memcpy(out, apdu.resp, len); - return len; - } - if (sc_debug) - fprintf(stderr, "sc_decipher(): SW1=%02X, SW2=%02X\n", p[0], p[1]); - return convert_sw_to_errorcode(p); -} - -int sc_compute_signature(struct sc_card *card, - const u8 * data, - int datalen, u8 * out, int outlen) -{ - int r; - struct sc_apdu apdu; - u8 recv[MAX_BUFFER_SIZE], *p; - u8 send[MAX_BUFFER_SIZE]; - - assert(card != NULL && data != NULL && out != NULL); - if (datalen > 255) - return SC_ERROR_INVALID_ARGUMENTS; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x9E, - 0x9A); - apdu.resp = recv; - apdu.resplen = 2; - - memcpy(send, data, datalen); - apdu.data = send; - apdu.lc = datalen; - apdu.datalen = datalen; - r = sc_transmit_apdu(card, &apdu); - if (r) - return r; - p = apdu.resp + apdu.resplen - 2; - if (p[0] == 0x90 && p[1] == 0x00) { /* FIXME */ - int l1 = apdu.resplen - 2, l2 = outlen; - int len = l1 > l2 ? l2 : l1; - - memcpy(out, apdu.resp, len); - return len; - } - if (sc_debug) - fprintf(stderr, "sc_compute_signature(): SW1=%02X, SW2=%02X\n", - p[0], p[1]); - return convert_sw_to_errorcode(p); -} - -int sc_lock(struct sc_card *card) +int _sc_lock_int(struct sc_card *card) { long rv; - + rv = SCardBeginTransaction(card->pcsc_card); if (rv != SCARD_S_SUCCESS) @@ -901,7 +779,13 @@ int sc_lock(struct sc_card *card) return 0; } -int sc_unlock(struct sc_card *card) +int sc_lock(struct sc_card *card) +{ + pthread_mutex_lock(&card->mutex); + return _sc_lock_int(card); +} + +int _sc_unlock_int(struct sc_card *card) { long rv; @@ -911,6 +795,12 @@ int sc_unlock(struct sc_card *card) return 0; } +int sc_unlock(struct sc_card *card) +{ + pthread_mutex_unlock(&card->mutex); + return _sc_unlock_int(card); +} + int sc_get_random(struct sc_card *card, u8 *rnd, int len) { int r; @@ -921,7 +811,7 @@ int sc_get_random(struct sc_card *card, u8 *rnd, int len) 0x84, 0x00, 0x00); apdu.le = 8; apdu.resp = buf; - apdu.resplen = 10; /* include SW's */ + apdu.resplen = 8; /* include SW's */ while (len > 0) { int n = len > 8 ? 8 : len; @@ -929,11 +819,8 @@ int sc_get_random(struct sc_card *card, u8 *rnd, int len) r = sc_transmit_apdu(card, &apdu); if (r) return r; - if (apdu.resplen != 10) { - if (apdu.resplen == 2) - return convert_sw_to_errorcode(apdu.resp); - return SC_ERROR_UNKNOWN; - } + if (apdu.resplen != 8) + return _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); memcpy(rnd, apdu.resp, n); len -= n; rnd += n; @@ -953,12 +840,8 @@ int sc_list_files(struct sc_card *card, u8 *buf, int buflen) r = sc_transmit_apdu(card, &apdu); if (r) return r; - if (apdu.resplen < 2) - return -1; /* FIXME */ - if (apdu.resplen == 2) - return convert_sw_to_errorcode(apdu.resp); - apdu.resplen -= 2; - + if (apdu.resplen == 0) + return _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); return apdu.resplen; } @@ -1018,11 +901,7 @@ int sc_create_file(struct sc_card *card, const struct sc_file *file) r = sc_transmit_apdu(card, &apdu); if (r) return r; - if (apdu.resplen != 2) - return -1; - if (apdu.resp[0] == 0x90 && apdu.resp[1] == 0x00) - return 0; - return convert_sw_to_errorcode(apdu.resp); + return _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); } int sc_delete_file(struct sc_card *card, int file_id) @@ -1046,9 +925,7 @@ int sc_delete_file(struct sc_card *card, int file_id) return r; if (apdu.resplen != 2) return -1; - if (apdu.resp[0] == 0x90 && apdu.resp[1] == 0x00) - return 0; - return convert_sw_to_errorcode(apdu.resp); + return _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); } int sc_file_valid(const struct sc_file *file) diff --git a/src/libopensc/sec.c b/src/libopensc/sec.c index dd5ffe05..ae207fcc 100644 --- a/src/libopensc/sec.c +++ b/src/libopensc/sec.c @@ -1,40 +1,240 @@ - -/* Copyright (C) 2001 Juha Yrjölä - * All rights reserved. +/* + * sc-sec.c: Cryptography and security (ISO7816-8) functions + * + * Copyright (C) 2001 Juha Yrjölä + * + * 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 */ #include "sc.h" -#include "sc-pkcs15.h" #include #include -#include +#include -int sc_sec_ask_pin_code(struct sc_pkcs15_pin_info *pin, - char *out, int outlen, const char *prompt) +int sc_set_security_env(struct sc_card *card, + const struct sc_security_env *env) { - char buf[80]; - int i; + struct sc_apdu apdu; + u8 sbuf[MAX_BUFFER_SIZE]; + u8 *p; + int r; - while (1) { - printf("%s [%s]: ", prompt, pin->com_attr.label); - fflush(stdin); - memset(buf, 0, sizeof(buf)); - if (fgets(buf, 80, stdin) == NULL) - return -1; - i = 0; - while (isdigit(buf[i])) { - out[i] = buf[i]; - i++; - if (i >= outlen) - continue; - } - out[i] = 0; - if (i < pin->min_length) - continue; - if (i > pin->stored_length) - continue; - memset(buf, 0, sizeof(buf)); - break; + assert(card != NULL && env != NULL); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); + if (env->signature) { + apdu.p1 = 0x81; + apdu.p2 = 0xB6; + } else { + apdu.p1 = 0x41; + apdu.p2 = 0xB8; } - return 0; + apdu.le = 0; + p = sbuf; + *p++ = 0x80; /* algorithm reference */ + *p++ = 1; + *p++ = env->algorithm_ref; + *p++ = 0x81; + *p++ = env->key_file_id.len; + memcpy(p, env->key_file_id.value, env->key_file_id.len); + p += env->key_file_id.len; + *p++ = 0x84; + *p++ = 1; + *p++ = env->key_ref; + r = p - sbuf; + apdu.lc = r; + apdu.datalen = r; + apdu.data = sbuf; + apdu.resplen = 0; + r = sc_transmit_apdu(card, &apdu); + if (r) + return r; + return _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); +} + +int sc_restore_security_env(struct sc_card *card, int num) +{ + struct sc_apdu apdu; + int r; + + assert(card != NULL); + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0xF3, num); + apdu.resplen = 0; + r = sc_transmit_apdu(card, &apdu); + if (r) + return r; + return _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); +} + +int sc_decipher(struct sc_card *card, + const u8 * crgram, int crgram_len, u8 * out, int outlen) +{ + int r; + struct sc_apdu apdu; + u8 rbuf[MAX_BUFFER_SIZE]; + u8 sbuf[MAX_BUFFER_SIZE]; + + assert(card != NULL && crgram != NULL && out != NULL); + if (crgram_len > 255) + return SC_ERROR_INVALID_ARGUMENTS; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x80, 0x86); + apdu.resp = rbuf; + apdu.resplen = 2; /* FIXME */ + + sbuf[0] = 0; /* padding indicator byte */ ; + memcpy(sbuf + 1, crgram, crgram_len); + apdu.data = sbuf; + apdu.lc = crgram_len + 1; + apdu.datalen = crgram_len + 1; + r = sc_transmit_apdu(card, &apdu); + if (r) + return r; + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + int len = apdu.resplen > outlen ? outlen : apdu.resplen; + + memcpy(out, apdu.resp, len); + return len; + } + if (sc_debug) + fprintf(stderr, "sc_decipher(): SW1=%02X, SW2=%02X\n", + apdu.sw1, apdu.sw2); + return _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); +} + +int sc_compute_signature(struct sc_card *card, + const u8 * data, + int datalen, u8 * out, int outlen) +{ + int r; + struct sc_apdu apdu; + u8 rbuf[MAX_BUFFER_SIZE]; + u8 sbuf[MAX_BUFFER_SIZE]; + + assert(card != NULL && data != NULL && out != NULL); + if (datalen > 255) + return SC_ERROR_INVALID_ARGUMENTS; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x9E, + 0x9A); + apdu.resp = rbuf; + apdu.resplen = 2; /* FIXME */ + + memcpy(sbuf, data, datalen); + apdu.data = sbuf; + apdu.lc = datalen; + apdu.datalen = datalen; + r = sc_transmit_apdu(card, &apdu); + if (r) + return r; + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + int len = apdu.resplen > outlen ? outlen : apdu.resplen; + + memcpy(out, apdu.resp, len); + return len; + } + if (sc_debug) + fprintf(stderr, "sc_compute_signature(): SW1=%02X, SW2=%02X\n", + apdu.sw1, apdu.sw2); + return _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); +} + +int sc_verify(struct sc_card *card, int ref, const u8 *pin, int pinlen, + int *tries_left) +{ + struct sc_apdu apdu; + u8 sbuf[MAX_BUFFER_SIZE]; + int r; + + if (pinlen >= MAX_BUFFER_SIZE) + return SC_ERROR_INVALID_ARGUMENTS; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, ref); + memcpy(sbuf, pin, pinlen); + apdu.lc = pinlen; + apdu.datalen = pinlen; + apdu.data = sbuf; + apdu.resplen = 0; + + r = sc_transmit_apdu(card, &apdu); + memset(sbuf, 0, pinlen); + if (r) + return r; + if (apdu.sw1 == 0x63 && (apdu.sw2 && 0xF0) == 0xC0) { + if (tries_left != NULL) + *tries_left = apdu.sw2 & 0x0F; + return SC_ERROR_PIN_CODE_INCORRECT; + } + return _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); +} + +int sc_change_reference_data(struct sc_card *card, int ref, const u8 *old, + int oldlen, const u8 *new, int newlen) +{ + struct sc_apdu apdu; + u8 sbuf[MAX_BUFFER_SIZE]; + int r, p1 = 0, len = oldlen + newlen; + + if (len >= MAX_BUFFER_SIZE) + return SC_ERROR_INVALID_ARGUMENTS; + if (oldlen == 0) + p1 = 1; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, p1, ref); + memcpy(sbuf, old, oldlen); + memcpy(sbuf + oldlen, new, newlen); + apdu.lc = len; + apdu.datalen = len; + apdu.data = sbuf; + apdu.resplen = 0; + + r = sc_transmit_apdu(card, &apdu); + memset(sbuf, 0, len); + if (r) + return r; + return _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); +} + +int sc_reset_retry_counter(struct sc_card *card, int ref, const u8 *puk, + int puklen, const u8 *new, int newlen) +{ + struct sc_apdu apdu; + u8 sbuf[MAX_BUFFER_SIZE]; + int r, p1 = 0, len = puklen + newlen; + + if (len >= MAX_BUFFER_SIZE) + return SC_ERROR_INVALID_ARGUMENTS; + if (puklen == 0) { + if (newlen == 0) + p1 = 3; + else + p1 = 2; + } else { + if (newlen == 0) + p1 = 1; + else + p1 = 0; + } + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2C, p1, ref); + memcpy(sbuf, puk, puklen); + memcpy(sbuf + puklen, new, newlen); + apdu.lc = len; + apdu.datalen = len; + apdu.data = sbuf; + apdu.resplen = 0; + + r = sc_transmit_apdu(card, &apdu); + memset(sbuf, 0, len); + if (r) + return r; + return _sc_sw_to_errorcode(apdu.sw1, apdu.sw2); } diff --git a/src/openssh/opensc-ssh.c b/src/openssh/opensc-ssh.c new file mode 100644 index 00000000..b2442567 --- /dev/null +++ b/src/openssh/opensc-ssh.c @@ -0,0 +1,315 @@ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +int quiet = 0; +char *opt_outfile = NULL; +char *opt_cert = NULL; + +const struct option options[] = { + { "extract-key", 0, 0, 'k' }, + { "certificate-id", 1, 0, 'c' }, + { "reader", 1, 0, 'r' }, + { "output", 1, 0, 'o' }, + { "quiet", 0, 0, 'q' }, + + { 0, 0, 0, 0 } +}; + +const char *option_help[] = { + "Extracts the public key from a certificate", + "Uses certificate with ID ", + "Uses reader number ", + "Outputs to file ", + "Quiet operation", +}; + +struct sc_context *ctx = NULL; +struct sc_card *card = NULL; +struct sc_pkcs15_card *p15card = NULL; + +void print_usage_and_die() +{ + int i = 0; + printf("Usage: sc-ssh [OPTIONS]\nOptions:\n"); + + while (options[i].name) { + char buf[40], tmp[5]; + const char *arg_str; + + if (options[i].val > 0 && options[i].val < 128) + sprintf(tmp, ", -%c", options[i].val); + else + tmp[0] = 0; + switch (options[i].has_arg) { + case 1: + arg_str = " "; + break; + case 2: + arg_str = " [arg]"; + break; + default: + arg_str = ""; + break; + } + sprintf(buf, "--%s%s%s", options[i].name, tmp, arg_str); + printf(" %-30s%s\n", buf, option_help[i]); + i++; + } + exit(2); +} + +u8 * bignum_to_buf(BIGNUM *value, int *length, int *skip) +{ + /* Function ripped from bufaux.c in OpenSSH + * Compliments to Tatu Ylönen */ + int bytes = BN_num_bytes(value) + 1; + u8 *buf = malloc(bytes); + int oi; + int hasnohigh = 0; + buf[0] = '\0'; + + if (buf == NULL) + return NULL; + /* Get the value of in binary */ + oi = BN_bn2bin(value, buf+1); + if (oi != bytes-1) + return NULL; + hasnohigh = (buf[1] & 0x80) ? 0 : 1; + if (value->neg) { + /**XXX should be two's-complement */ + int i, carry; + u_char *uc = buf; + for(i = bytes-1, carry = 1; i>=0; i--) { + uc[i] ^= 0xff; + if(carry) + carry = !++uc[i]; + } + } + *skip = hasnohigh; + *length = bytes; + + return buf; +} + +int put_string(const u8 *in, int inlen, u8 *out, int outlen, int *skip) +{ + u8 *out0 = out; + + if (outlen < 4 + inlen) + return -1; + *out++ = (inlen >> 24) & 0xFF; + *out++ = (inlen >> 16) & 0xFF; + *out++ = (inlen >> 8) & 0xFF; + *out++ = (inlen) & 0xFF; + memcpy(out, in, inlen); + out += inlen; + + *skip = out - out0; + + return 0; +} + +int write_ssh_key(struct sc_pkcs15_cert_info *cinfo, RSA *rsa) +{ + u8 *buf = malloc(10240), *p = buf, *num; + int r, len, skip, left = 10240; + + if (buf == NULL) + return 1; + put_string("ssh-rsa", 7, p, left, &skip); + left -= skip; + p += skip; + num = bignum_to_buf(rsa->e, &len, &skip); + if (num == NULL) + return 1; + put_string(num+skip, len-skip, p, left, &skip); + left -= skip; + p += skip; + free(num); + num = bignum_to_buf(rsa->n, &len, &skip); + if (num == NULL) + return 1; + put_string(num+skip, len-skip, p, left, &skip); + left -= skip; + p += skip; + free(num); + + len = p - buf; + p = malloc(len*5/3); + r = sc_base64_encode(buf, len, p, len*5/3, 0); + if (r) { + fprintf(stderr, "Base64 encoding failed: %s\n", sc_strerror(r)); + return 1; + } + printf("ssh-rsa %s libsc-cert-%02X\n", p, cinfo->id.value[0]); + free(p), + free(buf); + return 0; +} + +int extract_key() +{ + int r, i; + struct sc_pkcs15_id id; + u8 *p = id.value; + char *certp = opt_cert; + struct sc_pkcs15_cert_info *cinfo; + struct sc_pkcs15_cert *cert; + X509 *x509; + EVP_PKEY *pubkey; + + if (opt_cert) { + if (strlen(opt_cert)/2 >= SC_PKCS15_MAX_ID_SIZE) { + fprintf(stderr, "Certificate id too long.\n"); + return 2; + } + id.len = 0; + while (*certp) { + int byte; + + if (sscanf(certp, "%02X", &byte) != 1) + break; + certp += 2; + *p = byte; + p++; + id.len++; + } + } + r = sc_pkcs15_enum_certificates(p15card); + if (r < 0) { + fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r)); + return 1; + } + if (opt_cert) { + for (i = 0; i < p15card->cert_count; i++) { + cinfo = &p15card->cert_info[i]; + + if (sc_pkcs15_compare_id(&id, &cinfo->id) == 1) + break; + } + if (i == p15card->cert_count) { + fprintf(stderr, "Certificate with ID '%s' not found.\n", opt_cert); + return 2; + } + } else { + cinfo = &p15card->cert_info[0]; + } + if (!quiet) + fprintf(stderr, "Using certificate '%s'.\n", cinfo->com_attr.label); + r = sc_pkcs15_read_certificate(p15card, cinfo, &cert); + if (r) { + fprintf(stderr, "Certificate read failed: %s\n", sc_strerror(r)); + return 1; + } + x509 = X509_new(); + p = cert->data; + if (!d2i_X509(&x509, &p, cert->data_len)) { + fprintf(stderr, "Unable to parse X.509 certificate.\n"); + return 1; + } + pubkey = X509_get_pubkey(x509); + if (pubkey->type != EVP_PKEY_RSA) { + fprintf(stderr, "Public key is of unknown type.\n"); + return 1; + } + r = write_ssh_key(cinfo, pubkey->pkey.rsa); + EVP_PKEY_free(pubkey); + X509_free(x509); + + return r; +} + +int main(int argc, char *const argv[]) +{ + int err = 0, r, c, long_optind = 0; + int action_count = 0, do_extract_key = 0; + int opt_reader = 0; + + while (1) { + c = getopt_long(argc, argv, "r:o:qkc:", options, &long_optind); + if (c == -1) + break; + if (c == '?') + continue; + switch (c) { + case 'k': + do_extract_key = 1; + action_count++; + break; + case 'c': + opt_cert = optarg; + break; + case 'r': + opt_reader = atoi(optarg); + break; + case 'o': + opt_outfile = optarg; + break; + case 'q': + quiet++; + break; + } + } + if (action_count == 0) + print_usage_and_die(); + r = sc_establish_context(&ctx); + if (r) { + fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); + return 1; + } + if (opt_reader >= ctx->reader_count || opt_reader < 0) { + fprintf(stderr, "Illegal reader number. Only %d reader(s) configured.\n", ctx->reader_count); + err = 1; + goto end; + } + if (sc_detect_card(ctx, opt_reader) != 1) { + fprintf(stderr, "Card not present.\n"); + return 3; + } + if (!quiet) + fprintf(stderr, "Connecting to card in reader %s...\n", ctx->readers[opt_reader]); + r = sc_connect_card(ctx, opt_reader, &card); + if (r) { + fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(r)); + err = 1; + goto end; + } + + sc_lock(card); + if (!quiet) + fprintf(stderr, "Trying to find a PKCS#15 compatible card...\n"); + r = sc_pkcs15_init(card, &p15card); + if (r) { + fprintf(stderr, "PKCS#15 initialization failed: %s\n", sc_strerror(r)); + err = 1; + goto end; + } + if (!quiet) + fprintf(stderr, "Found %s!\n", p15card->label); + if (do_extract_key) { + if ((err = extract_key())) + goto end; + action_count--; + } + +end: + if (p15card) + sc_pkcs15_destroy(p15card); + if (card) { + sc_unlock(card); + sc_disconnect_card(card); + } + if (ctx) + sc_destroy_context(ctx); + return err; +} diff --git a/src/pam/README b/src/pam/README index 7d0fa780..17ef2c93 100644 --- a/src/pam/README +++ b/src/pam/README @@ -1,14 +1,16 @@ Steps required to make the PAM module work: -1. Compile and link the pam_pkcs15.so shared library. +1. Compile and link the pam_pkcs15.so shared library (type 'make'). 2. Copy pam_pkcs.so to /lib/security. 3. Edit /etc/pam.d/login file. Add the following line before the - pam_unix.so (on Debian) or pam_pwdb.so (on Red Hat) entry: + pam_unix.so (Debian) or pam_pwdb.so (Red Hat) entry: auth sufficient /lib/security/pam_pkcs15.so 4. Copy your PEM encoded certificate to a file in your home directory called '.eid/authorized_certificates'. - NOTE: You can use the 'certtest' program to get the PEM encoded - certificate. + NOTE: You can use the 'sc-tool -r -o ~/.eid/authorized_certificates' + command to get the PEM encoded certificate. Find the ID by saying + 'sc-tool -c'. FINEID card owners should use 45 for the ID. 5. Try to login with your card in the reader. - + If it doesn't work, remove -DNDEBUG from the CFLAGS line in Makefile, + recompile and try again. diff --git a/src/pam/pam_pkcs15.c b/src/pam/pam_pkcs15.c index f733959d..cf12c751 100644 --- a/src/pam/pam_pkcs15.c +++ b/src/pam/pam_pkcs15.c @@ -34,8 +34,6 @@ static const char *auth_cert_file = "authorized_certificates"; static int pamdebug = 1; -static const struct pam_message msg_pin = -{ PAM_PROMPT_ECHO_OFF, "Enter PIN 1: " }; static int format_eid_dir_path(const char *user, char **buf) { @@ -133,23 +131,31 @@ static int get_random(u8 *buf, int len) } #ifndef TEST -static int get_password(pam_handle_t * pamh, char **password) +static int get_password(pam_handle_t * pamh, char **password, const char *pinname) { int r; DBG(printf("Trying to get AUTHTOK...\n")); r = pam_get_item(pamh, PAM_AUTHTOK, (void *) password); if (!*password) { - const struct pam_message *msg[1] = { &msg_pin }; + char buf[50]; + struct pam_message msg_template = + { PAM_PROMPT_ECHO_OFF, buf }; + const struct pam_message *pin_msg[1] = { &msg_template }; struct pam_response *resp; struct pam_conv *conv; + char tmp[30]; + + strncpy(tmp, pinname, sizeof(tmp)-1); + tmp[sizeof(tmp)-1] = 0; + sprintf(buf, "Enter PIN [%s]: ", tmp); DBG(printf("failed; Trying to get CONV object...\n")); r = pam_get_item(pamh, PAM_CONV, (const void **) &conv); if (r != PAM_SUCCESS) return r; DBG(printf("Conversing...\n")); - r = conv->conv(1, msg, &resp, conv->appdata_ptr); + r = conv->conv(1, pin_msg, &resp, conv->appdata_ptr); if (r != PAM_SUCCESS) return r; if (resp) { @@ -164,23 +170,6 @@ static int get_password(pam_handle_t * pamh, char **password) } #endif -static int set_sec_env(struct sc_card *card, const struct sc_pkcs15_card *p15card, - const struct sc_pkcs15_prkey_info *prkinfo) -{ - struct sc_security_env senv; - int r; - - senv.signature = 0; - senv.algorithm_ref = 0x02; - senv.key_ref = 0; - senv.key_file_id = prkinfo->file_id; - senv.app_df_path = p15card->file_app.path; - r = sc_set_security_env(p15card->card, &senv); - if (r) - return PAM_AUTH_ERR; - return PAM_SUCCESS; -} - #ifdef TEST int main(int argc, const char **argv) #else @@ -194,7 +183,8 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, con EVP_PKEY *pubkey = NULL; struct sc_pkcs15_cert_info *cinfo; struct sc_pkcs15_prkey_info *prkinfo = NULL; - + struct sc_pkcs15_pin_info *pinfo = NULL; + u8 random_data[117]; u8 chg[128]; u8 plain_text[128]; @@ -272,38 +262,27 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, con for (i = 0; i < p15card->cert_count; i++) { } cinfo = &p15card->cert_info[0]; /* FIXME */ - DBG(printf("Enumerating private keys...\n")); - r = sc_pkcs15_enum_private_keys(p15card); - if (r <= 0) - goto end; - for (i = 0; i < p15card->prkey_count; i++) { - struct sc_pkcs15_prkey_info *tmp = &p15card->prkey_info[i]; - - if (sc_pkcs15_compare_id(&tmp->id, &cinfo->id)) { - prkinfo = tmp; - break; - } - } - if (prkinfo == NULL) - goto end; - /* FIXME: Determine if PIN code needed */ - DBG(printf("Enumerating PIN codes...\n")); - r = sc_pkcs15_enum_pins(p15card); + DBG(printf("Finding private key...\n")); + r = sc_pkcs15_find_prkey_by_id(p15card, &cinfo->id, &prkinfo); if (r < 0) goto end; - DBG(printf("Asking for PIN code...\n")); + DBG(printf("Finding PIN code...\n")); + r = sc_pkcs15_find_pin_by_auth_id(p15card, &prkinfo->com_attr.auth_id, &pinfo); + if (r < 0) + goto end; + DBG(printf("Asking for PIN code '%s'...\n", pinfo->com_attr.label)); #ifdef TEST password = strdup(getpass("Enter PIN1: ")); r = 0; #else - r = get_password(pamh, &password); + r = get_password(pamh, &password, pinfo->com_attr.label); #endif if (r) { err = r; goto end; } DBG(printf("Verifying PIN code...\n")); - r = sc_pkcs15_verify_pin(p15card, &p15card->pin_info[0], password, strlen(password)); + r = sc_pkcs15_verify_pin(p15card, pinfo, password, strlen(password)); if (r) { DBG(printf("PIN code verification failed: %s\n", sc_strerror(r))); if (r == SC_ERROR_CARD_REMOVED) @@ -312,26 +291,19 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, con } DBG(printf("Awright! PIN code correct!\n")); /* FIXME: clear password? */ -#if 0 - r = sc_restore_security_env(card, 0); /* empty SE */ - if (r) - goto end; -#endif - r = set_sec_env(card, p15card, prkinfo); - if (r != PAM_SUCCESS) { - err = r; + DBG(printf("Deciphering...\n")); + r = sc_pkcs15_decipher(p15card, prkinfo, chg, sizeof(chg), plain_text, sizeof(plain_text)); + if (r <= 0) { + DBG(printf("Decipher failed: %s\n", sc_strerror(r))); goto end; } - DBG(printf("Deciphering...\n")); - r = sc_decipher(card, chg, sizeof(chg), plain_text, sizeof(plain_text)); - if (r <= 0) - goto end; if (r != sizeof(random_data)) goto end; - if (memcmp(random_data, plain_text, sizeof(random_data)) != 0) + if (memcmp(random_data, plain_text, sizeof(random_data)) != 0) { + DBG(printf("No luck this time.\n")); goto end; - - DBG(printf("Cool, dude! You're in!\n")); + } + DBG(printf("You're in!\n")); err = PAM_SUCCESS; end: if (locked) diff --git a/src/signer/Makefile b/src/signer/Makefile index e7e65c67..5d741328 100644 --- a/src/signer/Makefile +++ b/src/signer/Makefile @@ -5,8 +5,8 @@ CC= gcc OPTIMIZER= -g CFLAGS=-Wall $(OPTIMIZER) $(PLUGIN_DEFINES) -SRC= UnixShell.c stubs.c -OBJ= UnixShell.o stubs.o +SRC= signer.c stubs.c +OBJ= signer.o stubs.o SHAREDTARGET=signer.so @@ -15,8 +15,8 @@ default all: $(SHAREDTARGET) $(SHAREDTARGET): $(OBJ) $(CC) -shared -o $(SHAREDTARGET) $(OBJ) $(LDFLAGS) -UnixShell.o: UnixShell.c - $(CC) -c $(CFLAGS) UnixShell.c +signer.o: signer.c + $(CC) -c $(CFLAGS) signer.c stubs.o: stubs.c $(CC) -c $(CFLAGS) stubs.c diff --git a/src/signer/signer.c b/src/signer/signer.c new file mode 100644 index 00000000..e8b386a3 --- /dev/null +++ b/src/signer/signer.c @@ -0,0 +1,333 @@ +/* -*- Mode: C; tab-width: 4; -*- */ +/****************************************************************************** + * Copyright (c) 1996 Netscape Communications. All rights reserved. + ******************************************************************************/ +/* + * UnixShell.c + * + * Netscape Client Plugin API + * - Function that need to be implemented by plugin developers + * + * This file defines a "Template" plugin that plugin developers can use + * as the basis for a real plugin. This shell just provides empty + * implementations of all functions that the plugin can implement + * that will be called by Netscape (the NPP_xxx methods defined in + * npapi.h). + * + * dp Suresh + * + */ + +#include +#include "npapi.h" + +/*********************************************************************** + * Instance state information about the plugin. + * + * PLUGIN DEVELOPERS: + * Use this struct to hold per-instance information that you'll + * need in the various functions in this file. + ***********************************************************************/ + +typedef struct _PluginInstance +{ + int nothing; +} PluginInstance; + + +/*********************************************************************** + * + * Empty implementations of plugin API functions + * + * PLUGIN DEVELOPERS: + * You will need to implement these functions as required by your + * plugin. + * + ***********************************************************************/ + +char* +NPP_GetMIMEDescription(void) +{ + return("text/x-text-to-sign:sample:Text to be signed"); +} + +NPError +NPP_GetValue(NPP instance, NPPVariable variable, void *value) +{ + NPError err = NPERR_NO_ERROR; + + switch (variable) { + case NPPVpluginNameString: + *((char **)value) = "Template plugin"; + break; + case NPPVpluginDescriptionString: + *((char **)value) = + "This plugins handles nothing. This is only" + " a template."; + break; + default: + err = NPERR_GENERIC_ERROR; + } + return err; +} + +NPError +NPP_Initialize(void) +{ + printf("NPP_Initialize()\n"); + return NPERR_NO_ERROR; +} + + +jref +NPP_GetJavaClass() +{ + return NULL; +} + +void +NPP_Shutdown(void) +{ +} + + +NPError +NPP_New(NPMIMEType pluginType, + NPP instance, + uint16 mode, + int16 argc, + char* argn[], + char* argv[], + NPSavedData* saved) +{ + PluginInstance* This; + int i; + + printf("NPP_New() called, attributes:\n"); + for (i = 0; i < argc; i++) { + printf("'%s' = '%s'\n", argn[i], argv[i]); + } + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + instance->pdata = NPN_MemAlloc(sizeof(PluginInstance)); + + This = (PluginInstance*) instance->pdata; + + if (This != NULL) + return NPERR_NO_ERROR; + else + return NPERR_OUT_OF_MEMORY_ERROR; +} + + +NPError +NPP_Destroy(NPP instance, NPSavedData** save) +{ + PluginInstance* This; + + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + This = (PluginInstance*) instance->pdata; + + /* PLUGIN DEVELOPERS: + * If desired, call NP_MemAlloc to create a + * NPSavedDate structure containing any state information + * that you want restored if this plugin instance is later + * recreated. + */ + + if (This != NULL) { + NPN_MemFree(instance->pdata); + instance->pdata = NULL; + } + + return NPERR_NO_ERROR; +} + + + +NPError +NPP_SetWindow(NPP instance, NPWindow* window) +{ + PluginInstance* This; + printf("NPP_SetWindow()\n"); + + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + if (window == NULL) + return NPERR_NO_ERROR; + + This = (PluginInstance*) instance->pdata; + + /* + * PLUGIN DEVELOPERS: + * Before setting window to point to the + * new window, you may wish to compare the new window + * info to the previous window (if any) to note window + * size changes, etc. + */ + + return NPERR_NO_ERROR; +} + + +NPError +NPP_NewStream(NPP instance, + NPMIMEType type, + NPStream *stream, + NPBool seekable, + uint16 *stype) +{ + NPByteRange range; + PluginInstance* This; + printf("NPP_NewStream()\n"); + + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + This = (PluginInstance*) instance->pdata; + + return NPERR_NO_ERROR; +} + + +/* PLUGIN DEVELOPERS: + * These next 2 functions are directly relevant in a plug-in which + * handles the data in a streaming manner. If you want zero bytes + * because no buffer space is YET available, return 0. As long as + * the stream has not been written to the plugin, Navigator will + * continue trying to send bytes. If the plugin doesn't want them, + * just return some large number from NPP_WriteReady(), and + * ignore them in NPP_Write(). For a NP_ASFILE stream, they are + * still called but can safely be ignored using this strategy. + */ + +int32 STREAMBUFSIZE = 0X0FFFFFFF; /* If we are reading from a file in NPAsFile + * mode so we can take any size stream in our + * write call (since we ignore it) */ + +int32 +NPP_WriteReady(NPP instance, NPStream *stream) +{ + PluginInstance* This; + if (instance != NULL) + This = (PluginInstance*) instance->pdata; + printf("NPP_WriteReady()\n"); + return STREAMBUFSIZE; +} + + +int32 +NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer) +{ + if (instance != NULL) + { + PluginInstance* This = (PluginInstance*) instance->pdata; + } + printf("NPP_Write(offset %d, len %d)\n", offset, len); + + return len; /* The number of bytes accepted */ +} + + +NPError +NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason) +{ + PluginInstance* This; + + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + This = (PluginInstance*) instance->pdata; + printf("NPP_DestroyStream()\n"); + + return NPERR_NO_ERROR; +} + + +void +NPP_StreamAsFile(NPP instance, NPStream *stream, const char* fname) +{ + PluginInstance* This; + FILE *inf, *outf; + unsigned char buf[1024]; + int i; + + if (instance != NULL) + This = (PluginInstance*) instance->pdata; + printf("NPP_StreamAsFile('%s')\n", fname); + inf = fopen(fname, "r"); + if (inf == NULL) + return; /* FIXME */ + i = 0; + outf = fopen("/tmp/empty.sgn", "w"); + if (outf == NULL) { + fclose(inf); + return; + } + while ((i = fread(buf, 1, 1024, inf)) > 0) { + fwrite(buf, 1, i, outf); + } + fclose(outf); + fclose(inf); + +} + + +void +NPP_Print(NPP instance, NPPrint* printInfo) +{ + if(printInfo == NULL) + return; + + if (instance != NULL) { + PluginInstance* This = (PluginInstance*) instance->pdata; + + if (printInfo->mode == NP_FULL) { + /* + * PLUGIN DEVELOPERS: + * If your plugin would like to take over + * printing completely when it is in full-screen mode, + * set printInfo->pluginPrinted to TRUE and print your + * plugin as you see fit. If your plugin wants Netscape + * to handle printing in this case, set + * printInfo->pluginPrinted to FALSE (the default) and + * do nothing. If you do want to handle printing + * yourself, printOne is true if the print button + * (as opposed to the print menu) was clicked. + * On the Macintosh, platformPrint is a THPrint; on + * Windows, platformPrint is a structure + * (defined in npapi.h) containing the printer name, port, + * etc. + */ + + void* platformPrint = + printInfo->print.fullPrint.platformPrint; + NPBool printOne = + printInfo->print.fullPrint.printOne; + + /* Do the default*/ + printInfo->print.fullPrint.pluginPrinted = FALSE; + } + else { /* If not fullscreen, we must be embedded */ + /* + * PLUGIN DEVELOPERS: + * If your plugin is embedded, or is full-screen + * but you returned false in pluginPrinted above, NPP_Print + * will be called with mode == NP_EMBED. The NPWindow + * in the printInfo gives the location and dimensions of + * the embedded plugin on the printed page. On the + * Macintosh, platformPrint is the printer port; on + * Windows, platformPrint is the handle to the printing + * device context. + */ + + NPWindow* printWindow = + &(printInfo->print.embedPrint.window); + void* platformPrint = + printInfo->print.embedPrint.platformPrint; + } + } +} diff --git a/src/tests/hst-test.c b/src/tests/hst-test.c index 8d949744..cf6e687e 100644 --- a/src/tests/hst-test.c +++ b/src/tests/hst-test.c @@ -11,40 +11,19 @@ #include #include "sc-test.h" -#define DO_PRKEY_ENUM 0 -#define DO_PIN_ENUM 0 -#define DO_PIN_VERIFY 0 -#define DO_DECIPHER 0 -#define DO_SIGN 0 -#define DO_TEST 0 - struct sc_pkcs15_card *p15card; -int enum_private_keys() -{ - int i; - i = sc_pkcs15_enum_private_keys(p15card); - if (i < 0) { - fprintf(stderr, "Private key enumeration failed with %s\n", - sc_strerror(i)); - return 1; - } - - printf("%d private keys found!\n", i); - for (i = 0; i < p15card->prkey_count; i++) { - sc_pkcs15_print_prkey_info(&p15card->prkey_info[i]); - } - return 0; -} - int test() { struct sc_file file; struct sc_path path; + struct sc_apdu apdu; + u8 rbuf[MAX_BUFFER_SIZE], sbuf[MAX_BUFFER_SIZE]; int r; sc_lock(card); + #if 1 r = sc_pkcs15_init(card, &p15card); if (r < 0) { @@ -56,18 +35,33 @@ int test() fprintf(stderr, "PIN code enum failed: %s\n", sc_strerror(r)); goto err; } + memcpy(path.value, "\x51\x10", 2); + path.len = 2; + sc_debug = 3; + r = sc_select_file(card, &file, &path, SC_SELECT_FILE_BY_PATH); + sc_debug = 0; + if (r) { + fprintf(stderr, "sc_select_file failed: %s\n", sc_strerror(r)); + goto err; + } r = sc_pkcs15_verify_pin(p15card, &p15card->pin_info[0], "\x31\x32\x33\x34", 4); if (r) { fprintf(stderr, "PIN code verification failed: %s\n", sc_strerror(r)); goto err; } #endif - memcpy(path.value, "\x3f\x00", 2); - path.len = 2; - sc_debug = 1; - r = sc_select_file(card, &file, &path, SC_SELECT_FILE_BY_PATH); + sc_debug = 3; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, 1); + apdu.lc = 8; + apdu.data = sbuf; + apdu.datalen = 8; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + memcpy(sbuf, "\x31\x32\x33\x34\x00\x00\x00\x00", 8); + r = sc_transmit_apdu(card, &apdu); if (r) { - fprintf(stderr, "sc_select_file failed: %s\n", sc_strerror(r)); + fprintf(stderr, "transmit failed: %s\n", sc_strerror(r)); goto err; } r = sc_delete_file(card, 0x5110); @@ -75,6 +69,7 @@ int test() fprintf(stderr, "fail: %s\n", sc_strerror(r)); goto err; } + return 0; memset(&file, 0, sizeof(file)); @@ -95,6 +90,67 @@ err: return r; } +int test2() +{ + int r; + struct sc_path path; + struct sc_file file; + + memcpy(path.value, "\x3F\x00", 2); + path.len = 2; + + sc_debug = 3; + r = sc_select_file(card, &file, &path, SC_SELECT_FILE_BY_PATH); + if (r) { + fprintf(stderr, "SELECT FILE failed: %s\n", sc_strerror(r)); + return r; + } + return 0; +} + +int test3() +{ + FILE *inf; + u8 buf[256], txt[256]; + int len, r; + struct sc_pkcs15_pin_info *pin; + struct sc_pkcs15_prkey_info *key; + + r = sc_pkcs15_init(card, &p15card); + if (r) { + fprintf(stderr, "pkcs15 init failed: %s\n", sc_strerror(r)); + return -1; + } + r = sc_pkcs15_enum_private_keys(p15card); + if (r < 0) { + fprintf(stderr, "pkcs15 enum prk: %s\n", sc_strerror(r)); + return -1; + } + key = &p15card->prkey_info[0]; + r = sc_pkcs15_enum_pins(p15card); + if (r < 0) { + fprintf(stderr, "pkcs15 enum pins: %s\n", sc_strerror(r)); + return -1; + } + pin = &p15card->pin_info[0]; + inf = fopen("crypt.dat", "r"); + if (inf == NULL) + return -1; + len = fread(buf, 1, sizeof(buf), inf); + + r = sc_pkcs15_verify_pin(p15card, pin, "\x31\x32\x33\x34", 4); + if (r) { + fprintf(stderr, "PIN code verification failed: %s\n", sc_strerror(r)); + return -1; + } + r = sc_pkcs15_decipher(p15card, key, buf, len, txt, sizeof(txt)); + if (r < 0) { + fprintf(stderr, "decipher failed: %s\n", sc_strerror(r)); + return -1; + } + return 0; +} + int main(int argc, char **argv) { int i; @@ -102,94 +158,11 @@ int main(int argc, char **argv) i = sc_test_init(&argc, argv); if (i != 0) return 1; - - if (test()) + + sc_debug = 3; + if (test3()) return 1; - return 0; - - i = sc_pkcs15_init(card, &p15card); - if (i != 0) { - fprintf(stderr, "PKCS#15 card init failed: %s\n", - sc_strerror(i)); - return 1; - } - sc_pkcs15_print_card(p15card); - -#if DO_PRKEY_ENUM - if (enum_private_keys()) - return 1; -#endif -#if DO_DECIPHER - senv.signature = 0; - senv.algorithm_ref = 0x02; - senv.key_ref = 0; - senv.key_file_id = p15card->prkey_info[0].file_id; - senv.app_df_path = p15card->file_app.path; - i = sc_set_security_env(p15card->card, &senv); - if (i) { - fprintf(stderr, "Security environment set failed: %s\n", - sc_strerror(i)); - return 1; - } - file = fopen("cryptogram", "r"); - if (file != NULL) { - i = fread(buf, 1, sizeof(buf), file); - c = sc_decipher(card, buf, i, buf2, sizeof(buf2)); - if (c < 0) { - fprintf(stderr, "Decipher failed: (%d) %s\n", c, - sc_strerror(c)); - } else { - printf("Decrypted payload: "); - for (i = 0; i < c; i++) { - printf("%02X ", buf2[i]); - } - printf("\n"); - fclose(file); - file = fopen("decrypted.dat", "w"); - fwrite(buf2, c, 1, file); - fclose(file); - } - } else { - printf("File 'cryptogram' not found, not decrypting.\n"); - } -#endif -#if DO_SIGN - senv.signature = 1; - senv.algorithm_ref = 0x02; - senv.key_ref = 0; - senv.key_file_id = p15card->prkey_info[0].file_id; - senv.app_df_path = p15card->file_app.path; - i = sc_set_security_env(p15card->card, &senv); - if (i) { - fprintf(stderr, "Security environment set failed: %s\n", - sc_strerror(i)); - return 1; - } - file = fopen("input", "r"); - if (file != NULL) { - i = fread(buf, 1, sizeof(buf), file); - SCardSetTimeout(ctx->pcsc_ctx, 15000); - c = sc_compute_signature(card, buf, i, buf2, sizeof(buf2)); - if (c < 0) { - fprintf(stderr, "Signing failed: (%d) %s\n", c, - sc_strerror(c)); - } else { - printf("Signed payload: "); - for (i = 0; i < c; i++) { - printf("%02X ", buf2[i]); - } - printf("\n"); - fclose(file); - file = fopen("signed.dat", "w"); - fwrite(buf2, c, 1, file); - fclose(file); - } - } else { - printf("File 'input' not found, not signing.\n"); - } -#endif - printf("Cleaning up...\n"); sc_test_cleanup(); return 0; diff --git a/src/tests/pintest.c b/src/tests/pintest.c index 0c467405..8b52cdbe 100644 --- a/src/tests/pintest.c +++ b/src/tests/pintest.c @@ -10,6 +10,7 @@ #include "sc-pkcs15.h" #include #include +#include struct sc_pkcs15_card *p15card; @@ -35,29 +36,40 @@ int enum_pins() int ask_and_verify_pin(struct sc_pkcs15_pin_info *pin) { int i = 0; - char buf[32]; + char prompt[80]; + char *pass; - i = sc_sec_ask_pin_code(pin, buf, sizeof(buf), - "Please enter PIN code"); - if (i == 0) { - sc_lock(card); - i = sc_pkcs15_verify_pin(p15card, pin, buf, strlen(buf)); - sc_unlock(card); - if (i) { - if (i == SC_ERROR_PIN_CODE_INCORRECT) - fprintf(stderr, - "Incorrect PIN code (%d tries left)\n", - pin->tries_left); - else - fprintf(stderr, - "PIN verifying failed: %s\n", - sc_strerror(i)); - return 1; + while (1) { + sprintf(prompt, "Please enter PIN code [%s]: ", pin->com_attr.label); + pass = getpass(prompt); + + if (strlen(pass) == 0) { + printf("Not verifying PIN code.\n"); + return -1; } - printf("PIN code correct.\n"); - } else { - printf("\nNot verifying PIN code.\n"); + if (strlen(pass) < pin->min_length) + break; + if (strlen(pass) > pin->stored_length) + break; + break; } + + sc_lock(card); + i = sc_pkcs15_verify_pin(p15card, pin, pass, strlen(pass)); + sc_unlock(card); + if (i) { + if (i == SC_ERROR_PIN_CODE_INCORRECT) + fprintf(stderr, + "Incorrect PIN code (%d tries left)\n", + pin->tries_left); + else + fprintf(stderr, + "PIN verifying failed: %s\n", + sc_strerror(i)); + return 1; + } else + printf("PIN code correct.\n"); + return 0; } diff --git a/src/tools/opensc-tool.c b/src/tools/opensc-tool.c index 9053ae24..317f3c2e 100644 --- a/src/tools/opensc-tool.c +++ b/src/tools/opensc-tool.c @@ -17,18 +17,22 @@ char * opt_cert = NULL; char * opt_outfile = NULL; char * opt_pincode = NULL; char * opt_newpin = NULL; +char * opt_apdu = NULL; int quiet = 0; const struct option options[] = { { "list-readers", 0, 0, 'l' }, { "list-files", 0, 0, 'f' }, + { "send-apdu", 1, 0, 's' }, { "read-certificate", 1, 0, 'r' }, { "list-certificates", 0, 0, 'c' }, { "list-pins", 0, 0, OPT_LIST_PINS }, { "change-pin", 2, 0, OPT_CHANGE_PIN }, + { "list-private-keys", 0, 0, 'k' }, { "reader", 1, 0, OPT_READER }, { "output", 1, 0, 'o' }, { "quiet", 0, 0, 'q' }, + { "debug", 0, 0, 'd' }, { "pin", 1, 0, 'p' }, { "pin-id", 1, &opt_pin, 0 }, { 0, 0, 0, 0 } @@ -37,15 +41,18 @@ const struct option options[] = { const char *option_help[] = { "Lists all configured readers", "Recursively lists files stored on card", - "Read certificate with ID ", - "Lists certificates", - "Lists PIN codes", - "Changes the PIN code to ", + "Sends an APDU in format AA:BB:CC:DD:EE:FF...", + "Reads certificate with ID [P15]", + "Lists certificates [P15]", + "Lists PIN codes [P15]", + "Changes the PIN code to [P15]", + "Lists private keys [P15]", "Uses reader number ", "Outputs to file ", "Quiet operation", - "Uses PIN ; if not supplied, asks the user", - "Choose which PIN to use", + "Debug output -- may be supplied several timeso" + "Uses password (PIN) ", + "The auth ID of the PIN to use [P15]", }; struct sc_context *ctx = NULL; @@ -113,69 +120,85 @@ int list_certificates() return 0; } +int print_pem_certificate(struct sc_pkcs15_cert *cert) +{ + int r; + u8 buf[2048]; + FILE *outf; + + r = sc_base64_encode(cert->data, cert->data_len, buf, + sizeof(buf), 64); + if (r) { + fprintf(stderr, "Base64 encoding failed: %s\n", sc_strerror(r)); + return 1; + } + if (opt_outfile != NULL) { + outf = fopen(opt_outfile, "w"); + if (outf == NULL) { + fprintf(stderr, "Error opening file '%s': %s\n", + opt_outfile, strerror(errno)); + return 2; + } + } else + outf = stdout; + fprintf(outf, "-----BEGIN CERTIFICATE-----\n%s-----END CERTIFICATE-----\n", + buf); + if (outf != stdout) + fclose(outf); + return 0; +} + int read_certificate() { int r, i; struct sc_pkcs15_id id; - u8 *p = id.value; - char *certp = opt_cert; - FILE *outf; - if (strlen(opt_cert)/2 >= SC_PKCS15_MAX_ID_SIZE) { - fprintf(stderr, "Certificate id too long.\n"); - return 2; - } - if (!quiet) - printf("Reading certificate with ID '%s'\n", opt_cert); - id.len = 0; - while (*certp) { - int byte; + id.len = SC_PKCS15_MAX_ID_SIZE; + sc_pkcs15_hex_string_to_id(opt_cert, &id); - if (sscanf(certp, "%02X", &byte) != 1) - break; - certp += 2; - *p = byte; - p++; - id.len++; - } r = sc_pkcs15_enum_certificates(p15card); if (r < 0) { fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r)); return 1; } + for (i = 0; i < p15card->cert_count; i++) { struct sc_pkcs15_cert_info *cinfo = &p15card->cert_info[i]; struct sc_pkcs15_cert *cert; - u8 buf[2048]; if (sc_pkcs15_compare_id(&id, &cinfo->id) != 1) continue; + if (!quiet) + printf("Reading certificate with ID '%s'\n", opt_cert); r = sc_pkcs15_read_certificate(p15card, cinfo, &cert); if (r) { fprintf(stderr, "Certificate read failed: %s\n", sc_strerror(r)); return 1; } - r = sc_base64_encode(cert->data, cert->data_len, buf, - sizeof(buf), 64); + r = print_pem_certificate(cert); sc_pkcs15_free_certificate(cert); - if (r) { - fprintf(stderr, "Base64 encoding failed: %s\n", sc_strerror(r)); - return 1; - } - if (opt_outfile != NULL) { - outf = fopen(opt_outfile, "w"); - if (outf == NULL) { - fprintf(stderr, "Error opening file '%s': %s\n", - opt_outfile, strerror(errno)); - return 2; - } - } else - outf = stdout; - fprintf(outf, "-----BEGIN CERTIFICATE-----\n%s-----END CERTIFICATE-----\n", - buf); - if (outf != stdout) - fclose(outf); + return r; + } + fprintf(stderr, "Certificate with ID '%s' not found.\n", opt_cert); + return 2; +} + +int list_private_keys() +{ + int r, i; + + r = sc_pkcs15_enum_private_keys(p15card); + if (r < 0) { + fprintf(stderr, "Private key enumeration failed: %s\n", sc_strerror(r)); + return 1; + } + if (!quiet) + printf("Card has %d private key(s).\n\n", p15card->prkey_count); + for (i = 0; i < p15card->prkey_count; i++) { + struct sc_pkcs15_prkey_info *pinfo = &p15card->prkey_info[i]; + sc_pkcs15_print_prkey_info(pinfo); + printf("\n"); } return 0; } @@ -343,6 +366,60 @@ int list_files() return r; } +int send_apdu() +{ + struct sc_apdu apdu; + u8 buf[MAX_BUFFER_SIZE], sbuf[MAX_BUFFER_SIZE], + rbuf[MAX_BUFFER_SIZE], *p = buf; + int len = sizeof(buf), r; + + sc_hex_to_bin(opt_apdu, buf, &len); + if (len < 5) { + fprintf(stderr, "APDU too short (must be at least 5 bytes).\n"); + return 2; + } + apdu.cla = *p++; + apdu.ins = *p++; + apdu.p1 = *p++; + apdu.p2 = *p++; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + len -= 4; + if (len > 1) { + apdu.lc = *p++; + len--; + memcpy(sbuf, p, apdu.lc); + apdu.data = sbuf; + apdu.datalen = apdu.lc; + len -= apdu.lc; + if (len) { + apdu.le = *p++; + len--; + apdu.cse = SC_APDU_CASE_4_SHORT; + } else + apdu.cse = SC_APDU_CASE_3_SHORT; + if (len) { + fprintf(stderr, "APDU too long (%d bytes extra).\n", len); + return 2; + } + } else if (len == 1) { + apdu.le = *p++; + len--; + apdu.cse = SC_APDU_CASE_2_SHORT; + } else + apdu.cse = SC_APDU_CASE_1; + + sc_debug = 3; + r = sc_transmit_apdu(card, &apdu); + sc_debug = 0; + if (r) { + fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); + return 1; + } + + return 0; +} + int main(int argc, char * const argv[]) { int err = 0, r, c, long_optind = 0; @@ -351,11 +428,13 @@ int main(int argc, char * const argv[]) int do_list_certs = 0; int do_list_pins = 0; int do_list_files = 0; + int do_list_prkeys = 0; int do_change_pin = 0; + int do_send_apdu = 0; int action_count = 0; while (1) { - c = getopt_long(argc, argv, "lfr:coqp:", options, &long_optind); + c = getopt_long(argc, argv, "lfr:kco:qdp:s:", options, &long_optind); if (c == -1) break; if (c == '?') @@ -378,6 +457,11 @@ int main(int argc, char * const argv[]) do_list_certs = 1; action_count++; break; + case 's': + opt_apdu = optarg; + do_send_apdu++; + action_count++; + break; case OPT_CHANGE_PIN: do_change_pin = 1; action_count++; @@ -386,6 +470,10 @@ int main(int argc, char * const argv[]) do_list_pins = 1; action_count++; break; + case 'k': + do_list_prkeys = 1; + action_count++; + break; case OPT_READER: opt_reader = atoi(optarg); break; @@ -395,6 +483,9 @@ int main(int argc, char * const argv[]) case 'q': quiet++; break; + case 'd': + sc_debug++; + break; case 'p': if (optarg == NULL && opt_pincode == NULL) opt_pincode = getpass("Enter PIN code: "); @@ -437,12 +528,17 @@ int main(int argc, char * const argv[]) sc_lock(card); + if (do_send_apdu) { + if ((err = send_apdu())) + goto end; + action_count--; + } + if (do_list_files) { if ((err = list_files())) goto end; action_count--; } - /* Here go the actions that do not require PKCS#15 */ if (action_count <= 0) goto end; @@ -466,6 +562,11 @@ int main(int argc, char * const argv[]) goto end; action_count--; } + if (do_list_prkeys) { + if ((err = list_private_keys())) + goto end; + action_count--; + } if (do_list_pins) { if ((err = list_pins())) goto end;