@lightsparkdev/core 0.3.11 → 1.0.1
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/CHANGELOG.md +13 -0
- package/dist/index.cjs +138 -8
- package/dist/index.d.ts +41 -3
- package/dist/index.js +127 -8
- package/package.json +6 -4
- package/src/crypto/NodeKeyCache.ts +45 -10
- package/src/crypto/SigningKey.ts +54 -0
- package/src/crypto/index.ts +2 -0
- package/src/crypto/types.ts +4 -0
- package/src/requester/Requester.ts +3 -2
- package/src/utils/createHash.ts +12 -0
- package/src/utils/errors.ts +36 -0
- package/src/utils/hex.ts +15 -0
- package/src/utils/index.ts +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @lightsparkdev/core
|
|
2
2
|
|
|
3
|
+
## 1.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 808c77a: Consolidate some imports to lightspark-sdk and core
|
|
8
|
+
|
|
9
|
+
## 1.0.0
|
|
10
|
+
|
|
11
|
+
### Major Changes
|
|
12
|
+
|
|
13
|
+
- 1f00a50: Change SigningKey hashing method depending on environment
|
|
14
|
+
BREAKING: NodeKeyCaches loadKey now requires signingKeyType as a parameter
|
|
15
|
+
|
|
3
16
|
## 0.3.11
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -36,14 +36,25 @@ __export(src_exports, {
|
|
|
36
36
|
LightsparkException: () => LightsparkException_default,
|
|
37
37
|
LightsparkSigningException: () => LightsparkSigningException_default,
|
|
38
38
|
NodeKeyCache: () => NodeKeyCache_default,
|
|
39
|
+
RSASigningKey: () => RSASigningKey,
|
|
39
40
|
Requester: () => Requester_default,
|
|
41
|
+
Secp256k1SigningKey: () => Secp256k1SigningKey,
|
|
40
42
|
ServerEnvironment: () => ServerEnvironment_default,
|
|
43
|
+
SigningKey: () => SigningKey,
|
|
44
|
+
SigningKeyType: () => SigningKeyType,
|
|
41
45
|
StubAuthProvider: () => StubAuthProvider,
|
|
42
46
|
apiDomainForEnvironment: () => apiDomainForEnvironment,
|
|
43
47
|
b64decode: () => b64decode,
|
|
44
48
|
b64encode: () => b64encode,
|
|
49
|
+
bytesToHex: () => bytesToHex,
|
|
45
50
|
convertCurrencyAmount: () => convertCurrencyAmount,
|
|
51
|
+
createSha256Hash: () => createSha256Hash,
|
|
52
|
+
getErrorMsg: () => getErrorMsg,
|
|
53
|
+
hexToBytes: () => hexToBytes,
|
|
46
54
|
isBrowser: () => isBrowser,
|
|
55
|
+
isError: () => isError,
|
|
56
|
+
isErrorMsg: () => isErrorMsg,
|
|
57
|
+
isErrorWithMessage: () => isErrorWithMessage,
|
|
47
58
|
isNode: () => isNode,
|
|
48
59
|
isType: () => isType,
|
|
49
60
|
urlsafe_b64decode: () => urlsafe_b64decode
|
|
@@ -348,6 +359,50 @@ var KeyOrAlias = {
|
|
|
348
359
|
|
|
349
360
|
// src/crypto/NodeKeyCache.ts
|
|
350
361
|
var import_auto_bind = __toESM(require("auto-bind"), 1);
|
|
362
|
+
|
|
363
|
+
// src/crypto/SigningKey.ts
|
|
364
|
+
var import_secp256k1 = __toESM(require("secp256k1"), 1);
|
|
365
|
+
function isAlias(key) {
|
|
366
|
+
return "alias" in key;
|
|
367
|
+
}
|
|
368
|
+
var SigningKey = class {
|
|
369
|
+
type;
|
|
370
|
+
constructor(type) {
|
|
371
|
+
this.type = type;
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
var RSASigningKey = class extends SigningKey {
|
|
375
|
+
constructor(privateKey, cryptoImpl) {
|
|
376
|
+
super("RSASigningKey" /* RSASigningKey */);
|
|
377
|
+
this.privateKey = privateKey;
|
|
378
|
+
this.cryptoImpl = cryptoImpl;
|
|
379
|
+
}
|
|
380
|
+
async sign(data) {
|
|
381
|
+
const key = isAlias(this.privateKey) ? this.privateKey.alias : this.privateKey;
|
|
382
|
+
return this.cryptoImpl.sign(key, data);
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
var Secp256k1SigningKey = class extends SigningKey {
|
|
386
|
+
constructor(privateKey) {
|
|
387
|
+
super("Secp256k1SigningKey" /* Secp256k1SigningKey */);
|
|
388
|
+
this.privateKey = privateKey;
|
|
389
|
+
}
|
|
390
|
+
async sign(data) {
|
|
391
|
+
const keyBytes = new Uint8Array(hexToBytes(this.privateKey));
|
|
392
|
+
const hash = await createSha256Hash(data);
|
|
393
|
+
const signResult = import_secp256k1.default.ecdsaSign(hash, keyBytes);
|
|
394
|
+
return signResult.signature;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
// src/crypto/types.ts
|
|
399
|
+
var SigningKeyType = /* @__PURE__ */ ((SigningKeyType2) => {
|
|
400
|
+
SigningKeyType2["RSASigningKey"] = "RSASigningKey";
|
|
401
|
+
SigningKeyType2["Secp256k1SigningKey"] = "Secp256k1SigningKey";
|
|
402
|
+
return SigningKeyType2;
|
|
403
|
+
})(SigningKeyType || {});
|
|
404
|
+
|
|
405
|
+
// src/crypto/NodeKeyCache.ts
|
|
351
406
|
var NodeKeyCache = class {
|
|
352
407
|
constructor(cryptoImpl = DefaultCrypto) {
|
|
353
408
|
this.cryptoImpl = cryptoImpl;
|
|
@@ -355,16 +410,35 @@ var NodeKeyCache = class {
|
|
|
355
410
|
(0, import_auto_bind.default)(this);
|
|
356
411
|
}
|
|
357
412
|
idToKey;
|
|
358
|
-
async loadKey(id, keyOrAlias) {
|
|
413
|
+
async loadKey(id, keyOrAlias, signingKeyType) {
|
|
414
|
+
let signingKey;
|
|
359
415
|
if (keyOrAlias.alias !== void 0) {
|
|
360
|
-
|
|
361
|
-
|
|
416
|
+
switch (signingKeyType) {
|
|
417
|
+
case "RSASigningKey" /* RSASigningKey */:
|
|
418
|
+
signingKey = new RSASigningKey(
|
|
419
|
+
{ alias: keyOrAlias.alias },
|
|
420
|
+
this.cryptoImpl
|
|
421
|
+
);
|
|
422
|
+
break;
|
|
423
|
+
default:
|
|
424
|
+
throw new LightsparkSigningException_default(
|
|
425
|
+
`Aliases are not supported for signing key type ${signingKeyType}`
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
this.idToKey.set(id, signingKey);
|
|
429
|
+
return signingKey;
|
|
362
430
|
}
|
|
363
|
-
const decoded = b64decode(this.stripPemTags(keyOrAlias.key));
|
|
364
431
|
try {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
432
|
+
if (signingKeyType === "Secp256k1SigningKey" /* Secp256k1SigningKey */) {
|
|
433
|
+
signingKey = new Secp256k1SigningKey(keyOrAlias.key);
|
|
434
|
+
} else {
|
|
435
|
+
const decoded = b64decode(this.stripPemTags(keyOrAlias.key));
|
|
436
|
+
const cryptoKeyOrAlias = await this.cryptoImpl.importPrivateSigningKey(decoded);
|
|
437
|
+
const key = typeof cryptoKeyOrAlias === "string" ? { alias: cryptoKeyOrAlias } : cryptoKeyOrAlias;
|
|
438
|
+
signingKey = new RSASigningKey(key, this.cryptoImpl);
|
|
439
|
+
}
|
|
440
|
+
this.idToKey.set(id, signingKey);
|
|
441
|
+
return signingKey;
|
|
368
442
|
} catch (e) {
|
|
369
443
|
console.log("Error importing key: ", e);
|
|
370
444
|
}
|
|
@@ -564,7 +638,7 @@ var Requester = class {
|
|
|
564
638
|
const encodedPayload = new TextEncoderImpl().encode(
|
|
565
639
|
JSON.stringify(payload)
|
|
566
640
|
);
|
|
567
|
-
const signedPayload = await
|
|
641
|
+
const signedPayload = await key.sign(encodedPayload);
|
|
568
642
|
const encodedSignedPayload = b64encode(signedPayload);
|
|
569
643
|
headers["X-Lightspark-Signing"] = JSON.stringify({
|
|
570
644
|
v: "1",
|
|
@@ -591,6 +665,16 @@ var apiDomainForEnvironment = (environment) => {
|
|
|
591
665
|
};
|
|
592
666
|
var ServerEnvironment_default = ServerEnvironment;
|
|
593
667
|
|
|
668
|
+
// src/utils/createHash.ts
|
|
669
|
+
var createSha256Hash = async (data) => {
|
|
670
|
+
if (isBrowser) {
|
|
671
|
+
return new Uint8Array(await window.crypto.subtle.digest("SHA-256", data));
|
|
672
|
+
} else {
|
|
673
|
+
const { createHash } = await import("crypto");
|
|
674
|
+
return createHash("sha256").update(data).digest();
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
|
|
594
678
|
// src/utils/currency.ts
|
|
595
679
|
var CONVERSION_MAP = {
|
|
596
680
|
["BITCOIN" /* BITCOIN */]: {
|
|
@@ -661,6 +745,41 @@ var convertCurrencyAmount = (from, toUnit) => {
|
|
|
661
745
|
};
|
|
662
746
|
};
|
|
663
747
|
|
|
748
|
+
// src/utils/errors.ts
|
|
749
|
+
var isError = (e) => {
|
|
750
|
+
return Boolean(
|
|
751
|
+
typeof e === "object" && e !== null && "name" in e && typeof e.name === "string" && "message" in e && typeof e.message === "string" && "stack" in e && (!e.stack || typeof e.stack === "string")
|
|
752
|
+
);
|
|
753
|
+
};
|
|
754
|
+
var isErrorWithMessage = (e) => {
|
|
755
|
+
return Boolean(
|
|
756
|
+
typeof e === "object" && e !== null && "message" in e && typeof e.message === "string"
|
|
757
|
+
);
|
|
758
|
+
};
|
|
759
|
+
var getErrorMsg = (e) => {
|
|
760
|
+
return isErrorWithMessage(e) ? e.message : "Unknown error";
|
|
761
|
+
};
|
|
762
|
+
var isErrorMsg = (e, msg) => {
|
|
763
|
+
if (isError(e)) {
|
|
764
|
+
return e.message === msg;
|
|
765
|
+
}
|
|
766
|
+
return false;
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
// src/utils/hex.ts
|
|
770
|
+
var bytesToHex = (bytes) => {
|
|
771
|
+
return bytes.reduce((acc, byte) => {
|
|
772
|
+
return acc += ("0" + byte.toString(16)).slice(-2);
|
|
773
|
+
}, "");
|
|
774
|
+
};
|
|
775
|
+
var hexToBytes = (hex) => {
|
|
776
|
+
const bytes = [];
|
|
777
|
+
for (let c = 0; c < hex.length; c += 2) {
|
|
778
|
+
bytes.push(parseInt(hex.substr(c, 2), 16));
|
|
779
|
+
}
|
|
780
|
+
return Uint8Array.from(bytes);
|
|
781
|
+
};
|
|
782
|
+
|
|
664
783
|
// src/utils/types.ts
|
|
665
784
|
var isType = (typename) => (node) => {
|
|
666
785
|
return node?.__typename === typename;
|
|
@@ -673,14 +792,25 @@ var isType = (typename) => (node) => {
|
|
|
673
792
|
LightsparkException,
|
|
674
793
|
LightsparkSigningException,
|
|
675
794
|
NodeKeyCache,
|
|
795
|
+
RSASigningKey,
|
|
676
796
|
Requester,
|
|
797
|
+
Secp256k1SigningKey,
|
|
677
798
|
ServerEnvironment,
|
|
799
|
+
SigningKey,
|
|
800
|
+
SigningKeyType,
|
|
678
801
|
StubAuthProvider,
|
|
679
802
|
apiDomainForEnvironment,
|
|
680
803
|
b64decode,
|
|
681
804
|
b64encode,
|
|
805
|
+
bytesToHex,
|
|
682
806
|
convertCurrencyAmount,
|
|
807
|
+
createSha256Hash,
|
|
808
|
+
getErrorMsg,
|
|
809
|
+
hexToBytes,
|
|
683
810
|
isBrowser,
|
|
811
|
+
isError,
|
|
812
|
+
isErrorMsg,
|
|
813
|
+
isErrorWithMessage,
|
|
684
814
|
isNode,
|
|
685
815
|
isType,
|
|
686
816
|
urlsafe_b64decode
|
package/dist/index.d.ts
CHANGED
|
@@ -66,12 +66,37 @@ declare const KeyOrAlias: {
|
|
|
66
66
|
alias: (alias: string) => OnlyAlias;
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
+
interface Alias {
|
|
70
|
+
alias: string;
|
|
71
|
+
}
|
|
72
|
+
declare abstract class SigningKey {
|
|
73
|
+
readonly type: SigningKeyType;
|
|
74
|
+
constructor(type: SigningKeyType);
|
|
75
|
+
abstract sign(data: Uint8Array): Promise<ArrayBuffer>;
|
|
76
|
+
}
|
|
77
|
+
declare class RSASigningKey extends SigningKey {
|
|
78
|
+
private readonly privateKey;
|
|
79
|
+
private readonly cryptoImpl;
|
|
80
|
+
constructor(privateKey: CryptoKey | Alias, cryptoImpl: CryptoInterface);
|
|
81
|
+
sign(data: Uint8Array): Promise<ArrayBuffer>;
|
|
82
|
+
}
|
|
83
|
+
declare class Secp256k1SigningKey extends SigningKey {
|
|
84
|
+
private readonly privateKey;
|
|
85
|
+
constructor(privateKey: string);
|
|
86
|
+
sign(data: Uint8Array): Promise<Uint8Array>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
declare enum SigningKeyType {
|
|
90
|
+
RSASigningKey = "RSASigningKey",
|
|
91
|
+
Secp256k1SigningKey = "Secp256k1SigningKey"
|
|
92
|
+
}
|
|
93
|
+
|
|
69
94
|
declare class NodeKeyCache {
|
|
70
95
|
private readonly cryptoImpl;
|
|
71
96
|
private idToKey;
|
|
72
97
|
constructor(cryptoImpl?: CryptoInterface);
|
|
73
|
-
loadKey(id: string, keyOrAlias: KeyOrAliasType): Promise<
|
|
74
|
-
getKey(id: string):
|
|
98
|
+
loadKey(id: string, keyOrAlias: KeyOrAliasType, signingKeyType: SigningKeyType): Promise<SigningKey | null>;
|
|
99
|
+
getKey(id: string): SigningKey | undefined;
|
|
75
100
|
hasKey(id: string): boolean;
|
|
76
101
|
private stripPemTags;
|
|
77
102
|
}
|
|
@@ -124,6 +149,8 @@ declare const b64decode: (encoded: string) => Uint8Array;
|
|
|
124
149
|
declare const urlsafe_b64decode: (encoded: string) => Uint8Array;
|
|
125
150
|
declare const b64encode: (data: ArrayBuffer) => string;
|
|
126
151
|
|
|
152
|
+
declare const createSha256Hash: (data: Uint8Array) => Promise<Uint8Array>;
|
|
153
|
+
|
|
127
154
|
/** Represents the value and unit for an amount of currency. **/
|
|
128
155
|
type CurrencyAmount = {
|
|
129
156
|
/** The original numeric value for this CurrencyAmount. **/
|
|
@@ -169,6 +196,17 @@ declare const convertCurrencyAmount: (from: CurrencyAmount, toUnit: CurrencyUnit
|
|
|
169
196
|
declare const isBrowser: boolean;
|
|
170
197
|
declare const isNode: boolean;
|
|
171
198
|
|
|
199
|
+
declare const isError: (e: unknown) => e is Error;
|
|
200
|
+
type ErrorWithMessage = {
|
|
201
|
+
message: string;
|
|
202
|
+
};
|
|
203
|
+
declare const isErrorWithMessage: (e: unknown) => e is ErrorWithMessage;
|
|
204
|
+
declare const getErrorMsg: (e: unknown) => string;
|
|
205
|
+
declare const isErrorMsg: (e: unknown, msg: string) => boolean;
|
|
206
|
+
|
|
207
|
+
declare const bytesToHex: (bytes: Uint8Array) => string;
|
|
208
|
+
declare const hexToBytes: (hex: string) => Uint8Array;
|
|
209
|
+
|
|
172
210
|
type Maybe<T> = T | null | undefined;
|
|
173
211
|
type ExpandRecursively<T> = T extends object ? T extends infer O ? {
|
|
174
212
|
[K in keyof O]: ExpandRecursively<O[K]>;
|
|
@@ -183,4 +221,4 @@ declare const isType: <T extends string>(typename: T) => <N extends {
|
|
|
183
221
|
__typename: T;
|
|
184
222
|
}>;
|
|
185
223
|
|
|
186
|
-
export { AuthProvider, ById, CryptoInterface, DefaultCrypto, ExpandRecursively, GeneratedKeyPair, KeyOrAlias, KeyOrAliasType, LightsparkAuthException, LightsparkException, LightsparkSigningException, Maybe, NodeKeyCache, OmitTypename, Query, Requester, ServerEnvironment, StubAuthProvider, apiDomainForEnvironment, b64decode, b64encode, convertCurrencyAmount, isBrowser, isNode, isType, urlsafe_b64decode };
|
|
224
|
+
export { AuthProvider, ById, CryptoInterface, DefaultCrypto, ExpandRecursively, GeneratedKeyPair, KeyOrAlias, KeyOrAliasType, LightsparkAuthException, LightsparkException, LightsparkSigningException, Maybe, NodeKeyCache, OmitTypename, Query, RSASigningKey, Requester, Secp256k1SigningKey, ServerEnvironment, SigningKey, SigningKeyType, StubAuthProvider, apiDomainForEnvironment, b64decode, b64encode, bytesToHex, convertCurrencyAmount, createSha256Hash, getErrorMsg, hexToBytes, isBrowser, isError, isErrorMsg, isErrorWithMessage, isNode, isType, urlsafe_b64decode };
|
package/dist/index.js
CHANGED
|
@@ -296,6 +296,50 @@ var KeyOrAlias = {
|
|
|
296
296
|
|
|
297
297
|
// src/crypto/NodeKeyCache.ts
|
|
298
298
|
import autoBind from "auto-bind";
|
|
299
|
+
|
|
300
|
+
// src/crypto/SigningKey.ts
|
|
301
|
+
import secp256k1 from "secp256k1";
|
|
302
|
+
function isAlias(key) {
|
|
303
|
+
return "alias" in key;
|
|
304
|
+
}
|
|
305
|
+
var SigningKey = class {
|
|
306
|
+
type;
|
|
307
|
+
constructor(type) {
|
|
308
|
+
this.type = type;
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
var RSASigningKey = class extends SigningKey {
|
|
312
|
+
constructor(privateKey, cryptoImpl) {
|
|
313
|
+
super("RSASigningKey" /* RSASigningKey */);
|
|
314
|
+
this.privateKey = privateKey;
|
|
315
|
+
this.cryptoImpl = cryptoImpl;
|
|
316
|
+
}
|
|
317
|
+
async sign(data) {
|
|
318
|
+
const key = isAlias(this.privateKey) ? this.privateKey.alias : this.privateKey;
|
|
319
|
+
return this.cryptoImpl.sign(key, data);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
var Secp256k1SigningKey = class extends SigningKey {
|
|
323
|
+
constructor(privateKey) {
|
|
324
|
+
super("Secp256k1SigningKey" /* Secp256k1SigningKey */);
|
|
325
|
+
this.privateKey = privateKey;
|
|
326
|
+
}
|
|
327
|
+
async sign(data) {
|
|
328
|
+
const keyBytes = new Uint8Array(hexToBytes(this.privateKey));
|
|
329
|
+
const hash = await createSha256Hash(data);
|
|
330
|
+
const signResult = secp256k1.ecdsaSign(hash, keyBytes);
|
|
331
|
+
return signResult.signature;
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// src/crypto/types.ts
|
|
336
|
+
var SigningKeyType = /* @__PURE__ */ ((SigningKeyType2) => {
|
|
337
|
+
SigningKeyType2["RSASigningKey"] = "RSASigningKey";
|
|
338
|
+
SigningKeyType2["Secp256k1SigningKey"] = "Secp256k1SigningKey";
|
|
339
|
+
return SigningKeyType2;
|
|
340
|
+
})(SigningKeyType || {});
|
|
341
|
+
|
|
342
|
+
// src/crypto/NodeKeyCache.ts
|
|
299
343
|
var NodeKeyCache = class {
|
|
300
344
|
constructor(cryptoImpl = DefaultCrypto) {
|
|
301
345
|
this.cryptoImpl = cryptoImpl;
|
|
@@ -303,16 +347,35 @@ var NodeKeyCache = class {
|
|
|
303
347
|
autoBind(this);
|
|
304
348
|
}
|
|
305
349
|
idToKey;
|
|
306
|
-
async loadKey(id, keyOrAlias) {
|
|
350
|
+
async loadKey(id, keyOrAlias, signingKeyType) {
|
|
351
|
+
let signingKey;
|
|
307
352
|
if (keyOrAlias.alias !== void 0) {
|
|
308
|
-
|
|
309
|
-
|
|
353
|
+
switch (signingKeyType) {
|
|
354
|
+
case "RSASigningKey" /* RSASigningKey */:
|
|
355
|
+
signingKey = new RSASigningKey(
|
|
356
|
+
{ alias: keyOrAlias.alias },
|
|
357
|
+
this.cryptoImpl
|
|
358
|
+
);
|
|
359
|
+
break;
|
|
360
|
+
default:
|
|
361
|
+
throw new LightsparkSigningException_default(
|
|
362
|
+
`Aliases are not supported for signing key type ${signingKeyType}`
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
this.idToKey.set(id, signingKey);
|
|
366
|
+
return signingKey;
|
|
310
367
|
}
|
|
311
|
-
const decoded = b64decode(this.stripPemTags(keyOrAlias.key));
|
|
312
368
|
try {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
369
|
+
if (signingKeyType === "Secp256k1SigningKey" /* Secp256k1SigningKey */) {
|
|
370
|
+
signingKey = new Secp256k1SigningKey(keyOrAlias.key);
|
|
371
|
+
} else {
|
|
372
|
+
const decoded = b64decode(this.stripPemTags(keyOrAlias.key));
|
|
373
|
+
const cryptoKeyOrAlias = await this.cryptoImpl.importPrivateSigningKey(decoded);
|
|
374
|
+
const key = typeof cryptoKeyOrAlias === "string" ? { alias: cryptoKeyOrAlias } : cryptoKeyOrAlias;
|
|
375
|
+
signingKey = new RSASigningKey(key, this.cryptoImpl);
|
|
376
|
+
}
|
|
377
|
+
this.idToKey.set(id, signingKey);
|
|
378
|
+
return signingKey;
|
|
316
379
|
} catch (e) {
|
|
317
380
|
console.log("Error importing key: ", e);
|
|
318
381
|
}
|
|
@@ -512,7 +575,7 @@ var Requester = class {
|
|
|
512
575
|
const encodedPayload = new TextEncoderImpl().encode(
|
|
513
576
|
JSON.stringify(payload)
|
|
514
577
|
);
|
|
515
|
-
const signedPayload = await
|
|
578
|
+
const signedPayload = await key.sign(encodedPayload);
|
|
516
579
|
const encodedSignedPayload = b64encode(signedPayload);
|
|
517
580
|
headers["X-Lightspark-Signing"] = JSON.stringify({
|
|
518
581
|
v: "1",
|
|
@@ -539,6 +602,16 @@ var apiDomainForEnvironment = (environment) => {
|
|
|
539
602
|
};
|
|
540
603
|
var ServerEnvironment_default = ServerEnvironment;
|
|
541
604
|
|
|
605
|
+
// src/utils/createHash.ts
|
|
606
|
+
var createSha256Hash = async (data) => {
|
|
607
|
+
if (isBrowser) {
|
|
608
|
+
return new Uint8Array(await window.crypto.subtle.digest("SHA-256", data));
|
|
609
|
+
} else {
|
|
610
|
+
const { createHash } = await import("crypto");
|
|
611
|
+
return createHash("sha256").update(data).digest();
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
|
|
542
615
|
// src/utils/currency.ts
|
|
543
616
|
var CONVERSION_MAP = {
|
|
544
617
|
["BITCOIN" /* BITCOIN */]: {
|
|
@@ -609,6 +682,41 @@ var convertCurrencyAmount = (from, toUnit) => {
|
|
|
609
682
|
};
|
|
610
683
|
};
|
|
611
684
|
|
|
685
|
+
// src/utils/errors.ts
|
|
686
|
+
var isError = (e) => {
|
|
687
|
+
return Boolean(
|
|
688
|
+
typeof e === "object" && e !== null && "name" in e && typeof e.name === "string" && "message" in e && typeof e.message === "string" && "stack" in e && (!e.stack || typeof e.stack === "string")
|
|
689
|
+
);
|
|
690
|
+
};
|
|
691
|
+
var isErrorWithMessage = (e) => {
|
|
692
|
+
return Boolean(
|
|
693
|
+
typeof e === "object" && e !== null && "message" in e && typeof e.message === "string"
|
|
694
|
+
);
|
|
695
|
+
};
|
|
696
|
+
var getErrorMsg = (e) => {
|
|
697
|
+
return isErrorWithMessage(e) ? e.message : "Unknown error";
|
|
698
|
+
};
|
|
699
|
+
var isErrorMsg = (e, msg) => {
|
|
700
|
+
if (isError(e)) {
|
|
701
|
+
return e.message === msg;
|
|
702
|
+
}
|
|
703
|
+
return false;
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
// src/utils/hex.ts
|
|
707
|
+
var bytesToHex = (bytes) => {
|
|
708
|
+
return bytes.reduce((acc, byte) => {
|
|
709
|
+
return acc += ("0" + byte.toString(16)).slice(-2);
|
|
710
|
+
}, "");
|
|
711
|
+
};
|
|
712
|
+
var hexToBytes = (hex) => {
|
|
713
|
+
const bytes = [];
|
|
714
|
+
for (let c = 0; c < hex.length; c += 2) {
|
|
715
|
+
bytes.push(parseInt(hex.substr(c, 2), 16));
|
|
716
|
+
}
|
|
717
|
+
return Uint8Array.from(bytes);
|
|
718
|
+
};
|
|
719
|
+
|
|
612
720
|
// src/utils/types.ts
|
|
613
721
|
var isType = (typename) => (node) => {
|
|
614
722
|
return node?.__typename === typename;
|
|
@@ -620,14 +728,25 @@ export {
|
|
|
620
728
|
LightsparkException_default as LightsparkException,
|
|
621
729
|
LightsparkSigningException_default as LightsparkSigningException,
|
|
622
730
|
NodeKeyCache_default as NodeKeyCache,
|
|
731
|
+
RSASigningKey,
|
|
623
732
|
Requester_default as Requester,
|
|
733
|
+
Secp256k1SigningKey,
|
|
624
734
|
ServerEnvironment_default as ServerEnvironment,
|
|
735
|
+
SigningKey,
|
|
736
|
+
SigningKeyType,
|
|
625
737
|
StubAuthProvider,
|
|
626
738
|
apiDomainForEnvironment,
|
|
627
739
|
b64decode,
|
|
628
740
|
b64encode,
|
|
741
|
+
bytesToHex,
|
|
629
742
|
convertCurrencyAmount,
|
|
743
|
+
createSha256Hash,
|
|
744
|
+
getErrorMsg,
|
|
745
|
+
hexToBytes,
|
|
630
746
|
isBrowser,
|
|
747
|
+
isError,
|
|
748
|
+
isErrorMsg,
|
|
749
|
+
isErrorWithMessage,
|
|
631
750
|
isNode,
|
|
632
751
|
isType,
|
|
633
752
|
urlsafe_b64decode
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightsparkdev/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Lightspark JS SDK",
|
|
5
5
|
"author": "Lightspark Inc.",
|
|
6
6
|
"keywords": [
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
39
|
+
"node": ">=18.17.0"
|
|
40
40
|
},
|
|
41
41
|
"browser": {
|
|
42
42
|
"crypto": false
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"dayjs": "^1.11.7",
|
|
69
69
|
"graphql": "^16.6.0",
|
|
70
70
|
"graphql-ws": "^5.11.3",
|
|
71
|
+
"secp256k1": "^5.0.0",
|
|
71
72
|
"text-encoding": "^0.7.0",
|
|
72
73
|
"ws": "^8.12.1",
|
|
73
74
|
"zen-observable-ts": "^1.1.0"
|
|
@@ -76,13 +77,14 @@
|
|
|
76
77
|
"@lightsparkdev/eslint-config": "*",
|
|
77
78
|
"@lightsparkdev/tsconfig": "0.0.0",
|
|
78
79
|
"@types/crypto-js": "^4.1.1",
|
|
80
|
+
"@types/secp256k1": "^4.0.3",
|
|
79
81
|
"@types/ws": "^8.5.4",
|
|
80
82
|
"eslint": "^8.3.0",
|
|
81
83
|
"eslint-watch": "^8.0.0",
|
|
82
|
-
"jest": "^29.
|
|
84
|
+
"jest": "^29.6.2",
|
|
83
85
|
"prettier": "3.0.2",
|
|
84
86
|
"prettier-plugin-organize-imports": "^3.2.2",
|
|
85
|
-
"ts-jest": "^29.
|
|
87
|
+
"ts-jest": "^29.1.1",
|
|
86
88
|
"tsc-absolute": "^1.0.1",
|
|
87
89
|
"tsup": "^6.7.0",
|
|
88
90
|
"typescript": "^4.9.5"
|
|
@@ -4,11 +4,18 @@ import autoBind from "auto-bind";
|
|
|
4
4
|
|
|
5
5
|
import { b64decode } from "../utils/base64.js";
|
|
6
6
|
import type { CryptoInterface } from "./crypto.js";
|
|
7
|
-
import { DefaultCrypto } from "./crypto.js";
|
|
7
|
+
import { DefaultCrypto, LightsparkSigningException } from "./crypto.js";
|
|
8
8
|
import type { KeyOrAliasType } from "./KeyOrAlias.js";
|
|
9
|
+
import {
|
|
10
|
+
RSASigningKey,
|
|
11
|
+
Secp256k1SigningKey,
|
|
12
|
+
type SigningKey,
|
|
13
|
+
} from "./SigningKey.js";
|
|
14
|
+
import { SigningKeyType } from "./types.js";
|
|
9
15
|
|
|
10
16
|
class NodeKeyCache {
|
|
11
|
-
private idToKey: Map<string,
|
|
17
|
+
private idToKey: Map<string, SigningKey>;
|
|
18
|
+
|
|
12
19
|
constructor(private readonly cryptoImpl: CryptoInterface = DefaultCrypto) {
|
|
13
20
|
this.idToKey = new Map();
|
|
14
21
|
autoBind(this);
|
|
@@ -17,23 +24,51 @@ class NodeKeyCache {
|
|
|
17
24
|
public async loadKey(
|
|
18
25
|
id: string,
|
|
19
26
|
keyOrAlias: KeyOrAliasType,
|
|
20
|
-
|
|
27
|
+
signingKeyType: SigningKeyType,
|
|
28
|
+
): Promise<SigningKey | null> {
|
|
29
|
+
let signingKey: SigningKey;
|
|
30
|
+
|
|
21
31
|
if (keyOrAlias.alias !== undefined) {
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
switch (signingKeyType) {
|
|
33
|
+
case SigningKeyType.RSASigningKey:
|
|
34
|
+
signingKey = new RSASigningKey(
|
|
35
|
+
{ alias: keyOrAlias.alias },
|
|
36
|
+
this.cryptoImpl,
|
|
37
|
+
);
|
|
38
|
+
break;
|
|
39
|
+
default:
|
|
40
|
+
throw new LightsparkSigningException(
|
|
41
|
+
`Aliases are not supported for signing key type ${signingKeyType}`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.idToKey.set(id, signingKey);
|
|
46
|
+
return signingKey;
|
|
24
47
|
}
|
|
25
|
-
|
|
48
|
+
|
|
26
49
|
try {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
50
|
+
if (signingKeyType === SigningKeyType.Secp256k1SigningKey) {
|
|
51
|
+
signingKey = new Secp256k1SigningKey(keyOrAlias.key);
|
|
52
|
+
} else {
|
|
53
|
+
const decoded = b64decode(this.stripPemTags(keyOrAlias.key));
|
|
54
|
+
const cryptoKeyOrAlias =
|
|
55
|
+
await this.cryptoImpl.importPrivateSigningKey(decoded);
|
|
56
|
+
const key =
|
|
57
|
+
typeof cryptoKeyOrAlias === "string"
|
|
58
|
+
? { alias: cryptoKeyOrAlias }
|
|
59
|
+
: cryptoKeyOrAlias;
|
|
60
|
+
signingKey = new RSASigningKey(key, this.cryptoImpl);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.idToKey.set(id, signingKey);
|
|
64
|
+
return signingKey;
|
|
30
65
|
} catch (e) {
|
|
31
66
|
console.log("Error importing key: ", e);
|
|
32
67
|
}
|
|
33
68
|
return null;
|
|
34
69
|
}
|
|
35
70
|
|
|
36
|
-
public getKey(id: string):
|
|
71
|
+
public getKey(id: string): SigningKey | undefined {
|
|
37
72
|
return this.idToKey.get(id);
|
|
38
73
|
}
|
|
39
74
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import secp256k1 from "secp256k1";
|
|
2
|
+
import {
|
|
3
|
+
createSha256Hash,
|
|
4
|
+
hexToBytes,
|
|
5
|
+
SigningKeyType,
|
|
6
|
+
type CryptoInterface,
|
|
7
|
+
} from "../index.js";
|
|
8
|
+
|
|
9
|
+
interface Alias {
|
|
10
|
+
alias: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isAlias(key: CryptoKey | Alias): key is Alias {
|
|
14
|
+
return "alias" in key;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export abstract class SigningKey {
|
|
18
|
+
readonly type: SigningKeyType;
|
|
19
|
+
|
|
20
|
+
constructor(type: SigningKeyType) {
|
|
21
|
+
this.type = type;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
abstract sign(data: Uint8Array): Promise<ArrayBuffer>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class RSASigningKey extends SigningKey {
|
|
28
|
+
constructor(
|
|
29
|
+
private readonly privateKey: CryptoKey | Alias,
|
|
30
|
+
private readonly cryptoImpl: CryptoInterface,
|
|
31
|
+
) {
|
|
32
|
+
super(SigningKeyType.RSASigningKey);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async sign(data: Uint8Array) {
|
|
36
|
+
const key = isAlias(this.privateKey)
|
|
37
|
+
? this.privateKey.alias
|
|
38
|
+
: this.privateKey;
|
|
39
|
+
return this.cryptoImpl.sign(key, data);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class Secp256k1SigningKey extends SigningKey {
|
|
44
|
+
constructor(private readonly privateKey: string) {
|
|
45
|
+
super(SigningKeyType.Secp256k1SigningKey);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async sign(data: Uint8Array) {
|
|
49
|
+
const keyBytes = new Uint8Array(hexToBytes(this.privateKey));
|
|
50
|
+
const hash = await createSha256Hash(data);
|
|
51
|
+
const signResult = secp256k1.ecdsaSign(hash, keyBytes);
|
|
52
|
+
return signResult.signature;
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/crypto/index.ts
CHANGED
|
@@ -4,3 +4,5 @@ export * from "./crypto.js";
|
|
|
4
4
|
export * from "./KeyOrAlias.js";
|
|
5
5
|
export { default as LightsparkSigningException } from "./LightsparkSigningException.js";
|
|
6
6
|
export { default as NodeKeyCache } from "./NodeKeyCache.js";
|
|
7
|
+
export * from "./SigningKey.js";
|
|
8
|
+
export * from "./types.js";
|
|
@@ -228,9 +228,10 @@ class Requester {
|
|
|
228
228
|
const encodedPayload = new TextEncoderImpl().encode(
|
|
229
229
|
JSON.stringify(payload),
|
|
230
230
|
);
|
|
231
|
-
const signedPayload = await this.cryptoImpl.sign(key, encodedPayload);
|
|
232
|
-
const encodedSignedPayload = b64encode(signedPayload);
|
|
233
231
|
|
|
232
|
+
const signedPayload = await key.sign(encodedPayload);
|
|
233
|
+
|
|
234
|
+
const encodedSignedPayload = b64encode(signedPayload);
|
|
234
235
|
headers["X-Lightspark-Signing"] = JSON.stringify({
|
|
235
236
|
v: "1",
|
|
236
237
|
signature: encodedSignedPayload,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { isBrowser } from "./environment.js";
|
|
2
|
+
|
|
3
|
+
export const createSha256Hash = async (
|
|
4
|
+
data: Uint8Array,
|
|
5
|
+
): Promise<Uint8Array> => {
|
|
6
|
+
if (isBrowser) {
|
|
7
|
+
return new Uint8Array(await window.crypto.subtle.digest("SHA-256", data));
|
|
8
|
+
} else {
|
|
9
|
+
const { createHash } = await import("crypto");
|
|
10
|
+
return createHash("sha256").update(data).digest();
|
|
11
|
+
}
|
|
12
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const isError = (e: unknown): e is Error => {
|
|
2
|
+
return Boolean(
|
|
3
|
+
typeof e === "object" &&
|
|
4
|
+
e !== null &&
|
|
5
|
+
"name" in e &&
|
|
6
|
+
typeof e.name === "string" &&
|
|
7
|
+
"message" in e &&
|
|
8
|
+
typeof e.message === "string" &&
|
|
9
|
+
"stack" in e &&
|
|
10
|
+
(!e.stack || typeof e.stack === "string"),
|
|
11
|
+
);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type ErrorWithMessage = {
|
|
15
|
+
message: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const isErrorWithMessage = (e: unknown): e is ErrorWithMessage => {
|
|
19
|
+
return Boolean(
|
|
20
|
+
typeof e === "object" &&
|
|
21
|
+
e !== null &&
|
|
22
|
+
"message" in e &&
|
|
23
|
+
typeof e.message === "string",
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const getErrorMsg = (e: unknown): string => {
|
|
28
|
+
return isErrorWithMessage(e) ? e.message : "Unknown error";
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const isErrorMsg = (e: unknown, msg: string) => {
|
|
32
|
+
if (isError(e)) {
|
|
33
|
+
return e.message === msg;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
};
|
package/src/utils/hex.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const bytesToHex = (bytes: Uint8Array): string => {
|
|
2
|
+
return bytes.reduce((acc: string, byte: number) => {
|
|
3
|
+
return (acc += ("0" + byte.toString(16)).slice(-2));
|
|
4
|
+
}, "");
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const hexToBytes = (hex: string): Uint8Array => {
|
|
8
|
+
const bytes: number[] = [];
|
|
9
|
+
|
|
10
|
+
for (let c = 0; c < hex.length; c += 2) {
|
|
11
|
+
bytes.push(parseInt(hex.substr(c, 2), 16));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return Uint8Array.from(bytes);
|
|
15
|
+
};
|
package/src/utils/index.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
|
|
2
2
|
|
|
3
3
|
export * from "./base64.js";
|
|
4
|
+
export * from "./createHash.js";
|
|
4
5
|
export * from "./currency.js";
|
|
5
6
|
export * from "./environment.js";
|
|
7
|
+
export * from "./errors.js";
|
|
8
|
+
export * from "./hex.js";
|
|
6
9
|
export * from "./types.js";
|