UNPKG

5.02 kBJavaScriptView Raw
1const MAX_OCTET = 0x80
2const CLASS_UNIVERSAL = 0
3const PRIMITIVE_BIT = 0x20
4const TAG_SEQ = 0x10
5const TAG_INT = 0x02
6const ENCODED_TAG_SEQ = (TAG_SEQ | PRIMITIVE_BIT) | (CLASS_UNIVERSAL << 6)
7const ENCODED_TAG_INT = TAG_INT | (CLASS_UNIVERSAL << 6)
8
9const getParamSize = keySize => ((keySize / 8) | 0) + (keySize % 8 === 0 ? 0 : 1)
10
11const paramBytesForAlg = {
12 ES256: getParamSize(256),
13 ES256K: getParamSize(256),
14 ES384: getParamSize(384),
15 ES512: getParamSize(521)
16}
17
18const countPadding = (buf, start, stop) => {
19 let padding = 0
20 while (start + padding < stop && buf[start + padding] === 0) {
21 ++padding
22 }
23
24 const needsSign = buf[start + padding] >= MAX_OCTET
25 if (needsSign) {
26 --padding
27 }
28
29 return padding
30}
31
32module.exports.derToJose = (signature, alg) => {
33 if (!Buffer.isBuffer(signature)) {
34 throw new TypeError('ECDSA signature must be a Buffer')
35 }
36
37 if (!paramBytesForAlg[alg]) {
38 throw new Error(`Unknown algorithm "${alg}"`)
39 }
40
41 const paramBytes = paramBytesForAlg[alg]
42
43 // the DER encoded param should at most be the param size, plus a padding
44 // zero, since due to being a signed integer
45 const maxEncodedParamLength = paramBytes + 1
46
47 const inputLength = signature.length
48
49 let offset = 0
50 if (signature[offset++] !== ENCODED_TAG_SEQ) {
51 throw new Error('Could not find expected "seq"')
52 }
53
54 let seqLength = signature[offset++]
55 if (seqLength === (MAX_OCTET | 1)) {
56 seqLength = signature[offset++]
57 }
58
59 if (inputLength - offset < seqLength) {
60 throw new Error(`"seq" specified length of ${seqLength}", only ${inputLength - offset}" remaining`)
61 }
62
63 if (signature[offset++] !== ENCODED_TAG_INT) {
64 throw new Error('Could not find expected "int" for "r"')
65 }
66
67 const rLength = signature[offset++]
68
69 if (inputLength - offset - 2 < rLength) {
70 throw new Error(`"r" specified length of "${rLength}", only "${inputLength - offset - 2}" available`)
71 }
72
73 if (maxEncodedParamLength < rLength) {
74 throw new Error(`"r" specified length of "${rLength}", max of "${maxEncodedParamLength}" is acceptable`)
75 }
76
77 const rOffset = offset
78 offset += rLength
79
80 if (signature[offset++] !== ENCODED_TAG_INT) {
81 throw new Error('Could not find expected "int" for "s"')
82 }
83
84 const sLength = signature[offset++]
85
86 if (inputLength - offset !== sLength) {
87 throw new Error(`"s" specified length of "${sLength}", expected "${inputLength - offset}"`)
88 }
89
90 if (maxEncodedParamLength < sLength) {
91 throw new Error(`"s" specified length of "${sLength}", max of "${maxEncodedParamLength}" is acceptable`)
92 }
93
94 const sOffset = offset
95 offset += sLength
96
97 if (offset !== inputLength) {
98 throw new Error(`Expected to consume entire buffer, but "${inputLength - offset}" bytes remain`)
99 }
100
101 const rPadding = paramBytes - rLength
102
103 const sPadding = paramBytes - sLength
104
105 const dst = Buffer.allocUnsafe(rPadding + rLength + sPadding + sLength)
106
107 for (offset = 0; offset < rPadding; ++offset) {
108 dst[offset] = 0
109 }
110 signature.copy(dst, offset, rOffset + Math.max(-rPadding, 0), rOffset + rLength)
111
112 offset = paramBytes
113
114 for (const o = offset; offset < o + sPadding; ++offset) {
115 dst[offset] = 0
116 }
117 signature.copy(dst, offset, sOffset + Math.max(-sPadding, 0), sOffset + sLength)
118
119 return dst
120}
121
122module.exports.joseToDer = (signature, alg) => {
123 if (!Buffer.isBuffer(signature)) {
124 throw new TypeError('ECDSA signature must be a Buffer')
125 }
126
127 if (!paramBytesForAlg[alg]) {
128 throw new TypeError(`Unknown algorithm "${alg}"`)
129 }
130
131 const paramBytes = paramBytesForAlg[alg]
132
133 const signatureBytes = signature.length
134 if (signatureBytes !== paramBytes * 2) {
135 throw new Error(`"${alg}" signatures must be "${paramBytes * 2}" bytes, saw "${signatureBytes}"`)
136 }
137
138 const rPadding = countPadding(signature, 0, paramBytes)
139 const sPadding = countPadding(signature, paramBytes, signature.length)
140 const rLength = paramBytes - rPadding
141 const sLength = paramBytes - sPadding
142
143 const rsBytes = 1 + 1 + rLength + 1 + 1 + sLength
144
145 const shortLength = rsBytes < MAX_OCTET
146
147 const dst = Buffer.allocUnsafe((shortLength ? 2 : 3) + rsBytes)
148
149 let offset = 0
150 dst[offset++] = ENCODED_TAG_SEQ
151 if (shortLength) {
152 // Bit 8 has value "0"
153 // bits 7-1 give the length.
154 dst[offset++] = rsBytes
155 } else {
156 // Bit 8 of first octet has value "1"
157 // bits 7-1 give the number of additional length octets.
158 dst[offset++] = MAX_OCTET | 1 // eslint-disable-line no-tabs
159 // length, base 256
160 dst[offset++] = rsBytes & 0xff
161 }
162 dst[offset++] = ENCODED_TAG_INT
163 dst[offset++] = rLength
164 if (rPadding < 0) {
165 dst[offset++] = 0
166 offset += signature.copy(dst, offset, 0, paramBytes)
167 } else {
168 offset += signature.copy(dst, offset, rPadding, paramBytes)
169 }
170 dst[offset++] = ENCODED_TAG_INT
171 dst[offset++] = sLength
172 if (sPadding < 0) {
173 dst[offset++] = 0
174 signature.copy(dst, offset, paramBytes)
175 } else {
176 signature.copy(dst, offset, paramBytes + sPadding)
177 }
178
179 return dst
180}