blob: 1d54df86fae93e87691250df6222117e6171ccc8 [file] [log] [blame]
Ben Lindstrom3133dbb2001-07-04 05:35:00 +00001/*
2 * Copyright (c) 2001 Markus Friedl. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000025#include "includes.h"
Damien Miller85de5802001-09-18 14:01:11 +100026#ifdef SMARTCARD
Ben Lindstrom0b675b12002-03-22 03:28:11 +000027RCSID("$OpenBSD: scard.c,v 1.18 2002/03/21 16:38:06 markus Exp $");
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000028
29#include <openssl/engine.h>
30#include <sectok.h>
31
32#include "key.h"
33#include "log.h"
34#include "xmalloc.h"
35#include "scard.h"
36
Ben Lindstrom0b675b12002-03-22 03:28:11 +000037#ifdef OPENSSL_VERSION_NUMBER
38#if OPENSSL_VERSION_NUMBER >= 0x00907000L
39#define RSA_get_default_openssl_method RSA_get_default_method
40#define DSA_get_default_openssl_method DSA_get_default_method
41#define DH_get_default_openssl_method DH_get_default_method
42#define ENGINE_set_BN_mod_exp(x,y)
43#endif
44#endif
45
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000046#define CLA_SSH 0x05
47#define INS_DECRYPT 0x10
48#define INS_GET_KEYLENGTH 0x20
49#define INS_GET_PUBKEY 0x30
50#define INS_GET_RESPONSE 0xc0
51
52#define MAX_BUF_SIZE 256
53
54static int sc_fd = -1;
Ben Lindstromf7db3bb2001-08-06 21:35:51 +000055static char *sc_reader_id = NULL;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000056static int cla = 0x00; /* class */
57
58/* interface to libsectok */
59
Damien Miller9f0f5c62001-12-21 14:45:46 +110060static int
Damien Miller694be4b2001-07-14 12:13:26 +100061sc_open(void)
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000062{
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000063 int sw;
64
65 if (sc_fd >= 0)
66 return sc_fd;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000067
Ben Lindstromf7db3bb2001-08-06 21:35:51 +000068 sc_fd = sectok_friendly_open(sc_reader_id, STONOWAIT, &sw);
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000069 if (sc_fd < 0) {
Damien Miller694be4b2001-07-14 12:13:26 +100070 error("sectok_open failed: %s", sectok_get_sw(sw));
Ben Lindstrom30b00be2001-08-06 21:22:10 +000071 return SCARD_ERROR_FAIL;
72 }
73 if (! sectok_cardpresent(sc_fd)) {
Ben Lindstromf7db3bb2001-08-06 21:35:51 +000074 debug("smartcard in reader %s not present, skipping",
75 sc_reader_id);
Ben Lindstrom3ab1dfa2001-08-06 21:33:44 +000076 sc_close();
Ben Lindstrom30b00be2001-08-06 21:22:10 +000077 return SCARD_ERROR_NOCARD;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000078 }
Ben Lindstrom60df8e42001-08-06 21:10:52 +000079 if (sectok_reset(sc_fd, 0, NULL, &sw) <= 0) {
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000080 error("sectok_reset failed: %s", sectok_get_sw(sw));
81 sc_fd = -1;
Ben Lindstrom30b00be2001-08-06 21:22:10 +000082 return SCARD_ERROR_FAIL;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000083 }
Ben Lindstrom60df8e42001-08-06 21:10:52 +000084 if ((cla = cyberflex_inq_class(sc_fd)) < 0)
85 cla = 0;
Damien Miller694be4b2001-07-14 12:13:26 +100086
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000087 debug("sc_open ok %d", sc_fd);
88 return sc_fd;
89}
90
Damien Miller9f0f5c62001-12-21 14:45:46 +110091static int
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000092sc_enable_applet(void)
93{
Ben Lindstrom60df8e42001-08-06 21:10:52 +000094 static u_char aid[] = {0xfc, 0x53, 0x73, 0x68, 0x2e, 0x62, 0x69, 0x6e};
95 int sw = 0;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000096
Ben Lindstrom60df8e42001-08-06 21:10:52 +000097 /* select applet id */
98 sectok_apdu(sc_fd, cla, 0xa4, 0x04, 0, sizeof aid, aid, 0, NULL, &sw);
Ben Lindstrom3133dbb2001-07-04 05:35:00 +000099 if (!sectok_swOK(sw)) {
100 error("sectok_apdu failed: %s", sectok_get_sw(sw));
Damien Miller694be4b2001-07-14 12:13:26 +1000101 sc_close();
102 return -1;
103 }
104 return 0;
105}
106
Damien Miller9f0f5c62001-12-21 14:45:46 +1100107static int
Damien Miller694be4b2001-07-14 12:13:26 +1000108sc_init(void)
109{
Ben Lindstrom30b00be2001-08-06 21:22:10 +0000110 int status;
111
112 status = sc_open();
113 if (status == SCARD_ERROR_NOCARD) {
114 return SCARD_ERROR_NOCARD;
115 }
116 if (status < 0 ) {
Damien Miller694be4b2001-07-14 12:13:26 +1000117 error("sc_open failed");
Ben Lindstrom30b00be2001-08-06 21:22:10 +0000118 return status;
Damien Miller694be4b2001-07-14 12:13:26 +1000119 }
120 if (sc_enable_applet() < 0) {
121 error("sc_enable_applet failed");
Ben Lindstrom30b00be2001-08-06 21:22:10 +0000122 return SCARD_ERROR_APPLET;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000123 }
124 return 0;
125}
126
Damien Miller9f0f5c62001-12-21 14:45:46 +1100127static int
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000128sc_read_pubkey(Key * k)
129{
130 u_char buf[2], *n;
131 char *p;
Ben Lindstroma2fec902001-09-18 05:45:44 +0000132 int len, sw, status = -1;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000133
134 len = sw = 0;
Damien Miller3ff36d62001-09-28 19:51:54 +1000135 n = NULL;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000136
Ben Lindstrom30b00be2001-08-06 21:22:10 +0000137 if (sc_fd < 0) {
138 status = sc_init();
139 if (status < 0 )
Ben Lindstroma2fec902001-09-18 05:45:44 +0000140 goto err;
Ben Lindstrom30b00be2001-08-06 21:22:10 +0000141 }
Damien Miller694be4b2001-07-14 12:13:26 +1000142
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000143 /* get key size */
144 sectok_apdu(sc_fd, CLA_SSH, INS_GET_KEYLENGTH, 0, 0, 0, NULL,
Damien Miller9f0f5c62001-12-21 14:45:46 +1100145 sizeof(buf), buf, &sw);
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000146 if (!sectok_swOK(sw)) {
147 error("could not obtain key length: %s", sectok_get_sw(sw));
Ben Lindstroma2fec902001-09-18 05:45:44 +0000148 goto err;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000149 }
150 len = (buf[0] << 8) | buf[1];
151 len /= 8;
152 debug("INS_GET_KEYLENGTH: len %d sw %s", len, sectok_get_sw(sw));
153
154 n = xmalloc(len);
155 /* get n */
156 sectok_apdu(sc_fd, CLA_SSH, INS_GET_PUBKEY, 0, 0, 0, NULL, len, n, &sw);
157 if (!sectok_swOK(sw)) {
158 error("could not obtain public key: %s", sectok_get_sw(sw));
Ben Lindstroma2fec902001-09-18 05:45:44 +0000159 goto err;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000160 }
Ben Lindstroma2fec902001-09-18 05:45:44 +0000161
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000162 debug("INS_GET_KEYLENGTH: sw %s", sectok_get_sw(sw));
163
164 if (BN_bin2bn(n, len, k->rsa->n) == NULL) {
165 error("c_read_pubkey: BN_bin2bn failed");
Ben Lindstroma2fec902001-09-18 05:45:44 +0000166 goto err;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000167 }
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000168
169 /* currently the java applet just stores 'n' */
170 if (!BN_set_word(k->rsa->e, 35)) {
171 error("c_read_pubkey: BN_set_word(e, 35) failed");
Ben Lindstroma2fec902001-09-18 05:45:44 +0000172 goto err;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000173 }
174
Ben Lindstroma2fec902001-09-18 05:45:44 +0000175 status = 0;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000176 p = key_fingerprint(k, SSH_FP_MD5, SSH_FP_HEX);
177 debug("fingerprint %d %s", key_size(k), p);
178 xfree(p);
179
Ben Lindstroma2fec902001-09-18 05:45:44 +0000180err:
181 if (n != NULL)
182 xfree(n);
183 sc_close();
184 return status;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000185}
186
187/* private key operations */
188
189static int
Ben Lindstrom0b675b12002-03-22 03:28:11 +0000190sc_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
191 int padding)
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000192{
193 u_char *padded = NULL;
Ben Lindstroma2fec902001-09-18 05:45:44 +0000194 int sw, len, olen, status = -1;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000195
196 debug("sc_private_decrypt called");
197
198 olen = len = sw = 0;
Ben Lindstrom30b00be2001-08-06 21:22:10 +0000199 if (sc_fd < 0) {
200 status = sc_init();
201 if (status < 0 )
Damien Miller694be4b2001-07-14 12:13:26 +1000202 goto err;
Ben Lindstrom30b00be2001-08-06 21:22:10 +0000203 }
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000204 if (padding != RSA_PKCS1_PADDING)
205 goto err;
206
207 len = BN_num_bytes(rsa->n);
208 padded = xmalloc(len);
209
Ben Lindstrom0b675b12002-03-22 03:28:11 +0000210 sectok_apdu(sc_fd, CLA_SSH, INS_DECRYPT, 0, 0, len, (u_char *)from,
211 0, NULL, &sw);
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000212 if (!sectok_swOK(sw)) {
213 error("sc_private_decrypt: INS_DECRYPT failed: %s",
214 sectok_get_sw(sw));
215 goto err;
216 }
217 sectok_apdu(sc_fd, CLA_SSH, INS_GET_RESPONSE, 0, 0, 0, NULL,
Damien Miller9f0f5c62001-12-21 14:45:46 +1100218 len, padded, &sw);
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000219 if (!sectok_swOK(sw)) {
220 error("sc_private_decrypt: INS_GET_RESPONSE failed: %s",
221 sectok_get_sw(sw));
222 goto err;
223 }
224 olen = RSA_padding_check_PKCS1_type_2(to, len, padded + 1, len - 1,
225 len);
226err:
227 if (padded)
228 xfree(padded);
Ben Lindstroma2fec902001-09-18 05:45:44 +0000229 sc_close();
Ben Lindstrom30b00be2001-08-06 21:22:10 +0000230 return (olen >= 0 ? olen : status);
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000231}
232
233static int
Ben Lindstrom0b675b12002-03-22 03:28:11 +0000234sc_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
235 int padding)
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000236{
237 u_char *padded = NULL;
Ben Lindstroma2fec902001-09-18 05:45:44 +0000238 int sw, len, status = -1;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000239
240 len = sw = 0;
Ben Lindstrom30b00be2001-08-06 21:22:10 +0000241 if (sc_fd < 0) {
242 status = sc_init();
243 if (status < 0 )
Damien Miller694be4b2001-07-14 12:13:26 +1000244 goto err;
Ben Lindstrom30b00be2001-08-06 21:22:10 +0000245 }
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000246 if (padding != RSA_PKCS1_PADDING)
247 goto err;
248
249 debug("sc_private_encrypt called");
250 len = BN_num_bytes(rsa->n);
251 padded = xmalloc(len);
252
Ben Lindstrom0b675b12002-03-22 03:28:11 +0000253 if (RSA_padding_add_PKCS1_type_1(padded, len, (u_char *)from, flen) <= 0) {
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000254 error("RSA_padding_add_PKCS1_type_1 failed");
255 goto err;
256 }
257 sectok_apdu(sc_fd, CLA_SSH, INS_DECRYPT, 0, 0, len, padded, 0, NULL, &sw);
258 if (!sectok_swOK(sw)) {
259 error("sc_private_decrypt: INS_DECRYPT failed: %s",
260 sectok_get_sw(sw));
261 goto err;
262 }
263 sectok_apdu(sc_fd, CLA_SSH, INS_GET_RESPONSE, 0, 0, 0, NULL,
Damien Miller9f0f5c62001-12-21 14:45:46 +1100264 len, to, &sw);
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000265 if (!sectok_swOK(sw)) {
266 error("sc_private_decrypt: INS_GET_RESPONSE failed: %s",
267 sectok_get_sw(sw));
268 goto err;
269 }
270err:
271 if (padded)
272 xfree(padded);
Ben Lindstroma2fec902001-09-18 05:45:44 +0000273 sc_close();
Ben Lindstrom30b00be2001-08-06 21:22:10 +0000274 return (len >= 0 ? len : status);
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000275}
276
Ben Lindstroma6c8a8d2001-08-06 21:42:00 +0000277/* called on free */
278
279static int (*orig_finish)(RSA *rsa) = NULL;
280
281static int
282sc_finish(RSA *rsa)
283{
284 if (orig_finish)
285 orig_finish(rsa);
286 sc_close();
287 return 1;
288}
289
290
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000291/* engine for overloading private key operations */
292
293static ENGINE *smart_engine = NULL;
Ben Lindstrom0b675b12002-03-22 03:28:11 +0000294static RSA_METHOD smart_rsa;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000295
296ENGINE *
297sc_get_engine(void)
298{
Ben Lindstrom0b675b12002-03-22 03:28:11 +0000299 const RSA_METHOD *def;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000300
301 def = RSA_get_default_openssl_method();
302
Ben Lindstrom0b675b12002-03-22 03:28:11 +0000303 /* use the OpenSSL version */
304 memcpy(&smart_rsa, def, sizeof(smart_rsa));
305
306 smart_rsa.name = "sectok";
307
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000308 /* overload */
309 smart_rsa.rsa_priv_enc = sc_private_encrypt;
310 smart_rsa.rsa_priv_dec = sc_private_decrypt;
311
Ben Lindstroma6c8a8d2001-08-06 21:42:00 +0000312 /* save original */
313 orig_finish = def->finish;
314 smart_rsa.finish = sc_finish;
315
Damien Millerda755162002-01-22 23:09:22 +1100316 if ((smart_engine = ENGINE_new()) == NULL)
317 fatal("ENGINE_new failed");
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000318
319 ENGINE_set_id(smart_engine, "sectok");
320 ENGINE_set_name(smart_engine, "libsectok");
Ben Lindstrom0b675b12002-03-22 03:28:11 +0000321
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000322 ENGINE_set_RSA(smart_engine, &smart_rsa);
323 ENGINE_set_DSA(smart_engine, DSA_get_default_openssl_method());
324 ENGINE_set_DH(smart_engine, DH_get_default_openssl_method());
325 ENGINE_set_RAND(smart_engine, RAND_SSLeay());
326 ENGINE_set_BN_mod_exp(smart_engine, BN_mod_exp);
327
328 return smart_engine;
329}
330
Damien Miller694be4b2001-07-14 12:13:26 +1000331void
332sc_close(void)
333{
334 if (sc_fd >= 0) {
335 sectok_close(sc_fd);
336 sc_fd = -1;
337 }
338}
339
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000340Key *
Ben Lindstromf7db3bb2001-08-06 21:35:51 +0000341sc_get_key(const char *id)
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000342{
343 Key *k;
Ben Lindstrom94baf302001-08-06 21:25:38 +0000344 int status;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000345
Ben Lindstromf7db3bb2001-08-06 21:35:51 +0000346 if (sc_reader_id != NULL)
347 xfree(sc_reader_id);
348 sc_reader_id = xstrdup(id);
349
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000350 k = key_new(KEY_RSA);
351 if (k == NULL) {
352 return NULL;
353 }
Ben Lindstrom94baf302001-08-06 21:25:38 +0000354 status = sc_read_pubkey(k);
355 if (status == SCARD_ERROR_NOCARD) {
356 key_free(k);
357 return NULL;
358 }
359 if (status < 0 ) {
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000360 error("sc_read_pubkey failed");
361 key_free(k);
362 return NULL;
363 }
364 return k;
Ben Lindstrom3133dbb2001-07-04 05:35:00 +0000365}
Ben Lindstrombcc18082001-08-06 21:59:25 +0000366#endif /* SMARTCARD */