@lightsparkdev/core 0.2.1 → 0.2.2
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 +6 -0
- package/dist/chunk-23X5L5S3.js +604 -0
- package/dist/chunk-4OZ5E62P.js +608 -0
- package/dist/chunk-5FD46UVI.js +600 -0
- package/dist/chunk-BAQMCJ7C.js +630 -0
- package/dist/chunk-GFTKZSHK.js +627 -0
- package/dist/chunk-I3HKDOEE.js +627 -0
- package/dist/chunk-J6LSW6XO.js +601 -0
- package/dist/chunk-JUD4MOOQ.js +608 -0
- package/dist/chunk-KX6HH6CX.js +629 -0
- package/dist/chunk-MZCDWVLH.js +634 -0
- package/dist/chunk-NY3BK66J.js +624 -0
- package/dist/chunk-Q7UUXZNC.js +26 -0
- package/dist/chunk-WT2HYC6Q.js +601 -0
- package/dist/chunk-YYVOX4YM.js +600 -0
- package/dist/chunk-ZUIUADXH.js +616 -0
- package/dist/crypto-rn-2BYOHLMG.js +72 -0
- package/dist/crypto-rn-2YV53C3B.js +72 -0
- package/dist/crypto-rn-3HFXYG7I.js +75 -0
- package/dist/crypto-rn-3HLWLVZS.js +76 -0
- package/dist/crypto-rn-3N3JTBLE.js +83 -0
- package/dist/crypto-rn-3OEMMMOD.js +74 -0
- package/dist/crypto-rn-5EZSRY5Y.js +82 -0
- package/dist/crypto-rn-5KM2YVOI.js +82 -0
- package/dist/crypto-rn-6335R2CU.js +80 -0
- package/dist/crypto-rn-6ZHSL7CX.js +72 -0
- package/dist/crypto-rn-7DWXMO2Q.js +75 -0
- package/dist/crypto-rn-7GTI374I.js +81 -0
- package/dist/crypto-rn-AXDY3LDG.js +124 -0
- package/dist/crypto-rn-BZ2KZ2YR.js +68 -0
- package/dist/crypto-rn-CAPL7MYC.js +80 -0
- package/dist/crypto-rn-CBAKEB7C.js +68 -0
- package/dist/crypto-rn-CBWHV2F5.js +82 -0
- package/dist/crypto-rn-CS36MQ4X.js +77 -0
- package/dist/crypto-rn-E4RZNGIB.js +82 -0
- package/dist/crypto-rn-H4NIT5CT.js +70 -0
- package/dist/crypto-rn-IAC27WLZ.js +74 -0
- package/dist/crypto-rn-LLY6FCWE.js +82 -0
- package/dist/crypto-rn-MOWVVG3L.js +78 -0
- package/dist/crypto-rn-OAPLHNM5.js +73 -0
- package/dist/crypto-rn-PVAG5TVH.js +80 -0
- package/dist/crypto-rn-QLVBL5DI.js +75 -0
- package/dist/crypto-rn-TBKXR3SR.js +68 -0
- package/dist/crypto-rn-TSQJA6A3.js +81 -0
- package/dist/crypto-rn-U3XEJXIM.js +77 -0
- package/dist/crypto-rn-UHTZEVAR.js +74 -0
- package/dist/crypto-rn-V3ZNQSFI.js +79 -0
- package/dist/crypto-rn-VGNP6VZW.js +75 -0
- package/dist/crypto-rn-XMYCUEGV.js +72 -0
- package/dist/crypto-rn-YPOTC5RI.js +73 -0
- package/dist/index.cjs +91 -114
- package/dist/index.d.ts +30 -17
- package/dist/index.js +90 -104
- package/package.json +2 -1
- package/src/crypto/NodeKeyCache.ts +18 -15
- package/src/crypto/crypto.ts +72 -78
- package/src/requester/Requester.ts +9 -13
- package/src/utils/base64.ts +51 -2
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
|
|
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
|
|
83
|
-
return
|
|
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
|
|
89
|
-
const password_key = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
205
|
-
return await
|
|
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
|
|
221
|
-
return await
|
|
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
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
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-
|
|
235
|
+
name: "RSA-PSS",
|
|
236
|
+
saltLength: 32
|
|
235
237
|
},
|
|
236
|
-
/*key*/
|
|
237
238
|
key,
|
|
238
|
-
|
|
239
|
-
encoded
|
|
239
|
+
data
|
|
240
240
|
);
|
|
241
|
-
return b64encode(encrypted);
|
|
242
241
|
};
|
|
243
|
-
var
|
|
244
|
-
const
|
|
245
|
-
|
|
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
|
-
|
|
246
|
+
format,
|
|
249
247
|
/*keyData*/
|
|
250
|
-
|
|
251
|
-
/*algorithm
|
|
248
|
+
keyData,
|
|
249
|
+
/*algorithm*/
|
|
252
250
|
{
|
|
253
|
-
name: "RSA-
|
|
251
|
+
name: "RSA-PSS",
|
|
254
252
|
hash: "SHA-256"
|
|
255
253
|
},
|
|
256
254
|
/*extractable*/
|
|
257
255
|
true,
|
|
258
256
|
/*keyUsages*/
|
|
259
|
-
["
|
|
257
|
+
["sign"]
|
|
260
258
|
);
|
|
261
259
|
};
|
|
262
|
-
var
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
+
constructor(cryptoImpl = DefaultCrypto) {
|
|
273
|
+
this.cryptoImpl = cryptoImpl;
|
|
272
274
|
this.idToKey = /* @__PURE__ */ new Map();
|
|
273
275
|
autoBind(this);
|
|
274
276
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
+
idToKey;
|
|
278
|
+
async loadKey(id, rawKey, format = "pkcs8") {
|
|
279
|
+
const decoded = b64decode(this.stripPemTags(rawKey));
|
|
277
280
|
try {
|
|
278
|
-
const
|
|
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
|
|
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.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Lightspark JS SDK",
|
|
5
5
|
"author": "Lightspark Inc.",
|
|
6
6
|
"keywords": [
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"dayjs": "^1.11.7",
|
|
53
53
|
"graphql": "^16.6.0",
|
|
54
54
|
"graphql-ws": "^5.11.3",
|
|
55
|
+
"text-encoding": "^0.7.0",
|
|
55
56
|
"ws": "^8.12.1",
|
|
56
57
|
"zen-observable-ts": "^1.1.0"
|
|
57
58
|
},
|
|
@@ -3,28 +3,25 @@
|
|
|
3
3
|
import autoBind from "auto-bind";
|
|
4
4
|
|
|
5
5
|
import { b64decode } from "../utils/base64.js";
|
|
6
|
-
import {
|
|
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(
|
|
16
|
-
|
|
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
|
|
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;
|
package/src/crypto/crypto.ts
CHANGED
|
@@ -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
|
|
4
|
+
import { b64decode } from "../utils/base64.js";
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
export type CryptoInterface = {
|
|
7
|
+
decryptSecretWithNodePassword: (
|
|
8
|
+
cipher: string,
|
|
9
|
+
encryptedSecret: string,
|
|
10
|
+
nodePassword: string
|
|
11
|
+
) => Promise<ArrayBuffer | null>;
|
|
7
12
|
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
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(
|
|
199
|
+
return await cryptoImpl.subtle.exportKey(
|
|
200
|
+
/*format*/ format,
|
|
201
|
+
/*key*/ key as CryptoKey
|
|
202
|
+
);
|
|
216
203
|
};
|
|
217
204
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
230
|
-
|
|
221
|
+
key as CryptoKey,
|
|
222
|
+
data
|
|
231
223
|
);
|
|
232
|
-
// @ts-ignore
|
|
233
|
-
return b64encode(encrypted);
|
|
234
224
|
};
|
|
235
225
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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*/
|
|
243
|
-
/*keyData*/
|
|
244
|
-
/*algorithm
|
|
245
|
-
name: "RSA-
|
|
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*/ ["
|
|
239
|
+
/*keyUsages*/ ["sign"]
|
|
250
240
|
);
|
|
251
241
|
};
|
|
252
242
|
|
|
253
|
-
export const
|
|
254
|
-
|
|
255
|
-
|
|
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";
|