Updated from latest NewPG project
[gnupg.git] / agent / divert-scd.c
1 /* divert-scd.c - divert operations to the scdaemon 
2  *      Copyright (C) 2002 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30
31 #include "agent.h"
32 #include "sexp-parse.h"
33 #include "i18n.h"
34
35
36 static int
37 ask_for_card (CTRL ctrl, const unsigned char *shadow_info, char **r_kid)
38 {
39   int rc, i;
40   const unsigned char *s;
41   size_t n;
42   char *serialno;
43   int no_card = 0;
44   char *desc;
45   char *want_sn, *want_kid;
46   int want_sn_displen;
47
48   *r_kid = NULL;
49   s = shadow_info;
50   if (*s != '(')
51     return GNUPG_Invalid_Sexp;
52   s++;
53   n = snext (&s);
54   if (!n)
55     return GNUPG_Invalid_Sexp;
56   want_sn = xtrymalloc (n*2+1);
57   if (!want_sn)
58     return GNUPG_Out_Of_Core;
59   for (i=0; i < n; i++)
60     sprintf (want_sn+2*i, "%02X", s[i]);
61   s += n;
62   /* We assume that a 20 byte serial number is a standard one which
63      seems to have the property to have a zero in the last nibble.  We
64      don't display this '0' because it may confuse the user */
65   want_sn_displen = strlen (want_sn);
66   if (want_sn_displen == 20 && want_sn[19] == '0')
67     want_sn_displen--;
68
69   n = snext (&s);
70   if (!n)
71     return GNUPG_Invalid_Sexp;
72   want_kid = xtrymalloc (n+1);
73   if (!want_kid)
74     {
75       xfree (want_sn);
76       return GNUPG_Out_Of_Core;
77     }
78   memcpy (want_kid, s, n);
79   want_kid[n] = 0;
80
81   for (;;)
82     {
83       rc = agent_card_serialno (&serialno);
84       if (!rc)
85         {
86           log_debug ("detected card with S/N %s\n", serialno);
87           i = strcmp (serialno, want_sn);
88           xfree (serialno);
89           serialno = NULL;
90           if (!i)
91             {
92               xfree (want_sn);
93               *r_kid = want_kid;
94               return 0; /* yes, we have the correct card */
95             }
96         }
97       else if (rc == GNUPG_Card_Not_Present)
98         {
99           log_debug ("no card present\n");
100           rc = 0;
101           no_card = 1;
102         }
103       else
104         {
105           log_error ("error accesing card: %s\n", gnupg_strerror (rc));
106         }
107
108       if (!rc)
109         {
110           if (asprintf (&desc,
111                     "%s:%%0A%%0A"
112                     "  \"%.*s\"",
113                     no_card? "Please insert the card with serial number" 
114                     : "Please remove the current card and "
115                     "insert the one with serial number",
116                     want_sn_displen, want_sn) < 0)
117             {
118               rc = GNUPG_Out_Of_Core;
119             }
120           else
121             {
122               rc = agent_get_confirmation (ctrl, desc, NULL, NULL);
123               free (desc);
124             }
125         }
126       if (rc)
127         {
128           xfree (want_sn);
129           xfree (want_kid);
130           return rc;
131         }
132     }
133 }
134
135
136 /* Put the DIGEST into an DER encoded comtainer and return it in R_VAL. */
137 static int
138 encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo,
139                     unsigned char **r_val, size_t *r_len)
140 {
141   byte *frame;
142   byte asn[100];
143   size_t asnlen;
144
145   asnlen = DIM(asn);
146   if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
147     {
148       log_error ("no object identifier for algo %d\n", algo);
149       return GNUPG_Internal_Error;
150     }
151
152   frame = xtrymalloc (asnlen + digestlen);
153   if (!frame)
154     return GNUPG_Out_Of_Core;
155   memcpy (frame, asn, asnlen);
156   memcpy (frame+asnlen, digest, digestlen);
157   if (DBG_CRYPTO)
158     log_printhex ("encoded hash:", frame, asnlen+digestlen);
159       
160   *r_val = frame;
161   *r_len = asnlen+digestlen;
162   return 0;
163 }
164
165
166 /* Callback used to ask for the PIN which should be set into BUF.  The
167    buf has been allocated by the caller and is of size MAXBUF which
168    includes the terminating null.  The function should return an UTF-8
169    string with the passphrase, the buffer may optionally be padded
170    with arbitrary characters */
171 static int 
172 getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
173 {
174   struct pin_entry_info_s *pi;
175   int rc;
176   char *desc;
177   CTRL ctrl = opaque;
178
179   if (maxbuf < 2)
180     return GNUPG_Invalid_Value;
181
182
183   /* FIXME: keep PI and TRIES in OPAQUE.  Frankly this is a whole
184      mess because we should call the card's verify function from the
185      pinentry check pin CB. */
186   pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
187   pi->max_length = maxbuf-1;
188   pi->min_digits = 0;  /* we want a real passphrase */
189   pi->max_digits = 8;
190   pi->max_tries = 3;
191
192   if ( asprintf (&desc, _("Please enter the PIN%s%s%s to unlock the card"), 
193                  info? " (`":"",
194                  info? info:"",
195                  info? "')":"") < 0)
196     desc = NULL;
197   rc = agent_askpin (ctrl, desc?desc:info, pi);
198   free (desc);
199   if (!rc)
200     {
201       strncpy (buf, pi->pin, maxbuf-1);
202       buf[maxbuf-1] = 0;
203     }
204   xfree (pi);
205   return rc;
206 }
207
208
209
210
211 int
212 divert_pksign (CTRL ctrl, 
213                const unsigned char *digest, size_t digestlen, int algo,
214                const unsigned char *shadow_info, unsigned char **r_sig)
215 {
216   int rc;
217   char *kid;
218   size_t siglen;
219   char *sigval;
220   unsigned char *data;
221   size_t ndata;
222
223   rc = ask_for_card (ctrl, shadow_info, &kid);
224   if (rc)
225     return rc;
226
227   rc = encode_md_for_card (digest, digestlen, algo, 
228                            &data, &ndata);
229   if (rc)
230     return rc;
231
232   rc = agent_card_pksign (kid, getpin_cb, ctrl,
233                           data, ndata, &sigval, &siglen);
234   if (!rc)
235     *r_sig = sigval;
236   xfree (data);
237   xfree (kid);
238   
239   return rc;
240 }
241
242
243 /* Decrypt the the value given asn an S-expression in CIPHER using the
244    key identified by SHADOW_INFO and return the plaintext in an
245    allocated buffer in R_BUF.  */
246 int  
247 divert_pkdecrypt (CTRL ctrl,
248                   const unsigned char *cipher,
249                   const unsigned char *shadow_info,
250                   char **r_buf, size_t *r_len)
251 {
252   int rc;
253   char *kid;
254   const unsigned char *s;
255   size_t n;
256   const unsigned char *ciphertext;
257   size_t ciphertextlen;
258   char *plaintext;
259   size_t plaintextlen;
260
261   s = cipher;
262   if (*s != '(')
263     return GNUPG_Invalid_Sexp;
264   s++;
265   n = snext (&s);
266   if (!n)
267     return GNUPG_Invalid_Sexp; 
268   if (!smatch (&s, n, "enc-val"))
269     return GNUPG_Unknown_Sexp; 
270   if (*s != '(')
271     return GNUPG_Unknown_Sexp;
272   s++;
273   n = snext (&s);
274   if (!n)
275     return GNUPG_Invalid_Sexp; 
276   if (!smatch (&s, n, "rsa"))
277     return GNUPG_Unsupported_Algorithm; 
278   if (*s != '(')
279     return GNUPG_Unknown_Sexp;
280   s++;
281   n = snext (&s);
282   if (!n)
283     return GNUPG_Invalid_Sexp; 
284   if (!smatch (&s, n, "a"))
285     return GNUPG_Unknown_Sexp;
286   n = snext (&s);
287   if (!n)
288     return GNUPG_Unknown_Sexp; 
289   ciphertext = s;
290   ciphertextlen = n;
291
292   rc = ask_for_card (ctrl, shadow_info, &kid);
293   if (rc)
294     return rc;
295
296   rc = agent_card_pkdecrypt (kid, getpin_cb, ctrl,
297                              ciphertext, ciphertextlen,
298                              &plaintext, &plaintextlen);
299   if (!rc)
300     {
301       *r_buf = plaintext;
302       *r_len = plaintextlen;
303     }
304   xfree (kid);
305   return rc;
306 }