(gpgsm_validate_chain): Changed the message printed
[gnupg.git] / scd / app-nks.c
1 /* app-nks.c - The Telesec NKS 2.0 card application.
2  *      Copyright (C) 2004 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 <assert.h>
27 #include <time.h>
28
29 #include "scdaemon.h"
30
31 #include "iso7816.h"
32 #include "app-common.h"
33 #include "tlv.h"
34
35 static struct {
36   int fid;      /* File ID. */
37   int certtype; /* Type of certificate or 0 if it is not a certificate. */
38   int iskeypair; /* If true has the FID of the correspoding certificate. */
39 } filelist[] = {
40   { 0x4531, 0,  0xC000 }, 
41   { 0xC000, 101 },
42   { 0x4331, 100 },
43   { 0x4332, 100 },
44   { 0xB000, 110 },
45   { 0x45B1, 0,  0xC200 },
46   { 0xC200, 101 },
47   { 0x43B1, 100 },
48   { 0x43B2, 100 },
49   { 0, 0 }
50 };
51
52
53
54 /* Given the slot and the File Id FID, return the length of the
55    certificate contained in that file. Returns 0 if the file does not
56    exists or does not contain a certificate. */
57 static size_t
58 get_length_of_cert (int slot, int fid)
59 {
60   gpg_error_t err;
61   unsigned char *buffer;
62   const unsigned char *p;
63   size_t buflen, n;
64   int class, tag, constructed, ndef;
65   size_t objlen, hdrlen;
66
67   err = iso7816_select_file (slot, fid, 0, NULL, NULL);
68   if (err)
69     {
70       log_info ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err));
71       return 0;
72     }
73
74   err = iso7816_read_binary (slot, 0, 32, &buffer, &buflen);
75   if (err)
76     {
77       log_info ("error reading certificate from FID 0x%04X: %s\n",
78                  fid, gpg_strerror (err));
79       return 0;
80     }
81   
82   if (!buflen || *buffer == 0xff)
83     {
84       log_info ("no certificate contained in FID 0x%04X\n", fid);
85       xfree (buffer);
86       return 0;
87     }
88
89   p = buffer;
90   n = buflen;
91   err = parse_ber_header (&p, &n, &class, &tag, &constructed,
92                           &ndef, &objlen, &hdrlen);
93   if (err)
94     {
95       log_info ("error parsing certificate in FID 0x%04X: %s\n",
96                 fid, gpg_strerror (err));
97       xfree (buffer);
98       return 0;
99     }
100
101   /* All certificates should commence with a SEQUENCE expect fro the
102      special ROOT CA which are enclosed in a SET. */
103   if ( !(class == CLASS_UNIVERSAL &&  constructed
104          && (tag == TAG_SEQUENCE || tag == TAG_SET)))
105     {
106       log_info ("contents of FID 0x%04X does not look like a certificate\n",
107                 fid);
108       return 0;
109     }
110  
111   return objlen + hdrlen;
112 }
113
114
115
116 /* Read the file with FID, assume it contains a public key and return
117    its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */
118 static gpg_error_t
119 keygripstr_from_pk_file (int slot, int fid, char *r_gripstr)
120 {
121   gpg_error_t err;
122   unsigned char grip[20];
123   unsigned char *buffer[2];
124   size_t buflen[2];
125   gcry_sexp_t sexp;
126   int i;
127   
128   err = iso7816_select_file (slot, fid, 0, NULL, NULL);
129   if (err)
130     return err;
131   err = iso7816_read_record (slot, 1, 1, &buffer[0], &buflen[0]);
132   if (err)
133     return err;
134   err = iso7816_read_record (slot, 2, 1, &buffer[1], &buflen[1]);
135   if (err)
136     {
137       xfree (buffer[0]);
138       return err;
139     }
140   
141   for (i=0; i < 2; i++)
142     {
143       /* Check that the value appears like an integer encoded as
144          Simple-TLV.  We don't check the tag because the tests cards I
145          have use 1 for both, the modulus and the exponent - the
146          example in the documentation gives 2 for the exponent. */
147       if (buflen[i] < 3)
148         err = gpg_error (GPG_ERR_TOO_SHORT);
149       else if (buffer[i][1] != buflen[i]-2 )
150         err = gpg_error (GPG_ERR_INV_OBJ);
151     }
152
153   if (!err)
154     err = gcry_sexp_build (&sexp, NULL,
155                            "(public-key (rsa (n %b) (e %b)))",
156                            (int)buflen[0]-2, buffer[0]+2,
157                            (int)buflen[1]-2, buffer[1]+2);
158
159   xfree (buffer[0]);
160   xfree (buffer[1]);
161   if (err)
162     return err;
163
164   if (!gcry_pk_get_keygrip (sexp, grip))
165     {
166       err = gpg_error (GPG_ERR_INTERNAL); /* i.e. RSA not supported by
167                                              libgcrypt. */
168     }
169   else
170     {
171       for (i=0; i < 20; i++)
172         sprintf (r_gripstr+i*2, "%02X", grip[i]);
173     }
174   gcry_sexp_release (sexp);
175   return err;
176 }
177
178
179
180 static int
181 do_learn_status (APP app, CTRL ctrl)
182 {
183   gpg_error_t err;
184   char ct_buf[100], id_buf[100];
185   int i;
186
187   /* Output information about all useful objects. */
188   for (i=0; filelist[i].fid; i++)
189     {
190       if (filelist[i].certtype)
191         {
192           size_t len = get_length_of_cert (app->slot, filelist[i].fid);
193
194           if (len)
195             {
196               /* FIXME: We should store the length in the application's
197                  context so that a following readcert does only need to
198                  read that many bytes. */
199               sprintf (ct_buf, "%d", filelist[i].certtype);
200               sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid);
201               send_status_info (ctrl, "CERTINFO",
202                                 ct_buf, strlen (ct_buf), 
203                                 id_buf, strlen (id_buf), 
204                                 NULL, (size_t)0);
205             }
206         }
207       else if (filelist[i].iskeypair)
208         {
209           char gripstr[40+1];
210
211           err = keygripstr_from_pk_file (app->slot, filelist[i].fid, gripstr);
212           if (err)
213             log_error ("can't get keygrip from FID 0x%04X: %s\n",
214                        filelist[i].fid, gpg_strerror (err));
215           else
216             {
217               sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid);
218               send_status_info (ctrl, "KEYPAIRINFO",
219                                 gripstr, 40, 
220                                 id_buf, strlen (id_buf), 
221                                 NULL, (size_t)0);
222             }
223         }
224     }
225
226   return 0;
227 }
228
229
230
231
232 /* Read the certificate with id CERTID (as returned by learn_status in
233    the CERTINFO status lines) and return it in the freshly allocated
234    buffer put into CERT and the length of the certificate put into
235    CERTLEN. */
236 static int
237 do_readcert (app_t app, const char *certid,
238              unsigned char **cert, size_t *certlen)
239 {
240   int i, fid;
241   gpg_error_t err;
242   unsigned char *buffer;
243   const unsigned char *p;
244   size_t buflen, n;
245   int class, tag, constructed, ndef;
246   size_t totobjlen, objlen, hdrlen;
247   int rootca = 0;
248
249   *cert = NULL;
250   *certlen = 0;
251   if (strncmp (certid, "NKS-DF01.", 9) ) 
252     return gpg_error (GPG_ERR_INV_ID);
253   certid += 9;
254   if (!hexdigitp (certid) || !hexdigitp (certid+1)
255       || !hexdigitp (certid+2) || !hexdigitp (certid+3) 
256       || certid[4])
257     return gpg_error (GPG_ERR_INV_ID);
258   fid = xtoi_4 (certid);
259   for (i=0; filelist[i].fid; i++)
260     if ((filelist[i].certtype || filelist[i].iskeypair)
261         && filelist[i].fid == fid)
262       break;
263   if (!filelist[i].fid)
264     return gpg_error (GPG_ERR_NOT_FOUND);
265
266   /* If the requested objects is a plain public key, redirect it to
267      the corresponding certificate.  The whole system is a bit messy
268      becuase we sometime use the key directly or let the caller
269      retrieve the key from the certificate.  The valid point behind
270      that is to support not-yet stored certificates. */
271   if (filelist[i].iskeypair)
272     fid = filelist[i].iskeypair;
273
274
275   /* Read the entire file.  fixme: This could be optimized by first
276      reading the header to figure out how long the certificate
277      actually is. */
278   err = iso7816_select_file (app->slot, fid, 0, NULL, NULL);
279   if (err)
280     {
281       log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err));
282       return err;
283     }
284
285   err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen);
286   if (err)
287     {
288       log_error ("error reading certificate from FID 0x%04X: %s\n",
289                  fid, gpg_strerror (err));
290       return err;
291     }
292   
293   if (!buflen || *buffer == 0xff)
294     {
295       log_info ("no certificate contained in FID 0x%04X\n", fid);
296       err = gpg_error (GPG_ERR_NOT_FOUND);
297       goto leave;
298     }
299
300   /* Now figure something out about the object. */
301   p = buffer;
302   n = buflen;
303   err = parse_ber_header (&p, &n, &class, &tag, &constructed,
304                           &ndef, &objlen, &hdrlen);
305   if (err)
306     goto leave;
307   if ( class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed )
308     ;
309   else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed )
310     rootca = 1;
311   else
312     return gpg_error (GPG_ERR_INV_OBJ);
313   totobjlen = objlen + hdrlen;
314   assert (totobjlen <= buflen);
315
316   err = parse_ber_header (&p, &n, &class, &tag, &constructed,
317                           &ndef, &objlen, &hdrlen);
318   if (err)
319     goto leave;
320   
321   if (rootca)
322     ;
323   else if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed)
324     {
325       const unsigned char *save_p;
326   
327       /* The certificate seems to be contained in a userCertificate
328          container.  Skip this and assume the following sequence is
329          the certificate. */
330       if (n < objlen)
331         {
332           err = gpg_error (GPG_ERR_INV_OBJ);
333           goto leave;
334         }
335       p += objlen;
336       n -= objlen;
337       save_p = p;
338       err = parse_ber_header (&p, &n, &class, &tag, &constructed,
339                               &ndef, &objlen, &hdrlen);
340       if (err) 
341         goto leave;
342       if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) )
343         return gpg_error (GPG_ERR_INV_OBJ);
344       totobjlen = objlen + hdrlen;
345       assert (save_p + totobjlen <= buffer + buflen);
346       memmove (buffer, save_p, totobjlen);
347     }
348   
349   *cert = buffer;
350   buffer = NULL;
351   *certlen = totobjlen;
352
353  leave:
354   xfree (buffer);
355   return err;
356 }
357
358
359
360 /* Select the NKS 2.0 application on the card in SLOT.  */
361 int
362 app_select_nks (APP app)
363 {
364   static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 };
365   int slot = app->slot;
366   int rc;
367   
368   rc = iso7816_select_application (slot, aid, sizeof aid);
369   if (!rc)
370     {
371       app->apptype = "NKS";
372
373       app->fnc.learn_status = do_learn_status;
374       app->fnc.readcert = do_readcert;
375       app->fnc.getattr = NULL;
376       app->fnc.setattr = NULL;
377       app->fnc.genkey = NULL;
378       app->fnc.sign = NULL;
379       app->fnc.auth = NULL;
380       app->fnc.decipher = NULL;
381       app->fnc.change_pin = NULL;
382       app->fnc.check_pin = NULL;
383    }
384
385   return rc;
386 }
387
388