khulnasoft commited on
Commit
3cfdf1f
1 Parent(s): 0d6f268

Create static/cve5sw.js

Browse files
Files changed (1) hide show
  1. static/cve5sw.js +313 -0
static/cve5sw.js ADDED
@@ -0,0 +1,313 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //
2
+ // CveServices - Service Worker Middleware
3
+ // Filename: sw.js
4
+ //
5
+ // Author: Ben N
6
+ //
7
+ // Description: Handles exchange of API requests with credentials.
8
+ //
9
+ // Copyright 2022, Ben Nott <[email protected]>.
10
+ // See LICENSE for a full copy of the license.
11
+ //
12
+
13
+ const storage = {};
14
+ const keyname = "cve-services.cvsskeyStore";
15
+ /* Encryption API using JavaScript native crypto.js and indexeDB for storing private keys */
16
+ const encrypt_storage_version = "1.1.14";
17
+ const cacheName = 'private';
18
+ const cacheURL = '/creds';
19
+
20
+ destroySession = () => {
21
+ if ('creds' in storage) {
22
+ delete storage['creds'];
23
+ let bc = new BroadcastChannel('logout');
24
+ bc.postMessage({ 'error': 'LOGOUT', message: 'The user has logged out' });
25
+ }
26
+ caches.open(cacheName).then(cache => {
27
+ cache.delete(cacheURL);
28
+ });
29
+ };
30
+
31
+ setSessionTimer = () => {
32
+ let defaultTimeout = 1000 * 60 * 60; //1 hour timeout
33
+
34
+ setTimeout(
35
+ destroySession,
36
+ defaultTimeout
37
+ );
38
+ };
39
+
40
+ setCredentials = (e) => {
41
+ storage.creds = e.data.creds;
42
+ check_create_key(e.data.creds.user).then(function (newkey) {
43
+ encryptMessage(e.data.creds.key, newkey.publicKey)
44
+ .then(function (encBuffer) {
45
+ arrayBuffertoURI(encBuffer)
46
+ .then(function (encURL) {
47
+ let f = JSON.parse(JSON.stringify(e.data.creds));
48
+ delete f['key'];
49
+ f['keyURL'] = encURL;
50
+ clientReply(e, { data: "ok" });
51
+ caches.open(cacheName).then(function (cache) {
52
+ let cachecreds = new Response(JSON.stringify(f));
53
+ cache.put(cacheURL, cachecreds);
54
+ });
55
+ setSessionTimer();
56
+ });
57
+ });
58
+ });
59
+ };
60
+
61
+ clientReply = (e, msg) => {
62
+ e.ports[0].postMessage(msg);
63
+ };
64
+
65
+ deadSession = (e, debugString) => {
66
+ clientReply(e, {
67
+ error: "NO_SESSION",
68
+ message: "Please login."
69
+ //debug: debugString
70
+ });
71
+ return false;
72
+ }
73
+
74
+ checkSession = async (e) => {
75
+ if (!('creds' in storage)) {
76
+ try {
77
+ let cache = await caches.open(cacheName);
78
+ let cachecreds = await cache.match(cacheURL);
79
+ if (cachecreds) {
80
+ let result = await cachecreds.json();
81
+ let ekey = await check_create_key(result.user);
82
+ let encBuffer = URItoarrayBuffer(result.keyURL);
83
+ let rawKey = await decryptMessage(encBuffer, ekey.privateKey);
84
+ result.key = rawKey;
85
+ delete result.keyURL;
86
+ storage.creds = JSON.parse(JSON.stringify(result));
87
+ return true;
88
+ } else {
89
+ return deadSession(e);
90
+ }
91
+ } catch (err) {
92
+ return deadSession(e, String(err));
93
+ }
94
+ };
95
+ return true;
96
+ };
97
+
98
+ defaultOpts = () => {
99
+ return {
100
+ headers: {
101
+ 'content-type': 'application/json',
102
+ 'CVE-API-KEY': storage.creds.key,
103
+ 'CVE-API-ORG': storage.creds.org,
104
+ 'CVE-API-USER': storage.creds.user,
105
+ },
106
+ };
107
+ };
108
+
109
+ getURL = (path, query) => {
110
+ let url = new URL(`/api/${path}`, storage.serviceUri);
111
+
112
+ if (query) {
113
+ for (const [k, v] of Object.entries(query)) {
114
+ url.searchParams.append(k, v);
115
+ }
116
+ }
117
+
118
+ return url.toString();
119
+ };
120
+
121
+ doFetch = (event, url, opts) => {
122
+ return fetch(url, opts)
123
+ .then(res => {
124
+ if (res.ok) {
125
+ res.json().then(data => clientReply(event, data));
126
+ } else {
127
+ res.json().then(msg => {
128
+ clientReply(event, msg);
129
+ });
130
+ }
131
+ })
132
+ .catch(err => {
133
+ clientReply(event, { error: err });
134
+ });
135
+ };
136
+
137
+ requestService = (event) => {
138
+ let { query, path, method } = event.data;
139
+
140
+ let opts = defaultOpts();
141
+ let url = getURL(path, query);
142
+
143
+ if (['PUT', 'POST'].includes(method)) {
144
+ opts.method = method;
145
+
146
+ if ('body' in event.data) {
147
+ opts.body = JSON.stringify(event.data.body);
148
+ }
149
+ }
150
+
151
+ return doFetch(event, url, opts);
152
+ };
153
+
154
+ self.onmessage = e => {
155
+ switch (e.data.type) {
156
+ case 'init':
157
+ if ('serviceUri' in e.data) {
158
+ storage.serviceUri = e.data.serviceUri;
159
+ clientReply(e, 'ok');
160
+ }
161
+ break;
162
+ case 'echo':
163
+ clientReply(e, 'echo');
164
+ break;
165
+ case 'login':
166
+ setCredentials(e);
167
+ break;
168
+ case 'request':
169
+ checkSession(e).then(function (success) {
170
+ if (success)
171
+ requestService(e);
172
+ else
173
+ clientReply(e, { error: "NO_SESSION" });
174
+ });
175
+ break;
176
+ case 'getOrg':
177
+ checkSession(e).then(function (success) {
178
+ if (success)
179
+ clientReply(e, storage.creds.org);
180
+ else
181
+ clientReply(e, { error: "NO_SESSION" })
182
+ });
183
+ break;
184
+ case 'destroy':
185
+ destroySession();
186
+ clientReply(e, "Cleaning up session");
187
+ break;
188
+ default:
189
+ clientReply(e, { error: 'Not supported' });
190
+ break;
191
+ }
192
+ };
193
+
194
+ async function encryptMessage(message, publicKey) {
195
+ let encoded = new TextEncoder().encode(message);
196
+ let ciphertext = await crypto.subtle.encrypt(
197
+ {
198
+ name: "RSA-OAEP"
199
+ },
200
+ publicKey,
201
+ encoded
202
+ );
203
+ return ciphertext;
204
+ }
205
+ async function decryptMessage(ciphertext, privateKey) {
206
+ let decrypted = await crypto.subtle.decrypt(
207
+ {
208
+ name: "RSA-OAEP"
209
+ },
210
+ privateKey,
211
+ ciphertext
212
+ );
213
+
214
+ let dec = new TextDecoder();
215
+ return dec.decode(decrypted);
216
+ }
217
+ async function arrayBuffertoURI(arrayBuffer) {
218
+ let blob = new Blob([arrayBuffer]);
219
+ return new Promise((resolve) => {
220
+ let reader = new FileReader()
221
+ reader.onloadend = function () {
222
+ resolve(reader.result);
223
+ };
224
+ reader.readAsDataURL(blob);
225
+ });
226
+ }
227
+ function URItoarrayBuffer(URI) {
228
+ var byteString = atob(URI.split(',')[1]);
229
+ var arrayBuffer = new ArrayBuffer(byteString.length);
230
+ var _ia = new Uint8Array(arrayBuffer);
231
+ for (var i = 0; i < byteString.length; i++) {
232
+ _ia[i] = byteString.charCodeAt(i);
233
+ }
234
+ return arrayBuffer;
235
+ }
236
+
237
+
238
+ async function sha256sum(msg) {
239
+ let enc = new TextEncoder().encode(msg);
240
+ const buff = await crypto.subtle.digest('SHA-256', enc);
241
+ const barray = Array.from(new Uint8Array(buff));
242
+ let hex = barray.map(function (b) {
243
+ return b.toString(16).padStart(2, '0');
244
+ }).join('');
245
+ return hex;
246
+ };
247
+ function dbManager(user, key, sum) {
248
+ return new Promise(function (resolve, reject) {
249
+ var open = indexedDB.open(keyname, 1);
250
+ open.onupgradeneeded = function () {
251
+ var db = open.result;
252
+ if (!db.objectStoreNames.contains("keyStore")) {
253
+ var store = db.createObjectStore("keyStore",
254
+ { keyPath: "user" });
255
+ var index = store.createIndex("sigIndex", ["sum.sha256"]);
256
+ }
257
+ };
258
+ open.onsuccess = function () {
259
+ var db = open.result;
260
+ var tx = db.transaction("keyStore", "readwrite");
261
+ var store = tx.objectStore("keyStore");
262
+ if (key) {
263
+ store.put({ user: user, key: key, sum: sum });
264
+ } else {
265
+ if (user) {
266
+ var getUser = store.get(user);
267
+ getUser.onsuccess = function (q) {
268
+ resolve(q);
269
+ };
270
+ } else if (sum.sha256) {
271
+ var index = store.index("sigIndex");
272
+ var getSum = index.get([sum.sha256]);
273
+ getSum.onsuccess = function (q) {
274
+ resolve(q);
275
+ };
276
+ } else {
277
+ reject("A user or a checksum is required");
278
+ }
279
+ };
280
+ tx.oncomplete = function () {
281
+ db.close();
282
+ };
283
+ }
284
+ });
285
+ }
286
+
287
+ async function save_key(user, key) {
288
+ let fpb = await crypto.subtle.exportKey("jwk", key.publicKey);
289
+ let sum = { sha256: await sha256sum(fpb.n) };
290
+ dbManager(user, key, sum);
291
+ return key;
292
+ }
293
+
294
+ async function check_create_key(user) {
295
+ let dbKey = await dbManager(user);
296
+ if (('target' in dbKey) && (dbKey.target.result) &&
297
+ (dbKey.target.result.user == user)) {
298
+ return dbKey.target.result.key;
299
+ }
300
+ return crypto.subtle.generateKey(
301
+ {
302
+ name: "RSA-OAEP",
303
+ modulusLength: 4096,
304
+ publicExponent: new Uint8Array([1, 0, 1]),
305
+ hash: "SHA-256",
306
+ },
307
+ false,
308
+ ["encrypt", "decrypt"]
309
+ ).then(function (key) {
310
+ save_key(user, key);
311
+ return key;
312
+ });
313
+ }