UNPKG

5.83 kBJavaScriptView Raw
1/**
2 * Public Key
3 * ==========
4 *
5 * A public key corresponds to a private key. If you have a private key, you
6 * can find the corresponding public key with new PubKey().fromPrivKey(privKey).
7 */
8'use strict'
9
10import { Point } from './point'
11import { Bn } from './bn'
12import { Bw } from './bw'
13import { Struct } from './struct'
14import { Workers } from './workers'
15
16class PubKey extends Struct {
17 constructor (point, compressed) {
18 super({ point, compressed })
19 }
20
21 fromJSON (json) {
22 this.fromFastHex(json)
23 return this
24 }
25
26 toJSON () {
27 return this.toFastHex()
28 }
29
30 fromPrivKey (privKey) {
31 this.fromObject({
32 point: Point.getG().mul(privKey.bn),
33 compressed: privKey.compressed
34 })
35 return this
36 }
37
38 static fromPrivKey (privKey) {
39 return new this().fromPrivKey(privKey)
40 }
41
42 async asyncFromPrivKey (privKey) {
43 const workersResult = await Workers.asyncObjectMethod(this, 'fromPrivKey', [
44 privKey
45 ])
46 return this.fromFastBuffer(workersResult.resbuf)
47 }
48
49 static asyncFromPrivKey (privKey) {
50 return new this().asyncFromPrivKey(privKey)
51 }
52
53 fromBuffer (buf, strict) {
54 return this.fromDer(buf, strict)
55 }
56
57 async asyncFromBuffer (buf, strict) {
58 const args = [buf, strict]
59 const workersResult = await Workers.asyncObjectMethod(
60 this,
61 'fromBuffer',
62 args
63 )
64 return this.fromFastBuffer(workersResult.resbuf)
65 }
66
67 fromFastBuffer (buf) {
68 if (buf.length === 0) {
69 return this
70 }
71 const compressed = Boolean(buf[0])
72 buf = buf.slice(1)
73 this.fromDer(buf)
74 this.compressed = compressed
75 return this
76 }
77
78 /**
79 * In order to mimic the non-strict style of OpenSSL, set strict = false. For
80 * information and what prefixes 0x06 and 0x07 mean, in addition to the normal
81 * compressed and uncompressed public keys, see the message by Peter Wuille
82 * where he discovered these "hybrid pubKeys" on the mailing list:
83 * http://sourceforge.net/p/bitcoin/mailman/message/29416133/
84 */
85 fromDer (buf, strict) {
86 if (strict === undefined) {
87 strict = true
88 } else {
89 strict = false
90 }
91 if (
92 buf[0] === 0x04 ||
93 (!strict && (buf[0] === 0x06 || buf[0] === 0x07))
94 ) {
95 const xbuf = buf.slice(1, 33)
96 const ybuf = buf.slice(33, 65)
97 if (xbuf.length !== 32 || ybuf.length !== 32 || buf.length !== 65) {
98 throw new Error('LEngth of x and y must be 32 bytes')
99 }
100 const x = new Bn(xbuf)
101 const y = new Bn(ybuf)
102 this.point = new Point(x, y)
103 this.compressed = false
104 } else if (buf[0] === 0x03) {
105 const xbuf = buf.slice(1)
106 const x = new Bn(xbuf)
107 this.fromX(true, x)
108 this.compressed = true
109 } else if (buf[0] === 0x02) {
110 const xbuf = buf.slice(1)
111 const x = new Bn(xbuf)
112 this.fromX(false, x)
113 this.compressed = true
114 } else {
115 throw new Error('Invalid DER format pubKey')
116 }
117 return this
118 }
119
120 static fromDer (buf, strict) {
121 return new this().fromDer(buf, strict)
122 }
123
124 fromString (str) {
125 this.fromDer(Buffer.from(str, 'hex'))
126 return this
127 }
128
129 fromX (odd, x) {
130 if (typeof odd !== 'boolean') {
131 throw new Error('Must specify whether y is odd or not (true or false)')
132 }
133 this.point = Point.fromX(odd, x)
134 return this
135 }
136
137 static fromX (odd, x) {
138 return new this().fromX(odd, x)
139 }
140
141 toBuffer () {
142 const compressed = this.compressed === undefined ? true : this.compressed
143 return this.toDer(compressed)
144 }
145
146 toFastBuffer () {
147 if (!this.point) {
148 return Buffer.alloc(0)
149 }
150 const bw = new Bw()
151 const compressed =
152 this.compressed === undefined ? true : Boolean(this.compressed)
153 bw.writeUInt8(Number(compressed))
154 bw.write(this.toDer(false))
155 return bw.toBuffer()
156 }
157
158 toDer (compressed) {
159 compressed = compressed === undefined ? this.compressed : compressed
160 if (typeof compressed !== 'boolean') {
161 throw new Error(
162 'Must specify whether the public key is compressed or not (true or false)'
163 )
164 }
165
166 const x = this.point.getX()
167 const y = this.point.getY()
168
169 const xbuf = x.toBuffer({ size: 32 })
170 const ybuf = y.toBuffer({ size: 32 })
171
172 let prefix
173 if (!compressed) {
174 prefix = Buffer.from([0x04])
175 return Buffer.concat([prefix, xbuf, ybuf])
176 } else {
177 const odd = ybuf[ybuf.length - 1] % 2
178 if (odd) {
179 prefix = Buffer.from([0x03])
180 } else {
181 prefix = Buffer.from([0x02])
182 }
183 return Buffer.concat([prefix, xbuf])
184 }
185 }
186
187 toString () {
188 const compressed = this.compressed === undefined ? true : this.compressed
189 return this.toDer(compressed).toString('hex')
190 }
191
192 /**
193 * Translated from bitcoind's IsCompressedOrUncompressedPubKey
194 */
195 static isCompressedOrUncompressed (buf) {
196 if (buf.length < 33) {
197 // Non-canonical public key: too short
198 return false
199 }
200 if (buf[0] === 0x04) {
201 if (buf.length !== 65) {
202 // Non-canonical public key: invalid length for uncompressed key
203 return false
204 }
205 } else if (buf[0] === 0x02 || buf[0] === 0x03) {
206 if (buf.length !== 33) {
207 // Non-canonical public key: invalid length for compressed key
208 return false
209 }
210 } else {
211 // Non-canonical public key: neither compressed nor uncompressed
212 return false
213 }
214 return true
215 }
216
217 // https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf
218 validate () {
219 if (this.point.isInfinity()) {
220 throw new Error('point: Point cannot be equal to Infinity')
221 }
222 if (this.point.eq(new Point(new Bn(0), new Bn(0)))) {
223 throw new Error('point: Point cannot be equal to 0, 0')
224 }
225 this.point.validate()
226 return this
227 }
228}
229
230export { PubKey }