2009-01-26 Marek Habersack <mhabersack@novell.com>
[mono.git] / mono / metadata / monosn.c
1 /*
2  * monosn.c: Mono String Name Utility
3  *
4  * Author:
5  *   Paolo Molaro (lupus@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  *
9  */
10 #include <mono/metadata/class.h>
11 #include <mono/metadata/debug-helpers.h>
12 #include <mono/metadata/tokentype.h>
13 #include <mono/metadata/appdomain.h>
14 #include <mono/metadata/assembly.h>
15 #include "mono/utils/mono-digest.h"
16 /* trim headers */
17
18 #include <string.h>
19 #include <ctype.h>
20
21 #define RSA1_MAGIC     0x32415351
22 #define RSA2_MAGIC     0x32415352
23 #define PRIVKEY_MAGIC  0x00000207
24 #define PUBKEY_MAGIC   0x00008004
25
26 typedef struct {
27         guchar type, version;
28         guint16 reserved1;
29         guint32 algid;
30 } MonoKeyHeader;
31
32 typedef struct {
33         MonoKeyHeader header;
34         guint32 bitlen;
35         guint32 exponent;
36         guchar  modulus [MONO_ZERO_LEN_ARRAY];
37 } MonoRSAPubHeader;
38
39 static void
40 print_data (const char *data, int len)
41 {
42         int i;
43         for (i = 0; i < len; ++i) {
44                 if (i && !(i % 32))
45                         printf ("\n");
46                 printf ("%02x", data [i] & 0xff);
47         }
48         printf ("\n");
49 }
50
51 static int
52 show_token (const char *file, int is_assembly, int show_pubkey) {
53         char token [20];
54         if (!is_assembly) {
55                 char *pubkey;
56                 gsize len;
57                 if (!g_file_get_contents (file, &pubkey, &len, NULL)) {
58                         printf ("Cannot load file: %s\n", file);
59                         return 2;
60                 }
61                 mono_digest_get_public_token (token, pubkey, len);
62                 if (show_pubkey) {
63                         printf ("Public key is\n");
64                         print_data (pubkey, len);
65                 }
66                 g_free (pubkey);
67         } else {
68                 MonoImage *image;
69                 const char *pubkey;
70                 guint32 len;
71
72                 mono_metadata_init ();
73         mono_images_init ();
74         mono_assemblies_init ();
75         mono_loader_init ();
76
77                 image = mono_image_open (file, NULL);
78                 if (!image) {
79                         printf ("Cannot open image file: %s\n", file);
80                         return 2;
81                 }
82                 pubkey = mono_image_get_public_key (image, &len);
83                 if (!pubkey) {
84                         printf ("%s does not represent a strongly named assembly\n", mono_image_get_name(image));
85                         mono_image_close (image);
86                         return 2;
87                 }
88                 if (show_pubkey) {
89                         printf ("Public key is\n");
90                         print_data (pubkey, len);
91                 }
92                 mono_digest_get_public_token (token, pubkey, len);
93                 mono_image_close (image);
94         }
95         printf ("Public key token is ");
96         print_data (token, 8);
97         return 0;
98 }
99
100 static int
101 extract_data_to_file (int pubk, const char *assembly, const char *outfile) {
102         MonoImage *image;
103         FILE *file;
104         const char *pubkey;
105         guint32 len;
106         
107         image = mono_image_open (assembly, NULL);
108         if (!image) {
109                 printf ("Cannot open image file: %s\n", assembly);
110                 return 2;
111         }
112         if (pubk)
113                 pubkey = mono_image_get_public_key (image, &len);
114         else
115                 pubkey = mono_image_get_strong_name (image, &len);
116         if (!pubkey) {
117                 printf ("%s does not represent a strongly named assembly\n", mono_image_get_name(image));
118                 mono_image_close (image);
119                 return 2;
120         }
121         if (!(file = fopen (outfile, "wb"))) {
122                 printf ("Cannot open output file: %s\n", outfile);
123                 return 2;
124         }
125         fwrite (pubkey, len, 1, file);
126         fclose (file);
127         mono_image_close (image);
128         return 0;
129 }
130
131 const static guint8 asciitable [128] = {
132         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
133         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
134         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
135         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
136         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
137         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
138         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
139         0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
140         0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
141         0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
142         0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
143         0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
144         0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
145         0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
146         0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
147         0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
148         0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
149         0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
150         0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
151         0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
152         0x31, 0x32, 0x33, 0xff, 0xff, 0xff,
153         0xff, 0xff
154 };
155
156 /* data is changed in place */
157 static char*
158 pem_decode (guchar *data, int len, int *rlen) {
159         guchar *p, *s;
160         int b64len, i, rem = 0, full;
161         int b0, b1, b2, b3, offset, dlen;
162
163         p = strstr (data, "-----BEGIN");
164         s = strstr (data, "\n-----END");
165         if (!p || !s)
166                 return NULL;
167         while (*p != '\n') p++;
168         *s = 0;
169         s = data = p;
170         while (*p) {
171                 if (isalnum (*p) || *p == '+' || *p == '=' || *p == '/') {
172                         *s++ = *p++;
173                 } else {
174                         p++;
175                 }
176         }
177         *s = 0;
178         b64len = s - data;
179
180         full = b64len >> 2;
181         if (data [b64len - 1] == '=') {
182                 full--;
183                 rem++;
184         }
185         if (data [b64len - 2] == '=')
186                 rem++;
187         offset = 0;
188         p = data;
189         for (i = 0; i < full; ++i) {
190                 b0 = asciitable [data [offset++]];
191                 b1 = asciitable [data [offset++]];
192                 b2 = asciitable [data [offset++]];
193                 b3 = asciitable [data [offset++]];
194
195                 *p++ = (b0 << 2) | (b1 >> 4);
196                 *p++ = (b1 << 4) | (b2 >> 2);
197                 *p++ = (b2 << 6) | b3;
198         }
199         dlen = full * 3;
200         switch (rem) {
201         case 1:
202                 b0 = asciitable [data [offset++]];
203                 b1 = asciitable [data [offset++]];
204                 b2 = asciitable [data [offset++]];
205
206                 *p++ = (b0 << 2) | (b1 >> 4);
207                 *p++ = (b1 << 4) | (b2 >> 2);
208                 dlen += 2;
209                 break;
210         case 2:
211                 b0 = asciitable [data [offset++]];
212                 b1 = asciitable [data [offset++]];
213
214                 *p++ = (b0 << 2) | (b1 >> 4);
215                 dlen++;
216                 break;
217         }
218         *rlen = dlen;
219         return data;
220 }
221
222 enum {
223         DER_INTEGER = 2,
224         DER_BITSTRING = 3,
225         DER_NULL = 5,
226         DER_OBJID = 6,
227         DER_SEQUENCE = 16,
228         DER_INVALID = -1,
229         DER_END = -2
230 };
231
232 static int
233 der_get_next (guchar *data, int dlen, int offset, int *len, guchar **rdata)
234 {
235         int i, l, type, val;
236
237         if (offset + 1 >= dlen)
238                 return DER_END;
239
240         type = data [offset++] & 0x1f;
241         if (data [offset] == 0x80) /* not supported */
242                 return DER_INVALID;
243         l = 0;
244         if (data [offset] & 0x80) {
245                 val = data [offset++] & 0x7f;
246                 for (i = 0; i < val; ++i) {
247                         l = (l << 8) | data [offset++];
248                 }
249         } else {
250                 l = data [offset++];
251         }
252         *len = l;
253         *rdata = data + offset;
254         return type;
255 }
256
257 static void
258 dump_asn1 (guchar *key, int len) {
259         int type, offset, elen;
260         guchar *edata;
261
262         offset = 0;
263         while ((type = der_get_next (key, len, offset, &elen, &edata)) >= 0) {
264                 switch (type) {
265                 case DER_SEQUENCE:
266                         g_print ("seq (%d) at %d\n", elen, offset);
267                         dump_asn1 (edata, elen);
268                         offset = elen + edata - key;
269                         break;
270                 case DER_BITSTRING:
271                         g_print ("bits (%d) at %p + %d\n", elen, edata, offset);
272                         dump_asn1 (edata + 1, elen);
273                         offset = 1 + elen + edata - key;
274                         break;
275                 case DER_INTEGER:
276                         g_print ("int (%d) at %d\n", elen, offset);
277                         offset = elen + edata - key;
278                         break;
279                 case DER_NULL:
280                         g_print ("null (%d) at %d\n", elen, offset);
281                         offset = elen + edata - key;
282                         break;
283                 case DER_OBJID:
284                         g_print ("objid (%d) at %d\n", elen, offset);
285                         offset = elen + edata - key;
286                         break;
287                 default:
288                         return;
289                 }
290         }
291 }
292
293 static guint32
294 get_der_int (guchar *data, int len)
295 {
296         guint32 val = 0;
297         int i;
298         for (i = 0; i < len; ++i)
299                 val = (val << 8) | data [i];
300         return val;
301 }
302
303 static void
304 mem_reverse (guchar *p, int len) {
305         int i, t;
306
307         for (i = 0; i < len/2; ++i) {
308                 t = p [i];
309                 p [i] = p [len - i - 1];
310                 p [len - i - 1] = t;
311         }
312 }
313
314 static int
315 convert_der_key (guchar *key, int len, guchar **ret, int *retlen)
316 {
317         int type, offset, val, elen;
318         guchar *r, *edata;
319
320         offset = 0;
321         type = der_get_next (key, len, offset, &elen, &edata);
322         if (type != DER_SEQUENCE)
323                 return 1;
324         key = edata;
325         len = elen;
326         type = der_get_next (key, len, offset, &elen, &edata);
327         if (type == DER_INTEGER) {
328                 int i;
329                 guchar *ints [6];
330                 int lengths [6];
331                 guchar *p;
332                 /* a private RSA key */
333                 val = get_der_int (edata, elen);
334                 if (val != 0)
335                         return 2;
336                 offset = elen + edata - key;
337                 /* the modulus */
338                 type = der_get_next (key, len, offset, &elen, &edata);
339                 if (type != DER_INTEGER)
340                         return 2;
341                 offset = elen + edata - key;
342                 if ((elen & 1) && *edata == 0) {
343                         edata ++;
344                         elen--;
345                 }
346                 r = g_new0 (guchar, elen*4 + elen/2 + 20);
347                 r [0] = 0x7; r [1] = 0x2; r [5] = 0x24;
348                 r [8] = 0x52; r [9] = 0x53; r [10] = 0x41; r [11] = 0x32;
349                 *(guint32*)(r + 12) = elen * 8;
350                 memcpy (r + 20, edata, elen);
351                 mem_reverse (r + 20, elen);
352                 p = r + 20 + elen;
353                 /* the exponent */
354                 type = der_get_next (key, len, offset, &elen, &edata);
355                 if (type != DER_INTEGER)
356                         return 2;
357                 offset = elen + edata - key;
358                 val = get_der_int (edata, elen);
359                 *(guint32*)(r + 16) = val;
360                 for (i = 0; i < 6; i++) {
361                         type = der_get_next (key, len, offset, &elen, &edata);
362                         if (type != DER_INTEGER)
363                                 return 2;
364                         offset = elen + edata - key;
365                         if ((elen & 1) && *edata == 0) {
366                                 edata++;
367                                 elen--;
368                         }
369                         ints [i] = edata;
370                         lengths [i] = elen;
371                         g_print ("len: %d\n", elen);
372                 }
373                 /* prime1 */
374                 g_print ("prime1 at %d (%d)\n", p-r, lengths [1]);
375                 memcpy (p, ints [1], lengths [1]);
376                 mem_reverse (p, lengths [1]);
377                 p += lengths [1];
378                 /* prime2 */
379                 g_print ("prime2 at %d (%d)\n", p-r, lengths [2]);
380                 memcpy (p, ints [2], lengths [2]);
381                 mem_reverse (p, lengths [2]);
382                 p += lengths [2];
383                 /* exponent1 */
384                 g_print ("exp1 at %d (%d)\n", p-r, lengths [3]);
385                 memcpy (p, ints [3], lengths [3]);
386                 mem_reverse (p, lengths [3]);
387                 p += lengths [3];
388                 /* exponent2 */
389                 g_print ("exp2 at %d (%d)\n", p-r, lengths [4]);
390                 memcpy (p, ints [4], lengths [4]);
391                 mem_reverse (p, lengths [4]);
392                 p += lengths [4];
393                 /* coeff */
394                 g_print ("coeff at %d (%d)\n", p-r, lengths [5]);
395                 memcpy (p, ints [5], lengths [5]);
396                 mem_reverse (p, lengths [5]);
397                 p += lengths [5];
398                 /* private exponent */
399                 g_print ("prive at %d (%d)\n", p-r, lengths [0]);
400                 memcpy (p, ints [0], lengths [0]);
401                 mem_reverse (p, lengths [0]);
402                 p += lengths [0];
403                 *ret = r;
404                 *retlen = p-r;
405                 return 0;
406         }
407         return 1;
408 }
409
410 static int
411 convert_format (const char *from, const char *outfile) {
412         guchar *key, *bindata, *keyout;
413         gsize len;
414         int binlen, ret, lenout;
415         FILE *file;
416         
417         if (!g_file_get_contents (from, (gchar**) &key, &len, NULL)) {
418                 printf ("Cannot load file: %s\n", from);
419                 return 2;
420         }
421
422         if (*key == 0 || *key == 0x24) {
423                 g_free (key);
424                 printf ("Cannot convert to pem format yet\n");
425                 return 2;
426         }
427         bindata = pem_decode (key, len, &binlen);
428         if (!(file = fopen (outfile, "wb"))) {
429                 g_free (key);
430                 printf ("Cannot open output file: %s\n", outfile);
431                 return 2;
432         }
433         dump_asn1 (bindata, binlen);
434         ret = convert_der_key (bindata, binlen, &keyout, &lenout);
435         if (!ret) {
436                 fwrite (keyout, lenout, 1, file);
437                 g_free (keyout);
438         } else {
439                 printf ("Cannot convert key\n");
440         }
441         fclose (file);
442         g_free (key);
443         return ret;
444 }
445
446 static int
447 get_digest (const char *from, const char *outfile)
448 {
449         guchar *ass;
450         guchar digest [20];
451         gsize len;
452         guint32 snpos, snsize;
453         FILE *file;
454         MonoImage *image;
455         MonoSHA1Context sha1;
456         
457         image = mono_image_open (from, NULL);
458         if (!image) {
459                 printf ("Cannot open image file: %s\n", from);
460                 return 2;
461         }
462         snpos = mono_image_strong_name_position (image, &snsize);
463         if (!snpos) {
464                 /*printf ("%s does not represent a strongly named assembly\n", from);
465                 mono_image_close (image);
466                 return 2;*/
467                 snsize = 0;
468         }
469         
470         if (!g_file_get_contents (from, (gchar**) &ass, &len, NULL)) {
471                 printf ("Cannot load file: %s\n", from);
472                 mono_image_close (image);
473                 return 2;
474         }
475         /* 
476          * FIXME: we may need to set the STRONGNAMESIGNED flag in the cli header 
477          * before taking the sha1 digest of the image.
478          */
479         mono_sha1_init (&sha1);
480         mono_sha1_update (&sha1, ass, snpos);
481         mono_sha1_update (&sha1, ass + snpos + snsize, len - snsize - snpos);
482         mono_sha1_final (&sha1, digest);
483
484         mono_image_close (image);
485         g_free (ass);
486         if (!(file = fopen (outfile, "wb"))) {
487                 printf ("Cannot open output file: %s\n", outfile);
488                 return 2;
489         }
490         fwrite (digest, 20, 1, file);
491         fclose (file);
492         return 0;
493 }
494
495 static void 
496 help (int err) {
497         printf ("monosn: Mono Strong Name Utility\nUsage: monosn option [arguments]\n");
498         printf ("Available options:\n");
499         printf ("\t-C keyin keyout   Convert key file format from PEM to cryptoAPI (or the reverse).\n");
500         printf ("\t-e assembly file  Extract the public key from assembly to file.\n");
501         printf ("\t-E assembly file  Extract the strong name from assembly to file.\n");
502         printf ("\t-r assembly file  Extract the sha1 digest from assembly to file.\n");
503         printf ("\t-t[p] file        Display the public key token from file.\n");
504         printf ("\t-T[p] assembly    Display the public key token from assembly.\n");
505         exit (err);
506 }
507
508 int 
509 main (int argc, char *argv[]) {
510         int opt;
511         
512         if (argc < 2 || argv [1] [0] != '-')
513                 help (1);
514
515         opt = argv [1] [1];
516         switch (opt) {
517         case 'C':
518                 if (argc != 4)
519                         help (1);
520                 return convert_format (argv [2], argv [3]);
521         case 'e':
522                 if (argc != 4)
523                         help (1);
524                 return extract_data_to_file (1, argv [2], argv [3]);
525         case 'E':
526                 if (argc != 4)
527                         help (1);
528                 return extract_data_to_file (0, argv [2], argv [3]);
529         case 'h':
530         case '?':
531                 help (0);
532                 return 0;
533         case 'r':
534                 if (argc != 4)
535                         help (1);
536                 return get_digest (argv [2], argv [3]);
537         case 't':
538                 if (argc != 3)
539                         help (1);
540                 return show_token (argv [2], 0, argv [1] [2] == 'p');
541         case 'T':
542                 if (argc != 3)
543                         help (1);
544                 return show_token (argv [2], 1, argv [1] [2] == 'p');
545         default:
546                 help (1);
547         }
548         return 0;
549 }
550