@lightsparkdev/core 0.2.1 → 0.2.3

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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # @lightsparkdev/core
2
2
 
3
+ ## 0.2.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 8bf7fe0: Patch release for core and wallet sdks.
8
+
9
+ - Expose a function for raw subscriptions from the wallet client.
10
+ - Fix type exports.
11
+ - Fix a parsing bug in the wallet dashboard query
12
+
13
+ ## 0.2.2
14
+
15
+ ### Patch Changes
16
+
17
+ - Refactor internals to allow for a custom react native crypto implementation to be injected into the LightsparkClient.
18
+
3
19
  ## 0.2.1
4
20
 
5
21
  ### Patch Changes
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # The Lightspark JS+TS Core package
2
2
 
3
+ ![npm (scoped)](https://img.shields.io/npm/v/@lightsparkdev/core)
4
+
3
5
  This is the Lightspark Typescript Core package, which can be used either from a node or browser environment. The package contains core objects and utilities that are used by all Lightspark Typescript SDKs and may be useful for your project.
4
6
 
5
7
  ## Getting started
@@ -9,3 +11,5 @@ To use the package, you'll need to install it from npm:
9
11
  ```bash
10
12
  $ npm install @lightsparkdev/core
11
13
  ```
14
+
15
+ TODO
package/dist/index.cjs CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
+ DefaultCrypto: () => DefaultCrypto,
33
34
  LightsparkAuthException: () => LightsparkAuthException_default,
34
35
  LightsparkException: () => LightsparkException_default,
35
36
  LightsparkSigningException: () => LightsparkSigningException_default,
@@ -41,19 +42,9 @@ __export(src_exports, {
41
42
  b64decode: () => b64decode,
42
43
  b64encode: () => b64encode,
43
44
  convertCurrencyAmount: () => convertCurrencyAmount,
44
- decode: () => decode,
45
- decrypt: () => decrypt,
46
- decryptSecretWithNodePassword: () => decryptSecretWithNodePassword,
47
- encrypt: () => encrypt,
48
- encryptWithNodeKey: () => encryptWithNodeKey,
49
- generateSigningKeyPair: () => generateSigningKeyPair,
50
- getCrypto: () => getCrypto,
51
- getNonce: () => getNonce,
52
45
  isBrowser: () => isBrowser,
53
46
  isNode: () => isNode,
54
47
  isType: () => isType,
55
- loadNodeEncryptionKey: () => loadNodeEncryptionKey,
56
- serializeSigningKey: () => serializeSigningKey,
57
48
  urlsafe_b64decode: () => urlsafe_b64decode
58
49
  });
59
50
  module.exports = __toCommonJS(src_exports);
@@ -94,14 +85,44 @@ var StubAuthProvider = class {
94
85
  };
95
86
 
96
87
  // src/utils/base64.ts
88
+ var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
89
+ var Base64 = {
90
+ btoa: (input = "") => {
91
+ let str = input;
92
+ let output = "";
93
+ for (let block = 0, charCode, i = 0, map = chars; str.charAt(i | 0) || (map = "=", i % 1); output += map.charAt(63 & block >> 8 - i % 1 * 8)) {
94
+ charCode = str.charCodeAt(i += 3 / 4);
95
+ if (charCode > 255) {
96
+ throw new Error(
97
+ "'btoa' failed: The string to be encoded contains characters outside of the Latin1 range."
98
+ );
99
+ }
100
+ block = block << 8 | charCode;
101
+ }
102
+ return output;
103
+ },
104
+ atob: (input = "") => {
105
+ let str = input.replace(/=+$/, "");
106
+ let output = "";
107
+ if (str.length % 4 == 1) {
108
+ throw new Error(
109
+ "'atob' failed: The string to be decoded is not correctly encoded."
110
+ );
111
+ }
112
+ for (let bc = 0, bs = 0, buffer, i = 0; buffer = str.charAt(i++); ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0) {
113
+ buffer = chars.indexOf(buffer);
114
+ }
115
+ return output;
116
+ }
117
+ };
97
118
  var b64decode = (encoded) => {
98
- return Uint8Array.from(atob(encoded), (c) => c.charCodeAt(0));
119
+ return Uint8Array.from(Base64.atob(encoded), (c) => c.charCodeAt(0));
99
120
  };
100
121
  var urlsafe_b64decode = (encoded) => {
101
122
  return b64decode(encoded.replace(/_/g, "/").replace(/-/g, "+"));
102
123
  };
103
124
  var b64encode = (data) => {
104
- return btoa(
125
+ return Base64.btoa(
105
126
  String.fromCharCode.apply(null, Array.from(new Uint8Array(data)))
106
127
  );
107
128
  };
@@ -115,8 +136,7 @@ var LightsparkSigningException = class extends LightsparkException_default {
115
136
  var LightsparkSigningException_default = LightsparkSigningException;
116
137
 
117
138
  // src/crypto/crypto.ts
118
- var ITERATIONS = 5e5;
119
- function getCrypto() {
139
+ var getCrypto = () => {
120
140
  let cryptoImplPromise;
121
141
  if (typeof crypto !== "undefined") {
122
142
  cryptoImplPromise = Promise.resolve(crypto);
@@ -126,34 +146,26 @@ function getCrypto() {
126
146
  });
127
147
  }
128
148
  return cryptoImplPromise;
129
- }
130
- var getRandomValues = async (arr) => {
131
- if (typeof crypto !== "undefined") {
132
- return crypto.getRandomValues(arr);
133
- } else {
134
- const cryptoImpl2 = await getCrypto();
135
- return cryptoImpl2.getRandomValues(arr);
136
- }
137
149
  };
138
150
  var getRandomValues32 = async (arr) => {
139
151
  if (typeof crypto !== "undefined") {
140
152
  return crypto.getRandomValues(arr);
141
153
  } else {
142
- const cryptoImpl2 = await getCrypto();
143
- return cryptoImpl2.getRandomValues(arr);
154
+ const cryptoImpl = await getCrypto();
155
+ return cryptoImpl.getRandomValues(arr);
144
156
  }
145
157
  };
146
158
  var deriveKey = async (password, salt, iterations, algorithm, bit_len) => {
147
159
  const enc = new TextEncoder();
148
- const cryptoImpl2 = await getCrypto();
149
- const password_key = await cryptoImpl2.subtle.importKey(
160
+ const cryptoImpl = await getCrypto();
161
+ const password_key = await cryptoImpl.subtle.importKey(
150
162
  "raw",
151
163
  enc.encode(password),
152
164
  "PBKDF2",
153
165
  false,
154
166
  ["deriveBits", "deriveKey"]
155
167
  );
156
- const derived = await cryptoImpl2.subtle.deriveBits(
168
+ const derived = await cryptoImpl.subtle.deriveBits(
157
169
  {
158
170
  name: "PBKDF2",
159
171
  salt,
@@ -163,7 +175,7 @@ var deriveKey = async (password, salt, iterations, algorithm, bit_len) => {
163
175
  password_key,
164
176
  bit_len
165
177
  );
166
- const key = await cryptoImpl2.subtle.importKey(
178
+ const key = await cryptoImpl.subtle.importKey(
167
179
  "raw",
168
180
  derived.slice(0, 32),
169
181
  { name: algorithm, length: 256 },
@@ -173,25 +185,6 @@ var deriveKey = async (password, salt, iterations, algorithm, bit_len) => {
173
185
  const iv = derived.slice(32);
174
186
  return [key, iv];
175
187
  };
176
- var encrypt = async (plaintext, password, salt) => {
177
- if (!salt) {
178
- salt = new Uint8Array(16);
179
- getRandomValues(salt);
180
- }
181
- const [key, iv] = await deriveKey(password, salt, ITERATIONS, "AES-GCM", 352);
182
- const cryptoImpl2 = await getCrypto();
183
- const encrypted = new Uint8Array(
184
- await cryptoImpl2.subtle.encrypt({ name: "AES-GCM", iv }, key, plaintext)
185
- );
186
- const output = new Uint8Array(salt.byteLength + encrypted.byteLength);
187
- output.set(salt);
188
- output.set(encrypted, salt.byteLength);
189
- const header = {
190
- v: 4,
191
- i: ITERATIONS
192
- };
193
- return [JSON.stringify(header), b64encode(output)];
194
- };
195
188
  var decrypt = async (header_json, ciphertext, password) => {
196
189
  var decoded = b64decode(ciphertext);
197
190
  var header;
@@ -210,7 +203,7 @@ var decrypt = async (header_json, ciphertext, password) => {
210
203
  "Unknown version ".concat(header.v)
211
204
  );
212
205
  }
213
- const cryptoImpl2 = await getCrypto();
206
+ const cryptoImpl = await getCrypto();
214
207
  const algorithm = header.v < 2 ? "AES-CBC" : "AES-GCM";
215
208
  const bit_len = header.v < 4 ? 384 : 352;
216
209
  const salt_len = header.v < 4 ? 8 : 16;
@@ -225,7 +218,7 @@ var decrypt = async (header_json, ciphertext, password) => {
225
218
  algorithm,
226
219
  256
227
220
  );
228
- return await cryptoImpl2.subtle.decrypt(
221
+ return await cryptoImpl.subtle.decrypt(
229
222
  { name: algorithm, iv: nonce.buffer },
230
223
  key,
231
224
  cipherText
@@ -240,7 +233,7 @@ var decrypt = async (header_json, ciphertext, password) => {
240
233
  algorithm,
241
234
  bit_len
242
235
  );
243
- return await cryptoImpl2.subtle.decrypt(
236
+ return await cryptoImpl.subtle.decrypt(
244
237
  { name: algorithm, iv },
245
238
  key,
246
239
  encrypted
@@ -256,13 +249,9 @@ async function decryptSecretWithNodePassword(cipher, encryptedSecret, nodePasswo
256
249
  }
257
250
  return decryptedValue;
258
251
  }
259
- function decode(arrBuff) {
260
- const dec = new TextDecoder();
261
- return dec.decode(arrBuff);
262
- }
263
252
  var generateSigningKeyPair = async () => {
264
- const cryptoImpl2 = await getCrypto();
265
- return await cryptoImpl2.subtle.generateKey(
253
+ const cryptoImpl = await getCrypto();
254
+ return await cryptoImpl.subtle.generateKey(
266
255
  /*algorithm:*/
267
256
  {
268
257
  name: "RSA-PSS",
@@ -277,74 +266,72 @@ var generateSigningKeyPair = async () => {
277
266
  );
278
267
  };
279
268
  var serializeSigningKey = async (key, format) => {
280
- const cryptoImpl2 = await getCrypto();
281
- return await cryptoImpl2.subtle.exportKey(
269
+ const cryptoImpl = await getCrypto();
270
+ return await cryptoImpl.subtle.exportKey(
282
271
  /*format*/
283
272
  format,
284
273
  /*key*/
285
274
  key
286
275
  );
287
276
  };
288
- var encryptWithNodeKey = async (key, data) => {
289
- const enc = new TextEncoder();
290
- const encoded = enc.encode(data);
291
- const encrypted = await cryptoImpl.subtle.encrypt(
292
- /*algorithm:*/
277
+ var getNonce = async () => {
278
+ const cryptoImpl = await getCrypto();
279
+ const nonceSt = await getRandomValues32(new Uint32Array(1));
280
+ return Number(nonceSt);
281
+ };
282
+ var sign = async (key, data) => {
283
+ const cryptoImpl = await getCrypto();
284
+ return await cryptoImpl.subtle.sign(
293
285
  {
294
- name: "RSA-OAEP"
286
+ name: "RSA-PSS",
287
+ saltLength: 32
295
288
  },
296
- /*key*/
297
289
  key,
298
- /*data*/
299
- encoded
290
+ data
300
291
  );
301
- return b64encode(encrypted);
302
292
  };
303
- var loadNodeEncryptionKey = async (rawPublicKey) => {
304
- const encoded = b64decode(rawPublicKey);
305
- const cryptoImpl2 = await getCrypto();
306
- return await cryptoImpl2.subtle.importKey(
293
+ var importPrivateSigningKey = async (keyData, format) => {
294
+ const cryptoImpl = await getCrypto();
295
+ return await cryptoImpl.subtle.importKey(
307
296
  /*format*/
308
- "spki",
297
+ format,
309
298
  /*keyData*/
310
- encoded,
311
- /*algorithm:*/
299
+ keyData,
300
+ /*algorithm*/
312
301
  {
313
- name: "RSA-OAEP",
302
+ name: "RSA-PSS",
314
303
  hash: "SHA-256"
315
304
  },
316
305
  /*extractable*/
317
306
  true,
318
307
  /*keyUsages*/
319
- ["encrypt"]
308
+ ["sign"]
320
309
  );
321
310
  };
322
- var getNonce = async () => {
323
- const nonceSt = await getRandomValues32(new Uint32Array(1));
324
- return Number(nonceSt);
311
+ var DefaultCrypto = {
312
+ decryptSecretWithNodePassword,
313
+ generateSigningKeyPair,
314
+ serializeSigningKey,
315
+ getNonce,
316
+ sign,
317
+ importPrivateSigningKey
325
318
  };
326
319
 
327
320
  // src/crypto/NodeKeyCache.ts
328
321
  var import_auto_bind = __toESM(require("auto-bind"), 1);
329
322
  var NodeKeyCache = class {
330
- idToKey;
331
- constructor() {
323
+ constructor(cryptoImpl = DefaultCrypto) {
324
+ this.cryptoImpl = cryptoImpl;
332
325
  this.idToKey = /* @__PURE__ */ new Map();
333
326
  (0, import_auto_bind.default)(this);
334
327
  }
335
- async loadKey(id, rawKey) {
336
- const decoded = b64decode(rawKey);
328
+ idToKey;
329
+ async loadKey(id, rawKey, format = "pkcs8") {
330
+ const decoded = b64decode(this.stripPemTags(rawKey));
337
331
  try {
338
- const cryptoImpl2 = await getCrypto();
339
- const key = await cryptoImpl2.subtle.importKey(
340
- "pkcs8",
332
+ const key = await this.cryptoImpl.importPrivateSigningKey(
341
333
  decoded,
342
- {
343
- name: "RSA-PSS",
344
- hash: "SHA-256"
345
- },
346
- true,
347
- ["sign"]
334
+ format
348
335
  );
349
336
  this.idToKey.set(id, key);
350
337
  return key;
@@ -359,6 +346,9 @@ var NodeKeyCache = class {
359
346
  hasKey(id) {
360
347
  return this.idToKey.has(id);
361
348
  }
349
+ stripPemTags(pem) {
350
+ return pem.replace(/-----BEGIN (.*)-----/, "").replace(/-----END (.*)----/, "");
351
+ }
362
352
  };
363
353
  var NodeKeyCache_default = NodeKeyCache;
364
354
 
@@ -380,12 +370,13 @@ var LIGHTSPARK_BETA_HEADER_KEY = "X-Lightspark-Beta";
380
370
  var LIGHTSPARK_BETA_HEADER_VALUE = "z2h0BBYxTA83cjW7fi8QwWtBPCzkQKiemcuhKY08LOo";
381
371
  import_dayjs.default.extend(import_utc.default);
382
372
  var Requester = class {
383
- constructor(nodeKeyCache, schemaEndpoint, sdkUserAgent, authProvider = new StubAuthProvider(), baseUrl = DEFAULT_BASE_URL) {
373
+ constructor(nodeKeyCache, schemaEndpoint, sdkUserAgent, authProvider = new StubAuthProvider(), baseUrl = DEFAULT_BASE_URL, cryptoImpl = DefaultCrypto) {
384
374
  this.nodeKeyCache = nodeKeyCache;
385
375
  this.schemaEndpoint = schemaEndpoint;
386
376
  this.sdkUserAgent = sdkUserAgent;
387
377
  this.authProvider = authProvider;
388
378
  this.baseUrl = baseUrl;
379
+ this.cryptoImpl = cryptoImpl;
389
380
  let websocketImpl;
390
381
  if (typeof WebSocket === "undefined" && typeof window === "undefined") {
391
382
  websocketImpl = import_ws.default;
@@ -511,7 +502,7 @@ var Requester = class {
511
502
  const query = queryPayload.query;
512
503
  const variables = queryPayload.variables;
513
504
  const operationName = queryPayload.operationName;
514
- const nonce = await getNonce();
505
+ const nonce = await this.cryptoImpl.getNonce();
515
506
  const expiration = import_dayjs.default.utc().add(1, "hour").format();
516
507
  const payload = {
517
508
  query,
@@ -526,16 +517,11 @@ var Requester = class {
526
517
  "Missing node of encrypted_signing_private_key"
527
518
  );
528
519
  }
520
+ if (typeof TextEncoder === "undefined") {
521
+ const TextEncoder2 = (await import("text-encoding")).TextEncoder;
522
+ }
529
523
  const encodedPayload = new TextEncoder().encode(JSON.stringify(payload));
530
- const cryptoImpl2 = await getCrypto();
531
- const signedPayload = await cryptoImpl2.subtle.sign(
532
- {
533
- name: "RSA-PSS",
534
- saltLength: 32
535
- },
536
- key,
537
- encodedPayload
538
- );
524
+ const signedPayload = await this.cryptoImpl.sign(key, encodedPayload);
539
525
  const encodedSignedPayload = b64encode(signedPayload);
540
526
  headers["X-Lightspark-Signing"] = JSON.stringify({
541
527
  v: "1",
@@ -635,6 +621,7 @@ var isType = (typename) => (node) => {
635
621
  };
636
622
  // Annotate the CommonJS export names for ESM import in node:
637
623
  0 && (module.exports = {
624
+ DefaultCrypto,
638
625
  LightsparkAuthException,
639
626
  LightsparkException,
640
627
  LightsparkSigningException,
@@ -646,18 +633,8 @@ var isType = (typename) => (node) => {
646
633
  b64decode,
647
634
  b64encode,
648
635
  convertCurrencyAmount,
649
- decode,
650
- decrypt,
651
- decryptSecretWithNodePassword,
652
- encrypt,
653
- encryptWithNodeKey,
654
- generateSigningKeyPair,
655
- getCrypto,
656
- getNonce,
657
636
  isBrowser,
658
637
  isNode,
659
638
  isType,
660
- loadNodeEncryptionKey,
661
- serializeSigningKey,
662
639
  urlsafe_b64decode
663
640
  });
package/dist/index.d.ts CHANGED
@@ -27,26 +27,38 @@ declare class LightsparkSigningException extends LightsparkException {
27
27
  constructor(message: string, extraInfo?: any);
28
28
  }
29
29
 
30
- declare function getCrypto(): Promise<Crypto>;
31
- declare const encrypt: (plaintext: ArrayBuffer, password: string, salt?: Uint8Array) => Promise<[
32
- string,
33
- string
34
- ]>;
35
- declare const decrypt: (header_json: string, ciphertext: string, password: string) => Promise<ArrayBuffer>;
30
+ type CryptoInterface = {
31
+ decryptSecretWithNodePassword: (cipher: string, encryptedSecret: string, nodePassword: string) => Promise<ArrayBuffer | null>;
32
+ generateSigningKeyPair: () => Promise<{
33
+ publicKey: CryptoKey | string;
34
+ privateKey: CryptoKey | string;
35
+ }>;
36
+ serializeSigningKey: (key: CryptoKey | string, format: "pkcs8" | "spki") => Promise<ArrayBuffer>;
37
+ getNonce: () => Promise<number>;
38
+ sign: (key: CryptoKey | Uint8Array, data: Uint8Array) => Promise<ArrayBuffer>;
39
+ importPrivateSigningKey: (keyData: Uint8Array, format: "pkcs8" | "spki") => Promise<CryptoKey | Uint8Array>;
40
+ };
36
41
  declare function decryptSecretWithNodePassword(cipher: string, encryptedSecret: string, nodePassword: string): Promise<ArrayBuffer | null>;
37
- declare function decode(arrBuff: ArrayBuffer): string;
38
- declare const generateSigningKeyPair: () => Promise<CryptoKeyPair>;
39
- declare const serializeSigningKey: (key: CryptoKey, format: "pkcs8" | "spki") => Promise<ArrayBuffer>;
40
- declare const encryptWithNodeKey: (key: CryptoKey, data: string) => Promise<string>;
41
- declare const loadNodeEncryptionKey: (rawPublicKey: string) => Promise<CryptoKey>;
42
- declare const getNonce: () => Promise<number>;
42
+ declare const DefaultCrypto: {
43
+ decryptSecretWithNodePassword: typeof decryptSecretWithNodePassword;
44
+ generateSigningKeyPair: () => Promise<CryptoKeyPair | {
45
+ publicKey: string;
46
+ privateKey: string;
47
+ }>;
48
+ serializeSigningKey: (key: CryptoKey | string, format: "pkcs8" | "spki") => Promise<ArrayBuffer>;
49
+ getNonce: () => Promise<number>;
50
+ sign: (key: CryptoKey | Uint8Array, data: Uint8Array) => Promise<ArrayBuffer>;
51
+ importPrivateSigningKey: (keyData: Uint8Array, format: "pkcs8" | "spki") => Promise<CryptoKey | Uint8Array>;
52
+ };
43
53
 
44
54
  declare class NodeKeyCache {
55
+ private readonly cryptoImpl;
45
56
  private idToKey;
46
- constructor();
47
- loadKey(id: string, rawKey: string): Promise<CryptoKey | null>;
48
- getKey(id: string): CryptoKey | undefined;
57
+ constructor(cryptoImpl?: CryptoInterface);
58
+ loadKey(id: string, rawKey: string, format?: "pkcs8" | "spki"): Promise<CryptoKey | Uint8Array | null>;
59
+ getKey(id: string): CryptoKey | Uint8Array | undefined;
49
60
  hasKey(id: string): boolean;
61
+ private stripPemTags;
50
62
  }
51
63
 
52
64
  type Query<T> = {
@@ -68,8 +80,9 @@ declare class Requester {
68
80
  private readonly sdkUserAgent;
69
81
  private readonly authProvider;
70
82
  private readonly baseUrl;
83
+ private readonly cryptoImpl;
71
84
  private readonly wsClient;
72
- constructor(nodeKeyCache: NodeKeyCache, schemaEndpoint: string, sdkUserAgent: string, authProvider?: AuthProvider, baseUrl?: string);
85
+ constructor(nodeKeyCache: NodeKeyCache, schemaEndpoint: string, sdkUserAgent: string, authProvider?: AuthProvider, baseUrl?: string, cryptoImpl?: CryptoInterface);
73
86
  executeQuery<T>(query: Query<T>): Promise<T | null>;
74
87
  subscribe(queryPayload: string, variables?: {
75
88
  [key: string]: any;
@@ -150,4 +163,4 @@ declare const isType: <T extends string>(typename: T) => <N extends {
150
163
  __typename: T;
151
164
  }>;
152
165
 
153
- export { AuthProvider, ById, ExpandRecursively, LightsparkAuthException, LightsparkException, LightsparkSigningException, Maybe, NodeKeyCache, OmitTypename, Query, Requester, ServerEnvironment, StubAuthProvider, apiDomainForEnvironment, b64decode, b64encode, convertCurrencyAmount, decode, decrypt, decryptSecretWithNodePassword, encrypt, encryptWithNodeKey, generateSigningKeyPair, getCrypto, getNonce, isBrowser, isNode, isType, loadNodeEncryptionKey, serializeSigningKey, urlsafe_b64decode };
166
+ export { AuthProvider, ById, CryptoInterface, DefaultCrypto, ExpandRecursively, LightsparkAuthException, LightsparkException, LightsparkSigningException, Maybe, NodeKeyCache, OmitTypename, Query, Requester, ServerEnvironment, StubAuthProvider, apiDomainForEnvironment, b64decode, b64encode, convertCurrencyAmount, isBrowser, isNode, isType, urlsafe_b64decode };
package/dist/index.js CHANGED
@@ -34,14 +34,44 @@ var StubAuthProvider = class {
34
34
  };
35
35
 
36
36
  // src/utils/base64.ts
37
+ var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
38
+ var Base64 = {
39
+ btoa: (input = "") => {
40
+ let str = input;
41
+ let output = "";
42
+ for (let block = 0, charCode, i = 0, map = chars; str.charAt(i | 0) || (map = "=", i % 1); output += map.charAt(63 & block >> 8 - i % 1 * 8)) {
43
+ charCode = str.charCodeAt(i += 3 / 4);
44
+ if (charCode > 255) {
45
+ throw new Error(
46
+ "'btoa' failed: The string to be encoded contains characters outside of the Latin1 range."
47
+ );
48
+ }
49
+ block = block << 8 | charCode;
50
+ }
51
+ return output;
52
+ },
53
+ atob: (input = "") => {
54
+ let str = input.replace(/=+$/, "");
55
+ let output = "";
56
+ if (str.length % 4 == 1) {
57
+ throw new Error(
58
+ "'atob' failed: The string to be decoded is not correctly encoded."
59
+ );
60
+ }
61
+ for (let bc = 0, bs = 0, buffer, i = 0; buffer = str.charAt(i++); ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0) {
62
+ buffer = chars.indexOf(buffer);
63
+ }
64
+ return output;
65
+ }
66
+ };
37
67
  var b64decode = (encoded) => {
38
- return Uint8Array.from(atob(encoded), (c) => c.charCodeAt(0));
68
+ return Uint8Array.from(Base64.atob(encoded), (c) => c.charCodeAt(0));
39
69
  };
40
70
  var urlsafe_b64decode = (encoded) => {
41
71
  return b64decode(encoded.replace(/_/g, "/").replace(/-/g, "+"));
42
72
  };
43
73
  var b64encode = (data) => {
44
- return btoa(
74
+ return Base64.btoa(
45
75
  String.fromCharCode.apply(null, Array.from(new Uint8Array(data)))
46
76
  );
47
77
  };
@@ -55,8 +85,7 @@ var LightsparkSigningException = class extends LightsparkException_default {
55
85
  var LightsparkSigningException_default = LightsparkSigningException;
56
86
 
57
87
  // src/crypto/crypto.ts
58
- var ITERATIONS = 5e5;
59
- function getCrypto() {
88
+ var getCrypto = () => {
60
89
  let cryptoImplPromise;
61
90
  if (typeof crypto !== "undefined") {
62
91
  cryptoImplPromise = Promise.resolve(crypto);
@@ -66,34 +95,26 @@ function getCrypto() {
66
95
  });
67
96
  }
68
97
  return cryptoImplPromise;
69
- }
70
- var getRandomValues = async (arr) => {
71
- if (typeof crypto !== "undefined") {
72
- return crypto.getRandomValues(arr);
73
- } else {
74
- const cryptoImpl2 = await getCrypto();
75
- return cryptoImpl2.getRandomValues(arr);
76
- }
77
98
  };
78
99
  var getRandomValues32 = async (arr) => {
79
100
  if (typeof crypto !== "undefined") {
80
101
  return crypto.getRandomValues(arr);
81
102
  } else {
82
- const cryptoImpl2 = await getCrypto();
83
- return cryptoImpl2.getRandomValues(arr);
103
+ const cryptoImpl = await getCrypto();
104
+ return cryptoImpl.getRandomValues(arr);
84
105
  }
85
106
  };
86
107
  var deriveKey = async (password, salt, iterations, algorithm, bit_len) => {
87
108
  const enc = new TextEncoder();
88
- const cryptoImpl2 = await getCrypto();
89
- const password_key = await cryptoImpl2.subtle.importKey(
109
+ const cryptoImpl = await getCrypto();
110
+ const password_key = await cryptoImpl.subtle.importKey(
90
111
  "raw",
91
112
  enc.encode(password),
92
113
  "PBKDF2",
93
114
  false,
94
115
  ["deriveBits", "deriveKey"]
95
116
  );
96
- const derived = await cryptoImpl2.subtle.deriveBits(
117
+ const derived = await cryptoImpl.subtle.deriveBits(
97
118
  {
98
119
  name: "PBKDF2",
99
120
  salt,
@@ -103,7 +124,7 @@ var deriveKey = async (password, salt, iterations, algorithm, bit_len) => {
103
124
  password_key,
104
125
  bit_len
105
126
  );
106
- const key = await cryptoImpl2.subtle.importKey(
127
+ const key = await cryptoImpl.subtle.importKey(
107
128
  "raw",
108
129
  derived.slice(0, 32),
109
130
  { name: algorithm, length: 256 },
@@ -113,25 +134,6 @@ var deriveKey = async (password, salt, iterations, algorithm, bit_len) => {
113
134
  const iv = derived.slice(32);
114
135
  return [key, iv];
115
136
  };
116
- var encrypt = async (plaintext, password, salt) => {
117
- if (!salt) {
118
- salt = new Uint8Array(16);
119
- getRandomValues(salt);
120
- }
121
- const [key, iv] = await deriveKey(password, salt, ITERATIONS, "AES-GCM", 352);
122
- const cryptoImpl2 = await getCrypto();
123
- const encrypted = new Uint8Array(
124
- await cryptoImpl2.subtle.encrypt({ name: "AES-GCM", iv }, key, plaintext)
125
- );
126
- const output = new Uint8Array(salt.byteLength + encrypted.byteLength);
127
- output.set(salt);
128
- output.set(encrypted, salt.byteLength);
129
- const header = {
130
- v: 4,
131
- i: ITERATIONS
132
- };
133
- return [JSON.stringify(header), b64encode(output)];
134
- };
135
137
  var decrypt = async (header_json, ciphertext, password) => {
136
138
  var decoded = b64decode(ciphertext);
137
139
  var header;
@@ -150,7 +152,7 @@ var decrypt = async (header_json, ciphertext, password) => {
150
152
  "Unknown version ".concat(header.v)
151
153
  );
152
154
  }
153
- const cryptoImpl2 = await getCrypto();
155
+ const cryptoImpl = await getCrypto();
154
156
  const algorithm = header.v < 2 ? "AES-CBC" : "AES-GCM";
155
157
  const bit_len = header.v < 4 ? 384 : 352;
156
158
  const salt_len = header.v < 4 ? 8 : 16;
@@ -165,7 +167,7 @@ var decrypt = async (header_json, ciphertext, password) => {
165
167
  algorithm,
166
168
  256
167
169
  );
168
- return await cryptoImpl2.subtle.decrypt(
170
+ return await cryptoImpl.subtle.decrypt(
169
171
  { name: algorithm, iv: nonce.buffer },
170
172
  key,
171
173
  cipherText
@@ -180,7 +182,7 @@ var decrypt = async (header_json, ciphertext, password) => {
180
182
  algorithm,
181
183
  bit_len
182
184
  );
183
- return await cryptoImpl2.subtle.decrypt(
185
+ return await cryptoImpl.subtle.decrypt(
184
186
  { name: algorithm, iv },
185
187
  key,
186
188
  encrypted
@@ -196,13 +198,9 @@ async function decryptSecretWithNodePassword(cipher, encryptedSecret, nodePasswo
196
198
  }
197
199
  return decryptedValue;
198
200
  }
199
- function decode(arrBuff) {
200
- const dec = new TextDecoder();
201
- return dec.decode(arrBuff);
202
- }
203
201
  var generateSigningKeyPair = async () => {
204
- const cryptoImpl2 = await getCrypto();
205
- return await cryptoImpl2.subtle.generateKey(
202
+ const cryptoImpl = await getCrypto();
203
+ return await cryptoImpl.subtle.generateKey(
206
204
  /*algorithm:*/
207
205
  {
208
206
  name: "RSA-PSS",
@@ -217,74 +215,72 @@ var generateSigningKeyPair = async () => {
217
215
  );
218
216
  };
219
217
  var serializeSigningKey = async (key, format) => {
220
- const cryptoImpl2 = await getCrypto();
221
- return await cryptoImpl2.subtle.exportKey(
218
+ const cryptoImpl = await getCrypto();
219
+ return await cryptoImpl.subtle.exportKey(
222
220
  /*format*/
223
221
  format,
224
222
  /*key*/
225
223
  key
226
224
  );
227
225
  };
228
- var encryptWithNodeKey = async (key, data) => {
229
- const enc = new TextEncoder();
230
- const encoded = enc.encode(data);
231
- const encrypted = await cryptoImpl.subtle.encrypt(
232
- /*algorithm:*/
226
+ var getNonce = async () => {
227
+ const cryptoImpl = await getCrypto();
228
+ const nonceSt = await getRandomValues32(new Uint32Array(1));
229
+ return Number(nonceSt);
230
+ };
231
+ var sign = async (key, data) => {
232
+ const cryptoImpl = await getCrypto();
233
+ return await cryptoImpl.subtle.sign(
233
234
  {
234
- name: "RSA-OAEP"
235
+ name: "RSA-PSS",
236
+ saltLength: 32
235
237
  },
236
- /*key*/
237
238
  key,
238
- /*data*/
239
- encoded
239
+ data
240
240
  );
241
- return b64encode(encrypted);
242
241
  };
243
- var loadNodeEncryptionKey = async (rawPublicKey) => {
244
- const encoded = b64decode(rawPublicKey);
245
- const cryptoImpl2 = await getCrypto();
246
- return await cryptoImpl2.subtle.importKey(
242
+ var importPrivateSigningKey = async (keyData, format) => {
243
+ const cryptoImpl = await getCrypto();
244
+ return await cryptoImpl.subtle.importKey(
247
245
  /*format*/
248
- "spki",
246
+ format,
249
247
  /*keyData*/
250
- encoded,
251
- /*algorithm:*/
248
+ keyData,
249
+ /*algorithm*/
252
250
  {
253
- name: "RSA-OAEP",
251
+ name: "RSA-PSS",
254
252
  hash: "SHA-256"
255
253
  },
256
254
  /*extractable*/
257
255
  true,
258
256
  /*keyUsages*/
259
- ["encrypt"]
257
+ ["sign"]
260
258
  );
261
259
  };
262
- var getNonce = async () => {
263
- const nonceSt = await getRandomValues32(new Uint32Array(1));
264
- return Number(nonceSt);
260
+ var DefaultCrypto = {
261
+ decryptSecretWithNodePassword,
262
+ generateSigningKeyPair,
263
+ serializeSigningKey,
264
+ getNonce,
265
+ sign,
266
+ importPrivateSigningKey
265
267
  };
266
268
 
267
269
  // src/crypto/NodeKeyCache.ts
268
270
  import autoBind from "auto-bind";
269
271
  var NodeKeyCache = class {
270
- idToKey;
271
- constructor() {
272
+ constructor(cryptoImpl = DefaultCrypto) {
273
+ this.cryptoImpl = cryptoImpl;
272
274
  this.idToKey = /* @__PURE__ */ new Map();
273
275
  autoBind(this);
274
276
  }
275
- async loadKey(id, rawKey) {
276
- const decoded = b64decode(rawKey);
277
+ idToKey;
278
+ async loadKey(id, rawKey, format = "pkcs8") {
279
+ const decoded = b64decode(this.stripPemTags(rawKey));
277
280
  try {
278
- const cryptoImpl2 = await getCrypto();
279
- const key = await cryptoImpl2.subtle.importKey(
280
- "pkcs8",
281
+ const key = await this.cryptoImpl.importPrivateSigningKey(
281
282
  decoded,
282
- {
283
- name: "RSA-PSS",
284
- hash: "SHA-256"
285
- },
286
- true,
287
- ["sign"]
283
+ format
288
284
  );
289
285
  this.idToKey.set(id, key);
290
286
  return key;
@@ -299,6 +295,9 @@ var NodeKeyCache = class {
299
295
  hasKey(id) {
300
296
  return this.idToKey.has(id);
301
297
  }
298
+ stripPemTags(pem) {
299
+ return pem.replace(/-----BEGIN (.*)-----/, "").replace(/-----END (.*)----/, "");
300
+ }
302
301
  };
303
302
  var NodeKeyCache_default = NodeKeyCache;
304
303
 
@@ -320,12 +319,13 @@ var LIGHTSPARK_BETA_HEADER_KEY = "X-Lightspark-Beta";
320
319
  var LIGHTSPARK_BETA_HEADER_VALUE = "z2h0BBYxTA83cjW7fi8QwWtBPCzkQKiemcuhKY08LOo";
321
320
  dayjs.extend(utc);
322
321
  var Requester = class {
323
- constructor(nodeKeyCache, schemaEndpoint, sdkUserAgent, authProvider = new StubAuthProvider(), baseUrl = DEFAULT_BASE_URL) {
322
+ constructor(nodeKeyCache, schemaEndpoint, sdkUserAgent, authProvider = new StubAuthProvider(), baseUrl = DEFAULT_BASE_URL, cryptoImpl = DefaultCrypto) {
324
323
  this.nodeKeyCache = nodeKeyCache;
325
324
  this.schemaEndpoint = schemaEndpoint;
326
325
  this.sdkUserAgent = sdkUserAgent;
327
326
  this.authProvider = authProvider;
328
327
  this.baseUrl = baseUrl;
328
+ this.cryptoImpl = cryptoImpl;
329
329
  let websocketImpl;
330
330
  if (typeof WebSocket === "undefined" && typeof window === "undefined") {
331
331
  websocketImpl = NodeWebSocket;
@@ -451,7 +451,7 @@ var Requester = class {
451
451
  const query = queryPayload.query;
452
452
  const variables = queryPayload.variables;
453
453
  const operationName = queryPayload.operationName;
454
- const nonce = await getNonce();
454
+ const nonce = await this.cryptoImpl.getNonce();
455
455
  const expiration = dayjs.utc().add(1, "hour").format();
456
456
  const payload = {
457
457
  query,
@@ -466,16 +466,11 @@ var Requester = class {
466
466
  "Missing node of encrypted_signing_private_key"
467
467
  );
468
468
  }
469
+ if (typeof TextEncoder === "undefined") {
470
+ const TextEncoder2 = (await import("text-encoding")).TextEncoder;
471
+ }
469
472
  const encodedPayload = new TextEncoder().encode(JSON.stringify(payload));
470
- const cryptoImpl2 = await getCrypto();
471
- const signedPayload = await cryptoImpl2.subtle.sign(
472
- {
473
- name: "RSA-PSS",
474
- saltLength: 32
475
- },
476
- key,
477
- encodedPayload
478
- );
473
+ const signedPayload = await this.cryptoImpl.sign(key, encodedPayload);
479
474
  const encodedSignedPayload = b64encode(signedPayload);
480
475
  headers["X-Lightspark-Signing"] = JSON.stringify({
481
476
  v: "1",
@@ -574,6 +569,7 @@ var isType = (typename) => (node) => {
574
569
  return node?.__typename === typename;
575
570
  };
576
571
  export {
572
+ DefaultCrypto,
577
573
  LightsparkAuthException_default as LightsparkAuthException,
578
574
  LightsparkException_default as LightsparkException,
579
575
  LightsparkSigningException_default as LightsparkSigningException,
@@ -585,18 +581,8 @@ export {
585
581
  b64decode,
586
582
  b64encode,
587
583
  convertCurrencyAmount,
588
- decode,
589
- decrypt,
590
- decryptSecretWithNodePassword,
591
- encrypt,
592
- encryptWithNodeKey,
593
- generateSigningKeyPair,
594
- getCrypto,
595
- getNonce,
596
584
  isBrowser,
597
585
  isNode,
598
586
  isType,
599
- loadNodeEncryptionKey,
600
- serializeSigningKey,
601
587
  urlsafe_b64decode
602
588
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightsparkdev/core",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Lightspark JS SDK",
5
5
  "author": "Lightspark Inc.",
6
6
  "keywords": [
@@ -22,6 +22,19 @@
22
22
  "main": "./dist/index.js",
23
23
  "module": "./dist/index.js",
24
24
  "types": "./dist/index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": {
29
+ "types": "./dist/index.d.ts",
30
+ "default": "./dist/index.js"
31
+ },
32
+ "require": {
33
+ "types": "./dist/index.d.ts",
34
+ "default": "./dist/index.cjs"
35
+ }
36
+ }
37
+ },
25
38
  "engines": {
26
39
  "node": ">=14.16"
27
40
  },
@@ -52,6 +65,7 @@
52
65
  "dayjs": "^1.11.7",
53
66
  "graphql": "^16.6.0",
54
67
  "graphql-ws": "^5.11.3",
68
+ "text-encoding": "^0.7.0",
55
69
  "ws": "^8.12.1",
56
70
  "zen-observable-ts": "^1.1.0"
57
71
  },
@@ -3,28 +3,25 @@
3
3
  import autoBind from "auto-bind";
4
4
 
5
5
  import { b64decode } from "../utils/base64.js";
6
- import { getCrypto } from "./crypto.js";
6
+ import { CryptoInterface, DefaultCrypto } from "./crypto.js";
7
7
 
8
8
  class NodeKeyCache {
9
- private idToKey: Map<string, CryptoKey>;
10
- constructor() {
9
+ private idToKey: Map<string, CryptoKey | Uint8Array>;
10
+ constructor(private readonly cryptoImpl: CryptoInterface = DefaultCrypto) {
11
11
  this.idToKey = new Map();
12
12
  autoBind(this);
13
13
  }
14
14
 
15
- public async loadKey(id: string, rawKey: string): Promise<CryptoKey | null> {
16
- const decoded = b64decode(rawKey);
15
+ public async loadKey(
16
+ id: string,
17
+ rawKey: string,
18
+ format: "pkcs8" | "spki" = "pkcs8"
19
+ ): Promise<CryptoKey | Uint8Array | null> {
20
+ const decoded = b64decode(this.stripPemTags(rawKey));
17
21
  try {
18
- const cryptoImpl = await getCrypto();
19
- const key = await cryptoImpl.subtle.importKey(
20
- "pkcs8",
22
+ const key = await this.cryptoImpl.importPrivateSigningKey(
21
23
  decoded,
22
- {
23
- name: "RSA-PSS",
24
- hash: "SHA-256",
25
- },
26
- true,
27
- ["sign"]
24
+ format
28
25
  );
29
26
  this.idToKey.set(id, key);
30
27
  return key;
@@ -34,13 +31,19 @@ class NodeKeyCache {
34
31
  return null;
35
32
  }
36
33
 
37
- public getKey(id: string): CryptoKey | undefined {
34
+ public getKey(id: string): CryptoKey | Uint8Array | undefined {
38
35
  return this.idToKey.get(id);
39
36
  }
40
37
 
41
38
  public hasKey(id: string): boolean {
42
39
  return this.idToKey.has(id);
43
40
  }
41
+
42
+ private stripPemTags(pem: string): string {
43
+ return pem
44
+ .replace(/-----BEGIN (.*)-----/, "")
45
+ .replace(/-----END (.*)----/, "");
46
+ }
44
47
  }
45
48
 
46
49
  export default NodeKeyCache;
@@ -1,11 +1,36 @@
1
1
  // Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
2
2
  import LightsparkException from "../LightsparkException.js";
3
3
 
4
- import { b64decode, b64encode } from "../utils/base64.js";
4
+ import { b64decode } from "../utils/base64.js";
5
5
 
6
- const ITERATIONS = 500000;
6
+ export type CryptoInterface = {
7
+ decryptSecretWithNodePassword: (
8
+ cipher: string,
9
+ encryptedSecret: string,
10
+ nodePassword: string
11
+ ) => Promise<ArrayBuffer | null>;
7
12
 
8
- export function getCrypto() {
13
+ generateSigningKeyPair: () => Promise<{
14
+ publicKey: CryptoKey | string;
15
+ privateKey: CryptoKey | string;
16
+ }>;
17
+
18
+ serializeSigningKey: (
19
+ key: CryptoKey | string,
20
+ format: "pkcs8" | "spki"
21
+ ) => Promise<ArrayBuffer>;
22
+
23
+ getNonce: () => Promise<number>;
24
+
25
+ sign: (key: CryptoKey | Uint8Array, data: Uint8Array) => Promise<ArrayBuffer>;
26
+
27
+ importPrivateSigningKey: (
28
+ keyData: Uint8Array,
29
+ format: "pkcs8" | "spki"
30
+ ) => Promise<CryptoKey | Uint8Array>;
31
+ };
32
+
33
+ const getCrypto = () => {
9
34
  let cryptoImplPromise: Promise<typeof crypto>;
10
35
  if (typeof crypto !== "undefined") {
11
36
  cryptoImplPromise = Promise.resolve(crypto);
@@ -15,15 +40,6 @@ export function getCrypto() {
15
40
  });
16
41
  }
17
42
  return cryptoImplPromise;
18
- }
19
-
20
- const getRandomValues = async (arr: Uint8Array): Promise<Uint8Array> => {
21
- if (typeof crypto !== "undefined") {
22
- return crypto.getRandomValues(arr);
23
- } else {
24
- const cryptoImpl = await getCrypto();
25
- return cryptoImpl.getRandomValues(arr);
26
- }
27
43
  };
28
44
 
29
45
  const getRandomValues32 = async (arr: Uint32Array): Promise<Uint32Array> => {
@@ -77,36 +93,7 @@ const deriveKey = async (
77
93
  return [key, iv];
78
94
  };
79
95
 
80
- export const encrypt = async (
81
- plaintext: ArrayBuffer,
82
- password: string,
83
- salt?: Uint8Array
84
- ): Promise<[string, string]> => {
85
- if (!salt) {
86
- salt = new Uint8Array(16);
87
- getRandomValues(salt);
88
- }
89
-
90
- const [key, iv] = await deriveKey(password, salt, ITERATIONS, "AES-GCM", 352);
91
- const cryptoImpl = await getCrypto();
92
-
93
- const encrypted = new Uint8Array(
94
- await cryptoImpl.subtle.encrypt({ name: "AES-GCM", iv }, key, plaintext)
95
- );
96
-
97
- const output = new Uint8Array(salt.byteLength + encrypted.byteLength);
98
- output.set(salt);
99
- output.set(encrypted, salt.byteLength);
100
-
101
- const header = {
102
- v: 4,
103
- i: ITERATIONS,
104
- };
105
-
106
- return [JSON.stringify(header), b64encode(output)];
107
- };
108
-
109
- export const decrypt = async (
96
+ const decrypt = async (
110
97
  header_json: string,
111
98
  ciphertext: string,
112
99
  password: string
@@ -172,7 +159,7 @@ export const decrypt = async (
172
159
  }
173
160
  };
174
161
 
175
- export async function decryptSecretWithNodePassword(
162
+ async function decryptSecretWithNodePassword(
176
163
  cipher: string,
177
164
  encryptedSecret: string,
178
165
  nodePassword: string
@@ -188,12 +175,9 @@ export async function decryptSecretWithNodePassword(
188
175
  return decryptedValue;
189
176
  }
190
177
 
191
- export function decode(arrBuff: ArrayBuffer): string {
192
- const dec = new TextDecoder();
193
- return dec.decode(arrBuff);
194
- }
195
-
196
- export const generateSigningKeyPair = async (): Promise<CryptoKeyPair> => {
178
+ const generateSigningKeyPair = async (): Promise<
179
+ CryptoKeyPair | { publicKey: string; privateKey: string }
180
+ > => {
197
181
  const cryptoImpl = await getCrypto();
198
182
  return await cryptoImpl.subtle.generateKey(
199
183
  /*algorithm:*/ {
@@ -207,52 +191,62 @@ export const generateSigningKeyPair = async (): Promise<CryptoKeyPair> => {
207
191
  );
208
192
  };
209
193
 
210
- export const serializeSigningKey = async (
211
- key: CryptoKey,
194
+ const serializeSigningKey = async (
195
+ key: CryptoKey | string,
212
196
  format: "pkcs8" | "spki"
213
197
  ): Promise<ArrayBuffer> => {
214
198
  const cryptoImpl = await getCrypto();
215
- return await cryptoImpl.subtle.exportKey(/*format*/ format, /*key*/ key);
199
+ return await cryptoImpl.subtle.exportKey(
200
+ /*format*/ format,
201
+ /*key*/ key as CryptoKey
202
+ );
216
203
  };
217
204
 
218
- export const encryptWithNodeKey = async (
219
- key: CryptoKey,
220
- data: string
221
- ): Promise<string> => {
222
- const enc = new TextEncoder();
223
- const encoded = enc.encode(data);
224
- // @ts-ignore
225
- const encrypted = await cryptoImpl.subtle.encrypt(
226
- /*algorithm:*/ {
227
- name: "RSA-OAEP",
205
+ const getNonce = async () => {
206
+ const cryptoImpl = await getCrypto();
207
+ const nonceSt = await getRandomValues32(new Uint32Array(1));
208
+ return Number(nonceSt);
209
+ };
210
+
211
+ const sign = async (
212
+ key: CryptoKey | Uint8Array,
213
+ data: Uint8Array
214
+ ): Promise<ArrayBuffer> => {
215
+ const cryptoImpl = await getCrypto();
216
+ return await cryptoImpl.subtle.sign(
217
+ {
218
+ name: "RSA-PSS",
219
+ saltLength: 32,
228
220
  },
229
- /*key*/ key,
230
- /*data*/ encoded
221
+ key as CryptoKey,
222
+ data
231
223
  );
232
- // @ts-ignore
233
- return b64encode(encrypted);
234
224
  };
235
225
 
236
- export const loadNodeEncryptionKey = async (
237
- rawPublicKey: string
238
- ): Promise<CryptoKey> => {
239
- const encoded = b64decode(rawPublicKey);
226
+ const importPrivateSigningKey = async (
227
+ keyData: Uint8Array,
228
+ format: "pkcs8" | "spki"
229
+ ): Promise<CryptoKey | Uint8Array> => {
240
230
  const cryptoImpl = await getCrypto();
241
231
  return await cryptoImpl.subtle.importKey(
242
- /*format*/ "spki",
243
- /*keyData*/ encoded,
244
- /*algorithm:*/ {
245
- name: "RSA-OAEP",
232
+ /*format*/ format,
233
+ /*keyData*/ keyData,
234
+ /*algorithm*/ {
235
+ name: "RSA-PSS",
246
236
  hash: "SHA-256",
247
237
  },
248
238
  /*extractable*/ true,
249
- /*keyUsages*/ ["encrypt"]
239
+ /*keyUsages*/ ["sign"]
250
240
  );
251
241
  };
252
242
 
253
- export const getNonce = async () => {
254
- const nonceSt = await getRandomValues32(new Uint32Array(1));
255
- return Number(nonceSt);
243
+ export const DefaultCrypto = {
244
+ decryptSecretWithNodePassword,
245
+ generateSigningKeyPair,
246
+ serializeSigningKey,
247
+ getNonce,
248
+ sign,
249
+ importPrivateSigningKey,
256
250
  };
257
251
 
258
252
  export { default as LightsparkSigningException } from "./LightsparkSigningException.js";
@@ -12,8 +12,8 @@ import Query from "./Query.js";
12
12
  import AuthProvider from "../auth/AuthProvider.js";
13
13
  import StubAuthProvider from "../auth/StubAuthProvider.js";
14
14
  import {
15
- getCrypto,
16
- getNonce,
15
+ CryptoInterface,
16
+ DefaultCrypto,
17
17
  LightsparkSigningException,
18
18
  } from "../crypto/crypto.js";
19
19
  import NodeKeyCache from "../crypto/NodeKeyCache.js";
@@ -34,7 +34,8 @@ class Requester {
34
34
  private readonly schemaEndpoint: string,
35
35
  private readonly sdkUserAgent: string,
36
36
  private readonly authProvider: AuthProvider = new StubAuthProvider(),
37
- private readonly baseUrl: string = DEFAULT_BASE_URL
37
+ private readonly baseUrl: string = DEFAULT_BASE_URL,
38
+ private readonly cryptoImpl: CryptoInterface = DefaultCrypto
38
39
  ) {
39
40
  let websocketImpl;
40
41
  if (typeof WebSocket === "undefined" && typeof window === "undefined") {
@@ -183,7 +184,7 @@ class Requester {
183
184
  const variables = queryPayload.variables;
184
185
  const operationName = queryPayload.operationName;
185
186
 
186
- const nonce = await getNonce();
187
+ const nonce = await this.cryptoImpl.getNonce();
187
188
  const expiration = dayjs.utc().add(1, "hour").format();
188
189
 
189
190
  const payload = {
@@ -201,16 +202,11 @@ class Requester {
201
202
  );
202
203
  }
203
204
 
205
+ if (typeof TextEncoder === "undefined") {
206
+ const TextEncoder = (await import("text-encoding")).TextEncoder;
207
+ }
204
208
  const encodedPayload = new TextEncoder().encode(JSON.stringify(payload));
205
- const cryptoImpl = await getCrypto();
206
- const signedPayload = await cryptoImpl.subtle.sign(
207
- {
208
- name: "RSA-PSS",
209
- saltLength: 32,
210
- },
211
- key,
212
- encodedPayload
213
- );
209
+ const signedPayload = await this.cryptoImpl.sign(key, encodedPayload);
214
210
  const encodedSignedPayload = b64encode(signedPayload);
215
211
 
216
212
  headers["X-Lightspark-Signing"] = JSON.stringify({
@@ -1,7 +1,56 @@
1
1
  // Copyright ©, 2023, Lightspark Group, Inc. - All Rights Reserved
2
2
 
3
+ const chars =
4
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
5
+ const Base64 = {
6
+ btoa: (input: string = "") => {
7
+ let str = input;
8
+ let output = "";
9
+
10
+ for (
11
+ let block = 0, charCode, i = 0, map = chars;
12
+ str.charAt(i | 0) || ((map = "="), i % 1);
13
+ output += map.charAt(63 & (block >> (8 - (i % 1) * 8)))
14
+ ) {
15
+ charCode = str.charCodeAt((i += 3 / 4));
16
+
17
+ if (charCode > 0xff) {
18
+ throw new Error(
19
+ "'btoa' failed: The string to be encoded contains characters outside of the Latin1 range."
20
+ );
21
+ }
22
+
23
+ block = (block << 8) | charCode;
24
+ }
25
+
26
+ return output;
27
+ },
28
+
29
+ atob: (input: string = "") => {
30
+ let str = input.replace(/=+$/, "");
31
+ let output = "";
32
+
33
+ if (str.length % 4 == 1) {
34
+ throw new Error(
35
+ "'atob' failed: The string to be decoded is not correctly encoded."
36
+ );
37
+ }
38
+ for (
39
+ let bc = 0, bs = 0, buffer, i = 0;
40
+ (buffer = str.charAt(i++));
41
+ ~buffer && ((bs = bc % 4 ? bs * 64 + buffer : buffer), bc++ % 4)
42
+ ? (output += String.fromCharCode(255 & (bs >> ((-2 * bc) & 6))))
43
+ : 0
44
+ ) {
45
+ buffer = chars.indexOf(buffer);
46
+ }
47
+
48
+ return output;
49
+ },
50
+ };
51
+
3
52
  export const b64decode = (encoded: string): Uint8Array => {
4
- return Uint8Array.from(atob(encoded), (c) => c.charCodeAt(0));
53
+ return Uint8Array.from(Base64.atob(encoded), (c) => c.charCodeAt(0));
5
54
  };
6
55
 
7
56
  export const urlsafe_b64decode = (encoded: string): Uint8Array => {
@@ -9,7 +58,7 @@ export const urlsafe_b64decode = (encoded: string): Uint8Array => {
9
58
  };
10
59
 
11
60
  export const b64encode = (data: ArrayBuffer): string => {
12
- return btoa(
61
+ return Base64.btoa(
13
62
  String.fromCharCode.apply(null, Array.from(new Uint8Array(data)))
14
63
  );
15
64
  };