1 | 'use strict';
|
2 |
|
3 | var expect = require('chai').expect;
|
4 | var should = require('chai').should();
|
5 |
|
6 | var keyLib = require('..');
|
7 | var owsCommon = require('@owstack/ows-common');
|
8 | var BN = owsCommon.BN;
|
9 | var Networks = require('@owstack/network-lib');
|
10 | var Point = keyLib.crypto.Point;
|
11 | var PublicKey = keyLib.PublicKey;
|
12 | var PrivateKey = keyLib.PrivateKey;
|
13 |
|
14 |
|
15 |
|
16 | describe('PublicKey', function() {
|
17 |
|
18 |
|
19 | var invalidPoint = '0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000';
|
20 |
|
21 | describe('validating errors on creation', function() {
|
22 | it('errors if data is missing', function() {
|
23 | (function() {
|
24 | return new PublicKey();
|
25 | }).should.throw('First argument is required, please include public key data.');
|
26 | });
|
27 |
|
28 | it('errors if an invalid point is provided', function() {
|
29 | (function() {
|
30 | return new PublicKey(invalidPoint);
|
31 | }).should.throw('Point does not lie on the curve');
|
32 | });
|
33 |
|
34 | it('errors if a point not on the secp256k1 curve is provided', function() {
|
35 | (function() {
|
36 | return new PublicKey(new Point(1000, 1000));
|
37 | }).should.throw('Point does not lie on the curve');
|
38 | });
|
39 |
|
40 | it('errors if the argument is of an unrecognized type', function() {
|
41 | (function() {
|
42 | return new PublicKey(new Error());
|
43 | }).should.throw('First argument is an unrecognized data format.');
|
44 | });
|
45 | });
|
46 |
|
47 | describe('instantiation', function() {
|
48 |
|
49 | it('from a private key', function() {
|
50 | var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff';
|
51 | var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc';
|
52 | var privkey = new PrivateKey(new BN(new Buffer(privhex, 'hex')));
|
53 | var pk = new PublicKey(privkey);
|
54 | pk.toString().should.equal(pubhex);
|
55 | });
|
56 |
|
57 | it('problematic secp256k1 public keys', function() {
|
58 | var knownKeys = [
|
59 | {
|
60 | wif: 'KzsjKq2FVqVuQv2ueHVFuB65A9uEZ6S1L6F8NuokCrE3V3kE3Ack',
|
61 | priv: '6d1229a6b24c2e775c062870ad26bc261051e0198c67203167273c7c62538846',
|
62 | pub: '03d6106302d2698d6a41e9c9a114269e7be7c6a0081317de444bb2980bf9265a01',
|
63 | pubx: 'd6106302d2698d6a41e9c9a114269e7be7c6a0081317de444bb2980bf9265a01',
|
64 | puby: 'e05fb262e64b108991a29979809fcef9d3e70cafceb3248c922c17d83d66bc9d'
|
65 | },
|
66 | {
|
67 | wif: 'L5MgSwNB2R76xBGorofRSTuQFd1bm3hQMFVf3u2CneFom8u1Yt7G',
|
68 | priv: 'f2cc9d2b008927db94b89e04e2f6e70c180e547b3e5e564b06b8215d1c264b53',
|
69 | pub: '03e275faa35bd1e88f5df6e8f9f6edb93bdf1d65f4915efc79fd7a726ec0c21700',
|
70 | pubx: 'e275faa35bd1e88f5df6e8f9f6edb93bdf1d65f4915efc79fd7a726ec0c21700',
|
71 | puby: '367216cb35b086e6686d69dddd822a8f4d52eb82ac5d9de18fdcd9bf44fa7df7'
|
72 | }
|
73 | ];
|
74 |
|
75 | for(var i = 0; i < knownKeys.length; i++) {
|
76 | var privkey = new PrivateKey(knownKeys[i].wif);
|
77 | var pubkey = privkey.toPublicKey();
|
78 | pubkey.toString().should.equal(knownKeys[i].pub);
|
79 | pubkey.point.x.toString('hex').should.equal(knownKeys[i].pubx);
|
80 | pubkey.point.y.toString('hex').should.equal(knownKeys[i].puby);
|
81 | }
|
82 | });
|
83 |
|
84 | it('from a compressed public key', function() {
|
85 | var publicKeyHex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a';
|
86 | var publicKey = new PublicKey(publicKeyHex);
|
87 | publicKey.toString().should.equal(publicKeyHex);
|
88 | });
|
89 |
|
90 | it('from another publicKey', function() {
|
91 | var publicKeyHex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a';
|
92 | var publicKey = new PublicKey(publicKeyHex);
|
93 | var publicKey2 = new PublicKey(publicKey);
|
94 | publicKey.should.equal(publicKey2);
|
95 | });
|
96 |
|
97 | it('sets the network as specified', function() {
|
98 | var publicKeyHex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a';
|
99 | var publicKey = new PublicKey(publicKeyHex, 'btc');
|
100 | publicKey.network.should.equal(Networks.get('btc'));
|
101 | });
|
102 |
|
103 | it('sets root network if none provided', function() {
|
104 | var publicKeyHex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a';
|
105 | var publicKey = new PublicKey(publicKeyHex);
|
106 | publicKey.network.should.equal(Networks.get('root'));
|
107 | });
|
108 |
|
109 | it('from a hex encoded DER string', function() {
|
110 | var pk = new PublicKey('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
|
111 | should.exist(pk.point);
|
112 | pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
113 | });
|
114 |
|
115 | it('from a hex encoded DER buffer', function() {
|
116 | var pk = new PublicKey(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex'));
|
117 | should.exist(pk.point);
|
118 | pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
119 | });
|
120 |
|
121 | it('from a point', function() {
|
122 | var p = new Point('86a80a5a2bfc48dddde2b0bd88bd56b0b6ddc4e6811445b175b90268924d7d48',
|
123 | '3b402dfc89712cfe50963e670a0598e6b152b3cd94735001cdac6794975d3afd');
|
124 | var a = new PublicKey(p);
|
125 | should.exist(a.point);
|
126 | a.point.toString().should.equal(p.toString());
|
127 | var c = new PublicKey(p);
|
128 | should.exist(c.point);
|
129 | c.point.toString().should.equal(p.toString());
|
130 | });
|
131 | });
|
132 |
|
133 | describe('#getValidationError', function() {
|
134 |
|
135 | it('should recieve an invalid point error', function() {
|
136 | var error = PublicKey.getValidationError(invalidPoint);
|
137 | should.exist(error);
|
138 | error.message.should.equal('Point does not lie on the curve');
|
139 | });
|
140 |
|
141 | it('should recieve a boolean as false', function() {
|
142 | var valid = PublicKey.isValid(invalidPoint);
|
143 | valid.should.equal(false);
|
144 | });
|
145 |
|
146 | it('should recieve a boolean as true for uncompressed', function() {
|
147 | var valid = PublicKey.isValid('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
|
148 | valid.should.equal(true);
|
149 | });
|
150 |
|
151 | it('should recieve a boolean as true for compressed', function() {
|
152 | var valid = PublicKey.isValid('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
153 | valid.should.equal(true);
|
154 | });
|
155 |
|
156 | });
|
157 |
|
158 | describe('#fromPoint', function() {
|
159 |
|
160 | it('should instantiate from a point', function() {
|
161 | var p = new Point('86a80a5a2bfc48dddde2b0bd88bd56b0b6ddc4e6811445b175b90268924d7d48',
|
162 | '3b402dfc89712cfe50963e670a0598e6b152b3cd94735001cdac6794975d3afd');
|
163 | var b = PublicKey.fromPoint(p);
|
164 | should.exist(b.point);
|
165 | b.point.toString().should.equal(p.toString());
|
166 | });
|
167 |
|
168 | it('should error because paramater is not a point', function() {
|
169 | (function() {
|
170 | PublicKey.fromPoint(new Error());
|
171 | }).should.throw('First argument must be an instance of Point.');
|
172 | });
|
173 | });
|
174 |
|
175 | describe('#json/object', function() {
|
176 |
|
177 | it('should input/ouput json', function() {
|
178 | var json = JSON.stringify({
|
179 | x: '1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a',
|
180 | y: '7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341',
|
181 | compressed: false
|
182 | });
|
183 | var pubkey = new PublicKey(JSON.parse(json));
|
184 | JSON.stringify(pubkey).should.deep.equal(json);
|
185 | });
|
186 |
|
187 | it('fails if "y" is not provided', function() {
|
188 | expect(function() {
|
189 | return new PublicKey({
|
190 | x: '1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'
|
191 | });
|
192 | }).to.throw();
|
193 | });
|
194 |
|
195 | it('fails if invalid JSON is provided', function() {
|
196 | expect(function() {
|
197 | return PublicKey._transformJSON('¹');
|
198 | }).to.throw();
|
199 | });
|
200 |
|
201 | it('works for X starting with 0x00', function() {
|
202 | var a = new PublicKey('030589ee559348bd6a7325994f9c8eff12bd5d73cc683142bd0dd1a17abc99b0dc');
|
203 | var b = new PublicKey('03'+a.toObject().x);
|
204 | b.toString().should.equal(a.toString());
|
205 | });
|
206 |
|
207 | });
|
208 |
|
209 | describe('#fromPrivateKey', function() {
|
210 |
|
211 | it('should make a public key from a privkey', function() {
|
212 | should.exist(PublicKey.fromPrivateKey(PrivateKey.fromRandom()));
|
213 | });
|
214 |
|
215 | it('should error because not an instance of privkey', function() {
|
216 | (function() {
|
217 | PublicKey.fromPrivateKey(new Error());
|
218 | }).should.throw('Must be an instance of PrivateKey');
|
219 | });
|
220 |
|
221 | });
|
222 |
|
223 | describe('#fromBuffer', function() {
|
224 |
|
225 | it('should parse this uncompressed public key', function() {
|
226 | var pk = PublicKey.fromBuffer(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex'));
|
227 | pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
228 | pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
|
229 | });
|
230 |
|
231 | it('should parse this compressed public key', function() {
|
232 | var pk = PublicKey.fromBuffer(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
|
233 | pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
234 | pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
|
235 | });
|
236 |
|
237 | it('should throw an error on this invalid public key', function() {
|
238 | (function() {
|
239 | PublicKey.fromBuffer(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
|
240 | }).should.throw();
|
241 | });
|
242 |
|
243 | it('should throw error because not a buffer', function() {
|
244 | (function() {
|
245 | PublicKey.fromBuffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
246 | }).should.throw('Must be a hex buffer of DER encoded public key');
|
247 | });
|
248 |
|
249 | it('should throw error because buffer is the incorrect length', function() {
|
250 | (function() {
|
251 | PublicKey.fromBuffer(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a34112', 'hex'));
|
252 | }).should.throw('Length of x and y must be 32 bytes');
|
253 | });
|
254 |
|
255 | });
|
256 |
|
257 | describe('#fromDER', function() {
|
258 |
|
259 | it('should parse this uncompressed public key', function() {
|
260 | var pk = PublicKey.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex'));
|
261 | pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
262 | pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
|
263 | });
|
264 |
|
265 | it('should parse this compressed public key', function() {
|
266 | var pk = PublicKey.fromDER(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
|
267 | pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
268 | pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
|
269 | });
|
270 |
|
271 | it('should throw an error on this invalid public key', function() {
|
272 | (function() {
|
273 | PublicKey.fromDER(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
|
274 | }).should.throw();
|
275 | });
|
276 |
|
277 | });
|
278 |
|
279 | describe('#fromString', function() {
|
280 |
|
281 | it('should parse this known valid public key', function() {
|
282 | var pk = PublicKey.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
|
283 | pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
284 | pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
|
285 | });
|
286 |
|
287 | });
|
288 |
|
289 | describe('#fromX', function() {
|
290 |
|
291 | it('should create this known public key', function() {
|
292 | var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
|
293 | var pk = PublicKey.fromX(true, x);
|
294 | pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
295 | pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
|
296 | });
|
297 |
|
298 |
|
299 | it('should error because odd was not included as a param', function() {
|
300 | var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
|
301 | (function() {
|
302 | return PublicKey.fromX(null, x);
|
303 | }).should.throw('Must specify whether y is odd or not (true or false)');
|
304 | });
|
305 |
|
306 | });
|
307 |
|
308 | describe('#toBuffer', function() {
|
309 |
|
310 | it('should return this compressed DER format', function() {
|
311 | var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
|
312 | var pk = PublicKey.fromX(true, x);
|
313 | pk.toBuffer().toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
314 | });
|
315 |
|
316 | it('should return this uncompressed DER format', function() {
|
317 | var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
|
318 | var pk = PublicKey.fromX(true, x);
|
319 | pk.toBuffer().toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
320 | });
|
321 |
|
322 | });
|
323 |
|
324 | describe('#toDER', function() {
|
325 |
|
326 | it('should return this compressed DER format', function() {
|
327 | var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
|
328 | var pk = PublicKey.fromX(true, x);
|
329 | pk.toDER().toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
330 | });
|
331 |
|
332 | it('should return this uncompressed DER format', function() {
|
333 | var pk = PublicKey.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
|
334 | pk.toDER().toString('hex').should.equal('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
|
335 | });
|
336 |
|
337 | });
|
338 |
|
339 | describe('#toString', function() {
|
340 |
|
341 | it('should print this known public key', function() {
|
342 | var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a';
|
343 | var pk = PublicKey.fromString(hex);
|
344 | pk.toString().should.equal(hex);
|
345 | });
|
346 |
|
347 | });
|
348 |
|
349 | describe('#inspect', function() {
|
350 |
|
351 | it('should output known uncompressed pubkey for console', function() {
|
352 | var pubkey = PublicKey.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
|
353 | pubkey.inspect().should.equal('<PublicKey: 041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341, uncompressed>');
|
354 | });
|
355 |
|
356 | it('should output known compressed pubkey for console', function() {
|
357 | var pubkey = PublicKey.fromString('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
|
358 | pubkey.inspect().should.equal('<PublicKey: 031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a>');
|
359 | });
|
360 |
|
361 | it('should output known compressed pubkey with network for console', function() {
|
362 | var privkey = PrivateKey.fromWIF('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m');
|
363 | var pubkey = new PublicKey(privkey);
|
364 | pubkey.inspect().should.equal('<PublicKey: 03c87bd0e162f26969da8509cafcb7b8c8d202af30b928c582e263dd13ee9a9781>');
|
365 | });
|
366 |
|
367 | });
|
368 |
|
369 | describe('#validate', function() {
|
370 |
|
371 | it('should not have an error if pubkey is valid', function() {
|
372 | var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a';
|
373 | expect(function() {
|
374 | return PublicKey.fromString(hex);
|
375 | }).to.not.throw();
|
376 | });
|
377 |
|
378 | it('should throw an error if pubkey is invalid', function() {
|
379 | var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a0000000000000000000000000000000000000000000000000000000000000000';
|
380 | (function() {
|
381 | return PublicKey.fromString(hex);
|
382 | }).should.throw('Invalid y value for curve.');
|
383 | });
|
384 |
|
385 | it('should throw an error if pubkey is invalid', function() {
|
386 | var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a00000000000000000000000000000000000000000000000000000000000000FF';
|
387 | (function() {
|
388 | return PublicKey.fromString(hex);
|
389 | }).should.throw('Invalid y value for curve.');
|
390 | });
|
391 |
|
392 | it('should throw an error if pubkey is infinity', function() {
|
393 | (function() {
|
394 | return new PublicKey(Point.getG().mul(Point.getN()));
|
395 | }).should.throw('Point cannot be equal to Infinity');
|
396 | });
|
397 |
|
398 | });
|
399 | });
|