* divert-scd.c (encode_md_for_card): Don't do the pkcs-1 padding,
[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
34
35
36 static int
37 ask_for_card (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 (desc, 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 optioanlly 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   int tries = 0;
177   const char *errtext;
178   
179   assert (!opaque);
180
181   if (maxbuf < 2)
182     return GNUPG_Invalid_Value;
183
184   /* FIXME: keep PI and TRIES in OPAQUE */
185   pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
186   pi->max_length = maxbuf-1;
187   pi->min_digits = 0;  /* we want a real passphrase */
188   pi->max_digits = 8;
189   pi->max_tries = 3;
190
191   errtext = NULL;
192   do
193     {
194       rc = agent_askpin (info, errtext, pi);
195       if (!rc)
196         {
197           strncpy (buf, pi->pin, maxbuf-1);
198           buf[maxbuf-1] = 0;
199           xfree (pi);
200           return 0;
201         }
202       errtext = pi->min_digits? trans ("Bad PIN") : trans ("Bad Passphrase");
203     }
204   while ((rc == GNUPG_Bad_Passphrase || rc == GNUPG_Bad_PIN)
205          && tries++ < 3);
206   xfree (pi);
207   return rc;
208 }
209
210
211
212
213 int
214 divert_pksign (const unsigned char *digest, size_t digestlen, int algo,
215                const unsigned char *shadow_info, unsigned char **r_sig)
216 {
217   int rc;
218   char *kid;
219   size_t siglen;
220   char *sigval;
221   unsigned char *data;
222   size_t ndata;
223
224   rc = ask_for_card (shadow_info, &kid);
225   if (rc)
226     return rc;
227
228   rc = encode_md_for_card (digest, digestlen, algo, 
229                            &data, &ndata);
230   if (rc)
231     return rc;
232
233   rc = agent_card_pksign (kid, getpin_cb, NULL,
234                           data, ndata, &sigval, &siglen);
235   if (!rc)
236     *r_sig = sigval;
237   xfree (data);
238   xfree (kid);
239   
240   return rc;
241 }
242
243
244 /* Decrypt the the value given asn an S-expression in CIPHER using the
245    key identified by SHADOW_INFO and return the plaintext in an
246    allocated buffer in R_BUF.  */
247 int  
248 divert_pkdecrypt (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 (shadow_info, &kid);
293   if (rc)
294     return rc;
295
296   rc = agent_card_pkdecrypt (kid, getpin_cb, NULL,
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 }