-
Notifications
You must be signed in to change notification settings - Fork 1
/
elliptic.ts
140 lines (120 loc) · 3.52 KB
/
elliptic.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import BN from 'bn.js';
import { ec } from 'elliptic';
import type { curve } from 'elliptic';
import { toBuffer } from './buffer';
import { ripemd160 } from './hash';
export const secp256k1 = new ec('secp256k1');
/**
* Get a Uint8Array for a public key.
*
* @param {ec.KeyPair} keyPair
* @param {boolean} compact
* @return {Uint8Array}
*/
export const publicToBuffer = (keyPair: ec.KeyPair, compact: boolean): Uint8Array => {
return toBuffer(keyPair.getPublic(compact, 'array'));
};
/**
* Get a Uint8Array for a private key.
*
* @param {ec.KeyPair} keyPair
* @return {Uint8Array}
*/
export const privateToBuffer = (keyPair: ec.KeyPair): Uint8Array => {
return toBuffer(keyPair.getPrivate().toArray());
};
/**
* Get a Uint8Array for a point.
*
* @param {curve.base.BasePoint} point
* @param {boolean} compact
* @return {Uint8Array}
*/
export const pointToBuffer = (point: curve.base.BasePoint, compact: boolean): Uint8Array => {
return toBuffer(point.encode('array', compact));
};
/**
* Compress a public key. This function takes both compressed and uncompressed public keys, and always returns the
* compressed variant.
*
* @param publicKey
*/
export const compressPublicKey = (publicKey: Uint8Array): Uint8Array => {
return publicToBuffer(secp256k1.keyFromPublic(publicKey), true);
};
/**
* Decompress a public key. This function takes both compressed and uncompressed public keys, and always returns the
* decompressed variant.
*
* @param {Uint8Array} publicKey
* @return {Uint8Array}
*/
export const decompressPublicKey = (publicKey: Uint8Array): Uint8Array => {
return publicToBuffer(secp256k1.keyFromPublic(publicKey), false);
};
/**
* Get the public key for a private key.
*
* @param {Uint8Array} privateKey
* @return {Uint8Array}
*/
export const getPublicKey = (privateKey: Uint8Array): Uint8Array => {
return publicToBuffer(secp256k1.keyFromPrivate(privateKey), true);
};
/**
* Get the fingerprint from a public key.
*
* @param {Uint8Array} publicKey
* @return {number}
*/
export const getFingerprint = (publicKey: Uint8Array): number => {
const fingerprint = ripemd160(publicKey).slice(0, 4);
const dataView = new DataView(fingerprint.buffer);
return dataView.getUint32(0);
};
/**
* Add a tweak to a private key. This function throws if the result is invalid.
*
* @param {Uint8Array} privateKey
* @param {Uint8Array} tweak
* @return {Uint8Array}
*/
export const privateAdd = (privateKey: Uint8Array, tweak: Uint8Array): Uint8Array => {
const n = secp256k1.n!;
const bn = new BN(tweak);
if (bn.cmp(n) >= 0) {
throw new Error('Invalid tweak');
}
const add = bn.add(new BN(privateKey));
if (add.cmp(n) >= 0) {
const tweaked = add.sub(n);
if (tweaked.isZero()) {
throw new Error('Invalid resulting number');
}
return toBuffer(tweaked.toArray());
}
if (add.isZero()) {
throw new Error('Invalid resulting number');
}
return toBuffer(add.toArray());
};
/**
* Add a point to a public key. This function throws if the result is invalid.
*
* @param {Uint8Array} publicKey
* @param {Uint8Array} tweak
* @return {Uint8Array}
*/
export const publicAdd = (publicKey: Uint8Array, tweak: Uint8Array): Uint8Array => {
const n = secp256k1.n!;
const bn = new BN(tweak);
if (bn.cmp(n) >= 0) {
throw new Error('Invalid tweak');
}
const keyPair = secp256k1.keyFromPublic(publicKey);
const point = keyPair.getPublic().add(secp256k1.g.mul(bn));
if (point.isInfinity()) {
throw new Error('Point is at infinity');
}
return pointToBuffer(point, true);
};