@lightsparkdev/core 0.3.11 → 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 +7 -0
- package/dist/index.cjs +109 -8
- package/dist/index.d.ts +33 -3
- package/dist/index.js +102 -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/hex.ts +15 -0
- package/src/utils/index.ts +2 -0
package/CHANGELOG.md
CHANGED
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.
|
|
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
|
+
};
|
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";
|