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