I have to keep on working on this beast.
[libksba.git] / src / keyinfo.c
1 /* keyinfo.c - Parse and build a keyInfo structure
2  *      Copyright (C) 2001 g10 Code GmbH
3  *
4  * This file is part of KSBA.
5  *
6  * KSBA 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  * KSBA 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 /* Instead of using the ASN parser - which is easily possible - we use
22    a simple handcoded one to speed the oepration up and make it more
23    robust. */
24
25 #include <config.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <assert.h>
30
31 #include "util.h"
32 #include "asn1-func.h"
33
34 #include "shared.h"
35
36 struct algo_table_s {
37   const unsigned char *oid;  /* NULL indicattes end of table */
38   int                  oidlen;
39   int supported;
40   const char *algo_string;
41   const char *elem_string; /* parameter name or '-' */
42   const char *ctrl_string; /* expected tag values (value > 127 are raw data)*/
43   int digest_algo;
44 };
45
46 static struct algo_table_s pk_algo_table[] = {
47   { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.1 */
48     /* 1.2.840.113549.1.1.1  rsaEncryption (RSAES-PKCA1-v1.5) */ 
49     "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01", 9, 
50     1, "rsa", "-ne", "\x30\x02\x02" },
51   { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.7 */
52     /* 1.2.840.113549.1.1.7  RSAES-OAEP */ 
53     "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x07", 9, 
54     0, "rsa", "-ne", "\x30\x02\x02"}, /* (patent problems) */
55   { /* */
56     /* 2.5.8.1.1 rsa (ambiguous due to missing padding rules)*/
57     "\x55\x08\x01\x01", 4, 
58     1, "ambiguous-rsa", "-ne", "\x30\x02\x02" },
59   { /* iso.member-body.us.x9-57.x9cm.1 */
60     /* 1.2.840.10040.4.1  dsa */
61     "\x2a\x86\x48\xce\x38\x04\x01", 7, 
62     1, "dsa"  "y", "\x02" }, 
63   /* FIXME: Need code to extract p,q,g from the parameters */
64
65   {NULL}
66 };
67
68
69 static struct algo_table_s sig_algo_table[] = {
70   {  /* iso.member-body.us.rsadsi.pkcs.pkcs-1.5 */
71     /* 1.2.840.113549.1.1.5  sha1WithRSAEncryption */ 
72     "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05", 9, 
73     1, "rsa", "s", "\x82", GCRY_MD_SHA1 },
74   { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.4 */
75     /* 1.2.840.113549.1.1.4  md5WithRSAEncryption */ 
76     "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x04", 9, 
77     1, "rsa", "s", "\x82", GCRY_MD_MD5 },
78   { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.2 */
79     /* 1.2.840.113549.1.1.2  md2WithRSAEncryption */ 
80     "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x02", 9, 
81     0, "rsa", "s", "\x82", 0 },
82   { /* iso.member-body.us.x9-57.x9cm.3 */
83     /* 1.2.840.10040.4.3  dsaWithSha1 */
84     "\x2a\x86\x48\xce\x38\x04\x03", 7, 
85     1, "dsa", "-rs", "\x30\x02\x02", GCRY_MD_SHA1 }, 
86
87   {NULL}
88 };
89
90
91 struct stringbuf {
92   size_t len;
93   size_t size;
94   char *buf;
95   int out_of_core;
96 };
97
98
99 #define TLV_LENGTH() do {         \
100   if (!derlen)                    \
101     return KSBA_Invalid_Keyinfo;  \
102   c = *der++; derlen--;           \
103   if (c == 0x80)                  \
104     return KSBA_Not_DER_Encoded;  \
105   if (c == 0xff)                  \
106     return KSBA_BER_Error;        \
107                                   \
108   if ( !(c & 0x80) )              \
109     len = c;                      \
110   else                            \
111     {                             \
112       int count = c & 0x7f;       \
113                                   \
114       for (len=0; count; count--) \
115         {                         \
116           len <<= 8;              \
117           if (!derlen)            \
118             return KSBA_BER_Error;\
119           c = *der++; derlen--;   \
120           len |= c & 0xff;        \
121         }                         \
122     }                             \
123   if (len > derlen)               \
124     return KSBA_Invalid_Keyinfo;  \
125 } while (0)
126
127
128 /* Return the OFF and the LEN of algorithm within DER.  Do some checks
129    and return the number of bytes read in r_nread, adding this to der
130    does point into the BIT STRING
131  */
132 static KsbaError
133 get_algorithm (const unsigned char *der, size_t derlen,
134                size_t *r_nread, size_t *r_pos, size_t *r_len)
135 {
136   int c;
137   const unsigned char *start = der;
138   const unsigned char *startseq;
139   unsigned long seqlen, len;
140
141   /* get the inner sequence */
142   if (!derlen)
143     return KSBA_Invalid_Keyinfo;
144   c = *der++; derlen--;
145   if ( c != 0x30 )
146     return KSBA_Unexpected_Tag; /* not a SEQUENCE */
147   TLV_LENGTH(); 
148   seqlen = len;
149   startseq = der;
150
151   /* get the object identifier */
152   if (!derlen)
153     return KSBA_Invalid_Keyinfo;
154   c = *der++; derlen--; 
155   if ( c != 0x06 )
156     return KSBA_Unexpected_Tag; /* not an OBJECT IDENTIFIER */
157   TLV_LENGTH();
158
159   /* der does now point to an oid of length LEN */
160   *r_pos = der - start;
161   *r_len = len;
162 /*    { */
163 /*      char *p = ksba_oid_to_str (der, len); */
164 /*      printf ("algorithm: %s\n", p); */
165 /*      xfree (p); */
166 /*    } */
167   der += len;
168   derlen -= len;
169   seqlen -= der - startseq;;
170
171   /* check that the parameter is NULL or not there */
172   if (!seqlen)
173     {
174 /*        printf ("parameter: none\n"); */
175     }
176   else
177     {
178       const unsigned char *startparm = der;
179
180       if (!derlen)
181         return KSBA_Invalid_Keyinfo;
182       c = *der++; derlen--;
183       if ( c == 0x05 ) 
184         {
185           /*printf ("parameter: NULL \n");*/ /* the only correct thing */
186           if (!derlen)
187             return KSBA_Invalid_Keyinfo;
188           c = *der++; derlen--;
189           if (c) 
190             return KSBA_BER_Error;  /* NULL must have a length of 0 */
191           seqlen -= 2;
192         }
193       else
194         {
195           printf ("parameter: with tag %02x - ignored\n", c);
196           TLV_LENGTH();
197           seqlen -= der - startparm;
198           /* skip the value */
199           der += len;
200           derlen -= len;
201           seqlen -= len;
202         }
203     }
204
205   if (seqlen)
206     return KSBA_Invalid_Keyinfo;
207
208   /* move forward to the BIT_STR */
209   if (!derlen)
210     return KSBA_Invalid_Keyinfo;
211   c = *der++; derlen--;
212     
213   if (c != 0x03)
214     return KSBA_Unexpected_Tag; /* not a BIT STRING */
215   TLV_LENGTH();
216
217   *r_nread = der - start;
218
219   return 0;
220 }
221
222
223 static void
224 init_stringbuf (struct stringbuf *sb, int initiallen)
225 {
226   sb->len = 0;
227   sb->size = initiallen;
228   sb->out_of_core = 0;
229   /* allocate one more, so that get_stringbuf can append a nul */
230   sb->buf = xtrymalloc (initiallen+1);
231   if (!sb->buf)
232       sb->out_of_core = 1;
233 }
234
235 static void
236 put_stringbuf (struct stringbuf *sb, const char *text)
237 {
238   size_t n = strlen (text);
239
240   if (sb->out_of_core)
241     return;
242
243   if (sb->len + n >= sb->size)
244     {
245       char *p;
246       
247       sb->size += n + 100;
248       p = xtryrealloc (sb->buf, sb->size);
249       if ( !p)
250         {
251           sb->out_of_core = 1;
252           return;
253         }
254       sb->buf = p;
255     }
256   memcpy (sb->buf+sb->len, text, n);
257   sb->len += n;
258 }
259
260 static char *
261 get_stringbuf (struct stringbuf *sb)
262 {
263   char *p;
264
265   if (sb->out_of_core)
266     {
267       xfree (sb->buf); sb->buf = NULL;
268       return NULL;
269     }
270
271   sb->buf[sb->len] = 0;
272   p = sb->buf;
273   sb->buf = NULL;
274   sb->out_of_core = 1; /* make sure the caller does an init before reuse */
275   return p;
276 }
277
278
279 /* Assume that der is a buffer of length DERLEN with a DER encoded
280  Asn.1 structure like this:
281  
282   keyInfo ::= SEQUENCE {
283                  SEQUENCE { 
284                     algorithm    OBJECT IDENTIFIER,
285                     parameters   ANY DEFINED BY algorithm OPTIONAL }
286                  publicKey  BIT STRING }
287   
288   We only allow parameters == NULL.
289
290   The function parses this structure and create a SEXP suitable to be
291   used as a public key in Libgcrypt.  The S-Exp will be returned in a
292   string which the caller must free.  
293   
294   We don't pass an ASN.1 node here but a plain memory block.  */
295
296 KsbaError
297 _ksba_keyinfo_to_sexp (const unsigned char *der, size_t derlen,
298                        char **r_string)
299 {
300   KsbaError err;
301   int c;
302   size_t nread, off, len;
303   int algoidx;
304   const unsigned char *ctrl;
305   const char *elem;
306   struct stringbuf sb;
307
308   *r_string = NULL;
309
310   /* check the outer sequence */
311   if (!derlen)
312     return KSBA_Invalid_Keyinfo;
313   c = *der++; derlen--;
314   if ( c != 0x30 )
315     return KSBA_Unexpected_Tag; /* not a SEQUENCE */
316   TLV_LENGTH();
317   /* and now the inner part */
318   err = get_algorithm (der, derlen, &nread, &off, &len);
319   if (err)
320     return err;
321   
322   /* look into our table of supported algorithms */
323   for (algoidx=0; pk_algo_table[algoidx].oid; algoidx++)
324     {
325       if ( len == pk_algo_table[algoidx].oidlen
326            && !memcmp (der+off, pk_algo_table[algoidx].oid, len))
327         break;
328     }
329   if (!pk_algo_table[algoidx].oid)
330     return KSBA_Unknown_Algorithm;
331   if (!pk_algo_table[algoidx].supported)
332     return KSBA_Unsupported_Algorithm;
333
334   der += nread;
335   derlen -= nread;
336
337   if (!derlen)
338     return KSBA_Invalid_Keyinfo;
339   c = *der++; derlen--;
340   if (c) 
341     fprintf (stderr, "warning: number of unused bits is not zero\n");
342
343   /* fixme: we should calculate the initial length form the size of the
344      sequence, so that we don't neen a realloc later */
345   init_stringbuf (&sb, 100);
346   put_stringbuf (&sb, "(public-key(");
347   put_stringbuf (&sb, pk_algo_table[algoidx].algo_string);
348
349   /* FIXME: We don't release the stringbuf in case of error
350      better let the macro jump to a label */
351   elem = pk_algo_table[algoidx].elem_string; 
352   ctrl = pk_algo_table[algoidx].ctrl_string; 
353   for (; *elem; ctrl++, elem++)
354     {
355       int is_int;
356
357       if (!derlen)
358         return KSBA_Invalid_Keyinfo;
359       c = *der++; derlen--;
360       if ( c != *ctrl )
361         return KSBA_Unexpected_Tag; /* not the required tag */
362       is_int = c == 0x02;
363       TLV_LENGTH ();
364       if (is_int && *elem != '-')
365         { /* take this integer */
366           char tmp[100];
367           int i, n;
368           
369           strcpy (tmp, "(. #"); 
370           tmp[1] = *elem;
371           put_stringbuf (&sb, tmp);
372           for (i=n=0; n < len; n++)
373             {
374               if (!derlen)
375                 return KSBA_Invalid_Keyinfo;
376               c = *der++; derlen--;
377               sprintf (tmp+2*i, "%02X", c);
378               if ( !(++i%10) )
379                 {
380                   put_stringbuf (&sb, tmp);
381                   i=0;
382                 }
383             }
384           if (i)
385             put_stringbuf (&sb, tmp);
386           put_stringbuf (&sb, "#)");
387         }
388     }
389   put_stringbuf (&sb, "))");
390   
391   *r_string = get_stringbuf (&sb);
392   if (!*r_string)
393     return KSBA_Out_Of_Core;
394
395   return 0;
396 }
397
398
399 /* Assume that der is a buffer of length DERLEN with a DER encoded
400  Asn.1 structure like this:
401  
402      SEQUENCE { 
403         algorithm    OBJECT IDENTIFIER,
404         parameters   ANY DEFINED BY algorithm OPTIONAL }
405      signature  BIT STRING 
406   
407   We only allow parameters == NULL.
408
409   The function parses this structure and creates a S-Exp suitable to be
410   used as signature value in Libgcrypt:
411   
412   (sig-val
413     (<algo>
414       (<param_name1> <mpi>)
415       ...
416       (<param_namen> <mpi>)
417     ))
418
419  The S-Exp will be returned in a string which the caller must free.
420  We don't pass an ASN.1 node here but a plain memory block.  */
421 KsbaError
422 _ksba_sigval_to_sexp (const unsigned char *der, size_t derlen,
423                        char **r_string)
424 {
425   KsbaError err;
426   int c;
427   size_t nread, off, len;
428   int algoidx;
429   const unsigned char *ctrl;
430   const char *elem;
431   struct stringbuf sb;
432
433   /* FIXME: The entire function is very similar to keyinfo_to_sexp */
434
435   *r_string = NULL;
436
437   err = get_algorithm (der, derlen, &nread, &off, &len);
438   if (err)
439     return err;
440   
441   /* look into our table of supported algorithms */
442   for (algoidx=0; sig_algo_table[algoidx].oid; algoidx++)
443     {
444       if ( len == sig_algo_table[algoidx].oidlen
445            && !memcmp (der+off, sig_algo_table[algoidx].oid, len))
446         break;
447     }
448   if (!sig_algo_table[algoidx].oid)
449     return KSBA_Unknown_Algorithm;
450   if (!sig_algo_table[algoidx].supported)
451     return KSBA_Unsupported_Algorithm;
452
453   der += nread;
454   derlen -= nread;
455
456   if (!derlen)
457     return KSBA_Invalid_Keyinfo;
458   c = *der++; derlen--;
459   if (c) 
460     fprintf (stderr, "warning: number of unused bits is not zero\n");
461
462   /* fixme: we should calculate the initial length form the size of the
463      sequence, so that we don't neen a realloc later */
464   init_stringbuf (&sb, 100);
465   put_stringbuf (&sb, "(sig-val(");
466   put_stringbuf (&sb, sig_algo_table[algoidx].algo_string);
467
468   /* FIXME: We don't release the stringbuf in case of error
469      better let the macro jump to a label */
470   elem = sig_algo_table[algoidx].elem_string; 
471   ctrl = sig_algo_table[algoidx].ctrl_string; 
472   for (; *elem; ctrl++, elem++)
473     {
474       int is_int;
475
476       if ( (*ctrl & 0x80) && !elem[1] )
477         {  /* Hack to allow a raw value */
478           is_int = 1;
479           len = derlen;
480         }
481       else
482         {
483           if (!derlen)
484             return KSBA_Invalid_Keyinfo;
485           c = *der++; derlen--;
486           if ( c != *ctrl )
487             return KSBA_Unexpected_Tag; /* not the required tag */
488           is_int = c == 0x02;
489           TLV_LENGTH ();
490         }
491       if (is_int && *elem != '-')
492         { /* take this integer */
493           char tmp[100];
494           int i, n;
495           
496           strcpy (tmp, "(. #"); 
497           tmp[1] = *elem;
498           put_stringbuf (&sb, tmp);
499           for (i=n=0; n < len; n++)
500             {
501               if (!derlen)
502                 return KSBA_Invalid_Keyinfo;
503               c = *der++; derlen--;
504               sprintf (tmp+2*i, "%02X", c);
505               if ( !(++i%10) )
506                 {
507                   put_stringbuf (&sb, tmp);
508                   i=0;
509                 }
510             }
511           if (i)
512             put_stringbuf (&sb, tmp);
513           put_stringbuf (&sb, "#)");
514         }
515     }
516   put_stringbuf (&sb, "))");
517   
518   *r_string = get_stringbuf (&sb);
519   if (!*r_string)
520     return KSBA_Out_Of_Core;
521
522   return 0;
523 }
524
525
526 /* Take the OID at the given node and map it to a libgcrypt digest algo.
527    Return 0 if the algo is unknown, -1 for an invalid OID 
528    
529    Fixme: This function belongs to another file, but as long as we
530    don't have a generic OID registry we put it here.  */
531 int
532 _ksba_map_oid_to_digest_algo (const unsigned char *image, AsnNode node) 
533 {
534   int algoidx;
535   int off, len;
536
537   if (!node || node->type != TYPE_OBJECT_ID || node->off == -1)
538     return -1;
539
540   off = node->off + node->nhdr;
541   len = node->len;
542   for (algoidx=0; sig_algo_table[algoidx].oid; algoidx++)
543     {
544       if ( len == sig_algo_table[algoidx].oidlen
545            && !memcmp (image + off, sig_algo_table[algoidx].oid, len))
546         break;
547     }
548   if (!sig_algo_table[algoidx].oid)
549     return 0;
550
551   return sig_algo_table[algoidx].digest_algo;
552 }
553