2
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
3
* License, v. 2.0. If a copy of the MPL was not distributed with this
4
4
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
-
6
5
// https://git.coolaj86.com/coolaj86/asn1-parser.js
7
-
8
- let ELOOPN = 102 ;
9
- let EDEEPN = 60 ;
10
- let CTYPES = [ 0x30 , 0x31 , 0xa0 , 0xa1 ] ;
11
- let VTYPES = [ 0x01 , 0x02 , 0x05 , 0x06 , 0x0c , 0x82 ] ;
12
-
13
- function parseAsn1 ( buf ) {
14
- //let ws = ' ';
15
- function parseAsn1 ( buf , depth , eager ) {
16
- if ( depth . length >= EDEEPN ) {
17
- throw new Error ( "EDEEP" ) ;
18
- }
19
-
20
- let index = 2 ; // we know, at minimum, data starts after type (0) and lengthSize (1)
21
- let asn1 = { type : buf [ 0 ] , lengthSize : 0 , length : buf [ 1 ] } ;
22
- let child ;
23
- let iters = 0 ;
24
- let adjust = 0 ;
25
- let adjustedLen ;
26
-
27
- // Determine how many bytes the length uses, and what it is
28
- if ( 0x80 & asn1 . length ) {
29
- asn1 . lengthSize = 0x7f & asn1 . length ;
30
- // I think that buf->hex->int solves the problem of Endianness... not sure
31
- asn1 . length = parseInt ( bufToHex ( buf . slice ( index , index + asn1 . lengthSize ) ) , 16 ) ;
32
- index += asn1 . lengthSize ;
33
- }
34
-
35
- // High-order bit Integers have a leading 0x00 to signify that they are positive.
36
- // Bit Streams use the first byte to signify padding, which x.509 doesn't use.
37
- if ( 0x00 === buf [ index ] && ( 0x02 === asn1 . type || 0x03 === asn1 . type ) ) {
38
- // However, 0x00 on its own is a valid number
39
- if ( asn1 . length > 1 ) {
40
- index += 1 ;
41
- adjust = - 1 ;
6
+ ; ( function ( exports ) {
7
+ 'use strict' ;
8
+
9
+ if ( ! exports . ASN1 ) { exports . ASN1 = { } ; }
10
+ if ( ! exports . Enc ) { exports . Enc = { } ; }
11
+ if ( ! exports . PEM ) { exports . PEM = { } ; }
12
+
13
+ var ASN1 = exports . ASN1 ;
14
+ var Enc = exports . Enc ;
15
+ var PEM = exports . PEM ;
16
+
17
+ //
18
+ // Parser
19
+ //
20
+ // Although I've only seen 9 max in https certificates themselves,
21
+ // but each domain list could have up to 100
22
+ ASN1 . ELOOPN = 102 ;
23
+ ASN1 . ELOOP = "uASN1.js Error: iterated over " + ASN1 . ELOOPN + "+ elements (probably a malformed file)" ;
24
+ // I've seen https certificates go 29 deep
25
+ ASN1 . EDEEPN = 60 ;
26
+ ASN1 . EDEEP = "uASN1.js Error: element nested " + ASN1 . EDEEPN + "+ layers deep (probably a malformed file)" ;
27
+ // Container Types are Sequence 0x30, Container Array? (0xA0, 0xA1)
28
+ // Value Types are Boolean 0x01, Integer 0x02, Null 0x05, Object ID 0x06, String 0x0C, 0x16, 0x13, 0x1e Value Array? (0x82)
29
+ // Bit String (0x03) and Octet String (0x04) may be values or containers
30
+ // Sometimes Bit String is used as a container (RSA Pub Spki)
31
+ ASN1 . CTYPES = [ 0x30 , 0x31 , 0xa0 , 0xa1 ] ;
32
+ ASN1 . VTYPES = [ 0x01 , 0x02 , 0x05 , 0x06 , 0x0c , 0x82 ] ;
33
+ ASN1 . parse = function parseAsn1Helper ( buf ) {
34
+ //var ws = ' ';
35
+ function parseAsn1 ( buf , depth , eager ) {
36
+ if ( depth . length >= ASN1 . EDEEPN ) { throw new Error ( ASN1 . EDEEP ) ; }
37
+
38
+ var index = 2 ; // we know, at minimum, data starts after type (0) and lengthSize (1)
39
+ var asn1 = { type : buf [ 0 ] , lengthSize : 0 , length : buf [ 1 ] } ;
40
+ var child ;
41
+ var iters = 0 ;
42
+ var adjust = 0 ;
43
+ var adjustedLen ;
44
+
45
+ // Determine how many bytes the length uses, and what it is
46
+ if ( 0x80 & asn1 . length ) {
47
+ asn1 . lengthSize = 0x7f & asn1 . length ;
48
+ // I think that buf->hex->int solves the problem of Endianness... not sure
49
+ asn1 . length = parseInt ( Enc . bufToHex ( buf . slice ( index , index + asn1 . lengthSize ) ) , 16 ) ;
50
+ index += asn1 . lengthSize ;
42
51
}
43
- }
44
- adjustedLen = asn1 . length + adjust ;
45
52
46
- function bufToHex ( u8 ) {
47
- let hex = [ ] ;
48
- let i , h ;
49
- let len = ( u8 . byteLength || u8 . length ) ;
50
-
51
- for ( i = 0 ; i < len ; i += 1 ) {
52
- h = u8 [ i ] . toString ( 16 ) ;
53
- if ( h . length % 2 ) {
54
- h = '0' + h ;
53
+ // High-order bit Integers have a leading 0x00 to signify that they are positive.
54
+ // Bit Streams use the first byte to signify padding, which x.509 doesn't use.
55
+ if ( 0x00 === buf [ index ] && ( 0x02 === asn1 . type || 0x03 === asn1 . type ) ) {
56
+ // However, 0x00 on its own is a valid number
57
+ if ( asn1 . length > 1 ) {
58
+ index += 1 ;
59
+ adjust = - 1 ;
55
60
}
56
- hex . push ( h ) ;
57
61
}
58
-
59
- return hex . join ( '' ) . toLowerCase ( ) ;
60
- }
61
-
62
- //console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1) ;
63
- function parseChildren ( eager ) {
64
- asn1 . children = [ ] ;
65
- //console.warn('1 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', 0) ;
66
- while ( iters < ELOOPN && index < ( 2 + asn1 . length + asn1 . lengthSize ) ) {
67
- iters += 1 ;
68
- depth . length + = 1 ;
69
- child = parseAsn1 ( buf . slice ( index , index + adjustedLen ) , depth , eager ) ;
70
- depth . length -= 1 ;
71
- // The numbers don't match up exactly and I don't remember why...
72
- // probably something with adjustedLen or some such, but the tests pass
73
- index += ( 2 + child . lengthSize + child . length ) ;
74
- //console.warn('2 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', (2 + child.lengthSize + child.length ));
75
- if ( index > ( 2 + asn1 . lengthSize + asn1 . length ) ) {
76
- if ( ! eager ) {
77
- console . error ( "error ") ;
62
+ adjustedLen = asn1 . length + adjust ;
63
+
64
+ //console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
65
+ function parseChildren ( eager ) {
66
+ asn1 . children = [ ] ;
67
+ //console.warn('1 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', 0);
68
+ while ( iters < ASN1 . ELOOPN && index < ( 2 + asn1 . length + asn1 . lengthSize ) ) {
69
+ iters += 1 ;
70
+ depth . length += 1 ;
71
+ child = parseAsn1 ( buf . slice ( index , index + adjustedLen ) , depth , eager ) ;
72
+ depth . length - = 1 ;
73
+ // The numbers don't match up exactly and I don't remember why...
74
+ // probably something with adjustedLen or some such, but the tests pass
75
+ index += ( 2 + child . lengthSize + child . length ) ;
76
+ //console.warn('2 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', (2 + child.lengthSize + child.length));
77
+ if ( index > ( 2 + asn1 . lengthSize + asn1 . length ) ) {
78
+ if ( ! eager ) { console . error ( JSON . stringify ( asn1 , ASN1 . _replacer , 2 ) ) ; }
79
+ throw new Error ( "Parse error: child value length (" + child . length
80
+ + ") is greater than remaining parent length (" + ( asn1 . length - index )
81
+ + " = " + asn1 . length + " - " + index + ") ") ;
78
82
}
79
- throw new Error ( "Parse error: child value length (" + child . length
80
- + ") is greater than remaining parent length (" + ( asn1 . length - index )
81
- + " = " + asn1 . length + " - " + index + ")" ) ;
83
+ asn1 . children . push ( child ) ;
84
+ //console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
82
85
}
83
- asn1 . children . push ( child ) ;
84
- //console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
85
- }
86
- if ( index !== ( 2 + asn1 . lengthSize + asn1 . length ) ) {
87
- //console.warn('index:', index, 'length:', (2 + asn1.lengthSize + asn1.length));
88
- throw new Error ( "premature end-of-file" ) ;
89
- }
90
- if ( iters >= ELOOPN ) {
91
- throw new Error ( "ELOOP" ) ;
86
+ if ( index !== ( 2 + asn1 . lengthSize + asn1 . length ) ) {
87
+ //console.warn('index:', index, 'length:', (2 + asn1.lengthSize + asn1.length));
88
+ throw new Error ( "premature end-of-file" ) ;
89
+ }
90
+ if ( iters >= ASN1 . ELOOPN ) { throw new Error ( ASN1 . ELOOP ) ; }
91
+
92
+ delete asn1 . value ;
93
+ return asn1 ;
92
94
}
93
95
94
- delete asn1 . value ;
95
- return asn1 ;
96
- }
96
+ // Recurse into types that are _always_ containers
97
+ if ( - 1 !== ASN1 . CTYPES . indexOf ( asn1 . type ) ) { return parseChildren ( eager ) ; }
97
98
98
- // Recurse into types that are _always_ containers
99
- if ( - 1 !== CTYPES . indexOf ( asn1 . type ) ) {
100
- return parseChildren ( eager ) ;
101
- }
99
+ // Return types that are _always_ values
100
+ asn1 . value = buf . slice ( index , index + adjustedLen ) ;
101
+ if ( - 1 !== ASN1 . VTYPES . indexOf ( asn1 . type ) ) { return asn1 ; }
102
102
103
- // Return types that are _always_ values
104
- asn1 . value = buf . slice ( index , index + adjustedLen ) ;
105
- if ( - 1 !== VTYPES . indexOf ( asn1 . type ) ) {
106
- return asn1 ;
103
+ // For ambigious / unknown types, recurse and return on failure
104
+ // (and return child array size to zero)
105
+ try { return parseChildren ( true ) ; }
106
+ catch ( e ) { asn1 . children . length = 0 ; return asn1 ; }
107
107
}
108
108
109
- // For ambigious / unknown types, recurse and return on failure
110
- // (and return child array size to zero)
111
- try {
112
- return parseChildren ( true ) ;
113
- } catch ( e ) {
114
- asn1 . children . length = 0 ;
115
- return asn1 ;
109
+ var asn1 = parseAsn1 ( buf , [ ] ) ;
110
+ var len = buf . byteLength || buf . length ;
111
+ if ( len !== 2 + asn1 . lengthSize + asn1 . length ) {
112
+ throw new Error ( "Length of buffer does not match length of ASN.1 sequence." ) ;
113
+ }
114
+ return asn1 ;
115
+ } ;
116
+ ASN1 . _replacer = function ( k , v ) {
117
+ if ( 'type' === k ) { return '0x' + Enc . numToHex ( v ) ; }
118
+ if ( v && 'value' === k ) { return '0x' + Enc . bufToHex ( v . data || v ) ; }
119
+ return v ;
120
+ } ;
121
+
122
+ // don't replace the full parseBlock, if it exists
123
+ PEM . parseBlock = PEM . parseBlock || function ( str ) {
124
+ var der = str . split ( / \n / ) . filter ( function ( line ) {
125
+ return ! / - - - - - / . test ( line ) ;
126
+ } ) . join ( '' ) ;
127
+ return { der : Enc . base64ToBuf ( der ) } ;
128
+ } ;
129
+
130
+ Enc . base64ToBuf = function ( b64 ) {
131
+ return Enc . binToBuf ( atob ( b64 ) ) ;
132
+ } ;
133
+ Enc . binToBuf = function ( bin ) {
134
+ var arr = bin . split ( '' ) . map ( function ( ch ) {
135
+ return ch . charCodeAt ( 0 ) ;
136
+ } ) ;
137
+ return 'undefined' !== typeof Uint8Array ? new Uint8Array ( arr ) : arr ;
138
+ } ;
139
+ Enc . bufToHex = function ( u8 ) {
140
+ var hex = [ ] ;
141
+ var i , h ;
142
+ var len = ( u8 . byteLength || u8 . length ) ;
143
+
144
+ for ( i = 0 ; i < len ; i += 1 ) {
145
+ h = u8 [ i ] . toString ( 16 ) ;
146
+ if ( h . length % 2 ) { h = '0' + h ; }
147
+ hex . push ( h ) ;
116
148
}
117
- }
118
149
119
- let asn1 = parseAsn1 ( buf , [ ] ) ;
120
- let len = buf . byteLength || buf . length ;
121
- if ( len !== 2 + asn1 . lengthSize + asn1 . length ) {
122
- throw new Error ( "Length of buffer does not match length of ASN.1 sequence." ) ;
123
- }
124
- return asn1 ;
125
- }
150
+ return hex . join ( '' ) . toLowerCase ( ) ;
151
+ } ;
152
+ Enc . numToHex = function ( d ) {
153
+ d = d . toString ( 16 ) ;
154
+ if ( d . length % 2 ) {
155
+ return '0' + d ;
156
+ }
157
+ return d ;
158
+ } ;
126
159
127
- export { parseAsn1 } ;
160
+ } ( 'undefined' !== typeof window ? window : module . exports ) ) ;
0 commit comments