From 0fbe06b8eb4f327f4f16e63e59fca8d18dfe0a1d Mon Sep 17 00:00:00 2001 From: jey Date: Fri, 7 Dec 2001 00:57:16 +0000 Subject: [PATCH] - first working version of signer plugin git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@63 c6295689-39f2-0310-b995-f0e70906c6a9 --- src/signer/Makefile | 10 +- src/signer/opensc-crypto.c | 217 +++++++++++++++++++++++++++++++++ src/signer/opensc-crypto.h | 19 +++ src/signer/opensc-support.c | 234 ++++++++++++++++++++++++++++++++++++ src/signer/opensc-support.h | 9 ++ src/signer/signer.c | 148 +++++++++++------------ src/signer/signer.h | 17 +++ src/signer/testprog.c | 50 ++++++++ 8 files changed, 627 insertions(+), 77 deletions(-) create mode 100644 src/signer/opensc-crypto.c create mode 100644 src/signer/opensc-crypto.h create mode 100644 src/signer/opensc-support.c create mode 100644 src/signer/opensc-support.h create mode 100644 src/signer/signer.h create mode 100644 src/signer/testprog.c diff --git a/src/signer/Makefile b/src/signer/Makefile index 5d741328..8985c5e9 100644 --- a/src/signer/Makefile +++ b/src/signer/Makefile @@ -4,13 +4,14 @@ PLUGIN_DEFINES=-DXP_UNIX -I/usr/include/mozilla -I/usr/include/mozilla/java CC= gcc OPTIMIZER= -g CFLAGS=-Wall $(OPTIMIZER) $(PLUGIN_DEFINES) +LDFLAGS=-lopensc -lcrypto -SRC= signer.c stubs.c -OBJ= signer.o stubs.o +SRC= signer.c stubs.c opensc-crypto.c opensc-support.c +OBJ= signer.o stubs.o opensc-crypto.o opensc-support.o SHAREDTARGET=signer.so -default all: $(SHAREDTARGET) +default all: $(SHAREDTARGET) testprog $(SHAREDTARGET): $(OBJ) $(CC) -shared -o $(SHAREDTARGET) $(OBJ) $(LDFLAGS) @@ -27,3 +28,6 @@ clean: test: .do cp $(SHAREDTARGET) /usr/lib/mozilla/plugins .do cp $(SHAREDTARGET) /usr/lib/netscape/plugins-libc6 + +testprog: $(OBJ) testprog.o opensc-crypto.o opensc-support.o + gcc -Wall -g -o testprog testprog.o opensc-crypto.o opensc-support.o -lopensc -lcrypto -ldl diff --git a/src/signer/opensc-crypto.c b/src/signer/opensc-crypto.c new file mode 100644 index 00000000..f3ab3dc0 --- /dev/null +++ b/src/signer/opensc-crypto.c @@ -0,0 +1,217 @@ + +#include +#include +#include +#include "opensc-crypto.h" + +#define SC_HARDCODED_PIN "1234" + +#define DBG(x) { x; } + +void +sc_close(struct sc_priv_data *priv) +{ + if (priv->p15card) { + sc_pkcs15_destroy(priv->p15card); + priv->p15card = NULL; + } + if (priv->card) { + sc_disconnect_card(priv->card); + priv->card = NULL; + } + if (priv->ctx) { + sc_destroy_context(priv->ctx); + priv->ctx = NULL; + } +} + +static int +sc_init(struct sc_priv_data *priv) +{ + int r; + + r = sc_establish_context(&priv->ctx); + if (r) + goto err; + r = sc_connect_card(priv->ctx, priv->reader_id, &priv->card); + if (r) + goto err; + r = sc_pkcs15_init(priv->card, &priv->p15card); + if (r) + goto err; + return 0; +err: + sc_close(priv); + return r; +} + +static int sc_private_decrypt(int flen, u_char *from, u_char *to, RSA *rsa, + int padding) +{ + int r; + struct sc_priv_data *priv; + struct sc_pkcs15_prkey_info *key; + struct sc_pkcs15_pin_info *pin; + + if (padding != RSA_PKCS1_PADDING) + return -1; + priv = (struct sc_priv_data *) RSA_get_app_data(rsa); + if (priv == NULL) + return -1; + if (priv->p15card == NULL) { + sc_close(priv); + r = sc_init(priv); + if (r) { + //error("SmartCard init failed: %s", sc_strerror(r)); + goto err; + } + } + r = sc_pkcs15_find_prkey_by_id(priv->p15card, &priv->cert_id, &key); + if (r) { + //error("Unable to find private key from SmartCard: %s", sc_strerror(r)); + goto err; + } + r = sc_pkcs15_find_pin_by_auth_id(priv->p15card, &key->com_attr.auth_id, &pin); + if (r) { +// error("Unable to find PIN object from SmartCard: %s", sc_strerror(r)); + goto err; + } + r = sc_pkcs15_verify_pin(priv->p15card, pin, SC_HARDCODED_PIN, + strlen(SC_HARDCODED_PIN)); + if (r) { +// error("PIN code verification failed: %s", sc_strerror(r)); + goto err; + } + r = sc_pkcs15_decipher(priv->p15card, key, from, flen, to, flen); + if (r < 0) { +// error("sc_pkcs15_decipher() failed: %s", sc_strerror(r)); + goto err; + } + return r; +err: + sc_close(priv); + return -1; +} + +static int +sc_private_encrypt(int flen, u_char *from, u_char *to, RSA *rsa, int padding) +{ +// error("unsupported function sc_private_encrypt() called"); + return -1; +} + +static int +sc_sign(int type, u_char *m, unsigned int m_len, + unsigned char *sigret, unsigned int *siglen, RSA *rsa) +{ + int r; + struct sc_priv_data *priv; + struct sc_pkcs15_prkey_info *key; + struct sc_pkcs15_pin_info *pin; + + priv = (struct sc_priv_data *) RSA_get_app_data(rsa); + if (priv == NULL) + return -1; +// debug("sc_sign() called on cert %02X: type = %d, m_len = %d", +// priv->cert_id.value[0], type, m_len); + DBG(printf("sc_sign() called\n")); + if (priv->p15card == NULL) { + sc_close(priv); + r = sc_init(priv); + if (r) { + DBG(printf("SmartCard init failed: %s", sc_strerror(r))); + goto err; + } + } + r = sc_pkcs15_find_prkey_by_id(priv->p15card, &priv->cert_id, &key); + if (r) { + DBG(printf("Unable to find private key from SmartCard: %s", sc_strerror(r))); + goto err; + } + r = sc_pkcs15_find_pin_by_auth_id(priv->p15card, &key->com_attr.auth_id, &pin); + if (r) { + DBG(printf("Unable to find PIN object from SmartCard: %s", sc_strerror(r))); + goto err; + } + r = sc_pkcs15_verify_pin(priv->p15card, pin, SC_HARDCODED_PIN, + strlen(SC_HARDCODED_PIN)); + if (r) { + DBG(printf("PIN code verification failed: %s", sc_strerror(r))); + goto err; + } + r = sc_pkcs15_compute_signature(priv->p15card, key, SC_PKCS15_HASH_SHA1, + m, m_len, sigret, RSA_size(rsa)); + if (r < 0) { + DBG(printf("sc_pkcs15_compute_signature() failed: %s", sc_strerror(r))); + goto err; + } + *siglen = r; + return 1; +err: + printf("Returning with error %s\n", sc_strerror(r)); + sc_close(priv); + return 0; +} + +static int (*orig_finish)(RSA *rsa) = NULL; + +static int +sc_finish(RSA *rsa) +{ + struct sc_priv_data *priv; + + DBG(printf("sc_finish() called\n")); + priv = RSA_get_app_data(rsa); + if (priv != NULL) { + priv->ref_count--; + if (priv->ref_count == 0) { + sc_close(priv); + free(priv); + } + } + if (orig_finish) + orig_finish(rsa); + return 1; +} + +static RSA_METHOD libsc_rsa = +{ + "OpenSC", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + 0, + NULL, +}; + +RSA_METHOD * sc_get_method() +{ + RSA_METHOD *def; + + def = RSA_get_default_method(); + + orig_finish = def->finish; + + /* overload */ + libsc_rsa.rsa_priv_enc = sc_private_encrypt; + libsc_rsa.rsa_priv_dec = sc_private_decrypt; + libsc_rsa.rsa_sign = sc_sign; + libsc_rsa.finish = sc_finish; + + /* just use the OpenSSL version */ + libsc_rsa.rsa_pub_enc = def->rsa_pub_enc; + libsc_rsa.rsa_pub_dec = def->rsa_pub_dec; + libsc_rsa.rsa_mod_exp = def->rsa_mod_exp; + libsc_rsa.bn_mod_exp = def->bn_mod_exp; + libsc_rsa.init = def->init; + libsc_rsa.flags = def->flags | RSA_FLAG_SIGN_VER; + libsc_rsa.app_data = def->app_data; + libsc_rsa.rsa_verify = def->rsa_verify; + + return &libsc_rsa; +} diff --git a/src/signer/opensc-crypto.h b/src/signer/opensc-crypto.h new file mode 100644 index 00000000..df67f3e8 --- /dev/null +++ b/src/signer/opensc-crypto.h @@ -0,0 +1,19 @@ + +#ifndef _OPENSC_CRYPTO_H +#define _OPENSC_CRYPTO_H + +#include +#include + +struct sc_priv_data +{ + struct sc_pkcs15_card *p15card; + struct sc_card *card; + struct sc_context *ctx; + struct sc_pkcs15_id cert_id; + int ref_count, reader_id; +}; + +extern RSA_METHOD * sc_get_method(); + +#endif diff --git a/src/signer/opensc-support.c b/src/signer/opensc-support.c new file mode 100644 index 00000000..d24c4c73 --- /dev/null +++ b/src/signer/opensc-support.c @@ -0,0 +1,234 @@ + +#include "opensc-support.h" +#include "opensc-crypto.h" +#include +#include +#include + +static int get_certificate(PluginInstance *inst, + X509 **cert_out, struct sc_pkcs15_id *certid_out) +{ + struct sc_pkcs15_cert *cert; + struct sc_pkcs15_cert_info *cinfo; + int r, i; + X509 *x509; + struct sc_pkcs15_id cert_id; + u8 *p; + + r = sc_pkcs15_enum_private_keys(inst->p15card); + if (r < 0) + return r; + if (r == 0) + return SC_ERROR_OBJECT_NOT_FOUND; + cert_id.len = 0; + for (i = 0; i < inst->p15card->prkey_count; i++) { + struct sc_pkcs15_prkey_info *key = &inst->p15card->prkey_info[i]; + +// if (key->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) { + /* Use the first available non-repudiation key */ + cert_id = key->id; + break; +// } + } + if (cert_id.len == 0) + return SC_ERROR_OBJECT_NOT_FOUND; + r = sc_pkcs15_find_cert_by_id(inst->p15card, &cert_id, &cinfo); + if (r) + return r; + r = sc_pkcs15_read_certificate(inst->p15card, cinfo, &cert); + if (r) + return r; + x509 = X509_new(); + p = cert->data; + if (!d2i_X509(&x509, &p, cert->data_len)) { + return -1; /* FIXME */ + } + *certid_out = cinfo->id; + sc_pkcs15_free_certificate(cert); + *cert_out = x509; + return 0; +} + +static int init_pkcs15(PluginInstance *inst) +{ + int r; + + r = sc_establish_context(&inst->ctx); + if (r) + return r; + r = sc_connect_card(inst->ctx, 0, &inst->card); + if (r) + return r; + r = sc_pkcs15_init(inst->card, &inst->p15card); + if (r) + return r; + return 0; +} + +#if 0 +static void close_pkcs15(PluginInstance *inst) +{ + if (inst->p15card) { + sc_pkcs15_destroy(inst->p15card); + inst->p15card = NULL; + } + if (inst->card) { + sc_disconnect_card(inst->card); + inst->card = NULL; + } + if (inst->ctx) { + sc_destroy_context(inst->ctx); + inst->ctx = NULL; + } +} +#endif + +static int extract_certificate_and_pkey(PluginInstance *inst, + X509 **x509_out, + EVP_PKEY **pkey_out) +{ + int r; + X509 *x509 = NULL; + struct sc_pkcs15_id cert_id; + struct sc_priv_data *priv = NULL; + EVP_PKEY *pkey = NULL; + RSA *rsa = NULL; + + r = init_pkcs15(inst); + if (r) + goto err; + r = get_certificate(inst, &x509, &cert_id); + if (r) + goto err; + + r = -1; + pkey = X509_get_pubkey(x509); + if (pkey == NULL) + goto err; + if (pkey->type != EVP_PKEY_RSA) + goto err; + rsa = EVP_PKEY_get1_RSA(pkey); /* increases ref count */ + if (rsa == NULL) + goto err; + rsa->flags |= RSA_FLAG_SIGN_VER; + RSA_set_method(rsa, sc_get_method()); + priv = malloc(sizeof(*priv)); + if (priv == NULL) + goto err; + memset(priv, 0, sizeof(struct sc_priv_data)); + priv->cert_id = cert_id; + priv->ref_count = 1; + RSA_set_app_data(rsa, priv); + RSA_free(rsa); /* decreases ref count */ + + *x509_out = x509; + *pkey_out = pkey; + + return 0; +err: + if (pkey) + EVP_PKEY_free(pkey); + if (x509) + X509_free(x509); + return -1; + +} + +int create_envelope(PluginInstance *inst, u8 **data, int *datalen) +{ + int r; + PKCS7 *p7 = NULL; + X509 *x509 = NULL; + PKCS7_SIGNER_INFO *si = NULL; + EVP_PKEY *pkey = NULL; + BIO *in = NULL, *p7bio = NULL; + u8 *buf; + + r = extract_certificate_and_pkey(inst, &x509, &pkey); + if (r) + goto err; + p7 = PKCS7_new(); + if (p7 == NULL) { + r = -1; + goto err; + } + r = PKCS7_set_type(p7, NID_pkcs7_signed); + if (r != 1) { + r = -1; + goto err; + } + EVP_add_digest(EVP_sha1()); + si = PKCS7_add_signature(p7, x509, pkey, EVP_sha1()); + if (si == NULL) { + r = -1; + goto err; + } + PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, V_ASN1_OBJECT, + OBJ_nid2obj(NID_pkcs7_data)); + r = PKCS7_add_certificate(p7, x509); + if (r != 1) { + printf("PKCS7_add_certificate failed.\n"); + goto err; + } + PKCS7_content_new(p7, NID_pkcs7_data); + + p7bio = PKCS7_dataInit(p7, NULL); + if (p7bio == NULL) { + r = -1; + goto err; + } + in = BIO_new_mem_buf(inst->signdata, inst->signdata_len); + if (in == NULL) { + r = -1; + goto err; + } + for (;;) { + char buf[1024]; + int i = BIO_read(in, buf, sizeof(buf)); + if (i <= 0) + break; + BIO_write(p7bio, buf, i); + } + if (!PKCS7_dataFinal(p7, p7bio)) { + r = -1; + goto err; + } + /* FIXME: remove this */ + r = i2d_PKCS7(p7, NULL); + if (r <= 0) { + r = -1; + goto err; + } + buf = malloc(r); + if (buf == NULL) + goto err; + *data = buf; + r = i2d_PKCS7(p7, &buf); + *datalen = r; + if (r <= 0) { + free(buf); + r = -1; + goto err; + } + r = 0; +err: + if (p7) + PKCS7_free(p7); + if (in) + BIO_free(in); + if (p7bio) + BIO_free(p7bio); +/* if (si) + PKCS7_SIGNER_INFO_free(si); */ + if (pkey) + EVP_PKEY_free(pkey); + if (x509) + X509_free(x509); + if (r) { +#if 0 + ERR_load_crypto_strings(); + ERR_print_errors_fp(stderr); +#endif + } + return r; +} diff --git a/src/signer/opensc-support.h b/src/signer/opensc-support.h new file mode 100644 index 00000000..21fcf06f --- /dev/null +++ b/src/signer/opensc-support.h @@ -0,0 +1,9 @@ + +#ifndef _OPENSC_SUPPORT_H +#define _OPENSC_SUPPORT_H + +#include "signer.h" + +extern int create_envelope(PluginInstance *inst, u8 **data, int *datalen); + +#endif diff --git a/src/signer/signer.c b/src/signer/signer.c index 696a3ecf..ebbb6d1e 100644 --- a/src/signer/signer.c +++ b/src/signer/signer.c @@ -1,57 +1,13 @@ -/* -*- 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 #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; - char *postUrl; - char *dataToSign; -} PluginInstance; - - -/*********************************************************************** - * - * Empty implementations of plugin API functions - * - * PLUGIN DEVELOPERS: - * You will need to implement these functions as required by your - * plugin. - * - ***********************************************************************/ +#include "signer.h" +#include "opensc-support.h" char* NPP_GetMIMEDescription(void) { - return("text/x-text-to-sign:sample:Text to be signed"); + return("text/x-text-to-sign:sgn:Text to be signed"); } NPError @@ -98,25 +54,35 @@ NPP_Shutdown(void) static NPError post_data(NPP instance, const char *url, const char *target, uint32 len, - const char* buf) + const char *buf, const char *tag) { NPError rv; char headers[256], *sendbuf; - int hdrlen; + char *content; + unsigned int content_len, hdrlen, taglen; - sprintf(headers, "Content-type: text/plain\r\n" - "Content-Length: %u\r\n\r\n", (unsigned int) len); + taglen = strlen(tag); + content_len = taglen + len + 1; + content = NPN_MemAlloc(content_len); + if (content == NULL) + return NPERR_OUT_OF_MEMORY_ERROR; + memcpy(content, tag, taglen); + content[taglen] = '='; + memcpy(content+taglen+1, buf, len); + + sprintf(headers, "Content-type: application/x-www-form-urlencoded\r\n" + "Content-Length: %u\r\n\r\n", (unsigned int) content_len); hdrlen = strlen(headers); - sendbuf = NPN_MemAlloc(hdrlen + len + 1); + sendbuf = NPN_MemAlloc(hdrlen + content_len); if (sendbuf == NULL) return NPERR_OUT_OF_MEMORY_ERROR; memcpy(sendbuf, headers, hdrlen); - memcpy(sendbuf + hdrlen, buf, len); - sendbuf[hdrlen + len] = 0; + memcpy(sendbuf + hdrlen, content, content_len); + sendbuf[hdrlen + content_len] = 0; + NPN_MemFree(content); printf("Sending:\n---\n%s---\n", sendbuf); - printf("Url: '%s', target: '%s', len: %d\n", url, target, hdrlen + len); - rv = NPN_PostURL(instance, url, target, hdrlen + len, sendbuf, FALSE); -// NPN_MemFree(sendbuf); + printf("Url: '%s', target: '%s', len: %ld\n", url, target, hdrlen + len); + rv = NPN_PostURL(instance, url, target, hdrlen + content_len, sendbuf, FALSE); return rv; } @@ -130,10 +96,11 @@ NPP_New(NPMIMEType pluginType, char* argv[], NPSavedData* saved) { - PluginInstance* This; + PluginInstance* This = NULL; NPError rv; - int i; - const char *resp = "Testing...1234567890 And testing, and testing\n"; + int r, i, datalen, b64datalen; + u8 *data = NULL, *b64data = NULL; + char *postUrl = NULL, *dataToSign = NULL, *fieldName = NULL; printf("NPP_New()\n"); if (instance == NULL) @@ -145,24 +112,61 @@ NPP_New(NPMIMEType pluginType, if (This == NULL) return NPERR_OUT_OF_MEMORY_ERROR; - This->postUrl = This->dataToSign = NULL; + This->ctx = NULL; + This->card = NULL; + This->p15card = NULL; + for (i = 0; i < argc; i++) { if (strcmp(argn[i], "wsxaction") == 0) { - This->postUrl = strdup(argv[i]); + postUrl = strdup(argv[i]); } else if (strcmp(argn[i], "wsxdatatosign") == 0) { - This->dataToSign = strdup(argv[i]); + dataToSign = strdup(argv[i]); + } else if (strcmp(argn[i], "wsxname") == 0) { + fieldName = strdup(argv[i]); } else printf("'%s' = '%s'\n", argn[i], argv[i]); } - if (This->postUrl == NULL) - return NPERR_GENERIC_ERROR; - printf("Posting to '%s'\n", This->postUrl); - rv = post_data(instance, This->postUrl, "_self", strlen(resp), resp); - printf("PostURL returned %d\n", rv); - return NPERR_NO_ERROR; + if (postUrl == NULL || dataToSign == NULL) { + r = NPERR_GENERIC_ERROR; + goto err; + } + if (fieldName == NULL) + fieldName = strdup("SignedData"); + This->signdata = dataToSign; + This->signdata_len = strlen(dataToSign); + r = create_envelope(This, &data, &datalen); + if (r) { + r = NPERR_GENERIC_ERROR; + goto err; + } + b64datalen = datalen * 4 / 3 + 4; + b64data = malloc(b64datalen); + r = sc_base64_encode(data, datalen, b64data, b64datalen, 0); + if (r) { + r = NPERR_GENERIC_ERROR; + goto err; + } + printf("Posting to '%s'\n", postUrl); + printf("Data to sign: %s\n", dataToSign); + printf("Signed: %s\n", b64data); + rv = post_data(instance, postUrl, "_self", strlen(b64data), b64data, + fieldName); + printf("post_data returned %d\n", rv); + r = NPERR_NO_ERROR; +err: + if (fieldName) + free(fieldName); + if (dataToSign) + free(dataToSign); + if (postUrl) + free(postUrl); + if (data) + free(data); + if (b64data) + free(b64data); + return r; } - NPError NPP_Destroy(NPP instance, NPSavedData** save) { @@ -183,10 +187,6 @@ NPP_Destroy(NPP instance, NPSavedData** save) if (This == NULL) return NPERR_NO_ERROR; - if (This->postUrl) - NPN_MemFree(This->postUrl); - if (This->dataToSign) - NPN_MemFree(This->dataToSign); NPN_MemFree(instance->pdata); instance->pdata = NULL; diff --git a/src/signer/signer.h b/src/signer/signer.h new file mode 100644 index 00000000..91e9d4a6 --- /dev/null +++ b/src/signer/signer.h @@ -0,0 +1,17 @@ + +#ifndef _SIGNER_H +#define _SIGNER_H + +#include +#include + +typedef struct _PluginInstance +{ + char *signdata; + int signdata_len; + struct sc_context *ctx; + struct sc_card *card; + struct sc_pkcs15_card *p15card; +} PluginInstance; + +#endif diff --git a/src/signer/testprog.c b/src/signer/testprog.c new file mode 100644 index 00000000..abe2c419 --- /dev/null +++ b/src/signer/testprog.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include + +#include "opensc-support.h" +#include "opensc-crypto.h" +#include "signer.h" + +int test() +{ + BIO *in; + PKCS7 *p7; + + in = BIO_new_file("sample.pem", "r"); + p7 = PEM_read_bio_PKCS7(in, NULL, NULL, NULL); + if (p7 == NULL) { + goto err; + } +// return prp7(p7); + return 0; +err: + ERR_load_crypto_strings(); + ERR_print_errors_fp(stderr); + return 1; + +} + +int main() +{ + PluginInstance pl; + u8 *data; + int datalen, r; + +#if 0 + test(); + return 0; +#endif + + pl.signdata = strdup("12345\nMoro moro, mitä poro\nTesting 1234567890"); + pl.signdata_len = strlen(pl.signdata); + r = create_envelope(&pl, &data, &datalen); + if (r) { + printf("create_env() failed\n"); + return 1; + } + return 0; +}