source: frontend/node_modules/node-forge/lib/asn1.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 44.3 KB
Line 
1/**
2 * Javascript implementation of Abstract Syntax Notation Number One.
3 *
4 * @author Dave Longley
5 *
6 * Copyright (c) 2010-2015 Digital Bazaar, Inc.
7 *
8 * An API for storing data using the Abstract Syntax Notation Number One
9 * format using DER (Distinguished Encoding Rules) encoding. This encoding is
10 * commonly used to store data for PKI, i.e. X.509 Certificates, and this
11 * implementation exists for that purpose.
12 *
13 * Abstract Syntax Notation Number One (ASN.1) is used to define the abstract
14 * syntax of information without restricting the way the information is encoded
15 * for transmission. It provides a standard that allows for open systems
16 * communication. ASN.1 defines the syntax of information data and a number of
17 * simple data types as well as a notation for describing them and specifying
18 * values for them.
19 *
20 * The RSA algorithm creates public and private keys that are often stored in
21 * X.509 or PKCS#X formats -- which use ASN.1 (encoded in DER format). This
22 * class provides the most basic functionality required to store and load DSA
23 * keys that are encoded according to ASN.1.
24 *
25 * The most common binary encodings for ASN.1 are BER (Basic Encoding Rules)
26 * and DER (Distinguished Encoding Rules). DER is just a subset of BER that
27 * has stricter requirements for how data must be encoded.
28 *
29 * Each ASN.1 structure has a tag (a byte identifying the ASN.1 structure type)
30 * and a byte array for the value of this ASN1 structure which may be data or a
31 * list of ASN.1 structures.
32 *
33 * Each ASN.1 structure using BER is (Tag-Length-Value):
34 *
35 * | byte 0 | bytes X | bytes Y |
36 * |--------|---------|----------
37 * | tag | length | value |
38 *
39 * ASN.1 allows for tags to be of "High-tag-number form" which allows a tag to
40 * be two or more octets, but that is not supported by this class. A tag is
41 * only 1 byte. Bits 1-5 give the tag number (ie the data type within a
42 * particular 'class'), 6 indicates whether or not the ASN.1 value is
43 * constructed from other ASN.1 values, and bits 7 and 8 give the 'class'. If
44 * bits 7 and 8 are both zero, the class is UNIVERSAL. If only bit 7 is set,
45 * then the class is APPLICATION. If only bit 8 is set, then the class is
46 * CONTEXT_SPECIFIC. If both bits 7 and 8 are set, then the class is PRIVATE.
47 * The tag numbers for the data types for the class UNIVERSAL are listed below:
48 *
49 * UNIVERSAL 0 Reserved for use by the encoding rules
50 * UNIVERSAL 1 Boolean type
51 * UNIVERSAL 2 Integer type
52 * UNIVERSAL 3 Bitstring type
53 * UNIVERSAL 4 Octetstring type
54 * UNIVERSAL 5 Null type
55 * UNIVERSAL 6 Object identifier type
56 * UNIVERSAL 7 Object descriptor type
57 * UNIVERSAL 8 External type and Instance-of type
58 * UNIVERSAL 9 Real type
59 * UNIVERSAL 10 Enumerated type
60 * UNIVERSAL 11 Embedded-pdv type
61 * UNIVERSAL 12 UTF8String type
62 * UNIVERSAL 13 Relative object identifier type
63 * UNIVERSAL 14-15 Reserved for future editions
64 * UNIVERSAL 16 Sequence and Sequence-of types
65 * UNIVERSAL 17 Set and Set-of types
66 * UNIVERSAL 18-22, 25-30 Character string types
67 * UNIVERSAL 23-24 Time types
68 *
69 * The length of an ASN.1 structure is specified after the tag identifier.
70 * There is a definite form and an indefinite form. The indefinite form may
71 * be used if the encoding is constructed and not all immediately available.
72 * The indefinite form is encoded using a length byte with only the 8th bit
73 * set. The end of the constructed object is marked using end-of-contents
74 * octets (two zero bytes).
75 *
76 * The definite form looks like this:
77 *
78 * The length may take up 1 or more bytes, it depends on the length of the
79 * value of the ASN.1 structure. DER encoding requires that if the ASN.1
80 * structure has a value that has a length greater than 127, more than 1 byte
81 * will be used to store its length, otherwise just one byte will be used.
82 * This is strict.
83 *
84 * In the case that the length of the ASN.1 value is less than 127, 1 octet
85 * (byte) is used to store the "short form" length. The 8th bit has a value of
86 * 0 indicating the length is "short form" and not "long form" and bits 7-1
87 * give the length of the data. (The 8th bit is the left-most, most significant
88 * bit: also known as big endian or network format).
89 *
90 * In the case that the length of the ASN.1 value is greater than 127, 2 to
91 * 127 octets (bytes) are used to store the "long form" length. The first
92 * byte's 8th bit is set to 1 to indicate the length is "long form." Bits 7-1
93 * give the number of additional octets. All following octets are in base 256
94 * with the most significant digit first (typical big-endian binary unsigned
95 * integer storage). So, for instance, if the length of a value was 257, the
96 * first byte would be set to:
97 *
98 * 10000010 = 130 = 0x82.
99 *
100 * This indicates there are 2 octets (base 256) for the length. The second and
101 * third bytes (the octets just mentioned) would store the length in base 256:
102 *
103 * octet 2: 00000001 = 1 * 256^1 = 256
104 * octet 3: 00000001 = 1 * 256^0 = 1
105 * total = 257
106 *
107 * The algorithm for converting a js integer value of 257 to base-256 is:
108 *
109 * var value = 257;
110 * var bytes = [];
111 * bytes[0] = (value >>> 8) & 0xFF; // most significant byte first
112 * bytes[1] = value & 0xFF; // least significant byte last
113 *
114 * On the ASN.1 UNIVERSAL Object Identifier (OID) type:
115 *
116 * An OID can be written like: "value1.value2.value3...valueN"
117 *
118 * The DER encoding rules:
119 *
120 * The first byte has the value 40 * value1 + value2.
121 * The following bytes, if any, encode the remaining values. Each value is
122 * encoded in base 128, most significant digit first (big endian), with as
123 * few digits as possible, and the most significant bit of each byte set
124 * to 1 except the last in each value's encoding. For example: Given the
125 * OID "1.2.840.113549", its DER encoding is (remember each byte except the
126 * last one in each encoding is OR'd with 0x80):
127 *
128 * byte 1: 40 * 1 + 2 = 42 = 0x2A.
129 * bytes 2-3: 128 * 6 + 72 = 840 = 6 72 = 6 72 = 0x0648 = 0x8648
130 * bytes 4-6: 16384 * 6 + 128 * 119 + 13 = 6 119 13 = 0x06770D = 0x86F70D
131 *
132 * The final value is: 0x2A864886F70D.
133 * The full OID (including ASN.1 tag and length of 6 bytes) is:
134 * 0x06062A864886F70D
135 */
136var forge = require('./forge');
137require('./util');
138require('./oids');
139
140/* ASN.1 API */
141var asn1 = module.exports = forge.asn1 = forge.asn1 || {};
142
143/**
144 * ASN.1 classes.
145 */
146asn1.Class = {
147 UNIVERSAL: 0x00,
148 APPLICATION: 0x40,
149 CONTEXT_SPECIFIC: 0x80,
150 PRIVATE: 0xC0
151};
152
153/**
154 * ASN.1 types. Not all types are supported by this implementation, only
155 * those necessary to implement a simple PKI are implemented.
156 */
157asn1.Type = {
158 NONE: 0,
159 BOOLEAN: 1,
160 INTEGER: 2,
161 BITSTRING: 3,
162 OCTETSTRING: 4,
163 NULL: 5,
164 OID: 6,
165 ODESC: 7,
166 EXTERNAL: 8,
167 REAL: 9,
168 ENUMERATED: 10,
169 EMBEDDED: 11,
170 UTF8: 12,
171 ROID: 13,
172 SEQUENCE: 16,
173 SET: 17,
174 PRINTABLESTRING: 19,
175 IA5STRING: 22,
176 UTCTIME: 23,
177 GENERALIZEDTIME: 24,
178 BMPSTRING: 30
179};
180
181/**
182 * Sets the default maximum recursion depth when parsing ASN.1 structures.
183 */
184asn1.maxDepth = 256;
185
186/**
187 * Creates a new asn1 object.
188 *
189 * @param tagClass the tag class for the object.
190 * @param type the data type (tag number) for the object.
191 * @param constructed true if the asn1 object is in constructed form.
192 * @param value the value for the object, if it is not constructed.
193 * @param [options] the options to use:
194 * [bitStringContents] the plain BIT STRING content including padding
195 * byte.
196 *
197 * @return the asn1 object.
198 */
199asn1.create = function(tagClass, type, constructed, value, options) {
200 /* An asn1 object has a tagClass, a type, a constructed flag, and a
201 value. The value's type depends on the constructed flag. If
202 constructed, it will contain a list of other asn1 objects. If not,
203 it will contain the ASN.1 value as an array of bytes formatted
204 according to the ASN.1 data type. */
205
206 // remove undefined values
207 if(forge.util.isArray(value)) {
208 var tmp = [];
209 for(var i = 0; i < value.length; ++i) {
210 if(value[i] !== undefined) {
211 tmp.push(value[i]);
212 }
213 }
214 value = tmp;
215 }
216
217 var obj = {
218 tagClass: tagClass,
219 type: type,
220 constructed: constructed,
221 composed: constructed || forge.util.isArray(value),
222 value: value
223 };
224 if(options && 'bitStringContents' in options) {
225 // TODO: copy byte buffer if it's a buffer not a string
226 obj.bitStringContents = options.bitStringContents;
227 // TODO: add readonly flag to avoid this overhead
228 // save copy to detect changes
229 obj.original = asn1.copy(obj);
230 }
231 return obj;
232};
233
234/**
235 * Copies an asn1 object.
236 *
237 * @param obj the asn1 object.
238 * @param [options] copy options:
239 * [excludeBitStringContents] true to not copy bitStringContents
240 *
241 * @return the a copy of the asn1 object.
242 */
243asn1.copy = function(obj, options) {
244 var copy;
245
246 if(forge.util.isArray(obj)) {
247 copy = [];
248 for(var i = 0; i < obj.length; ++i) {
249 copy.push(asn1.copy(obj[i], options));
250 }
251 return copy;
252 }
253
254 if(typeof obj === 'string') {
255 // TODO: copy byte buffer if it's a buffer not a string
256 return obj;
257 }
258
259 copy = {
260 tagClass: obj.tagClass,
261 type: obj.type,
262 constructed: obj.constructed,
263 composed: obj.composed,
264 value: asn1.copy(obj.value, options)
265 };
266 if(options && !options.excludeBitStringContents) {
267 // TODO: copy byte buffer if it's a buffer not a string
268 copy.bitStringContents = obj.bitStringContents;
269 }
270 return copy;
271};
272
273/**
274 * Compares asn1 objects for equality.
275 *
276 * Note this function does not run in constant time.
277 *
278 * @param obj1 the first asn1 object.
279 * @param obj2 the second asn1 object.
280 * @param [options] compare options:
281 * [includeBitStringContents] true to compare bitStringContents
282 *
283 * @return true if the asn1 objects are equal.
284 */
285asn1.equals = function(obj1, obj2, options) {
286 if(forge.util.isArray(obj1)) {
287 if(!forge.util.isArray(obj2)) {
288 return false;
289 }
290 if(obj1.length !== obj2.length) {
291 return false;
292 }
293 for(var i = 0; i < obj1.length; ++i) {
294 if(!asn1.equals(obj1[i], obj2[i])) {
295 return false;
296 }
297 }
298 return true;
299 }
300
301 if(typeof obj1 !== typeof obj2) {
302 return false;
303 }
304
305 if(typeof obj1 === 'string') {
306 return obj1 === obj2;
307 }
308
309 var equal = obj1.tagClass === obj2.tagClass &&
310 obj1.type === obj2.type &&
311 obj1.constructed === obj2.constructed &&
312 obj1.composed === obj2.composed &&
313 asn1.equals(obj1.value, obj2.value);
314 if(options && options.includeBitStringContents) {
315 equal = equal && (obj1.bitStringContents === obj2.bitStringContents);
316 }
317
318 return equal;
319};
320
321/**
322 * Gets the length of a BER-encoded ASN.1 value.
323 *
324 * In case the length is not specified, undefined is returned.
325 *
326 * @param b the BER-encoded ASN.1 byte buffer, starting with the first
327 * length byte.
328 *
329 * @return the length of the BER-encoded ASN.1 value or undefined.
330 */
331asn1.getBerValueLength = function(b) {
332 // TODO: move this function and related DER/BER functions to a der.js
333 // file; better abstract ASN.1 away from der/ber.
334 var b2 = b.getByte();
335 if(b2 === 0x80) {
336 return undefined;
337 }
338
339 // see if the length is "short form" or "long form" (bit 8 set)
340 var length;
341 var longForm = b2 & 0x80;
342 if(!longForm) {
343 // length is just the first byte
344 length = b2;
345 } else {
346 // the number of bytes the length is specified in bits 7 through 1
347 // and each length byte is in big-endian base-256
348 length = b.getInt((b2 & 0x7F) << 3);
349 }
350 return length;
351};
352
353/**
354 * Check if the byte buffer has enough bytes. Throws an Error if not.
355 *
356 * @param bytes the byte buffer to parse from.
357 * @param remaining the bytes remaining in the current parsing state.
358 * @param n the number of bytes the buffer must have.
359 */
360function _checkBufferLength(bytes, remaining, n) {
361 if(n > remaining) {
362 var error = new Error('Too few bytes to parse DER.');
363 error.available = bytes.length();
364 error.remaining = remaining;
365 error.requested = n;
366 throw error;
367 }
368}
369
370/**
371 * Gets the length of a BER-encoded ASN.1 value.
372 *
373 * In case the length is not specified, undefined is returned.
374 *
375 * @param bytes the byte buffer to parse from.
376 * @param remaining the bytes remaining in the current parsing state.
377 *
378 * @return the length of the BER-encoded ASN.1 value or undefined.
379 */
380var _getValueLength = function(bytes, remaining) {
381 // TODO: move this function and related DER/BER functions to a der.js
382 // file; better abstract ASN.1 away from der/ber.
383 // fromDer already checked that this byte exists
384 var b2 = bytes.getByte();
385 remaining--;
386 if(b2 === 0x80) {
387 return undefined;
388 }
389
390 // see if the length is "short form" or "long form" (bit 8 set)
391 var length;
392 var longForm = b2 & 0x80;
393 if(!longForm) {
394 // length is just the first byte
395 length = b2;
396 } else {
397 // the number of bytes the length is specified in bits 7 through 1
398 // and each length byte is in big-endian base-256
399 var longFormBytes = b2 & 0x7F;
400 _checkBufferLength(bytes, remaining, longFormBytes);
401 length = bytes.getInt(longFormBytes << 3);
402 }
403 // FIXME: this will only happen for 32 bit getInt with high bit set
404 if(length < 0) {
405 throw new Error('Negative length: ' + length);
406 }
407 return length;
408};
409
410/**
411 * Parses an asn1 object from a byte buffer in DER format.
412 *
413 * @param bytes the byte buffer to parse from.
414 * @param [strict] true to be strict when checking value lengths, false to
415 * allow truncated values (default: true).
416 * @param [options] object with options or boolean strict flag
417 * [strict] true to be strict when checking value lengths, false to
418 * allow truncated values (default: true).
419 * [parseAllBytes] true to ensure all bytes are parsed
420 * (default: true)
421 * [decodeBitStrings] true to attempt to decode the content of
422 * BIT STRINGs (not OCTET STRINGs) using strict mode. Note that
423 * without schema support to understand the data context this can
424 * erroneously decode values that happen to be valid ASN.1. This
425 * flag will be deprecated or removed as soon as schema support is
426 * available. (default: true)
427 * [maxDepth] override asn1.maxDepth recursion limit
428 * (default: asn1.maxDepth)
429 *
430 * @throws Will throw an error for various malformed input conditions.
431 *
432 * @return the parsed asn1 object.
433 */
434asn1.fromDer = function(bytes, options) {
435 if(options === undefined) {
436 options = {
437 strict: true,
438 parseAllBytes: true,
439 decodeBitStrings: true
440 };
441 }
442 if(typeof options === 'boolean') {
443 options = {
444 strict: options,
445 parseAllBytes: true,
446 decodeBitStrings: true
447 };
448 }
449 if(!('strict' in options)) {
450 options.strict = true;
451 }
452 if(!('parseAllBytes' in options)) {
453 options.parseAllBytes = true;
454 }
455 if(!('decodeBitStrings' in options)) {
456 options.decodeBitStrings = true;
457 }
458 if(!('maxDepth' in options)) {
459 options.maxDepth = asn1.maxDepth;
460 }
461
462 // wrap in buffer if needed
463 if(typeof bytes === 'string') {
464 bytes = forge.util.createBuffer(bytes);
465 }
466
467 var byteCount = bytes.length();
468 var value = _fromDer(bytes, bytes.length(), 0, options);
469 if(options.parseAllBytes && bytes.length() !== 0) {
470 var error = new Error('Unparsed DER bytes remain after ASN.1 parsing.');
471 error.byteCount = byteCount;
472 error.remaining = bytes.length();
473 throw error;
474 }
475 return value;
476};
477
478/**
479 * Internal function to parse an asn1 object from a byte buffer in DER format.
480 *
481 * @param bytes the byte buffer to parse from.
482 * @param remaining the number of bytes remaining for this chunk.
483 * @param depth the current parsing depth.
484 * @param options object with same options as fromDer().
485 *
486 * @return the parsed asn1 object.
487 */
488function _fromDer(bytes, remaining, depth, options) {
489
490 // check depth limit
491 if(depth >= options.maxDepth) {
492 throw new Error('ASN.1 parsing error: Max depth exceeded.');
493 }
494
495 // temporary storage for consumption calculations
496 var start;
497
498 // minimum length for ASN.1 DER structure is 2
499 _checkBufferLength(bytes, remaining, 2);
500
501 // get the first byte
502 var b1 = bytes.getByte();
503 // consumed one byte
504 remaining--;
505
506 // get the tag class
507 var tagClass = (b1 & 0xC0);
508
509 // get the type (bits 1-5)
510 var type = b1 & 0x1F;
511
512 // get the variable value length and adjust remaining bytes
513 start = bytes.length();
514 var length = _getValueLength(bytes, remaining);
515 remaining -= start - bytes.length();
516
517 // ensure there are enough bytes to get the value
518 if(length !== undefined && length > remaining) {
519 if(options.strict) {
520 var error = new Error('Too few bytes to read ASN.1 value.');
521 error.available = bytes.length();
522 error.remaining = remaining;
523 error.requested = length;
524 throw error;
525 }
526 // Note: be lenient with truncated values and use remaining state bytes
527 length = remaining;
528 }
529
530 // value storage
531 var value;
532 // possible BIT STRING contents storage
533 var bitStringContents;
534
535 // constructed flag is bit 6 (32 = 0x20) of the first byte
536 var constructed = ((b1 & 0x20) === 0x20);
537 if(constructed) {
538 // parse child asn1 objects from the value
539 value = [];
540 if(length === undefined) {
541 // asn1 object of indefinite length, read until end tag
542 for(;;) {
543 _checkBufferLength(bytes, remaining, 2);
544 if(bytes.bytes(2) === String.fromCharCode(0, 0)) {
545 bytes.getBytes(2);
546 remaining -= 2;
547 break;
548 }
549 start = bytes.length();
550 value.push(_fromDer(bytes, remaining, depth + 1, options));
551 remaining -= start - bytes.length();
552 }
553 } else {
554 // parsing asn1 object of definite length
555 while(length > 0) {
556 start = bytes.length();
557 value.push(_fromDer(bytes, length, depth + 1, options));
558 remaining -= start - bytes.length();
559 length -= start - bytes.length();
560 }
561 }
562 }
563
564 // if a BIT STRING, save the contents including padding
565 if(value === undefined && tagClass === asn1.Class.UNIVERSAL &&
566 type === asn1.Type.BITSTRING) {
567 bitStringContents = bytes.bytes(length);
568 }
569
570 // determine if a non-constructed value should be decoded as a composed
571 // value that contains other ASN.1 objects. BIT STRINGs (and OCTET STRINGs)
572 // can be used this way.
573 if(value === undefined && options.decodeBitStrings &&
574 tagClass === asn1.Class.UNIVERSAL &&
575 // FIXME: OCTET STRINGs not yet supported here
576 // .. other parts of forge expect to decode OCTET STRINGs manually
577 (type === asn1.Type.BITSTRING /*|| type === asn1.Type.OCTETSTRING*/) &&
578 length > 1) {
579 // save read position
580 var savedRead = bytes.read;
581 var savedRemaining = remaining;
582 var unused = 0;
583 if(type === asn1.Type.BITSTRING) {
584 /* The first octet gives the number of bits by which the length of the
585 bit string is less than the next multiple of eight (this is called
586 the "number of unused bits").
587
588 The second and following octets give the value of the bit string
589 converted to an octet string. */
590 _checkBufferLength(bytes, remaining, 1);
591 unused = bytes.getByte();
592 remaining--;
593 }
594 // if all bits are used, maybe the BIT/OCTET STRING holds ASN.1 objs
595 if(unused === 0) {
596 try {
597 // attempt to parse child asn1 object from the value
598 // (stored in array to signal composed value)
599 start = bytes.length();
600 var subOptions = {
601 // enforce strict mode to avoid parsing ASN.1 from plain data
602 strict: true,
603 decodeBitStrings: true
604 };
605 var composed = _fromDer(bytes, remaining, depth + 1, subOptions);
606 var used = start - bytes.length();
607 remaining -= used;
608 if(type == asn1.Type.BITSTRING) {
609 used++;
610 }
611
612 // if the data all decoded and the class indicates UNIVERSAL or
613 // CONTEXT_SPECIFIC then assume we've got an encapsulated ASN.1 object
614 var tc = composed.tagClass;
615 if(used === length &&
616 (tc === asn1.Class.UNIVERSAL || tc === asn1.Class.CONTEXT_SPECIFIC)) {
617 value = [composed];
618 }
619 } catch(ex) {
620 }
621 }
622 if(value === undefined) {
623 // restore read position
624 bytes.read = savedRead;
625 remaining = savedRemaining;
626 }
627 }
628
629 if(value === undefined) {
630 // asn1 not constructed or composed, get raw value
631 // TODO: do DER to OID conversion and vice-versa in .toDer?
632
633 if(length === undefined) {
634 if(options.strict) {
635 throw new Error('Non-constructed ASN.1 object of indefinite length.');
636 }
637 // be lenient and use remaining state bytes
638 length = remaining;
639 }
640
641 if(type === asn1.Type.BMPSTRING) {
642 value = '';
643 for(; length > 0; length -= 2) {
644 _checkBufferLength(bytes, remaining, 2);
645 value += String.fromCharCode(bytes.getInt16());
646 remaining -= 2;
647 }
648 } else {
649 value = bytes.getBytes(length);
650 remaining -= length;
651 }
652 }
653
654 // add BIT STRING contents if available
655 var asn1Options = bitStringContents === undefined ? null : {
656 bitStringContents: bitStringContents
657 };
658
659 // create and return asn1 object
660 return asn1.create(tagClass, type, constructed, value, asn1Options);
661}
662
663/**
664 * Converts the given asn1 object to a buffer of bytes in DER format.
665 *
666 * @param asn1 the asn1 object to convert to bytes.
667 *
668 * @return the buffer of bytes.
669 */
670asn1.toDer = function(obj) {
671 var bytes = forge.util.createBuffer();
672
673 // build the first byte
674 var b1 = obj.tagClass | obj.type;
675
676 // for storing the ASN.1 value
677 var value = forge.util.createBuffer();
678
679 // use BIT STRING contents if available and data not changed
680 var useBitStringContents = false;
681 if('bitStringContents' in obj) {
682 useBitStringContents = true;
683 if(obj.original) {
684 useBitStringContents = asn1.equals(obj, obj.original);
685 }
686 }
687
688 if(useBitStringContents) {
689 value.putBytes(obj.bitStringContents);
690 } else if(obj.composed) {
691 // if composed, use each child asn1 object's DER bytes as value
692 // turn on 6th bit (0x20 = 32) to indicate asn1 is constructed
693 // from other asn1 objects
694 if(obj.constructed) {
695 b1 |= 0x20;
696 } else {
697 // type is a bit string, add unused bits of 0x00
698 value.putByte(0x00);
699 }
700
701 // add all of the child DER bytes together
702 for(var i = 0; i < obj.value.length; ++i) {
703 if(obj.value[i] !== undefined) {
704 value.putBuffer(asn1.toDer(obj.value[i]));
705 }
706 }
707 } else {
708 // use asn1.value directly
709 if(obj.type === asn1.Type.BMPSTRING) {
710 for(var i = 0; i < obj.value.length; ++i) {
711 value.putInt16(obj.value.charCodeAt(i));
712 }
713 } else {
714 // ensure integer is minimally-encoded
715 // TODO: should all leading bytes be stripped vs just one?
716 // .. ex '00 00 01' => '01'?
717 if(obj.type === asn1.Type.INTEGER &&
718 obj.value.length > 1 &&
719 // leading 0x00 for positive integer
720 ((obj.value.charCodeAt(0) === 0 &&
721 (obj.value.charCodeAt(1) & 0x80) === 0) ||
722 // leading 0xFF for negative integer
723 (obj.value.charCodeAt(0) === 0xFF &&
724 (obj.value.charCodeAt(1) & 0x80) === 0x80))) {
725 value.putBytes(obj.value.substr(1));
726 } else {
727 value.putBytes(obj.value);
728 }
729 }
730 }
731
732 // add tag byte
733 bytes.putByte(b1);
734
735 // use "short form" encoding
736 if(value.length() <= 127) {
737 // one byte describes the length
738 // bit 8 = 0 and bits 7-1 = length
739 bytes.putByte(value.length() & 0x7F);
740 } else {
741 // use "long form" encoding
742 // 2 to 127 bytes describe the length
743 // first byte: bit 8 = 1 and bits 7-1 = # of additional bytes
744 // other bytes: length in base 256, big-endian
745 var len = value.length();
746 var lenBytes = '';
747 do {
748 lenBytes += String.fromCharCode(len & 0xFF);
749 len = len >>> 8;
750 } while(len > 0);
751
752 // set first byte to # bytes used to store the length and turn on
753 // bit 8 to indicate long-form length is used
754 bytes.putByte(lenBytes.length | 0x80);
755
756 // concatenate length bytes in reverse since they were generated
757 // little endian and we need big endian
758 for(var i = lenBytes.length - 1; i >= 0; --i) {
759 bytes.putByte(lenBytes.charCodeAt(i));
760 }
761 }
762
763 // concatenate value bytes
764 bytes.putBuffer(value);
765 return bytes;
766};
767
768/**
769 * Converts an OID dot-separated string to a byte buffer. The byte buffer
770 * contains only the DER-encoded value, not any tag or length bytes.
771 *
772 * @param oid the OID dot-separated string.
773 *
774 * @return the byte buffer.
775 */
776asn1.oidToDer = function(oid) {
777 // split OID into individual values
778 var values = oid.split('.');
779 var bytes = forge.util.createBuffer();
780
781 // first byte is 40 * value1 + value2
782 bytes.putByte(40 * parseInt(values[0], 10) + parseInt(values[1], 10));
783 // other bytes are each value in base 128 with 8th bit set except for
784 // the last byte for each value
785 var last, valueBytes, value, b;
786 for(var i = 2; i < values.length; ++i) {
787 // produce value bytes in reverse because we don't know how many
788 // bytes it will take to store the value
789 last = true;
790 valueBytes = [];
791 value = parseInt(values[i], 10);
792 // TODO: Change bitwise logic to allow larger values.
793 if(value > 0xffffffff) {
794 throw new Error('OID value too large; max is 32-bits.');
795 }
796 do {
797 b = value & 0x7F;
798 value = value >>> 7;
799 // if value is not last, then turn on 8th bit
800 if(!last) {
801 b |= 0x80;
802 }
803 valueBytes.push(b);
804 last = false;
805 } while(value > 0);
806
807 // add value bytes in reverse (needs to be in big endian)
808 for(var n = valueBytes.length - 1; n >= 0; --n) {
809 bytes.putByte(valueBytes[n]);
810 }
811 }
812
813 return bytes;
814};
815
816/**
817 * Converts a DER-encoded byte buffer to an OID dot-separated string. The
818 * byte buffer should contain only the DER-encoded value, not any tag or
819 * length bytes.
820 *
821 * @param bytes the byte buffer.
822 *
823 * @return the OID dot-separated string.
824 */
825asn1.derToOid = function(bytes) {
826 var oid;
827
828 // wrap in buffer if needed
829 if(typeof bytes === 'string') {
830 bytes = forge.util.createBuffer(bytes);
831 }
832
833 // first byte is 40 * value1 + value2
834 var b = bytes.getByte();
835 oid = Math.floor(b / 40) + '.' + (b % 40);
836
837 // other bytes are each value in base 128 with 8th bit set except for
838 // the last byte for each value
839 var value = 0;
840 while(bytes.length() > 0) {
841 // error if 7b shift would exceed Number.MAX_SAFE_INTEGER
842 // (Number.MAX_SAFE_INTEGER / 128)
843 if(value > 0x3fffffffffff) {
844 throw new Error('OID value too large; max is 53-bits.');
845 }
846 b = bytes.getByte();
847 value = value * 128;
848 // not the last byte for the value
849 if(b & 0x80) {
850 value += b & 0x7F;
851 } else {
852 // last byte
853 oid += '.' + (value + b);
854 value = 0;
855 }
856 }
857
858 return oid;
859};
860
861/**
862 * Converts a UTCTime value to a date.
863 *
864 * Note: GeneralizedTime has 4 digits for the year and is used for X.509
865 * dates past 2049. Parsing that structure hasn't been implemented yet.
866 *
867 * @param utc the UTCTime value to convert.
868 *
869 * @return the date.
870 */
871asn1.utcTimeToDate = function(utc) {
872 /* The following formats can be used:
873
874 YYMMDDhhmmZ
875 YYMMDDhhmm+hh'mm'
876 YYMMDDhhmm-hh'mm'
877 YYMMDDhhmmssZ
878 YYMMDDhhmmss+hh'mm'
879 YYMMDDhhmmss-hh'mm'
880
881 Where:
882
883 YY is the least significant two digits of the year
884 MM is the month (01 to 12)
885 DD is the day (01 to 31)
886 hh is the hour (00 to 23)
887 mm are the minutes (00 to 59)
888 ss are the seconds (00 to 59)
889 Z indicates that local time is GMT, + indicates that local time is
890 later than GMT, and - indicates that local time is earlier than GMT
891 hh' is the absolute value of the offset from GMT in hours
892 mm' is the absolute value of the offset from GMT in minutes */
893 var date = new Date();
894
895 // if YY >= 50 use 19xx, if YY < 50 use 20xx
896 var year = parseInt(utc.substr(0, 2), 10);
897 year = (year >= 50) ? 1900 + year : 2000 + year;
898 var MM = parseInt(utc.substr(2, 2), 10) - 1; // use 0-11 for month
899 var DD = parseInt(utc.substr(4, 2), 10);
900 var hh = parseInt(utc.substr(6, 2), 10);
901 var mm = parseInt(utc.substr(8, 2), 10);
902 var ss = 0;
903
904 // not just YYMMDDhhmmZ
905 if(utc.length > 11) {
906 // get character after minutes
907 var c = utc.charAt(10);
908 var end = 10;
909
910 // see if seconds are present
911 if(c !== '+' && c !== '-') {
912 // get seconds
913 ss = parseInt(utc.substr(10, 2), 10);
914 end += 2;
915 }
916 }
917
918 // update date
919 date.setUTCFullYear(year, MM, DD);
920 date.setUTCHours(hh, mm, ss, 0);
921
922 if(end) {
923 // get +/- after end of time
924 c = utc.charAt(end);
925 if(c === '+' || c === '-') {
926 // get hours+minutes offset
927 var hhoffset = parseInt(utc.substr(end + 1, 2), 10);
928 var mmoffset = parseInt(utc.substr(end + 4, 2), 10);
929
930 // calculate offset in milliseconds
931 var offset = hhoffset * 60 + mmoffset;
932 offset *= 60000;
933
934 // apply offset
935 if(c === '+') {
936 date.setTime(+date - offset);
937 } else {
938 date.setTime(+date + offset);
939 }
940 }
941 }
942
943 return date;
944};
945
946/**
947 * Converts a GeneralizedTime value to a date.
948 *
949 * @param gentime the GeneralizedTime value to convert.
950 *
951 * @return the date.
952 */
953asn1.generalizedTimeToDate = function(gentime) {
954 /* The following formats can be used:
955
956 YYYYMMDDHHMMSS
957 YYYYMMDDHHMMSS.fff
958 YYYYMMDDHHMMSSZ
959 YYYYMMDDHHMMSS.fffZ
960 YYYYMMDDHHMMSS+hh'mm'
961 YYYYMMDDHHMMSS.fff+hh'mm'
962 YYYYMMDDHHMMSS-hh'mm'
963 YYYYMMDDHHMMSS.fff-hh'mm'
964
965 Where:
966
967 YYYY is the year
968 MM is the month (01 to 12)
969 DD is the day (01 to 31)
970 hh is the hour (00 to 23)
971 mm are the minutes (00 to 59)
972 ss are the seconds (00 to 59)
973 .fff is the second fraction, accurate to three decimal places
974 Z indicates that local time is GMT, + indicates that local time is
975 later than GMT, and - indicates that local time is earlier than GMT
976 hh' is the absolute value of the offset from GMT in hours
977 mm' is the absolute value of the offset from GMT in minutes */
978 var date = new Date();
979
980 var YYYY = parseInt(gentime.substr(0, 4), 10);
981 var MM = parseInt(gentime.substr(4, 2), 10) - 1; // use 0-11 for month
982 var DD = parseInt(gentime.substr(6, 2), 10);
983 var hh = parseInt(gentime.substr(8, 2), 10);
984 var mm = parseInt(gentime.substr(10, 2), 10);
985 var ss = parseInt(gentime.substr(12, 2), 10);
986 var fff = 0;
987 var offset = 0;
988 var isUTC = false;
989
990 if(gentime.charAt(gentime.length - 1) === 'Z') {
991 isUTC = true;
992 }
993
994 var end = gentime.length - 5, c = gentime.charAt(end);
995 if(c === '+' || c === '-') {
996 // get hours+minutes offset
997 var hhoffset = parseInt(gentime.substr(end + 1, 2), 10);
998 var mmoffset = parseInt(gentime.substr(end + 4, 2), 10);
999
1000 // calculate offset in milliseconds
1001 offset = hhoffset * 60 + mmoffset;
1002 offset *= 60000;
1003
1004 // apply offset
1005 if(c === '+') {
1006 offset *= -1;
1007 }
1008
1009 isUTC = true;
1010 }
1011
1012 // check for second fraction
1013 if(gentime.charAt(14) === '.') {
1014 fff = parseFloat(gentime.substr(14), 10) * 1000;
1015 }
1016
1017 if(isUTC) {
1018 date.setUTCFullYear(YYYY, MM, DD);
1019 date.setUTCHours(hh, mm, ss, fff);
1020
1021 // apply offset
1022 date.setTime(+date + offset);
1023 } else {
1024 date.setFullYear(YYYY, MM, DD);
1025 date.setHours(hh, mm, ss, fff);
1026 }
1027
1028 return date;
1029};
1030
1031/**
1032 * Converts a date to a UTCTime value.
1033 *
1034 * Note: GeneralizedTime has 4 digits for the year and is used for X.509
1035 * dates past 2049. Converting to a GeneralizedTime hasn't been
1036 * implemented yet.
1037 *
1038 * @param date the date to convert.
1039 *
1040 * @return the UTCTime value.
1041 */
1042asn1.dateToUtcTime = function(date) {
1043 // TODO: validate; currently assumes proper format
1044 if(typeof date === 'string') {
1045 return date;
1046 }
1047
1048 var rval = '';
1049
1050 // create format YYMMDDhhmmssZ
1051 var format = [];
1052 format.push(('' + date.getUTCFullYear()).substr(2));
1053 format.push('' + (date.getUTCMonth() + 1));
1054 format.push('' + date.getUTCDate());
1055 format.push('' + date.getUTCHours());
1056 format.push('' + date.getUTCMinutes());
1057 format.push('' + date.getUTCSeconds());
1058
1059 // ensure 2 digits are used for each format entry
1060 for(var i = 0; i < format.length; ++i) {
1061 if(format[i].length < 2) {
1062 rval += '0';
1063 }
1064 rval += format[i];
1065 }
1066 rval += 'Z';
1067
1068 return rval;
1069};
1070
1071/**
1072 * Converts a date to a GeneralizedTime value.
1073 *
1074 * @param date the date to convert.
1075 *
1076 * @return the GeneralizedTime value as a string.
1077 */
1078asn1.dateToGeneralizedTime = function(date) {
1079 // TODO: validate; currently assumes proper format
1080 if(typeof date === 'string') {
1081 return date;
1082 }
1083
1084 var rval = '';
1085
1086 // create format YYYYMMDDHHMMSSZ
1087 var format = [];
1088 format.push('' + date.getUTCFullYear());
1089 format.push('' + (date.getUTCMonth() + 1));
1090 format.push('' + date.getUTCDate());
1091 format.push('' + date.getUTCHours());
1092 format.push('' + date.getUTCMinutes());
1093 format.push('' + date.getUTCSeconds());
1094
1095 // ensure 2 digits are used for each format entry
1096 for(var i = 0; i < format.length; ++i) {
1097 if(format[i].length < 2) {
1098 rval += '0';
1099 }
1100 rval += format[i];
1101 }
1102 rval += 'Z';
1103
1104 return rval;
1105};
1106
1107/**
1108 * Converts a javascript integer to a DER-encoded byte buffer to be used
1109 * as the value for an INTEGER type.
1110 *
1111 * @param x the integer.
1112 *
1113 * @return the byte buffer.
1114 */
1115asn1.integerToDer = function(x) {
1116 var rval = forge.util.createBuffer();
1117 if(x >= -0x80 && x < 0x80) {
1118 return rval.putSignedInt(x, 8);
1119 }
1120 if(x >= -0x8000 && x < 0x8000) {
1121 return rval.putSignedInt(x, 16);
1122 }
1123 if(x >= -0x800000 && x < 0x800000) {
1124 return rval.putSignedInt(x, 24);
1125 }
1126 if(x >= -0x80000000 && x < 0x80000000) {
1127 return rval.putSignedInt(x, 32);
1128 }
1129 var error = new Error('Integer too large; max is 32-bits.');
1130 error.integer = x;
1131 throw error;
1132};
1133
1134/**
1135 * Converts a DER-encoded byte buffer to a javascript integer. This is
1136 * typically used to decode the value of an INTEGER type.
1137 *
1138 * @param bytes the byte buffer.
1139 *
1140 * @return the integer.
1141 */
1142asn1.derToInteger = function(bytes) {
1143 // wrap in buffer if needed
1144 if(typeof bytes === 'string') {
1145 bytes = forge.util.createBuffer(bytes);
1146 }
1147
1148 var n = bytes.length() * 8;
1149 if(n > 32) {
1150 throw new Error('Integer too large; max is 32-bits.');
1151 }
1152 return bytes.getSignedInt(n);
1153};
1154
1155/**
1156 * Validates that the given ASN.1 object is at least a super set of the
1157 * given ASN.1 structure. Only tag classes and types are checked. An
1158 * optional map may also be provided to capture ASN.1 values while the
1159 * structure is checked.
1160 *
1161 * To capture an ASN.1 value, set an object in the validator's 'capture'
1162 * parameter to the key to use in the capture map. To capture the full
1163 * ASN.1 object, specify 'captureAsn1'. To capture BIT STRING bytes, including
1164 * the leading unused bits counter byte, specify 'captureBitStringContents'.
1165 * To capture BIT STRING bytes, without the leading unused bits counter byte,
1166 * specify 'captureBitStringValue'.
1167 *
1168 * Objects in the validator may set a field 'optional' to true to indicate
1169 * that it isn't necessary to pass validation.
1170 *
1171 * @param obj the ASN.1 object to validate.
1172 * @param v the ASN.1 structure validator.
1173 * @param capture an optional map to capture values in.
1174 * @param errors an optional array for storing validation errors.
1175 *
1176 * @return true on success, false on failure.
1177 */
1178asn1.validate = function(obj, v, capture, errors) {
1179 var rval = false;
1180
1181 // ensure tag class and type are the same if specified
1182 if((obj.tagClass === v.tagClass || typeof(v.tagClass) === 'undefined') &&
1183 (obj.type === v.type || typeof(v.type) === 'undefined')) {
1184 // ensure constructed flag is the same if specified
1185 if(obj.constructed === v.constructed ||
1186 typeof(v.constructed) === 'undefined') {
1187 rval = true;
1188
1189 // handle sub values
1190 if(v.value && forge.util.isArray(v.value)) {
1191 var j = 0;
1192 for(var i = 0; rval && i < v.value.length; ++i) {
1193 var schemaItem = v.value[i];
1194 rval = !!schemaItem.optional;
1195
1196 // current child in the object
1197 var objChild = obj.value[j];
1198
1199 // if there is no child left to match
1200 if(!objChild) {
1201 // if optional, ok (rval already true), else fail below
1202 if(!schemaItem.optional) {
1203 rval = false;
1204 if(errors) {
1205 errors.push('[' + v.name + '] ' +
1206 'Missing required element. Expected tag class "' +
1207 schemaItem.tagClass + '", type "' + schemaItem.type + '"');
1208 }
1209 }
1210 continue;
1211 }
1212
1213 // If schema explicitly specifies tagClass/type, do a quick structural check
1214 // to avoid unnecessary recursion/side-effects when tags clearly don't match.
1215 var schemaHasTag = (typeof schemaItem.tagClass !== 'undefined' &&
1216 typeof schemaItem.type !== 'undefined');
1217
1218 if(schemaHasTag &&
1219 (objChild.tagClass !== schemaItem.tagClass || objChild.type !== schemaItem.type)) {
1220 // Tags do not match.
1221 if(schemaItem.optional) {
1222 // Skip this schema element (don't consume objChild; don't call recursive validate).
1223 rval = true;
1224 continue;
1225 } else {
1226 // Required schema item mismatched - fail.
1227 rval = false;
1228 if(errors) {
1229 errors.push('[' + v.name + '] ' +
1230 'Tag mismatch. Expected (' +
1231 schemaItem.tagClass + ',' + schemaItem.type + '), got (' +
1232 objChild.tagClass + ',' + objChild.type + ')');
1233 }
1234 break;
1235 }
1236 }
1237
1238 // Tags are compatible (or schema did not declare tags) - dive into recursive validate.
1239 var childRval = asn1.validate(objChild, schemaItem, capture, errors);
1240 if(childRval) {
1241 // consume this child
1242 ++j;
1243 rval = true;
1244 } else if(schemaItem.optional) {
1245 // validation failed but element is optional => skip schema item (don't consume child)
1246 rval = true;
1247 } else {
1248 // required item failed
1249 rval = false;
1250 // errors should already be populated by recursive call; keep failing
1251 break;
1252 }
1253 }
1254 }
1255
1256 if(rval && capture) {
1257 if(v.capture) {
1258 capture[v.capture] = obj.value;
1259 }
1260 if(v.captureAsn1) {
1261 capture[v.captureAsn1] = obj;
1262 }
1263 if(v.captureBitStringContents && 'bitStringContents' in obj) {
1264 capture[v.captureBitStringContents] = obj.bitStringContents;
1265 }
1266 if(v.captureBitStringValue && 'bitStringContents' in obj) {
1267 var value;
1268 if(obj.bitStringContents.length < 2) {
1269 capture[v.captureBitStringValue] = '';
1270 } else {
1271 // FIXME: support unused bits with data shifting
1272 var unused = obj.bitStringContents.charCodeAt(0);
1273 if(unused !== 0) {
1274 throw new Error(
1275 'captureBitStringValue only supported for zero unused bits');
1276 }
1277 capture[v.captureBitStringValue] = obj.bitStringContents.slice(1);
1278 }
1279 }
1280 }
1281 } else if(errors) {
1282 errors.push(
1283 '[' + v.name + '] ' +
1284 'Expected constructed "' + v.constructed + '", got "' +
1285 obj.constructed + '"');
1286 }
1287 } else if(errors) {
1288 if(obj.tagClass !== v.tagClass) {
1289 errors.push(
1290 '[' + v.name + '] ' +
1291 'Expected tag class "' + v.tagClass + '", got "' +
1292 obj.tagClass + '"');
1293 }
1294 if(obj.type !== v.type) {
1295 errors.push(
1296 '[' + v.name + '] ' +
1297 'Expected type "' + v.type + '", got "' +
1298 obj.type + '"');
1299 }
1300 }
1301 return rval;
1302};
1303
1304// regex for testing for non-latin characters
1305var _nonLatinRegex = /[^\\u0000-\\u00ff]/;
1306
1307/**
1308 * Pretty prints an ASN.1 object to a string.
1309 *
1310 * @param obj the object to write out.
1311 * @param level the level in the tree.
1312 * @param indentation the indentation to use.
1313 *
1314 * @return the string.
1315 */
1316asn1.prettyPrint = function(obj, level, indentation) {
1317 var rval = '';
1318
1319 // set default level and indentation
1320 level = level || 0;
1321 indentation = indentation || 2;
1322
1323 // start new line for deep levels
1324 if(level > 0) {
1325 rval += '\n';
1326 }
1327
1328 // create indent
1329 var indent = '';
1330 for(var i = 0; i < level * indentation; ++i) {
1331 indent += ' ';
1332 }
1333
1334 // print class:type
1335 rval += indent + 'Tag: ';
1336 switch(obj.tagClass) {
1337 case asn1.Class.UNIVERSAL:
1338 rval += 'Universal:';
1339 break;
1340 case asn1.Class.APPLICATION:
1341 rval += 'Application:';
1342 break;
1343 case asn1.Class.CONTEXT_SPECIFIC:
1344 rval += 'Context-Specific:';
1345 break;
1346 case asn1.Class.PRIVATE:
1347 rval += 'Private:';
1348 break;
1349 }
1350
1351 if(obj.tagClass === asn1.Class.UNIVERSAL) {
1352 rval += obj.type;
1353
1354 // known types
1355 switch(obj.type) {
1356 case asn1.Type.NONE:
1357 rval += ' (None)';
1358 break;
1359 case asn1.Type.BOOLEAN:
1360 rval += ' (Boolean)';
1361 break;
1362 case asn1.Type.INTEGER:
1363 rval += ' (Integer)';
1364 break;
1365 case asn1.Type.BITSTRING:
1366 rval += ' (Bit string)';
1367 break;
1368 case asn1.Type.OCTETSTRING:
1369 rval += ' (Octet string)';
1370 break;
1371 case asn1.Type.NULL:
1372 rval += ' (Null)';
1373 break;
1374 case asn1.Type.OID:
1375 rval += ' (Object Identifier)';
1376 break;
1377 case asn1.Type.ODESC:
1378 rval += ' (Object Descriptor)';
1379 break;
1380 case asn1.Type.EXTERNAL:
1381 rval += ' (External or Instance of)';
1382 break;
1383 case asn1.Type.REAL:
1384 rval += ' (Real)';
1385 break;
1386 case asn1.Type.ENUMERATED:
1387 rval += ' (Enumerated)';
1388 break;
1389 case asn1.Type.EMBEDDED:
1390 rval += ' (Embedded PDV)';
1391 break;
1392 case asn1.Type.UTF8:
1393 rval += ' (UTF8)';
1394 break;
1395 case asn1.Type.ROID:
1396 rval += ' (Relative Object Identifier)';
1397 break;
1398 case asn1.Type.SEQUENCE:
1399 rval += ' (Sequence)';
1400 break;
1401 case asn1.Type.SET:
1402 rval += ' (Set)';
1403 break;
1404 case asn1.Type.PRINTABLESTRING:
1405 rval += ' (Printable String)';
1406 break;
1407 case asn1.Type.IA5String:
1408 rval += ' (IA5String (ASCII))';
1409 break;
1410 case asn1.Type.UTCTIME:
1411 rval += ' (UTC time)';
1412 break;
1413 case asn1.Type.GENERALIZEDTIME:
1414 rval += ' (Generalized time)';
1415 break;
1416 case asn1.Type.BMPSTRING:
1417 rval += ' (BMP String)';
1418 break;
1419 }
1420 } else {
1421 rval += obj.type;
1422 }
1423
1424 rval += '\n';
1425 rval += indent + 'Constructed: ' + obj.constructed + '\n';
1426
1427 if(obj.composed) {
1428 var subvalues = 0;
1429 var sub = '';
1430 for(var i = 0; i < obj.value.length; ++i) {
1431 if(obj.value[i] !== undefined) {
1432 subvalues += 1;
1433 sub += asn1.prettyPrint(obj.value[i], level + 1, indentation);
1434 if((i + 1) < obj.value.length) {
1435 sub += ',';
1436 }
1437 }
1438 }
1439 rval += indent + 'Sub values: ' + subvalues + sub;
1440 } else {
1441 rval += indent + 'Value: ';
1442 if(obj.type === asn1.Type.OID) {
1443 var oid = asn1.derToOid(obj.value);
1444 rval += oid;
1445 if(forge.pki && forge.pki.oids) {
1446 if(oid in forge.pki.oids) {
1447 rval += ' (' + forge.pki.oids[oid] + ') ';
1448 }
1449 }
1450 }
1451 if(obj.type === asn1.Type.INTEGER) {
1452 try {
1453 rval += asn1.derToInteger(obj.value);
1454 } catch(ex) {
1455 rval += '0x' + forge.util.bytesToHex(obj.value);
1456 }
1457 } else if(obj.type === asn1.Type.BITSTRING) {
1458 // TODO: shift bits as needed to display without padding
1459 if(obj.value.length > 1) {
1460 // remove unused bits field
1461 rval += '0x' + forge.util.bytesToHex(obj.value.slice(1));
1462 } else {
1463 rval += '(none)';
1464 }
1465 // show unused bit count
1466 if(obj.value.length > 0) {
1467 var unused = obj.value.charCodeAt(0);
1468 if(unused == 1) {
1469 rval += ' (1 unused bit shown)';
1470 } else if(unused > 1) {
1471 rval += ' (' + unused + ' unused bits shown)';
1472 }
1473 }
1474 } else if(obj.type === asn1.Type.OCTETSTRING) {
1475 if(!_nonLatinRegex.test(obj.value)) {
1476 rval += '(' + obj.value + ') ';
1477 }
1478 rval += '0x' + forge.util.bytesToHex(obj.value);
1479 } else if(obj.type === asn1.Type.UTF8) {
1480 try {
1481 rval += forge.util.decodeUtf8(obj.value);
1482 } catch(e) {
1483 if(e.message === 'URI malformed') {
1484 rval +=
1485 '0x' + forge.util.bytesToHex(obj.value) + ' (malformed UTF8)';
1486 } else {
1487 throw e;
1488 }
1489 }
1490 } else if(obj.type === asn1.Type.PRINTABLESTRING ||
1491 obj.type === asn1.Type.IA5String) {
1492 rval += obj.value;
1493 } else if(_nonLatinRegex.test(obj.value)) {
1494 rval += '0x' + forge.util.bytesToHex(obj.value);
1495 } else if(obj.value.length === 0) {
1496 rval += '[null]';
1497 } else {
1498 rval += obj.value;
1499 }
1500 }
1501
1502 return rval;
1503};
Note: See TracBrowser for help on using the repository browser.