@lightsparkdev/core 0.3.10 → 1.0.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/CHANGELOG.md +13 -0
- package/dist/index.cjs +109 -8
- package/dist/index.d.ts +33 -3
- package/dist/index.js +102 -8
- package/package.json +7 -5
- package/src/LightsparkException.ts +1 -1
- package/src/ServerEnvironment.ts +1 -1
- package/src/auth/AuthProvider.ts +1 -1
- package/src/crypto/NodeKeyCache.ts +46 -11
- package/src/crypto/SigningKey.ts +54 -0
- package/src/crypto/crypto.ts +22 -22
- package/src/crypto/index.ts +2 -0
- package/src/crypto/types.ts +4 -0
- package/src/requester/Requester.ts +16 -15
- package/src/utils/base64.ts +3 -3
- package/src/utils/createHash.ts +12 -0
- package/src/utils/currency.ts +2 -2
- package/src/utils/hex.ts +15 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/types.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @lightsparkdev/core
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 1f00a50: Change SigningKey hashing method depending on environment
|
|
8
|
+
BREAKING: NodeKeyCaches loadKey now requires signingKeyType as a parameter
|
|
9
|
+
|
|
10
|
+
## 0.3.11
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 4ffd9a1: Upgrade prettier, fix lint configs, move ls-react-native-crypto-app to examples
|
|
15
|
+
|
|
3
16
|
## 0.3.10
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -36,13 +36,20 @@ __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
|
+
hexToBytes: () => hexToBytes,
|
|
46
53
|
isBrowser: () => isBrowser,
|
|
47
54
|
isNode: () => isNode,
|
|
48
55
|
isType: () => isType,
|
|
@@ -348,6 +355,50 @@ var KeyOrAlias = {
|
|
|
348
355
|
|
|
349
356
|
// src/crypto/NodeKeyCache.ts
|
|
350
357
|
var import_auto_bind = __toESM(require("auto-bind"), 1);
|
|
358
|
+
|
|
359
|
+
// src/crypto/SigningKey.ts
|
|
360
|
+
var import_secp256k1 = __toESM(require("secp256k1"), 1);
|
|
361
|
+
function isAlias(key) {
|
|
362
|
+
return "alias" in key;
|
|
363
|
+
}
|
|
364
|
+
var SigningKey = class {
|
|
365
|
+
type;
|
|
366
|
+
constructor(type) {
|
|
367
|
+
this.type = type;
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
var RSASigningKey = class extends SigningKey {
|
|
371
|
+
constructor(privateKey, cryptoImpl) {
|
|
372
|
+
super("RSASigningKey" /* RSASigningKey */);
|
|
373
|
+
this.privateKey = privateKey;
|
|
374
|
+
this.cryptoImpl = cryptoImpl;
|
|
375
|
+
}
|
|
376
|
+
async sign(data) {
|
|
377
|
+
const key = isAlias(this.privateKey) ? this.privateKey.alias : this.privateKey;
|
|
378
|
+
return this.cryptoImpl.sign(key, data);
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
var Secp256k1SigningKey = class extends SigningKey {
|
|
382
|
+
constructor(privateKey) {
|
|
383
|
+
super("Secp256k1SigningKey" /* Secp256k1SigningKey */);
|
|
384
|
+
this.privateKey = privateKey;
|
|
385
|
+
}
|
|
386
|
+
async sign(data) {
|
|
387
|
+
const keyBytes = new Uint8Array(hexToBytes(this.privateKey));
|
|
388
|
+
const hash = await createSha256Hash(data);
|
|
389
|
+
const signResult = import_secp256k1.default.ecdsaSign(hash, keyBytes);
|
|
390
|
+
return signResult.signature;
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// src/crypto/types.ts
|
|
395
|
+
var SigningKeyType = /* @__PURE__ */ ((SigningKeyType2) => {
|
|
396
|
+
SigningKeyType2["RSASigningKey"] = "RSASigningKey";
|
|
397
|
+
SigningKeyType2["Secp256k1SigningKey"] = "Secp256k1SigningKey";
|
|
398
|
+
return SigningKeyType2;
|
|
399
|
+
})(SigningKeyType || {});
|
|
400
|
+
|
|
401
|
+
// src/crypto/NodeKeyCache.ts
|
|
351
402
|
var NodeKeyCache = class {
|
|
352
403
|
constructor(cryptoImpl = DefaultCrypto) {
|
|
353
404
|
this.cryptoImpl = cryptoImpl;
|
|
@@ -355,16 +406,35 @@ var NodeKeyCache = class {
|
|
|
355
406
|
(0, import_auto_bind.default)(this);
|
|
356
407
|
}
|
|
357
408
|
idToKey;
|
|
358
|
-
async loadKey(id, keyOrAlias) {
|
|
409
|
+
async loadKey(id, keyOrAlias, signingKeyType) {
|
|
410
|
+
let signingKey;
|
|
359
411
|
if (keyOrAlias.alias !== void 0) {
|
|
360
|
-
|
|
361
|
-
|
|
412
|
+
switch (signingKeyType) {
|
|
413
|
+
case "RSASigningKey" /* RSASigningKey */:
|
|
414
|
+
signingKey = new RSASigningKey(
|
|
415
|
+
{ alias: keyOrAlias.alias },
|
|
416
|
+
this.cryptoImpl
|
|
417
|
+
);
|
|
418
|
+
break;
|
|
419
|
+
default:
|
|
420
|
+
throw new LightsparkSigningException_default(
|
|
421
|
+
`Aliases are not supported for signing key type ${signingKeyType}`
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
this.idToKey.set(id, signingKey);
|
|
425
|
+
return signingKey;
|
|
362
426
|
}
|
|
363
|
-
const decoded = b64decode(this.stripPemTags(keyOrAlias.key));
|
|
364
427
|
try {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
428
|
+
if (signingKeyType === "Secp256k1SigningKey" /* Secp256k1SigningKey */) {
|
|
429
|
+
signingKey = new Secp256k1SigningKey(keyOrAlias.key);
|
|
430
|
+
} else {
|
|
431
|
+
const decoded = b64decode(this.stripPemTags(keyOrAlias.key));
|
|
432
|
+
const cryptoKeyOrAlias = await this.cryptoImpl.importPrivateSigningKey(decoded);
|
|
433
|
+
const key = typeof cryptoKeyOrAlias === "string" ? { alias: cryptoKeyOrAlias } : cryptoKeyOrAlias;
|
|
434
|
+
signingKey = new RSASigningKey(key, this.cryptoImpl);
|
|
435
|
+
}
|
|
436
|
+
this.idToKey.set(id, signingKey);
|
|
437
|
+
return signingKey;
|
|
368
438
|
} catch (e) {
|
|
369
439
|
console.log("Error importing key: ", e);
|
|
370
440
|
}
|
|
@@ -564,7 +634,7 @@ var Requester = class {
|
|
|
564
634
|
const encodedPayload = new TextEncoderImpl().encode(
|
|
565
635
|
JSON.stringify(payload)
|
|
566
636
|
);
|
|
567
|
-
const signedPayload = await
|
|
637
|
+
const signedPayload = await key.sign(encodedPayload);
|
|
568
638
|
const encodedSignedPayload = b64encode(signedPayload);
|
|
569
639
|
headers["X-Lightspark-Signing"] = JSON.stringify({
|
|
570
640
|
v: "1",
|
|
@@ -591,6 +661,16 @@ var apiDomainForEnvironment = (environment) => {
|
|
|
591
661
|
};
|
|
592
662
|
var ServerEnvironment_default = ServerEnvironment;
|
|
593
663
|
|
|
664
|
+
// src/utils/createHash.ts
|
|
665
|
+
var createSha256Hash = async (data) => {
|
|
666
|
+
if (isBrowser) {
|
|
667
|
+
return new Uint8Array(await window.crypto.subtle.digest("SHA-256", data));
|
|
668
|
+
} else {
|
|
669
|
+
const { createHash } = await import("crypto");
|
|
670
|
+
return createHash("sha256").update(data).digest();
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
|
|
594
674
|
// src/utils/currency.ts
|
|
595
675
|
var CONVERSION_MAP = {
|
|
596
676
|
["BITCOIN" /* BITCOIN */]: {
|
|
@@ -661,6 +741,20 @@ var convertCurrencyAmount = (from, toUnit) => {
|
|
|
661
741
|
};
|
|
662
742
|
};
|
|
663
743
|
|
|
744
|
+
// src/utils/hex.ts
|
|
745
|
+
var bytesToHex = (bytes) => {
|
|
746
|
+
return bytes.reduce((acc, byte) => {
|
|
747
|
+
return acc += ("0" + byte.toString(16)).slice(-2);
|
|
748
|
+
}, "");
|
|
749
|
+
};
|
|
750
|
+
var hexToBytes = (hex) => {
|
|
751
|
+
const bytes = [];
|
|
752
|
+
for (let c = 0; c < hex.length; c += 2) {
|
|
753
|
+
bytes.push(parseInt(hex.substr(c, 2), 16));
|
|
754
|
+
}
|
|
755
|
+
return Uint8Array.from(bytes);
|
|
756
|
+
};
|
|
757
|
+
|
|
664
758
|
// src/utils/types.ts
|
|
665
759
|
var isType = (typename) => (node) => {
|
|
666
760
|
return node?.__typename === typename;
|
|
@@ -673,13 +767,20 @@ var isType = (typename) => (node) => {
|
|
|
673
767
|
LightsparkException,
|
|
674
768
|
LightsparkSigningException,
|
|
675
769
|
NodeKeyCache,
|
|
770
|
+
RSASigningKey,
|
|
676
771
|
Requester,
|
|
772
|
+
Secp256k1SigningKey,
|
|
677
773
|
ServerEnvironment,
|
|
774
|
+
SigningKey,
|
|
775
|
+
SigningKeyType,
|
|
678
776
|
StubAuthProvider,
|
|
679
777
|
apiDomainForEnvironment,
|
|
680
778
|
b64decode,
|
|
681
779
|
b64encode,
|
|
780
|
+
bytesToHex,
|
|
682
781
|
convertCurrencyAmount,
|
|
782
|
+
createSha256Hash,
|
|
783
|
+
hexToBytes,
|
|
683
784
|
isBrowser,
|
|
684
785
|
isNode,
|
|
685
786
|
isType,
|
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,9 @@ declare const convertCurrencyAmount: (from: CurrencyAmount, toUnit: CurrencyUnit
|
|
|
169
196
|
declare const isBrowser: boolean;
|
|
170
197
|
declare const isNode: boolean;
|
|
171
198
|
|
|
199
|
+
declare const bytesToHex: (bytes: Uint8Array) => string;
|
|
200
|
+
declare const hexToBytes: (hex: string) => Uint8Array;
|
|
201
|
+
|
|
172
202
|
type Maybe<T> = T | null | undefined;
|
|
173
203
|
type ExpandRecursively<T> = T extends object ? T extends infer O ? {
|
|
174
204
|
[K in keyof O]: ExpandRecursively<O[K]>;
|
|
@@ -183,4 +213,4 @@ declare const isType: <T extends string>(typename: T) => <N extends {
|
|
|
183
213
|
__typename: T;
|
|
184
214
|
}>;
|
|
185
215
|
|
|
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 };
|
|
216
|
+
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, hexToBytes, isBrowser, 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,20 @@ var convertCurrencyAmount = (from, toUnit) => {
|
|
|
609
682
|
};
|
|
610
683
|
};
|
|
611
684
|
|
|
685
|
+
// src/utils/hex.ts
|
|
686
|
+
var bytesToHex = (bytes) => {
|
|
687
|
+
return bytes.reduce((acc, byte) => {
|
|
688
|
+
return acc += ("0" + byte.toString(16)).slice(-2);
|
|
689
|
+
}, "");
|
|
690
|
+
};
|
|
691
|
+
var hexToBytes = (hex) => {
|
|
692
|
+
const bytes = [];
|
|
693
|
+
for (let c = 0; c < hex.length; c += 2) {
|
|
694
|
+
bytes.push(parseInt(hex.substr(c, 2), 16));
|
|
695
|
+
}
|
|
696
|
+
return Uint8Array.from(bytes);
|
|
697
|
+
};
|
|
698
|
+
|
|
612
699
|
// src/utils/types.ts
|
|
613
700
|
var isType = (typename) => (node) => {
|
|
614
701
|
return node?.__typename === typename;
|
|
@@ -620,13 +707,20 @@ export {
|
|
|
620
707
|
LightsparkException_default as LightsparkException,
|
|
621
708
|
LightsparkSigningException_default as LightsparkSigningException,
|
|
622
709
|
NodeKeyCache_default as NodeKeyCache,
|
|
710
|
+
RSASigningKey,
|
|
623
711
|
Requester_default as Requester,
|
|
712
|
+
Secp256k1SigningKey,
|
|
624
713
|
ServerEnvironment_default as ServerEnvironment,
|
|
714
|
+
SigningKey,
|
|
715
|
+
SigningKeyType,
|
|
625
716
|
StubAuthProvider,
|
|
626
717
|
apiDomainForEnvironment,
|
|
627
718
|
b64decode,
|
|
628
719
|
b64encode,
|
|
720
|
+
bytesToHex,
|
|
629
721
|
convertCurrencyAmount,
|
|
722
|
+
createSha256Hash,
|
|
723
|
+
hexToBytes,
|
|
630
724
|
isBrowser,
|
|
631
725
|
isNode,
|
|
632
726
|
isType,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightsparkdev/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
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.
|
|
83
|
-
"prettier": "
|
|
84
|
+
"jest": "^29.6.2",
|
|
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"
|
package/src/ServerEnvironment.ts
CHANGED
package/src/auth/AuthProvider.ts
CHANGED
|
@@ -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);
|
|
@@ -16,24 +23,52 @@ class NodeKeyCache {
|
|
|
16
23
|
|
|
17
24
|
public async loadKey(
|
|
18
25
|
id: string,
|
|
19
|
-
keyOrAlias: KeyOrAliasType
|
|
20
|
-
|
|
26
|
+
keyOrAlias: KeyOrAliasType,
|
|
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/crypto.ts
CHANGED
|
@@ -14,21 +14,21 @@ export type CryptoInterface = {
|
|
|
14
14
|
decryptSecretWithNodePassword: (
|
|
15
15
|
cipher: string,
|
|
16
16
|
encryptedSecret: string,
|
|
17
|
-
nodePassword: string
|
|
17
|
+
nodePassword: string,
|
|
18
18
|
) => Promise<ArrayBuffer | null>;
|
|
19
19
|
|
|
20
20
|
generateSigningKeyPair: () => Promise<GeneratedKeyPair>;
|
|
21
21
|
|
|
22
22
|
serializeSigningKey: (
|
|
23
23
|
key: CryptoKey | string,
|
|
24
|
-
format: "pkcs8" | "spki"
|
|
24
|
+
format: "pkcs8" | "spki",
|
|
25
25
|
) => Promise<ArrayBuffer>;
|
|
26
26
|
|
|
27
27
|
getNonce: () => Promise<number>;
|
|
28
28
|
|
|
29
29
|
sign: (
|
|
30
30
|
keyOrAlias: CryptoKey | string,
|
|
31
|
-
data: Uint8Array
|
|
31
|
+
data: Uint8Array,
|
|
32
32
|
) => Promise<ArrayBuffer>;
|
|
33
33
|
|
|
34
34
|
importPrivateSigningKey: (keyData: Uint8Array) => Promise<CryptoKey | string>;
|
|
@@ -80,7 +80,7 @@ const deriveKey = async (
|
|
|
80
80
|
salt: ArrayBuffer,
|
|
81
81
|
iterations: number,
|
|
82
82
|
algorithm: string,
|
|
83
|
-
bit_len: number
|
|
83
|
+
bit_len: number,
|
|
84
84
|
): Promise<[CryptoKey, ArrayBuffer]> => {
|
|
85
85
|
const enc = new TextEncoder();
|
|
86
86
|
const cryptoImpl = await getCrypto();
|
|
@@ -89,7 +89,7 @@ const deriveKey = async (
|
|
|
89
89
|
enc.encode(password),
|
|
90
90
|
"PBKDF2",
|
|
91
91
|
false,
|
|
92
|
-
["deriveBits", "deriveKey"]
|
|
92
|
+
["deriveBits", "deriveKey"],
|
|
93
93
|
);
|
|
94
94
|
|
|
95
95
|
const derived = await cryptoImpl.subtle.deriveBits(
|
|
@@ -100,7 +100,7 @@ const deriveKey = async (
|
|
|
100
100
|
hash: "SHA-256",
|
|
101
101
|
},
|
|
102
102
|
password_key,
|
|
103
|
-
bit_len
|
|
103
|
+
bit_len,
|
|
104
104
|
);
|
|
105
105
|
|
|
106
106
|
// Split the derived bytes into a 32 byte AES key and a 16 byte IV
|
|
@@ -109,7 +109,7 @@ const deriveKey = async (
|
|
|
109
109
|
derived.slice(0, 32),
|
|
110
110
|
{ name: algorithm, length: 256 },
|
|
111
111
|
false,
|
|
112
|
-
["encrypt", "decrypt"]
|
|
112
|
+
["encrypt", "decrypt"],
|
|
113
113
|
);
|
|
114
114
|
|
|
115
115
|
const iv = derived.slice(32);
|
|
@@ -120,7 +120,7 @@ const deriveKey = async (
|
|
|
120
120
|
const decrypt = async (
|
|
121
121
|
header_json: string,
|
|
122
122
|
ciphertext: string,
|
|
123
|
-
password: string
|
|
123
|
+
password: string,
|
|
124
124
|
): Promise<ArrayBuffer> => {
|
|
125
125
|
let decoded = b64decode(ciphertext);
|
|
126
126
|
|
|
@@ -139,7 +139,7 @@ const decrypt = async (
|
|
|
139
139
|
if (header.v < 0 || header.v > 4) {
|
|
140
140
|
throw new LightsparkException(
|
|
141
141
|
"DecryptionError",
|
|
142
|
-
"Unknown version ".concat(header.v)
|
|
142
|
+
"Unknown version ".concat(header.v),
|
|
143
143
|
);
|
|
144
144
|
}
|
|
145
145
|
|
|
@@ -158,12 +158,12 @@ const decrypt = async (
|
|
|
158
158
|
salt,
|
|
159
159
|
header.i,
|
|
160
160
|
algorithm,
|
|
161
|
-
256
|
|
161
|
+
256,
|
|
162
162
|
);
|
|
163
163
|
return await cryptoImpl.subtle.decrypt(
|
|
164
164
|
{ name: algorithm, iv: nonce.buffer },
|
|
165
165
|
key,
|
|
166
|
-
cipherText
|
|
166
|
+
cipherText,
|
|
167
167
|
);
|
|
168
168
|
} else {
|
|
169
169
|
const salt = decoded.slice(0, salt_len);
|
|
@@ -173,12 +173,12 @@ const decrypt = async (
|
|
|
173
173
|
salt,
|
|
174
174
|
header.i,
|
|
175
175
|
algorithm,
|
|
176
|
-
bit_len
|
|
176
|
+
bit_len,
|
|
177
177
|
);
|
|
178
178
|
return await cryptoImpl.subtle.decrypt(
|
|
179
179
|
{ name: algorithm, iv },
|
|
180
180
|
key,
|
|
181
|
-
encrypted
|
|
181
|
+
encrypted,
|
|
182
182
|
);
|
|
183
183
|
}
|
|
184
184
|
};
|
|
@@ -186,7 +186,7 @@ const decrypt = async (
|
|
|
186
186
|
async function decryptSecretWithNodePassword(
|
|
187
187
|
cipher: string,
|
|
188
188
|
encryptedSecret: string,
|
|
189
|
-
nodePassword: string
|
|
189
|
+
nodePassword: string,
|
|
190
190
|
): Promise<ArrayBuffer | null> {
|
|
191
191
|
let decryptedValue: ArrayBuffer | null = null;
|
|
192
192
|
try {
|
|
@@ -209,18 +209,18 @@ const generateSigningKeyPair = async (): Promise<GeneratedKeyPair> => {
|
|
|
209
209
|
hash: "SHA-256",
|
|
210
210
|
},
|
|
211
211
|
/*extractable*/ true,
|
|
212
|
-
/*keyUsages*/ ["sign", "verify"]
|
|
212
|
+
/*keyUsages*/ ["sign", "verify"],
|
|
213
213
|
);
|
|
214
214
|
};
|
|
215
215
|
|
|
216
216
|
const serializeSigningKey = async (
|
|
217
217
|
key: CryptoKey | string,
|
|
218
|
-
format: "pkcs8" | "spki"
|
|
218
|
+
format: "pkcs8" | "spki",
|
|
219
219
|
): Promise<ArrayBuffer> => {
|
|
220
220
|
const cryptoImpl = await getCrypto();
|
|
221
221
|
return await cryptoImpl.subtle.exportKey(
|
|
222
222
|
/*format*/ format,
|
|
223
|
-
/*key*/ key as CryptoKey
|
|
223
|
+
/*key*/ key as CryptoKey,
|
|
224
224
|
);
|
|
225
225
|
};
|
|
226
226
|
|
|
@@ -231,11 +231,11 @@ const getNonce = async () => {
|
|
|
231
231
|
|
|
232
232
|
const sign = async (
|
|
233
233
|
keyOrAlias: CryptoKey | string,
|
|
234
|
-
data: Uint8Array
|
|
234
|
+
data: Uint8Array,
|
|
235
235
|
): Promise<ArrayBuffer> => {
|
|
236
236
|
if (typeof keyOrAlias === "string") {
|
|
237
237
|
throw new LightsparkSigningException(
|
|
238
|
-
"Key alias not supported for default crypto."
|
|
238
|
+
"Key alias not supported for default crypto.",
|
|
239
239
|
);
|
|
240
240
|
}
|
|
241
241
|
const cryptoImpl = await getCrypto();
|
|
@@ -245,12 +245,12 @@ const sign = async (
|
|
|
245
245
|
saltLength: 32,
|
|
246
246
|
},
|
|
247
247
|
keyOrAlias as CryptoKey,
|
|
248
|
-
data
|
|
248
|
+
data,
|
|
249
249
|
);
|
|
250
250
|
};
|
|
251
251
|
|
|
252
252
|
const importPrivateSigningKey = async (
|
|
253
|
-
keyData: Uint8Array
|
|
253
|
+
keyData: Uint8Array,
|
|
254
254
|
): Promise<CryptoKey | string> => {
|
|
255
255
|
const cryptoImpl = await getCrypto();
|
|
256
256
|
return await cryptoImpl.subtle.importKey(
|
|
@@ -261,7 +261,7 @@ const importPrivateSigningKey = async (
|
|
|
261
261
|
hash: "SHA-256",
|
|
262
262
|
},
|
|
263
263
|
/*extractable*/ true,
|
|
264
|
-
/*keyUsages*/ ["sign"]
|
|
264
|
+
/*keyUsages*/ ["sign"],
|
|
265
265
|
);
|
|
266
266
|
};
|
|
267
267
|
|
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";
|
|
@@ -33,7 +33,7 @@ class Requester {
|
|
|
33
33
|
private readonly sdkUserAgent: string,
|
|
34
34
|
private readonly authProvider: AuthProvider = new StubAuthProvider(),
|
|
35
35
|
private readonly baseUrl: string = DEFAULT_BASE_URL,
|
|
36
|
-
private readonly cryptoImpl: CryptoInterface = DefaultCrypto
|
|
36
|
+
private readonly cryptoImpl: CryptoInterface = DefaultCrypto,
|
|
37
37
|
) {
|
|
38
38
|
let websocketImpl;
|
|
39
39
|
if (typeof WebSocket === "undefined" && typeof window === "undefined") {
|
|
@@ -58,14 +58,14 @@ class Requester {
|
|
|
58
58
|
query.queryPayload,
|
|
59
59
|
query.variables || {},
|
|
60
60
|
query.signingNodeId,
|
|
61
|
-
!!query.skipAuth
|
|
61
|
+
!!query.skipAuth,
|
|
62
62
|
);
|
|
63
63
|
return query.constructObject(data);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
public subscribe<T>(
|
|
67
67
|
queryPayload: string,
|
|
68
|
-
variables: { [key: string]: unknown } = {}
|
|
68
|
+
variables: { [key: string]: unknown } = {},
|
|
69
69
|
) {
|
|
70
70
|
const operationNameRegex = /^\s*(query|mutation|subscription)\s+(\w+)/i;
|
|
71
71
|
const operationMatch = queryPayload.match(operationNameRegex);
|
|
@@ -76,7 +76,7 @@ class Requester {
|
|
|
76
76
|
if (operationType == "mutation") {
|
|
77
77
|
throw new LightsparkException(
|
|
78
78
|
"InvalidQuery",
|
|
79
|
-
"Mutation queries should call makeRawRequest instead"
|
|
79
|
+
"Mutation queries should call makeRawRequest instead",
|
|
80
80
|
);
|
|
81
81
|
}
|
|
82
82
|
// Undefined variables need to be null instead.
|
|
@@ -97,7 +97,7 @@ class Requester {
|
|
|
97
97
|
next: (data) => observer.next(data as { data: T }),
|
|
98
98
|
error: (err) => observer.error(err),
|
|
99
99
|
complete: () => observer.complete(),
|
|
100
|
-
})
|
|
100
|
+
}),
|
|
101
101
|
);
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -105,7 +105,7 @@ class Requester {
|
|
|
105
105
|
queryPayload: string,
|
|
106
106
|
variables: { [key: string]: unknown } = {},
|
|
107
107
|
signingNodeId: string | undefined = undefined,
|
|
108
|
-
skipAuth: boolean = false
|
|
108
|
+
skipAuth: boolean = false,
|
|
109
109
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any -- LIG-3400 */
|
|
110
110
|
): Promise<any | null> {
|
|
111
111
|
const operationNameRegex = /^\s*(query|mutation|subscription)\s+(\w+)/i;
|
|
@@ -117,7 +117,7 @@ class Requester {
|
|
|
117
117
|
if (operationType == "subscription") {
|
|
118
118
|
throw new LightsparkException(
|
|
119
119
|
"InvalidQuery",
|
|
120
|
-
"Subscription queries should call subscribe instead"
|
|
120
|
+
"Subscription queries should call subscribe instead",
|
|
121
121
|
);
|
|
122
122
|
}
|
|
123
123
|
// Undefined variables need to be null instead.
|
|
@@ -147,7 +147,7 @@ class Requester {
|
|
|
147
147
|
bodyData = await this.addSigningDataIfNeeded(
|
|
148
148
|
bodyData,
|
|
149
149
|
headers,
|
|
150
|
-
signingNodeId
|
|
150
|
+
signingNodeId,
|
|
151
151
|
);
|
|
152
152
|
|
|
153
153
|
let urlWithProtocol = this.baseUrl;
|
|
@@ -165,7 +165,7 @@ class Requester {
|
|
|
165
165
|
if (!response.ok) {
|
|
166
166
|
throw new LightsparkException(
|
|
167
167
|
"RequestFailed",
|
|
168
|
-
`Request ${operation} failed. ${response.statusText}
|
|
168
|
+
`Request ${operation} failed. ${response.statusText}`,
|
|
169
169
|
);
|
|
170
170
|
}
|
|
171
171
|
const responseJson = await response.json();
|
|
@@ -173,7 +173,7 @@ class Requester {
|
|
|
173
173
|
if (!data) {
|
|
174
174
|
throw new LightsparkException(
|
|
175
175
|
"RequestFailed",
|
|
176
|
-
`Request ${operation} failed. ${JSON.stringify(responseJson.errors)}
|
|
176
|
+
`Request ${operation} failed. ${JSON.stringify(responseJson.errors)}`,
|
|
177
177
|
);
|
|
178
178
|
}
|
|
179
179
|
return data;
|
|
@@ -192,7 +192,7 @@ class Requester {
|
|
|
192
192
|
private async addSigningDataIfNeeded(
|
|
193
193
|
queryPayload: { query: string; variables: unknown; operationName: string },
|
|
194
194
|
headers: { [key: string]: string },
|
|
195
|
-
signingNodeId: string | undefined
|
|
195
|
+
signingNodeId: string | undefined,
|
|
196
196
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any -- LIG-3400 */
|
|
197
197
|
): Promise<any> {
|
|
198
198
|
if (!signingNodeId) {
|
|
@@ -217,7 +217,7 @@ class Requester {
|
|
|
217
217
|
const key = await this.nodeKeyCache.getKey(signingNodeId);
|
|
218
218
|
if (!key) {
|
|
219
219
|
throw new LightsparkSigningException(
|
|
220
|
-
"Missing node of encrypted_signing_private_key"
|
|
220
|
+
"Missing node of encrypted_signing_private_key",
|
|
221
221
|
);
|
|
222
222
|
}
|
|
223
223
|
|
|
@@ -226,11 +226,12 @@ class Requester {
|
|
|
226
226
|
TextEncoderImpl = (await import("text-encoding")).TextEncoder;
|
|
227
227
|
}
|
|
228
228
|
const encodedPayload = new TextEncoderImpl().encode(
|
|
229
|
-
JSON.stringify(payload)
|
|
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,
|
package/src/utils/base64.ts
CHANGED
|
@@ -16,7 +16,7 @@ const Base64 = {
|
|
|
16
16
|
|
|
17
17
|
if (charCode > 0xff) {
|
|
18
18
|
throw new Error(
|
|
19
|
-
"'btoa' failed: The string to be encoded contains characters outside of the Latin1 range."
|
|
19
|
+
"'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.",
|
|
20
20
|
);
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -32,7 +32,7 @@ const Base64 = {
|
|
|
32
32
|
|
|
33
33
|
if (str.length % 4 == 1) {
|
|
34
34
|
throw new Error(
|
|
35
|
-
"'atob' failed: The string to be decoded is not correctly encoded."
|
|
35
|
+
"'atob' failed: The string to be decoded is not correctly encoded.",
|
|
36
36
|
);
|
|
37
37
|
}
|
|
38
38
|
for (
|
|
@@ -59,6 +59,6 @@ export const urlsafe_b64decode = (encoded: string): Uint8Array => {
|
|
|
59
59
|
|
|
60
60
|
export const b64encode = (data: ArrayBuffer): string => {
|
|
61
61
|
return Base64.btoa(
|
|
62
|
-
String.fromCharCode.apply(null, Array.from(new Uint8Array(data)))
|
|
62
|
+
String.fromCharCode.apply(null, Array.from(new Uint8Array(data))),
|
|
63
63
|
);
|
|
64
64
|
};
|
|
@@ -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
|
+
};
|
package/src/utils/currency.ts
CHANGED
|
@@ -101,7 +101,7 @@ const CONVERSION_MAP = {
|
|
|
101
101
|
|
|
102
102
|
export const convertCurrencyAmount = (
|
|
103
103
|
from: CurrencyAmount,
|
|
104
|
-
toUnit: CurrencyUnit
|
|
104
|
+
toUnit: CurrencyUnit,
|
|
105
105
|
): CurrencyAmount => {
|
|
106
106
|
if (
|
|
107
107
|
from.originalUnit === CurrencyUnit.FUTURE_VALUE ||
|
|
@@ -116,7 +116,7 @@ export const convertCurrencyAmount = (
|
|
|
116
116
|
if (!conversionFn) {
|
|
117
117
|
throw new LightsparkException(
|
|
118
118
|
"CurrencyError",
|
|
119
|
-
`Cannot convert from ${from.originalUnit} to ${toUnit}
|
|
119
|
+
`Cannot convert from ${from.originalUnit} to ${toUnit}`,
|
|
120
120
|
);
|
|
121
121
|
}
|
|
122
122
|
return {
|
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,8 @@
|
|
|
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 "./hex.js";
|
|
6
8
|
export * from "./types.js";
|
package/src/utils/types.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type OmitTypename<T> = Omit<T, "__typename">;
|
|
|
17
17
|
export const isType =
|
|
18
18
|
<T extends string>(typename: T) =>
|
|
19
19
|
<N extends { __typename: string }>(
|
|
20
|
-
node: N | undefined | null
|
|
20
|
+
node: N | undefined | null,
|
|
21
21
|
): node is Extract<N, { __typename: T }> => {
|
|
22
22
|
return node?.__typename === typename;
|
|
23
23
|
};
|