@did-btcr2/keypair 0.9.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/pair.js +45 -51
- package/dist/cjs/pair.js.map +1 -1
- package/dist/cjs/public.js +31 -141
- package/dist/cjs/public.js.map +1 -1
- package/dist/cjs/secret.js +63 -81
- package/dist/cjs/secret.js.map +1 -1
- package/dist/esm/pair.js +45 -51
- package/dist/esm/pair.js.map +1 -1
- package/dist/esm/public.js +31 -141
- package/dist/esm/public.js.map +1 -1
- package/dist/esm/secret.js +63 -81
- package/dist/esm/secret.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/pair.d.ts +19 -4
- package/dist/types/pair.d.ts.map +1 -1
- package/dist/types/public.d.ts +9 -48
- package/dist/types/public.d.ts.map +1 -1
- package/dist/types/secret.d.ts +21 -25
- package/dist/types/secret.d.ts.map +1 -1
- package/dist/types/types.d.ts +3 -2
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/pair.ts +52 -57
- package/src/public.ts +37 -184
- package/src/secret.ts +70 -116
- package/src/types.ts +2 -2
package/dist/cjs/pair.js
CHANGED
|
@@ -7,22 +7,8 @@ import { Secp256k1SecretKey } from './secret.js';
|
|
|
7
7
|
* @type {SchnorrKeyPair}
|
|
8
8
|
*/
|
|
9
9
|
export class SchnorrKeyPair {
|
|
10
|
-
/**
|
|
11
|
-
* The secret key objec
|
|
12
|
-
*/
|
|
13
10
|
#secretKey;
|
|
14
|
-
/**
|
|
15
|
-
* The public key object
|
|
16
|
-
*/ ;
|
|
17
11
|
#publicKey;
|
|
18
|
-
/**
|
|
19
|
-
* The public key in multibase forma
|
|
20
|
-
*/
|
|
21
|
-
#publicKeyMultibase;
|
|
22
|
-
/**
|
|
23
|
-
* The secret key in multibase forma
|
|
24
|
-
*/
|
|
25
|
-
#secretKeyMultibase;
|
|
26
12
|
/**
|
|
27
13
|
* Creates an instance of Keys. Must provide a at least a secret key.
|
|
28
14
|
* Can optionally provide both a secret and public key, but must be a valid pair.
|
|
@@ -54,8 +40,13 @@ export class SchnorrKeyPair {
|
|
|
54
40
|
else {
|
|
55
41
|
this.#publicKey = this.#secretKey.computePublicKey();
|
|
56
42
|
}
|
|
57
|
-
|
|
58
|
-
|
|
43
|
+
// Validate that an explicitly provided public key matches the secret key
|
|
44
|
+
if (this.#secretKey && params.publicKey) {
|
|
45
|
+
const derived = this.#secretKey.computePublicKey();
|
|
46
|
+
if (!this.#publicKey.equals(derived)) {
|
|
47
|
+
throw new KeyPairError('Public key does not match secret key', 'CONSTRUCTOR_ERROR');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
59
50
|
}
|
|
60
51
|
/**
|
|
61
52
|
* Get the Secp256k1SecretKey.
|
|
@@ -81,26 +72,27 @@ export class SchnorrKeyPair {
|
|
|
81
72
|
* @throws {KeyPairError} If the public key is not a valid pair with the secret key.
|
|
82
73
|
*/
|
|
83
74
|
set publicKey(publicKey) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (!
|
|
87
|
-
throw new KeyPairError('
|
|
75
|
+
if (this.#secretKey) {
|
|
76
|
+
const derived = this.#secretKey.computePublicKey();
|
|
77
|
+
if (!publicKey.equals(derived)) {
|
|
78
|
+
throw new KeyPairError('Public key does not match secret key', 'PUBLIC_KEY_ERROR');
|
|
88
79
|
}
|
|
89
|
-
const cPk = this.secretKey.computePublicKey();
|
|
90
|
-
if (!publicKey.equals(cPk))
|
|
91
|
-
throw new KeyPairError('Public key is not a valid pair with the secret key', 'PUBLIC_KEY_ERROR');
|
|
92
80
|
}
|
|
93
81
|
this.#publicKey = publicKey;
|
|
94
|
-
this.#publicKeyMultibase = publicKey.multibase.encoded;
|
|
95
|
-
this.#secretKeyMultibase = this.#secretKey ? this.#secretKey.multibase : '';
|
|
96
82
|
}
|
|
97
83
|
/**
|
|
98
84
|
* Get the CompressedSecp256k1PublicKey.
|
|
99
85
|
* @returns {CompressedSecp256k1PublicKey} The CompressedSecp256k1PublicKey object
|
|
100
86
|
*/
|
|
101
87
|
get publicKey() {
|
|
102
|
-
|
|
103
|
-
|
|
88
|
+
return this.#publicKey;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Whether this key pair contains a secret key.
|
|
92
|
+
* @returns {boolean} True if the secret key is present.
|
|
93
|
+
*/
|
|
94
|
+
get hasSecretKey() {
|
|
95
|
+
return !!this.#secretKey;
|
|
104
96
|
}
|
|
105
97
|
/**
|
|
106
98
|
* Get the `raw` bytes of each key in the SchnorrKeyPair.
|
|
@@ -109,7 +101,7 @@ export class SchnorrKeyPair {
|
|
|
109
101
|
get raw() {
|
|
110
102
|
return {
|
|
111
103
|
public: this.publicKey.x,
|
|
112
|
-
secret: this
|
|
104
|
+
secret: this.#secretKey ? this.#secretKey.bytes : undefined
|
|
113
105
|
};
|
|
114
106
|
}
|
|
115
107
|
/**
|
|
@@ -119,7 +111,7 @@ export class SchnorrKeyPair {
|
|
|
119
111
|
get hex() {
|
|
120
112
|
return {
|
|
121
113
|
public: this.publicKey.hex,
|
|
122
|
-
secret: this.#secretKey ? this
|
|
114
|
+
secret: this.#secretKey ? this.#secretKey.hex : undefined
|
|
123
115
|
};
|
|
124
116
|
}
|
|
125
117
|
/**
|
|
@@ -128,20 +120,36 @@ export class SchnorrKeyPair {
|
|
|
128
120
|
*/
|
|
129
121
|
get multibase() {
|
|
130
122
|
return {
|
|
131
|
-
publicKeyMultibase: this.#
|
|
132
|
-
secretKeyMultibase: this.#
|
|
123
|
+
publicKeyMultibase: this.#publicKey.multibase.encoded,
|
|
124
|
+
secretKeyMultibase: this.#secretKey ? this.#secretKey.multibase : '',
|
|
133
125
|
};
|
|
134
126
|
}
|
|
135
127
|
/**
|
|
136
|
-
* JSON representation
|
|
137
|
-
*
|
|
128
|
+
* Safe JSON representation. Only includes the public key.
|
|
129
|
+
* Called implicitly by JSON.stringify(). Use exportJSON() for full serialization.
|
|
130
|
+
* @returns {{ publicKey: PublicKeyObject }} The JSON representation of the public key
|
|
138
131
|
*/
|
|
139
|
-
|
|
132
|
+
toJSON() {
|
|
133
|
+
return { publicKey: this.publicKey.toJSON() };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Exports the full key pair as a JSON object. Contains sensitive material.
|
|
137
|
+
* @returns {SchnorrKeyPairObject} The key pair as a JSON object
|
|
138
|
+
* @throws {KeyPairError} If the secret key is not available
|
|
139
|
+
*/
|
|
140
|
+
exportJSON() {
|
|
141
|
+
if (!this.#secretKey) {
|
|
142
|
+
throw new KeyPairError('Cannot export: secret key required. Use publicKey.toJSON() for public-key-only pairs.', 'SERIALIZE_ERROR');
|
|
143
|
+
}
|
|
140
144
|
return {
|
|
141
|
-
secretKey: this
|
|
142
|
-
publicKey: this.publicKey.
|
|
145
|
+
secretKey: this.#secretKey.exportJSON(),
|
|
146
|
+
publicKey: this.publicKey.toJSON()
|
|
143
147
|
};
|
|
144
148
|
}
|
|
149
|
+
/** @override Prevents secret material from appearing in Node.js inspect */
|
|
150
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
151
|
+
return `[SchnorrKeyPair ${this.publicKey.hex}]`;
|
|
152
|
+
}
|
|
145
153
|
/**
|
|
146
154
|
* Static method creates a new Keys from a JSON object.
|
|
147
155
|
* @param {SchnorrKeyPairObject} keys The JSON object to initialize the Keys.
|
|
@@ -200,21 +208,7 @@ export class SchnorrKeyPair {
|
|
|
200
208
|
* @returns {boolean} True if the public key and secret key are equal, false otherwise.
|
|
201
209
|
*/
|
|
202
210
|
static equals(kp, otherKp) {
|
|
203
|
-
|
|
204
|
-
const pk = kp.publicKey;
|
|
205
|
-
const otherPk = otherKp.publicKey;
|
|
206
|
-
// If publicKeys present, use to compare as hex strings.
|
|
207
|
-
if (pk && otherPk) {
|
|
208
|
-
return pk.hex === otherPk.hex;
|
|
209
|
-
}
|
|
210
|
-
// Deconstruct the secret keys from the key pairs
|
|
211
|
-
const sk = kp.secretKey;
|
|
212
|
-
const otherSk = otherKp.secretKey;
|
|
213
|
-
if (sk && otherSk) {
|
|
214
|
-
// Get the public key hex strings for both key pair publicKeys
|
|
215
|
-
return sk.hex === otherSk.hex;
|
|
216
|
-
}
|
|
217
|
-
throw new KeyPairError('Cannot compare invalid key pair(s)', 'KEYPAIR_EQUALS_ERROR');
|
|
211
|
+
return kp.publicKey.equals(otherKp.publicKey);
|
|
218
212
|
}
|
|
219
213
|
/**
|
|
220
214
|
* Static method to generate a new random SchnorrKeyPair instance.
|
package/dist/cjs/pair.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pair.js","sourceRoot":"","sources":["../../src/pair.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,YAAY,
|
|
1
|
+
{"version":3,"file":"pair.js","sourceRoot":"","sources":["../../src/pair.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/public.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
1
|
+
import { PublicKeyError } from '@did-btcr2/common';
|
|
2
|
+
/** Fixed public key header bytes per the Data Integrity BIP340 Cryptosuite spec: [0xe7, 0x01] */
|
|
3
|
+
export const BIP340_PUBLIC_KEY_MULTIBASE_PREFIX = new Uint8Array([0xe7, 0x01]);
|
|
4
|
+
import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
|
|
5
|
+
import { timingSafeEqual } from 'crypto';
|
|
6
|
+
import { base58 } from '@scure/base';
|
|
6
7
|
/**
|
|
7
8
|
* Encapsulates a secp256k1 public key compliant to BIP-340 BIP schnorr signature scheme.
|
|
8
9
|
* Provides get methods for different formats (compressed, x-only, multibase).
|
|
@@ -11,9 +12,13 @@ import { Secp256k1SecretKey } from './secret.js';
|
|
|
11
12
|
* @type {CompressedSecp256k1PublicKey}
|
|
12
13
|
*/
|
|
13
14
|
export class CompressedSecp256k1PublicKey {
|
|
14
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* The public key bytes
|
|
17
|
+
**/
|
|
15
18
|
#bytes;
|
|
16
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* The public key as a MultibaseObject
|
|
21
|
+
*/
|
|
17
22
|
#multibase = {
|
|
18
23
|
prefix: BIP340_PUBLIC_KEY_MULTIBASE_PREFIX,
|
|
19
24
|
key: [],
|
|
@@ -34,11 +39,11 @@ export class CompressedSecp256k1PublicKey {
|
|
|
34
39
|
throw new PublicKeyError('Invalid argument: byte length must be 33 (compressed)', 'CONSTRUCTOR_ERROR', { keyBytes });
|
|
35
40
|
}
|
|
36
41
|
// Validate the point is on curve and in compressed form
|
|
37
|
-
if (!
|
|
42
|
+
if (!secp256k1.utils.isValidPublicKey(keyBytes)) {
|
|
38
43
|
throw new PublicKeyError('Invalid argument: not a valid secp256k1 compressed point', 'CONSTRUCTOR_ERROR', { keyBytes });
|
|
39
44
|
}
|
|
40
|
-
//
|
|
41
|
-
this.#bytes = keyBytes;
|
|
45
|
+
// Defensive copy — caller cannot mutate internal state
|
|
46
|
+
this.#bytes = new Uint8Array(keyBytes);
|
|
42
47
|
// Set multibase
|
|
43
48
|
this.#multibase.encoded = this.encode();
|
|
44
49
|
this.#multibase.key = [...this.#multibase.prefix, ...this.compressed];
|
|
@@ -57,8 +62,7 @@ export class CompressedSecp256k1PublicKey {
|
|
|
57
62
|
* @returns {Uint8Array} The 65-byte uncompressed public key (0x04, x, y).
|
|
58
63
|
*/
|
|
59
64
|
get uncompressed() {
|
|
60
|
-
|
|
61
|
-
return uncompressed;
|
|
65
|
+
return secp256k1.Point.fromBytes(this.compressed).toBytes(false);
|
|
62
66
|
}
|
|
63
67
|
/**
|
|
64
68
|
* X-only (32-byte) view of the public key per BIP-340.
|
|
@@ -107,8 +111,11 @@ export class CompressedSecp256k1PublicKey {
|
|
|
107
111
|
* @returns {MultibaseObject} An object containing the multibase bytes, address and prefix.
|
|
108
112
|
*/
|
|
109
113
|
get multibase() {
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
return {
|
|
115
|
+
prefix: new Uint8Array(this.#multibase.prefix),
|
|
116
|
+
key: [...this.#multibase.key],
|
|
117
|
+
encoded: this.#multibase.encoded
|
|
118
|
+
};
|
|
112
119
|
}
|
|
113
120
|
/**
|
|
114
121
|
* Returns the raw public key as a hex string.
|
|
@@ -140,40 +147,17 @@ export class CompressedSecp256k1PublicKey {
|
|
|
140
147
|
* @returns {KeyBytes} The decoded public key: prefix and public key bytes
|
|
141
148
|
*/
|
|
142
149
|
decode() {
|
|
143
|
-
|
|
144
|
-
const decoded = base58btc.decode(this.multibase.encoded);
|
|
145
|
-
// If the public key bytes are not 35 bytes, throw an error
|
|
146
|
-
if (decoded.length !== 35) {
|
|
147
|
-
throw new PublicKeyError('Invalid argument: must be 35 byte publicKeyMultibase', 'DECODE_MULTIBASE_ERROR');
|
|
148
|
-
}
|
|
149
|
-
// Grab the prefix bytes
|
|
150
|
-
const prefix = decoded.slice(0, 2);
|
|
151
|
-
// Compute the prefix hash
|
|
152
|
-
const prefixHash = Buffer.from(sha256(prefix)).toString('hex');
|
|
153
|
-
// If the prefix hash does not equal the BIP340 prefix hash, throw an error
|
|
154
|
-
if (prefixHash !== BIP340_PUBLIC_KEY_MULTIBASE_PREFIX_HASH) {
|
|
155
|
-
throw new PublicKeyError(`Invalid prefix: malformed multibase prefix ${prefix}`, 'DECODE_MULTIBASE_ERROR');
|
|
156
|
-
}
|
|
157
|
-
// Return the decoded public key bytes
|
|
158
|
-
return decoded;
|
|
150
|
+
return base58.decode(this.multibase.encoded.slice(1));
|
|
159
151
|
}
|
|
160
152
|
/**
|
|
161
153
|
* Encodes compressed secp256k1 public key from bytes to BIP340 multibase format.
|
|
162
154
|
* @returns {string} The public key encoded in base-58-btc multibase format.
|
|
163
155
|
*/
|
|
164
156
|
encode() {
|
|
165
|
-
// Convert public key bytes to an array
|
|
166
157
|
const pk = Array.from(this.compressed);
|
|
167
|
-
// Ensure the public key is 33-byte secp256k1 compressed public key
|
|
168
|
-
if (pk.length !== 33) {
|
|
169
|
-
throw new PublicKeyError('Invalid argument: must be 33-byte (compressed) public key', 'ENCODE_MULTIBASE_ERROR');
|
|
170
|
-
}
|
|
171
|
-
// Convert prefix to an array
|
|
172
158
|
const publicKeyMultibase = Array.from(BIP340_PUBLIC_KEY_MULTIBASE_PREFIX);
|
|
173
|
-
// Push the public key bytes at the end of the prefix
|
|
174
159
|
publicKeyMultibase.push(...pk);
|
|
175
|
-
|
|
176
|
-
return base58btc.encode(Uint8Array.from(publicKeyMultibase));
|
|
160
|
+
return 'z' + base58.encode(Uint8Array.from(publicKeyMultibase));
|
|
177
161
|
}
|
|
178
162
|
/**
|
|
179
163
|
* Verify a signature using schnorr or ecdsa.
|
|
@@ -184,29 +168,30 @@ export class CompressedSecp256k1PublicKey {
|
|
|
184
168
|
* @returns {boolean} If the signature is valid against the public key.
|
|
185
169
|
*/
|
|
186
170
|
verify(signature, data, opts) {
|
|
171
|
+
// Default to schnorr scheme
|
|
187
172
|
opts ??= { scheme: 'schnorr' };
|
|
188
|
-
// Verify the signature depending on the scheme and return the result
|
|
189
173
|
if (opts.scheme === 'ecdsa') {
|
|
190
|
-
return
|
|
174
|
+
return secp256k1.verify(signature, data, this.compressed);
|
|
191
175
|
}
|
|
192
176
|
else if (opts.scheme === 'schnorr') {
|
|
193
|
-
return
|
|
177
|
+
return schnorr.verify(signature, data, this.x);
|
|
194
178
|
}
|
|
179
|
+
// If scheme is neither ecdsa nor schnorr, throw an error
|
|
195
180
|
throw new PublicKeyError(`Invalid scheme: ${opts.scheme}.`, 'VERIFY_SIGNATURE_ERROR', opts);
|
|
196
181
|
}
|
|
197
182
|
/**
|
|
198
183
|
* Compares this public key to another public key.
|
|
199
|
-
* @param {
|
|
184
|
+
* @param {PublicKey} other The other public key to compare
|
|
200
185
|
* @returns {boolean} True if the public keys are equal, false otherwise.
|
|
201
186
|
*/
|
|
202
187
|
equals(other) {
|
|
203
|
-
return this.
|
|
188
|
+
return timingSafeEqual(this.compressed, other.compressed);
|
|
204
189
|
}
|
|
205
190
|
/**
|
|
206
191
|
* JSON representation of a CompressedSecp256k1PublicKey object.
|
|
207
192
|
* @returns {PublicKeyObject} The CompressedSecp256k1PublicKey as a JSON object.
|
|
208
193
|
*/
|
|
209
|
-
|
|
194
|
+
toJSON() {
|
|
210
195
|
return {
|
|
211
196
|
hex: this.hex,
|
|
212
197
|
multibase: this.multibase,
|
|
@@ -217,61 +202,6 @@ export class CompressedSecp256k1PublicKey {
|
|
|
217
202
|
},
|
|
218
203
|
};
|
|
219
204
|
}
|
|
220
|
-
/**
|
|
221
|
-
* Computes modular exponentiation: (base^exp) % mod.
|
|
222
|
-
* Used for computing modular square roots.
|
|
223
|
-
* @param {bigint} base The base value
|
|
224
|
-
* @param {bigint} exp The exponent value
|
|
225
|
-
* @param {bigint} mod The modulus value
|
|
226
|
-
* @returns {bigint} The result of the modular exponentiation
|
|
227
|
-
*/
|
|
228
|
-
modPow(base, exp, mod) {
|
|
229
|
-
let result = 1n;
|
|
230
|
-
while (exp > 0n) {
|
|
231
|
-
if (exp & 1n)
|
|
232
|
-
result = (result * base) % mod;
|
|
233
|
-
base = (base * base) % mod;
|
|
234
|
-
exp >>= 1n;
|
|
235
|
-
}
|
|
236
|
-
return result;
|
|
237
|
-
}
|
|
238
|
-
;
|
|
239
|
-
/**
|
|
240
|
-
* Computes `sqrt(a) mod p` using Tonelli-Shanks algorithm.
|
|
241
|
-
* This finds `y` such that `y^2 ≡ a mod p`.
|
|
242
|
-
* @param {bigint} a The value to find the square root of
|
|
243
|
-
* @param {bigint} p The prime modulus
|
|
244
|
-
* @returns {bigint} The square root of `a` mod `p`
|
|
245
|
-
*/
|
|
246
|
-
sqrtMod(a, p) {
|
|
247
|
-
return this.modPow(a, (p + 1n) >> 2n, p);
|
|
248
|
-
}
|
|
249
|
-
;
|
|
250
|
-
/**
|
|
251
|
-
* Lifts a 32-byte x-only coordinate into a full secp256k1 point (x, y).
|
|
252
|
-
* @param xBytes 32-byte x-coordinate
|
|
253
|
-
* @returns {Uint8Array} 65-byte uncompressed public key (starts with `0x04`)
|
|
254
|
-
*/
|
|
255
|
-
liftX() {
|
|
256
|
-
// Ensure x-coordinate is 32 bytes
|
|
257
|
-
if (this.x.length !== 32) {
|
|
258
|
-
throw new PublicKeyError('Invalid argument: x-coordinate length must be 32 bytes', 'LIFT_X_ERROR');
|
|
259
|
-
}
|
|
260
|
-
// Convert x from Uint8Array → BigInt
|
|
261
|
-
const x = BigInt('0x' + Buffer.from(this.x).toString('hex'));
|
|
262
|
-
if (x <= 0n || x >= CURVE.p) {
|
|
263
|
-
throw new PublicKeyError('Invalid conversion: x out of range as BigInt', 'LIFT_X_ERROR');
|
|
264
|
-
}
|
|
265
|
-
// Compute y² = x³ + 7 mod p
|
|
266
|
-
const ySquared = BigInt((x ** 3n + CURVE.b) % CURVE.p);
|
|
267
|
-
// Compute y (do not enforce parity)
|
|
268
|
-
const y = this.sqrtMod(ySquared, CURVE.p);
|
|
269
|
-
// Convert x and y to Uint8Array
|
|
270
|
-
const yBytes = Buffer.from(y.toString(16).padStart(64, '0'), 'hex');
|
|
271
|
-
// Return 65-byte uncompressed public key: `0x04 || x || y`
|
|
272
|
-
return new Uint8Array(Buffer.concat([Buffer.from([0x04]), Buffer.from(this.x), yBytes]));
|
|
273
|
-
}
|
|
274
|
-
;
|
|
275
205
|
/**
|
|
276
206
|
* Static method to validate a public key.
|
|
277
207
|
* @param {Hex} pk The public key in hex (Uint8Array or string) format.
|
|
@@ -286,53 +216,13 @@ export class CompressedSecp256k1PublicKey {
|
|
|
286
216
|
return false;
|
|
287
217
|
}
|
|
288
218
|
}
|
|
289
|
-
/**
|
|
290
|
-
* Returns the point of the public key.
|
|
291
|
-
* @param {Hex} pk The public key in hex (Uint8Array or string) format.
|
|
292
|
-
* @returns {Point} The point of the public key.
|
|
293
|
-
* @throws {PublicKeyError} If the public key is not a valid hex string or byte array.
|
|
294
|
-
*/
|
|
295
|
-
static point(pk) {
|
|
296
|
-
// If the public key is a hex string, convert it to a CompressedSecp256k1PublicKey object and return the point
|
|
297
|
-
if (typeof pk === 'string' && /^[0-9a-fA-F]+$/.test(pk)) {
|
|
298
|
-
const publicKey = new CompressedSecp256k1PublicKey(Buffer.from(pk, 'hex'));
|
|
299
|
-
return publicKey.point;
|
|
300
|
-
}
|
|
301
|
-
// If the public key is a byte array or ArrayBuffer, convert it to a CompressedSecp256k1PublicKey object and return the point
|
|
302
|
-
if (pk instanceof Uint8Array || ArrayBuffer.isView(pk)) {
|
|
303
|
-
const publicKey = new CompressedSecp256k1PublicKey(pk);
|
|
304
|
-
return publicKey.point;
|
|
305
|
-
}
|
|
306
|
-
// If the public key is neither a hex string nor a byte array, throw an error
|
|
307
|
-
throw new PublicKeyError('Invalid publicKey: must be a hex string or byte array', 'POINT_ERROR', { publicKey: pk });
|
|
308
|
-
}
|
|
309
219
|
/**
|
|
310
220
|
* Creates a CompressedSecp256k1PublicKey object from a JSON representation.
|
|
311
221
|
* @param {PublicKeyObject} json The JSON object to initialize the CompressedSecp256k1PublicKey.
|
|
312
222
|
* @returns {CompressedSecp256k1PublicKey} The initialized CompressedSecp256k1PublicKey object.
|
|
313
223
|
*/
|
|
314
224
|
static fromJSON(json) {
|
|
315
|
-
json.point.
|
|
316
|
-
return new CompressedSecp256k1PublicKey(Uint8Array.from(json.point.x));
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Computes the deterministic public key for a given secret key.
|
|
320
|
-
* @param {Secp256k1SecretKey | KeyBytes} sk The Secp256k1SecretKey object or the secret key bytes
|
|
321
|
-
* @returns {CompressedSecp256k1PublicKey} A new CompressedSecp256k1PublicKey object
|
|
322
|
-
*/
|
|
323
|
-
static fromSecretKey(sk) {
|
|
324
|
-
// If the secret key is a Secp256k1SecretKey object, get the raw bytes else use the bytes
|
|
325
|
-
const bytes = sk instanceof Secp256k1SecretKey ? sk.bytes : sk;
|
|
326
|
-
// Throw error if the secret key is not 32 bytes
|
|
327
|
-
if (bytes.length !== 32) {
|
|
328
|
-
throw new PublicKeyError('Invalid arg: must be 32 byte secret key', 'FROM_SECRET_KEY_ERROR');
|
|
329
|
-
}
|
|
330
|
-
// Compute the public key from the secret key
|
|
331
|
-
const secret = sk instanceof Secp256k1SecretKey
|
|
332
|
-
? sk
|
|
333
|
-
: new Secp256k1SecretKey(sk);
|
|
334
|
-
// Return a new CompressedSecp256k1PublicKey object
|
|
335
|
-
return secret.computePublicKey();
|
|
225
|
+
return new CompressedSecp256k1PublicKey(Uint8Array.from([json.point.parity, ...json.point.x]));
|
|
336
226
|
}
|
|
337
227
|
}
|
|
338
228
|
//# sourceMappingURL=public.js.map
|
package/dist/cjs/public.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.js","sourceRoot":"","sources":["../../src/public.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"public.js","sourceRoot":"","sources":["../../src/public.ts"],"names":[],"mappings":"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"}
|