gun-eth 1.4.21 → 1.4.23

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.
@@ -3,16 +3,1532 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var Gun = require('gun');
6
- var SEA = require('gun/sea.js');
7
6
  var ethers = require('ethers');
8
7
 
9
8
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
10
- var STEALTH_ANNOUNCER_ADDRESS$1 = "";
11
- var PROOF_OF_INTEGRITY_ADDRESS = "";
12
- var require$$0 = {
13
- STEALTH_ANNOUNCER_ADDRESS: STEALTH_ANNOUNCER_ADDRESS$1,
14
- PROOF_OF_INTEGRITY_ADDRESS: PROOF_OF_INTEGRITY_ADDRESS
15
- };
9
+ var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
10
+
11
+ function getDefaultExportFromCjs (x) {
12
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
13
+ }
14
+
15
+ function commonjsRequire(path) {
16
+ throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
17
+ }
18
+
19
+ var sea = {exports: {}};
20
+
21
+ sea.exports;
22
+
23
+ (function (module) {
24
+ (function(){
25
+
26
+ /* UNBUILD */
27
+ function USE(arg, req){
28
+ return req? commonjsRequire(arg) : arg.slice? USE[R(arg)] : function(mod, path){
29
+ arg(mod = {exports: {}});
30
+ USE[R(path)] = mod.exports;
31
+ }
32
+ function R(p){
33
+ return p.split('/').slice(-1).toString().replace('.js','');
34
+ }
35
+ }
36
+ { var MODULE = module; }
37
+ USE(function(module){
38
+ // Security, Encryption, and Authorization: SEA.js
39
+ // MANDATORY READING: https://gun.eco/explainers/data/security.html
40
+ // IT IS IMPLEMENTED IN A POLYFILL/SHIM APPROACH.
41
+ // THIS IS AN EARLY ALPHA!
42
+
43
+ if(typeof self !== "undefined"){ module.window = self; } // should be safe for at least browser/worker/nodejs, need to check other envs like RN etc.
44
+ if(typeof window !== "undefined"){ module.window = window; }
45
+
46
+ var tmp = module.window || module, u;
47
+ var SEA = tmp.SEA || {};
48
+
49
+ if(SEA.window = module.window){ SEA.window.SEA = SEA; }
50
+
51
+ try{ if(u+'' !== typeof MODULE){ MODULE.exports = SEA; } }catch(e){}
52
+ module.exports = SEA;
53
+ })(USE, './root');
54
+ USE(function(module){
55
+ var SEA = USE('./root');
56
+ try{ if(SEA.window){
57
+ if(location.protocol.indexOf('s') < 0
58
+ && location.host.indexOf('localhost') < 0
59
+ && ! /^127\.\d+\.\d+\.\d+$/.test(location.hostname)
60
+ && location.protocol.indexOf('file:') < 0){
61
+ console.warn('HTTPS needed for WebCrypto in SEA, redirecting...');
62
+ location.protocol = 'https:'; // WebCrypto does NOT work without HTTPS!
63
+ }
64
+ } }catch(e){}
65
+ })(USE, './https');
66
+ USE(function(module){
67
+ var u;
68
+ if(u+''== typeof btoa){
69
+ if(u+'' == typeof Buffer){
70
+ try{ commonjsGlobal.Buffer = USE("buffer", 1).Buffer; }catch(e){ console.log("Please `npm install buffer` or add it to your package.json !"); }
71
+ }
72
+ commonjsGlobal.btoa = function(data){ return Buffer.from(data, "binary").toString("base64") };
73
+ commonjsGlobal.atob = function(data){ return Buffer.from(data, "base64").toString("binary") };
74
+ }
75
+ })(USE, './base64');
76
+ USE(function(module){
77
+ USE('./base64');
78
+ // This is Array extended to have .toString(['utf8'|'hex'|'base64'])
79
+ function SeaArray() {}
80
+ Object.assign(SeaArray, { from: Array.from });
81
+ SeaArray.prototype = Object.create(Array.prototype);
82
+ SeaArray.prototype.toString = function(enc, start, end) { enc = enc || 'utf8'; start = start || 0;
83
+ const length = this.length;
84
+ if (enc === 'hex') {
85
+ const buf = new Uint8Array(this);
86
+ return [ ...Array(((end && (end + 1)) || length) - start).keys()]
87
+ .map((i) => buf[ i + start ].toString(16).padStart(2, '0')).join('')
88
+ }
89
+ if (enc === 'utf8') {
90
+ return Array.from(
91
+ { length: (end || length) - start },
92
+ (_, i) => String.fromCharCode(this[ i + start])
93
+ ).join('')
94
+ }
95
+ if (enc === 'base64') {
96
+ return btoa(this)
97
+ }
98
+ };
99
+ module.exports = SeaArray;
100
+ })(USE, './array');
101
+ USE(function(module){
102
+ USE('./base64');
103
+ // This is Buffer implementation used in SEA. Functionality is mostly
104
+ // compatible with NodeJS 'safe-buffer' and is used for encoding conversions
105
+ // between binary and 'hex' | 'utf8' | 'base64'
106
+ // See documentation and validation for safe implementation in:
107
+ // https://github.com/feross/safe-buffer#update
108
+ var SeaArray = USE('./array');
109
+ function SafeBuffer(...props) {
110
+ console.warn('new SafeBuffer() is depreciated, please use SafeBuffer.from()');
111
+ return SafeBuffer.from(...props)
112
+ }
113
+ SafeBuffer.prototype = Object.create(Array.prototype);
114
+ Object.assign(SafeBuffer, {
115
+ // (data, enc) where typeof data === 'string' then enc === 'utf8'|'hex'|'base64'
116
+ from() {
117
+ if (!Object.keys(arguments).length || arguments[0]==null) {
118
+ throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')
119
+ }
120
+ const input = arguments[0];
121
+ let buf;
122
+ if (typeof input === 'string') {
123
+ const enc = arguments[1] || 'utf8';
124
+ if (enc === 'hex') {
125
+ const bytes = input.match(/([\da-fA-F]{2})/g)
126
+ .map((byte) => parseInt(byte, 16));
127
+ if (!bytes || !bytes.length) {
128
+ throw new TypeError('Invalid first argument for type \'hex\'.')
129
+ }
130
+ buf = SeaArray.from(bytes);
131
+ } else if (enc === 'utf8' || 'binary' === enc) { // EDIT BY MARK: I think this is safe, tested it against a couple "binary" strings. This lets SafeBuffer match NodeJS Buffer behavior more where it safely btoas regular strings.
132
+ const length = input.length;
133
+ const words = new Uint16Array(length);
134
+ Array.from({ length: length }, (_, i) => words[i] = input.charCodeAt(i));
135
+ buf = SeaArray.from(words);
136
+ } else if (enc === 'base64') {
137
+ const dec = atob(input);
138
+ const length = dec.length;
139
+ const bytes = new Uint8Array(length);
140
+ Array.from({ length: length }, (_, i) => bytes[i] = dec.charCodeAt(i));
141
+ buf = SeaArray.from(bytes);
142
+ } else if (enc === 'binary') { // deprecated by above comment
143
+ buf = SeaArray.from(input); // some btoas were mishandled.
144
+ } else {
145
+ console.info('SafeBuffer.from unknown encoding: '+enc);
146
+ }
147
+ return buf
148
+ }
149
+ input.byteLength; // what is going on here? FOR MARTTI
150
+ const length = input.byteLength ? input.byteLength : input.length;
151
+ if (length) {
152
+ let buf;
153
+ if (input instanceof ArrayBuffer) {
154
+ buf = new Uint8Array(input);
155
+ }
156
+ return SeaArray.from(buf || input)
157
+ }
158
+ },
159
+ // This is 'safe-buffer.alloc' sans encoding support
160
+ alloc(length, fill = 0 /*, enc*/ ) {
161
+ return SeaArray.from(new Uint8Array(Array.from({ length: length }, () => fill)))
162
+ },
163
+ // This is normal UNSAFE 'buffer.alloc' or 'new Buffer(length)' - don't use!
164
+ allocUnsafe(length) {
165
+ return SeaArray.from(new Uint8Array(Array.from({ length : length })))
166
+ },
167
+ // This puts together array of array like members
168
+ concat(arr) { // octet array
169
+ if (!Array.isArray(arr)) {
170
+ throw new TypeError('First argument must be Array containing ArrayBuffer or Uint8Array instances.')
171
+ }
172
+ return SeaArray.from(arr.reduce((ret, item) => ret.concat(Array.from(item)), []))
173
+ }
174
+ });
175
+ SafeBuffer.prototype.from = SafeBuffer.from;
176
+ SafeBuffer.prototype.toString = SeaArray.prototype.toString;
177
+
178
+ module.exports = SafeBuffer;
179
+ })(USE, './buffer');
180
+ USE(function(module){
181
+ const SEA = USE('./root');
182
+ const api = {Buffer: USE('./buffer')};
183
+ var o = {}, u;
184
+
185
+ // ideally we can move away from JSON entirely? unlikely due to compatibility issues... oh well.
186
+ JSON.parseAsync = JSON.parseAsync || function(t,cb,r){ var u; try{ cb(u, JSON.parse(t,r)); }catch(e){ cb(e); } };
187
+ JSON.stringifyAsync = JSON.stringifyAsync || function(v,cb,r,s){ var u; try{ cb(u, JSON.stringify(v,r,s)); }catch(e){ cb(e); } };
188
+
189
+ api.parse = function(t,r){ return new Promise(function(res, rej){
190
+ JSON.parseAsync(t,function(err, raw){ err? rej(err) : res(raw); },r);
191
+ })};
192
+ api.stringify = function(v,r,s){ return new Promise(function(res, rej){
193
+ JSON.stringifyAsync(v,function(err, raw){ err? rej(err) : res(raw); },r,s);
194
+ })};
195
+
196
+ if(SEA.window){
197
+ api.crypto = SEA.window.crypto || SEA.window.msCrypto;
198
+ api.subtle = (api.crypto||o).subtle || (api.crypto||o).webkitSubtle;
199
+ api.TextEncoder = SEA.window.TextEncoder;
200
+ api.TextDecoder = SEA.window.TextDecoder;
201
+ api.random = (len) => api.Buffer.from(api.crypto.getRandomValues(new Uint8Array(api.Buffer.alloc(len))));
202
+ }
203
+ if(!api.TextDecoder)
204
+ {
205
+ const { TextEncoder, TextDecoder } = USE((u+'' == typeof MODULE?'.':'')+'./lib/text-encoding', 1);
206
+ api.TextDecoder = TextDecoder;
207
+ api.TextEncoder = TextEncoder;
208
+ }
209
+ if(!api.crypto)
210
+ {
211
+ try
212
+ {
213
+ var crypto = USE('crypto', 1);
214
+ Object.assign(api, {
215
+ crypto,
216
+ random: (len) => api.Buffer.from(crypto.randomBytes(len))
217
+ });
218
+ const { Crypto: WebCrypto } = USE('@peculiar/webcrypto', 1);
219
+ api.ossl = api.subtle = new WebCrypto({directory: 'ossl'}).subtle; // ECDH
220
+ }
221
+ catch(e){
222
+ console.log("Please `npm install @peculiar/webcrypto` or add it to your package.json !");
223
+ }}
224
+
225
+ module.exports = api;
226
+ })(USE, './shim');
227
+ USE(function(module){
228
+ var SEA = USE('./root');
229
+ var shim = USE('./shim');
230
+ var s = {};
231
+ s.pbkdf2 = {hash: {name : 'SHA-256'}, iter: 100000, ks: 64};
232
+ s.ecdsa = {
233
+ pair: {name: 'ECDSA', namedCurve: 'P-256'},
234
+ sign: {name: 'ECDSA', hash: {name: 'SHA-256'}}
235
+ };
236
+ s.ecdh = {name: 'ECDH', namedCurve: 'P-256'};
237
+
238
+ // This creates Web Cryptography API compliant JWK for sign/verify purposes
239
+ s.jwk = function(pub, d){ // d === priv
240
+ pub = pub.split('.');
241
+ var x = pub[0], y = pub[1];
242
+ var jwk = {kty: "EC", crv: "P-256", x: x, y: y, ext: true};
243
+ jwk.key_ops = d ? ['sign'] : ['verify'];
244
+ if(d){ jwk.d = d; }
245
+ return jwk;
246
+ };
247
+
248
+ s.keyToJwk = function(keyBytes) {
249
+ const keyB64 = keyBytes.toString('base64');
250
+ const k = keyB64.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
251
+ return { kty: 'oct', k: k, ext: false, alg: 'A256GCM' };
252
+ };
253
+
254
+ s.recall = {
255
+ validity: 12 * 60 * 60, // internally in seconds : 12 hours
256
+ hook: function(props){ return props } // { iat, exp, alias, remember } // or return new Promise((resolve, reject) => resolve(props)
257
+ };
258
+
259
+ s.check = function(t){ return (typeof t == 'string') && ('SEA{' === t.slice(0,4)) };
260
+ s.parse = async function p(t){ try {
261
+ var yes = (typeof t == 'string');
262
+ if(yes && 'SEA{' === t.slice(0,4)){ t = t.slice(3); }
263
+ return yes ? await shim.parse(t) : t;
264
+ } catch (e) {}
265
+ return t;
266
+ };
267
+
268
+ SEA.opt = s;
269
+ module.exports = s;
270
+ })(USE, './settings');
271
+ USE(function(module){
272
+ var shim = USE('./shim');
273
+ module.exports = async function(d, o){
274
+ var t = (typeof d == 'string')? d : await shim.stringify(d);
275
+ var hash = await shim.subtle.digest({name: o||'SHA-256'}, new shim.TextEncoder().encode(t));
276
+ return shim.Buffer.from(hash);
277
+ };
278
+ })(USE, './sha256');
279
+ USE(function(module){
280
+ // This internal func returns SHA-1 hashed data for KeyID generation
281
+ const __shim = USE('./shim');
282
+ const subtle = __shim.subtle;
283
+ const ossl = __shim.ossl ? __shim.ossl : subtle;
284
+ const sha1hash = (b) => ossl.digest({name: 'SHA-1'}, new ArrayBuffer(b));
285
+ module.exports = sha1hash;
286
+ })(USE, './sha1');
287
+ USE(function(module){
288
+ var SEA = USE('./root');
289
+ var shim = USE('./shim');
290
+ var S = USE('./settings');
291
+ var sha = USE('./sha256');
292
+ var u;
293
+
294
+ SEA.work = SEA.work || (async (data, pair, cb, opt) => { try { // used to be named `proof`
295
+ var salt = (pair||{}).epub || pair; // epub not recommended, salt should be random!
296
+ opt = opt || {};
297
+ if(salt instanceof Function){
298
+ cb = salt;
299
+ salt = u;
300
+ }
301
+ data = (typeof data == 'string')? data : await shim.stringify(data);
302
+ if('sha' === (opt.name||'').toLowerCase().slice(0,3)){
303
+ var rsha = shim.Buffer.from(await sha(data, opt.name), 'binary').toString(opt.encode || 'base64');
304
+ if(cb){ try{ cb(rsha); }catch(e){console.log(e);} }
305
+ return rsha;
306
+ }
307
+ salt = salt || shim.random(9);
308
+ var key = await (shim.ossl || shim.subtle).importKey('raw', new shim.TextEncoder().encode(data), {name: opt.name || 'PBKDF2'}, false, ['deriveBits']);
309
+ var work = await (shim.ossl || shim.subtle).deriveBits({
310
+ name: opt.name || 'PBKDF2',
311
+ iterations: opt.iterations || S.pbkdf2.iter,
312
+ salt: new shim.TextEncoder().encode(opt.salt || salt),
313
+ hash: opt.hash || S.pbkdf2.hash,
314
+ }, key, opt.length || (S.pbkdf2.ks * 8));
315
+ data = shim.random(data.length); // Erase data in case of passphrase
316
+ var r = shim.Buffer.from(work, 'binary').toString(opt.encode || 'base64');
317
+ if(cb){ try{ cb(r); }catch(e){console.log(e);} }
318
+ return r;
319
+ } catch(e) {
320
+ console.log(e);
321
+ SEA.err = e;
322
+ if(SEA.throw){ throw e }
323
+ if(cb){ cb(); }
324
+ return;
325
+ }});
326
+
327
+ module.exports = SEA.work;
328
+ })(USE, './work');
329
+ USE(function(module){
330
+ var SEA = USE('./root');
331
+ var shim = USE('./shim');
332
+ USE('./settings');
333
+
334
+ SEA.name = SEA.name || (async (cb, opt) => { try {
335
+ if(cb){ try{ cb(); }catch(e){console.log(e);} }
336
+ return;
337
+ } catch(e) {
338
+ console.log(e);
339
+ SEA.err = e;
340
+ if(SEA.throw){ throw e }
341
+ if(cb){ cb(); }
342
+ return;
343
+ }});
344
+
345
+ //SEA.pair = async (data, proof, cb) => { try {
346
+ SEA.pair = SEA.pair || (async (cb, opt) => { try {
347
+
348
+ var ecdhSubtle = shim.ossl || shim.subtle;
349
+ // First: ECDSA keys for signing/verifying...
350
+ var sa = await shim.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, [ 'sign', 'verify' ])
351
+ .then(async (keys) => {
352
+ // privateKey scope doesn't leak out from here!
353
+ //const { d: priv } = await shim.subtle.exportKey('jwk', keys.privateKey)
354
+ var key = {};
355
+ key.priv = (await shim.subtle.exportKey('jwk', keys.privateKey)).d;
356
+ var pub = await shim.subtle.exportKey('jwk', keys.publicKey);
357
+ //const pub = Buff.from([ x, y ].join(':')).toString('base64') // old
358
+ key.pub = pub.x+'.'+pub.y; // new
359
+ // x and y are already base64
360
+ // pub is UTF8 but filename/URL safe (https://www.ietf.org/rfc/rfc3986.txt)
361
+ // but split on a non-base64 letter.
362
+ return key;
363
+ });
364
+
365
+ // To include PGPv4 kind of keyId:
366
+ // const pubId = await SEA.keyid(keys.pub)
367
+ // Next: ECDH keys for encryption/decryption...
368
+
369
+ try{
370
+ var dh = await ecdhSubtle.generateKey({name: 'ECDH', namedCurve: 'P-256'}, true, ['deriveKey'])
371
+ .then(async (keys) => {
372
+ // privateKey scope doesn't leak out from here!
373
+ var key = {};
374
+ key.epriv = (await ecdhSubtle.exportKey('jwk', keys.privateKey)).d;
375
+ var pub = await ecdhSubtle.exportKey('jwk', keys.publicKey);
376
+ //const epub = Buff.from([ ex, ey ].join(':')).toString('base64') // old
377
+ key.epub = pub.x+'.'+pub.y; // new
378
+ // ex and ey are already base64
379
+ // epub is UTF8 but filename/URL safe (https://www.ietf.org/rfc/rfc3986.txt)
380
+ // but split on a non-base64 letter.
381
+ return key;
382
+ });
383
+ }catch(e){
384
+ if(SEA.window){ throw e }
385
+ if(e == 'Error: ECDH is not a supported algorithm'){ console.log('Ignoring ECDH...'); }
386
+ else { throw e }
387
+ } dh = dh || {};
388
+
389
+ var r = { pub: sa.pub, priv: sa.priv, /* pubId, */ epub: dh.epub, epriv: dh.epriv };
390
+ if(cb){ try{ cb(r); }catch(e){console.log(e);} }
391
+ return r;
392
+ } catch(e) {
393
+ console.log(e);
394
+ SEA.err = e;
395
+ if(SEA.throw){ throw e }
396
+ if(cb){ cb(); }
397
+ return;
398
+ }});
399
+
400
+ module.exports = SEA.pair;
401
+ })(USE, './pair');
402
+ USE(function(module){
403
+ var SEA = USE('./root');
404
+ var shim = USE('./shim');
405
+ var S = USE('./settings');
406
+ var sha = USE('./sha256');
407
+ var u;
408
+
409
+ SEA.sign = SEA.sign || (async (data, pair, cb, opt) => { try {
410
+ opt = opt || {};
411
+ if(!(pair||opt).priv){
412
+ if(!SEA.I){ throw 'No signing key.' }
413
+ pair = await SEA.I(null, {what: data, how: 'sign', why: opt.why});
414
+ }
415
+ if(u === data){ throw '`undefined` not allowed.' }
416
+ var json = await S.parse(data);
417
+ var check = opt.check = opt.check || json;
418
+ if(SEA.verify && (SEA.opt.check(check) || (check && check.s && check.m))
419
+ && u !== await SEA.verify(check, pair)){ // don't sign if we already signed it.
420
+ var r = await S.parse(check);
421
+ if(!opt.raw){ r = 'SEA' + await shim.stringify(r); }
422
+ if(cb){ try{ cb(r); }catch(e){console.log(e);} }
423
+ return r;
424
+ }
425
+ var pub = pair.pub;
426
+ var priv = pair.priv;
427
+ var jwk = S.jwk(pub, priv);
428
+ var hash = await sha(json);
429
+ var sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ['sign'])
430
+ .then((key) => (shim.ossl || shim.subtle).sign({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, new Uint8Array(hash))); // privateKey scope doesn't leak out from here!
431
+ var r = {m: json, s: shim.Buffer.from(sig, 'binary').toString(opt.encode || 'base64')};
432
+ if(!opt.raw){ r = 'SEA' + await shim.stringify(r); }
433
+
434
+ if(cb){ try{ cb(r); }catch(e){console.log(e);} }
435
+ return r;
436
+ } catch(e) {
437
+ console.log(e);
438
+ SEA.err = e;
439
+ if(SEA.throw){ throw e }
440
+ if(cb){ cb(); }
441
+ return;
442
+ }});
443
+
444
+ module.exports = SEA.sign;
445
+ })(USE, './sign');
446
+ USE(function(module){
447
+ var SEA = USE('./root');
448
+ var shim = USE('./shim');
449
+ var S = USE('./settings');
450
+ var sha = USE('./sha256');
451
+ var u;
452
+
453
+ SEA.verify = SEA.verify || (async (data, pair, cb, opt) => { try {
454
+ var json = await S.parse(data);
455
+ if(false === pair){ // don't verify!
456
+ var raw = await S.parse(json.m);
457
+ if(cb){ try{ cb(raw); }catch(e){console.log(e);} }
458
+ return raw;
459
+ }
460
+ opt = opt || {};
461
+ // SEA.I // verify is free! Requires no user permission.
462
+ var pub = pair.pub || pair;
463
+ var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', S.jwk(pub), {name: 'ECDSA', namedCurve: 'P-256'}, false, ['verify']);
464
+ var hash = await sha(json.m);
465
+ var buf, sig, check, tmp; try{
466
+ buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT!
467
+ sig = new Uint8Array(buf);
468
+ check = await (shim.ossl || shim.subtle).verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, sig, new Uint8Array(hash));
469
+ if(!check){ throw "Signature did not match." }
470
+ }catch(e){
471
+ if(SEA.opt.fallback){
472
+ return await SEA.opt.fall_verify(data, pair, cb, opt);
473
+ }
474
+ }
475
+ var r = check? await S.parse(json.m) : u;
476
+
477
+ if(cb){ try{ cb(r); }catch(e){console.log(e);} }
478
+ return r;
479
+ } catch(e) {
480
+ console.log(e); // mismatched owner FOR MARTTI
481
+ SEA.err = e;
482
+ if(SEA.throw){ throw e }
483
+ if(cb){ cb(); }
484
+ return;
485
+ }});
486
+
487
+ module.exports = SEA.verify;
488
+ // legacy & ossl memory leak mitigation:
489
+
490
+ var knownKeys = {};
491
+ SEA.opt.slow_leak = pair => {
492
+ if (knownKeys[pair]) return knownKeys[pair];
493
+ var jwk = S.jwk(pair);
494
+ knownKeys[pair] = (shim.ossl || shim.subtle).importKey("jwk", jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ["verify"]);
495
+ return knownKeys[pair];
496
+ };
497
+
498
+ var O = SEA.opt;
499
+ SEA.opt.fall_verify = async function(data, pair, cb, opt, f){
500
+ if(f === SEA.opt.fallback){ throw "Signature did not match" } f = f || 1;
501
+ var tmp = data||'';
502
+ data = SEA.opt.unpack(data) || data;
503
+ var json = await S.parse(data), pub = pair.pub || pair, key = await SEA.opt.slow_leak(pub);
504
+ var hash = (f <= SEA.opt.fallback)? shim.Buffer.from(await shim.subtle.digest({name: 'SHA-256'}, new shim.TextEncoder().encode(await S.parse(json.m)))) : await sha(json.m); // this line is old bad buggy code but necessary for old compatibility.
505
+ var buf; var sig; var check; try{
506
+ buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT!
507
+ sig = new Uint8Array(buf);
508
+ check = await (shim.ossl || shim.subtle).verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, sig, new Uint8Array(hash));
509
+ if(!check){ throw "Signature did not match." }
510
+ }catch(e){ try{
511
+ buf = shim.Buffer.from(json.s, 'utf8'); // AUTO BACKWARD OLD UTF8 DATA!
512
+ sig = new Uint8Array(buf);
513
+ check = await (shim.ossl || shim.subtle).verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, sig, new Uint8Array(hash));
514
+ }catch(e){
515
+ if(!check){ throw "Signature did not match." }
516
+ }
517
+ }
518
+ var r = check? await S.parse(json.m) : u;
519
+ O.fall_soul = tmp['#']; O.fall_key = tmp['.']; O.fall_val = data; O.fall_state = tmp['>'];
520
+ if(cb){ try{ cb(r); }catch(e){console.log(e);} }
521
+ return r;
522
+ };
523
+ SEA.opt.fallback = 2;
524
+
525
+ })(USE, './verify');
526
+ USE(function(module){
527
+ var shim = USE('./shim');
528
+ var S = USE('./settings');
529
+ var sha256hash = USE('./sha256');
530
+
531
+ const importGen = async (key, salt, opt) => {
532
+ const combo = key + (salt || shim.random(8)).toString('utf8'); // new
533
+ const hash = shim.Buffer.from(await sha256hash(combo), 'binary');
534
+
535
+ const jwkKey = S.keyToJwk(hash);
536
+ return await shim.subtle.importKey('jwk', jwkKey, {name:'AES-GCM'}, false, ['encrypt', 'decrypt'])
537
+ };
538
+ module.exports = importGen;
539
+ })(USE, './aeskey');
540
+ USE(function(module){
541
+ var SEA = USE('./root');
542
+ var shim = USE('./shim');
543
+ USE('./settings');
544
+ var aeskey = USE('./aeskey');
545
+ var u;
546
+
547
+ SEA.encrypt = SEA.encrypt || (async (data, pair, cb, opt) => { try {
548
+ opt = opt || {};
549
+ var key = (pair||opt).epriv || pair;
550
+ if(u === data){ throw '`undefined` not allowed.' }
551
+ if(!key){
552
+ if(!SEA.I){ throw 'No encryption key.' }
553
+ pair = await SEA.I(null, {what: data, how: 'encrypt', why: opt.why});
554
+ key = pair.epriv || pair;
555
+ }
556
+ var msg = (typeof data == 'string')? data : await shim.stringify(data);
557
+ var rand = {s: shim.random(9), iv: shim.random(15)}; // consider making this 9 and 15 or 18 or 12 to reduce == padding.
558
+ var ct = await aeskey(key, rand.s, opt).then((aes) => (/*shim.ossl ||*/ shim.subtle).encrypt({ // Keeping the AES key scope as private as possible...
559
+ name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
560
+ }, aes, new shim.TextEncoder().encode(msg)));
561
+ var r = {
562
+ ct: shim.Buffer.from(ct, 'binary').toString(opt.encode || 'base64'),
563
+ iv: rand.iv.toString(opt.encode || 'base64'),
564
+ s: rand.s.toString(opt.encode || 'base64')
565
+ };
566
+ if(!opt.raw){ r = 'SEA' + await shim.stringify(r); }
567
+
568
+ if(cb){ try{ cb(r); }catch(e){console.log(e);} }
569
+ return r;
570
+ } catch(e) {
571
+ console.log(e);
572
+ SEA.err = e;
573
+ if(SEA.throw){ throw e }
574
+ if(cb){ cb(); }
575
+ return;
576
+ }});
577
+
578
+ module.exports = SEA.encrypt;
579
+ })(USE, './encrypt');
580
+ USE(function(module){
581
+ var SEA = USE('./root');
582
+ var shim = USE('./shim');
583
+ var S = USE('./settings');
584
+ var aeskey = USE('./aeskey');
585
+
586
+ SEA.decrypt = SEA.decrypt || (async (data, pair, cb, opt) => { try {
587
+ opt = opt || {};
588
+ var key = (pair||opt).epriv || pair;
589
+ if(!key){
590
+ if(!SEA.I){ throw 'No decryption key.' }
591
+ pair = await SEA.I(null, {what: data, how: 'decrypt', why: opt.why});
592
+ key = pair.epriv || pair;
593
+ }
594
+ var json = await S.parse(data);
595
+ var buf, bufiv, bufct; try{
596
+ buf = shim.Buffer.from(json.s, opt.encode || 'base64');
597
+ bufiv = shim.Buffer.from(json.iv, opt.encode || 'base64');
598
+ bufct = shim.Buffer.from(json.ct, opt.encode || 'base64');
599
+ var ct = await aeskey(key, buf, opt).then((aes) => (/*shim.ossl ||*/ shim.subtle).decrypt({ // Keeping aesKey scope as private as possible...
600
+ name: opt.name || 'AES-GCM', iv: new Uint8Array(bufiv), tagLength: 128
601
+ }, aes, new Uint8Array(bufct)));
602
+ }catch(e){
603
+ if('utf8' === opt.encode){ throw "Could not decrypt" }
604
+ if(SEA.opt.fallback){
605
+ opt.encode = 'utf8';
606
+ return await SEA.decrypt(data, pair, cb, opt);
607
+ }
608
+ }
609
+ var r = await S.parse(new shim.TextDecoder('utf8').decode(ct));
610
+ if(cb){ try{ cb(r); }catch(e){console.log(e);} }
611
+ return r;
612
+ } catch(e) {
613
+ console.log(e);
614
+ SEA.err = e;
615
+ if(SEA.throw){ throw e }
616
+ if(cb){ cb(); }
617
+ return;
618
+ }});
619
+
620
+ module.exports = SEA.decrypt;
621
+ })(USE, './decrypt');
622
+ USE(function(module){
623
+ var SEA = USE('./root');
624
+ var shim = USE('./shim');
625
+ USE('./settings');
626
+ // Derive shared secret from other's pub and my epub/epriv
627
+ SEA.secret = SEA.secret || (async (key, pair, cb, opt) => { try {
628
+ opt = opt || {};
629
+ if(!pair || !pair.epriv || !pair.epub){
630
+ if(!SEA.I){ throw 'No secret mix.' }
631
+ pair = await SEA.I(null, {what: key, how: 'secret', why: opt.why});
632
+ }
633
+ var pub = key.epub || key;
634
+ var epub = pair.epub;
635
+ var epriv = pair.epriv;
636
+ var ecdhSubtle = shim.ossl || shim.subtle;
637
+ var pubKeyData = keysToEcdhJwk(pub);
638
+ var props = Object.assign({ public: await ecdhSubtle.importKey(...pubKeyData, true, []) },{name: 'ECDH', namedCurve: 'P-256'}); // Thanks to @sirpy !
639
+ var privKeyData = keysToEcdhJwk(epub, epriv);
640
+ var derived = await ecdhSubtle.importKey(...privKeyData, false, ['deriveBits']).then(async (privKey) => {
641
+ // privateKey scope doesn't leak out from here!
642
+ var derivedBits = await ecdhSubtle.deriveBits(props, privKey, 256);
643
+ var rawBits = new Uint8Array(derivedBits);
644
+ var derivedKey = await ecdhSubtle.importKey('raw', rawBits,{ name: 'AES-GCM', length: 256 }, true, [ 'encrypt', 'decrypt' ]);
645
+ return ecdhSubtle.exportKey('jwk', derivedKey).then(({ k }) => k);
646
+ });
647
+ var r = derived;
648
+ if(cb){ try{ cb(r); }catch(e){console.log(e);} }
649
+ return r;
650
+ } catch(e) {
651
+ console.log(e);
652
+ SEA.err = e;
653
+ if(SEA.throw){ throw e }
654
+ if(cb){ cb(); }
655
+ return;
656
+ }});
657
+
658
+ // can this be replaced with settings.jwk?
659
+ var keysToEcdhJwk = (pub, d) => { // d === priv
660
+ //var [ x, y ] = shim.Buffer.from(pub, 'base64').toString('utf8').split(':') // old
661
+ var [ x, y ] = pub.split('.'); // new
662
+ var jwk = d ? { d: d } : {};
663
+ return [ // Use with spread returned value...
664
+ 'jwk',
665
+ Object.assign(
666
+ jwk,
667
+ { x: x, y: y, kty: 'EC', crv: 'P-256', ext: true }
668
+ ), // ??? refactor
669
+ {name: 'ECDH', namedCurve: 'P-256'}
670
+ ]
671
+ };
672
+
673
+ module.exports = SEA.secret;
674
+ })(USE, './secret');
675
+ USE(function(module){
676
+ var SEA = USE('./root');
677
+ // This is to certify that a group of "certificants" can "put" anything at a group of matched "paths" to the certificate authority's graph
678
+ SEA.certify = SEA.certify || (async (certificants, policy = {}, authority, cb, opt = {}) => { try {
679
+ /*
680
+ The Certify Protocol was made out of love by a Vietnamese code enthusiast. Vietnamese people around the world deserve respect!
681
+ IMPORTANT: A Certificate is like a Signature. No one knows who (authority) created/signed a cert until you put it into their graph.
682
+ "certificants": '*' or a String (Bob.pub) || an Object that contains "pub" as a key || an array of [object || string]. These people will have the rights.
683
+ "policy": A string ('inbox'), or a RAD/LEX object {'*': 'inbox'}, or an Array of RAD/LEX objects or strings. RAD/LEX object can contain key "?" with indexOf("*") > -1 to force key equals certificant pub. This rule is used to check against soul+'/'+key using Gun.text.match or String.match.
684
+ "authority": Key pair or priv of the certificate authority.
685
+ "cb": A callback function after all things are done.
686
+ "opt": If opt.expiry (a timestamp) is set, SEA won't sync data after opt.expiry. If opt.block is set, SEA will look for block before syncing.
687
+ */
688
+ console.log('SEA.certify() is an early experimental community supported method that may change API behavior without warning in any future version.');
689
+
690
+ certificants = (() => {
691
+ var data = [];
692
+ if (certificants) {
693
+ if ((typeof certificants === 'string' || Array.isArray(certificants)) && certificants.indexOf('*') > -1) return '*'
694
+ if (typeof certificants === 'string') return certificants
695
+ if (Array.isArray(certificants)) {
696
+ if (certificants.length === 1 && certificants[0]) return typeof certificants[0] === 'object' && certificants[0].pub ? certificants[0].pub : typeof certificants[0] === 'string' ? certificants[0] : null
697
+ certificants.map(certificant => {
698
+ if (typeof certificant ==='string') data.push(certificant);
699
+ else if (typeof certificant === 'object' && certificant.pub) data.push(certificant.pub);
700
+ });
701
+ }
702
+
703
+ if (typeof certificants === 'object' && certificants.pub) return certificants.pub
704
+ return data.length > 0 ? data : null
705
+ }
706
+ return
707
+ })();
708
+
709
+ if (!certificants) return console.log("No certificant found.")
710
+
711
+ const expiry = opt.expiry && (typeof opt.expiry === 'number' || typeof opt.expiry === 'string') ? parseFloat(opt.expiry) : null;
712
+ const readPolicy = (policy || {}).read ? policy.read : null;
713
+ const writePolicy = (policy || {}).write ? policy.write : typeof policy === 'string' || Array.isArray(policy) || policy["+"] || policy["#"] || policy["."] || policy["="] || policy["*"] || policy[">"] || policy["<"] ? policy : null;
714
+ // The "blacklist" feature is now renamed to "block". Why ? BECAUSE BLACK LIVES MATTER!
715
+ // We can now use 3 keys: block, blacklist, ban
716
+ const block = (opt || {}).block || (opt || {}).blacklist || (opt || {}).ban || {};
717
+ const readBlock = block.read && (typeof block.read === 'string' || (block.read || {})['#']) ? block.read : null;
718
+ const writeBlock = typeof block === 'string' ? block : block.write && (typeof block.write === 'string' || block.write['#']) ? block.write : null;
719
+
720
+ if (!readPolicy && !writePolicy) return console.log("No policy found.")
721
+
722
+ // reserved keys: c, e, r, w, rb, wb
723
+ const data = JSON.stringify({
724
+ c: certificants,
725
+ ...(expiry ? {e: expiry} : {}), // inject expiry if possible
726
+ ...(readPolicy ? {r: readPolicy } : {}), // "r" stands for read, which means read permission.
727
+ ...(writePolicy ? {w: writePolicy} : {}), // "w" stands for write, which means write permission.
728
+ ...(readBlock ? {rb: readBlock} : {}), // inject READ block if possible
729
+ ...(writeBlock ? {wb: writeBlock} : {}), // inject WRITE block if possible
730
+ });
731
+
732
+ const certificate = await SEA.sign(data, authority, null, {raw:1});
733
+
734
+ var r = certificate;
735
+ if(!opt.raw){ r = 'SEA'+JSON.stringify(r); }
736
+ if(cb){ try{ cb(r); }catch(e){console.log(e);} }
737
+ return r;
738
+ } catch(e) {
739
+ SEA.err = e;
740
+ if(SEA.throw){ throw e }
741
+ if(cb){ cb(); }
742
+ return;
743
+ }});
744
+
745
+ module.exports = SEA.certify;
746
+ })(USE, './certify');
747
+ USE(function(module){
748
+ var shim = USE('./shim');
749
+ // Practical examples about usage found in tests.
750
+ var SEA = USE('./root');
751
+ SEA.work = USE('./work');
752
+ SEA.sign = USE('./sign');
753
+ SEA.verify = USE('./verify');
754
+ SEA.encrypt = USE('./encrypt');
755
+ SEA.decrypt = USE('./decrypt');
756
+ SEA.certify = USE('./certify');
757
+ //SEA.opt.aeskey = USE('./aeskey'); // not official! // this causes problems in latest WebCrypto.
758
+
759
+ SEA.random = SEA.random || shim.random;
760
+
761
+ // This is Buffer used in SEA and usable from Gun/SEA application also.
762
+ // For documentation see https://nodejs.org/api/buffer.html
763
+ SEA.Buffer = SEA.Buffer || USE('./buffer');
764
+
765
+ // These SEA functions support now ony Promises or
766
+ // async/await (compatible) code, use those like Promises.
767
+ //
768
+ // Creates a wrapper library around Web Crypto API
769
+ // for various AES, ECDSA, PBKDF2 functions we called above.
770
+ // Calculate public key KeyID aka PGPv4 (result: 8 bytes as hex string)
771
+ SEA.keyid = SEA.keyid || (async (pub) => {
772
+ try {
773
+ // base64('base64(x):base64(y)') => shim.Buffer(xy)
774
+ const pb = shim.Buffer.concat(
775
+ pub.replace(/-/g, '+').replace(/_/g, '/').split('.')
776
+ .map((t) => shim.Buffer.from(t, 'base64'))
777
+ );
778
+ // id is PGPv4 compliant raw key
779
+ const id = shim.Buffer.concat([
780
+ shim.Buffer.from([0x99, pb.length / 0x100, pb.length % 0x100]), pb
781
+ ]);
782
+ const sha1 = await sha1hash(id);
783
+ const hash = shim.Buffer.from(sha1, 'binary');
784
+ return hash.toString('hex', hash.length - 8) // 16-bit ID as hex
785
+ } catch (e) {
786
+ console.log(e);
787
+ throw e
788
+ }
789
+ });
790
+ // all done!
791
+ // Obviously it is missing MANY necessary features. This is only an alpha release.
792
+ // Please experiment with it, audit what I've done so far, and complain about what needs to be added.
793
+ // SEA should be a full suite that is easy and seamless to use.
794
+ // Again, scroll naer the top, where I provide an EXAMPLE of how to create a user and sign in.
795
+ // Once logged in, the rest of the code you just read handled automatically signing/validating data.
796
+ // But all other behavior needs to be equally easy, like opinionated ways of
797
+ // Adding friends (trusted public keys), sending private messages, etc.
798
+ // Cheers! Tell me what you think.
799
+ ((SEA.window||{}).GUN||{}).SEA = SEA;
800
+
801
+ module.exports = SEA;
802
+ // -------------- END SEA MODULES --------------------
803
+ // -- BEGIN SEA+GUN MODULES: BUNDLED BY DEFAULT UNTIL OTHERS USE SEA ON OWN -------
804
+ })(USE, './sea');
805
+ USE(function(module){
806
+ var SEA = USE('./sea'), Gun, u;
807
+ if(SEA.window){
808
+ Gun = SEA.window.GUN || {chain:{}};
809
+ } else {
810
+ Gun = USE((u+'' == typeof MODULE?'.':'')+'./gun', 1);
811
+ }
812
+ SEA.GUN = Gun;
813
+
814
+ function User(root){
815
+ this._ = {$: this};
816
+ }
817
+ User.prototype = (function(){ function F(){} F.prototype = Gun.chain; return new F() }()); // Object.create polyfill
818
+ User.prototype.constructor = User;
819
+
820
+ // let's extend the gun chain with a `user` function.
821
+ // only one user can be logged in at a time, per gun instance.
822
+ Gun.chain.user = function(pub){
823
+ var gun = this, root = gun.back(-1), user;
824
+ if(pub){
825
+ pub = SEA.opt.pub((pub._||'')['#']) || pub;
826
+ return root.get('~'+pub);
827
+ }
828
+ if(user = root.back('user')){ return user }
829
+ var root = (root._), at = root, uuid = at.opt.uuid || lex;
830
+ (at = (user = at.user = gun.chain(new User))._).opt = {};
831
+ at.opt.uuid = function(cb){
832
+ var id = uuid(), pub = root.user;
833
+ if(!pub || !(pub = pub.is) || !(pub = pub.pub)){ return id }
834
+ id = '~' + pub + '/' + id;
835
+ if(cb && cb.call){ cb(null, id); }
836
+ return id;
837
+ };
838
+ return user;
839
+ };
840
+ function lex(){ return Gun.state().toString(36).replace('.','') }
841
+ Gun.User = User;
842
+ User.GUN = Gun;
843
+ User.SEA = Gun.SEA = SEA;
844
+ module.exports = User;
845
+ })(USE, './user');
846
+ USE(function(module){
847
+ var u, Gun = (''+u != typeof GUN)? (GUN||{chain:{}}) : USE((''+u === typeof MODULE?'.':'')+'./gun', 1);
848
+ Gun.chain.then = function(cb, opt){
849
+ var gun = this, p = (new Promise(function(res, rej){
850
+ gun.once(res, opt);
851
+ }));
852
+ return cb? p.then(cb) : p;
853
+ };
854
+ })(USE, './then');
855
+ USE(function(module){
856
+ var User = USE('./user'), SEA = User.SEA, Gun = User.GUN, noop = function(){};
857
+
858
+ // Well first we have to actually create a user. That is what this function does.
859
+ User.prototype.create = function(...args){
860
+ var pair = typeof args[0] === 'object' && (args[0].pub || args[0].epub) ? args[0] : typeof args[1] === 'object' && (args[1].pub || args[1].epub) ? args[1] : null;
861
+ var alias = pair && (pair.pub || pair.epub) ? pair.pub : typeof args[0] === 'string' ? args[0] : null;
862
+ var pass = pair && (pair.pub || pair.epub) ? pair : alias && typeof args[1] === 'string' ? args[1] : null;
863
+ var cb = args.filter(arg => typeof arg === 'function')[0] || null; // cb now can stand anywhere, after alias/pass or pair
864
+ var opt = args && args.length > 1 && typeof args[args.length-1] === 'object' ? args[args.length-1] : {}; // opt is always the last parameter which typeof === 'object' and stands after cb
865
+
866
+ var gun = this, cat = (gun._), root = gun.back(-1);
867
+ cb = cb || noop;
868
+ opt = opt || {};
869
+ if(false !== opt.check){
870
+ var err;
871
+ if(!alias){ err = "No user."; }
872
+ if((pass||'').length < 8){ err = "Password too short!"; }
873
+ if(err){
874
+ cb({err: Gun.log(err)});
875
+ return gun;
876
+ }
877
+ }
878
+ if(cat.ing){
879
+ (cb || noop)({err: Gun.log("User is already being created or authenticated!"), wait: true});
880
+ return gun;
881
+ }
882
+ cat.ing = true;
883
+ var act = {};
884
+ act.a = function(pubs){
885
+ act.pubs = pubs;
886
+ if(pubs && !opt.already){
887
+ // If we can enforce that a user name is already taken, it might be nice to try, but this is not guaranteed.
888
+ var ack = {err: Gun.log('User already created!')};
889
+ cat.ing = false;
890
+ (cb || noop)(ack);
891
+ gun.leave();
892
+ return;
893
+ }
894
+ act.salt = String.random(64); // pseudo-randomly create a salt, then use PBKDF2 function to extend the password with it.
895
+ SEA.work(pass, act.salt, act.b); // this will take some short amount of time to produce a proof, which slows brute force attacks.
896
+ };
897
+ act.b = function(proof){
898
+ act.proof = proof;
899
+ pair ? act.c(pair) : SEA.pair(act.c); // generate a brand new key pair or use the existing.
900
+ };
901
+ act.c = function(pair){
902
+ var tmp;
903
+ act.pair = pair || {};
904
+ if(tmp = cat.root.user){
905
+ tmp._.sea = pair;
906
+ tmp.is = {pub: pair.pub, epub: pair.epub, alias: alias};
907
+ }
908
+ // the user's public key doesn't need to be signed. But everything else needs to be signed with it! // we have now automated it! clean up these extra steps now!
909
+ act.data = {pub: pair.pub};
910
+ act.d();
911
+ };
912
+ act.d = function(){
913
+ act.data.alias = alias;
914
+ act.e();
915
+ };
916
+ act.e = function(){
917
+ act.data.epub = act.pair.epub;
918
+ SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, act.proof, act.f, {raw:1}); // to keep the private key safe, we AES encrypt it with the proof of work!
919
+ };
920
+ act.f = function(auth){
921
+ act.data.auth = JSON.stringify({ek: auth, s: act.salt});
922
+ act.g(act.data.auth);
923
+ };
924
+ act.g = function(auth){ var tmp;
925
+ act.data.auth = act.data.auth || auth;
926
+ root.get(tmp = '~'+act.pair.pub).put(act.data).on(act.h); // awesome, now we can actually save the user with their public key as their ID.
927
+ var link = {}; link[tmp] = {'#': tmp}; root.get('~@'+alias).put(link).get(tmp).on(act.i); // next up, we want to associate the alias with the public key. So we add it to the alias list.
928
+ };
929
+ act.h = function(data, key, msg, eve){
930
+ eve.off(); act.h.ok = 1; act.i();
931
+ };
932
+ act.i = function(data, key, msg, eve){
933
+ if(eve){ act.i.ok = 1; eve.off(); }
934
+ if(!act.h.ok || !act.i.ok){ return }
935
+ cat.ing = false;
936
+ cb({ok: 0, pub: act.pair.pub}); // callback that the user has been created. (Note: ok = 0 because we didn't wait for disk to ack)
937
+ if(noop === cb){ pair ? gun.auth(pair) : gun.auth(alias, pass); } // if no callback is passed, auto-login after signing up.
938
+ };
939
+ root.get('~@'+alias).once(act.a);
940
+ return gun;
941
+ };
942
+ User.prototype.leave = function(opt, cb){
943
+ var gun = this, user = (gun.back(-1)._).user;
944
+ if(user){
945
+ delete user.is;
946
+ delete user._.is;
947
+ delete user._.sea;
948
+ }
949
+ if(SEA.window){
950
+ try{var sS = {};
951
+ sS = SEA.window.sessionStorage;
952
+ delete sS.recall;
953
+ delete sS.pair;
954
+ }catch(e){} }
955
+ return gun;
956
+ };
957
+ })(USE, './create');
958
+ USE(function(module){
959
+ var User = USE('./user'), SEA = User.SEA, Gun = User.GUN, noop = function(){};
960
+ // now that we have created a user, we want to authenticate them!
961
+ User.prototype.auth = function(...args){ // TODO: this PR with arguments need to be cleaned up / refactored.
962
+ var pair = typeof args[0] === 'object' && (args[0].pub || args[0].epub) ? args[0] : typeof args[1] === 'object' && (args[1].pub || args[1].epub) ? args[1] : null;
963
+ var alias = !pair && typeof args[0] === 'string' ? args[0] : null;
964
+ var pass = (alias || (pair && !(pair.priv && pair.epriv))) && typeof args[1] === 'string' ? args[1] : null;
965
+ var cb = args.filter(arg => typeof arg === 'function')[0] || null; // cb now can stand anywhere, after alias/pass or pair
966
+ var opt = args && args.length > 1 && typeof args[args.length-1] === 'object' ? args[args.length-1] : {}; // opt is always the last parameter which typeof === 'object' and stands after cb
967
+
968
+ var gun = this, cat = (gun._), root = gun.back(-1);
969
+
970
+ if(cat.ing){
971
+ (cb || noop)({err: Gun.log("User is already being created or authenticated!"), wait: true});
972
+ return gun;
973
+ }
974
+ cat.ing = true;
975
+
976
+ var act = {}, u, tries = 9;
977
+ act.a = function(data){
978
+ if(!data){ return act.b() }
979
+ if(!data.pub){
980
+ var tmp = []; Object.keys(data).forEach(function(k){ if('_'==k){ return } tmp.push(data[k]); });
981
+ return act.b(tmp);
982
+ }
983
+ if(act.name){ return act.f(data) }
984
+ act.c((act.data = data).auth);
985
+ };
986
+ act.b = function(list){
987
+ var get = (act.list = (act.list||[]).concat(list||[])).shift();
988
+ if(u === get){
989
+ if(act.name){ return act.err('Your user account is not published for dApps to access, please consider syncing it online, or allowing local access by adding your device as a peer.') }
990
+ if(alias && tries--){
991
+ root.get('~@'+alias).once(act.a);
992
+ return;
993
+ }
994
+ return act.err('Wrong user or password.')
995
+ }
996
+ root.get(get).once(act.a);
997
+ };
998
+ act.c = function(auth){
999
+ if(u === auth){ return act.b() }
1000
+ if('string' == typeof auth){ return act.c(obj_ify(auth)) } // in case of legacy
1001
+ SEA.work(pass, (act.auth = auth).s, act.d, act.enc); // the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
1002
+ };
1003
+ act.d = function(proof){
1004
+ SEA.decrypt(act.auth.ek, proof, act.e, act.enc);
1005
+ };
1006
+ act.e = function(half){
1007
+ if(u === half){
1008
+ if(!act.enc){ // try old format
1009
+ act.enc = {encode: 'utf8'};
1010
+ return act.c(act.auth);
1011
+ } act.enc = null; // end backwards
1012
+ return act.b();
1013
+ }
1014
+ act.half = half;
1015
+ act.f(act.data);
1016
+ };
1017
+ act.f = function(pair){
1018
+ var half = act.half || {}, data = act.data || {};
1019
+ act.g(act.lol = {pub: pair.pub || data.pub, epub: pair.epub || data.epub, priv: pair.priv || half.priv, epriv: pair.epriv || half.epriv});
1020
+ };
1021
+ act.g = function(pair){
1022
+ if(!pair || !pair.pub || !pair.epub){ return act.b() }
1023
+ act.pair = pair;
1024
+ var user = (root._).user, at = (user._);
1025
+ at.tag;
1026
+ var upt = at.opt;
1027
+ at = user._ = root.get('~'+pair.pub)._;
1028
+ at.opt = upt;
1029
+ // add our credentials in-memory only to our root user instance
1030
+ user.is = {pub: pair.pub, epub: pair.epub, alias: alias || pair.pub};
1031
+ at.sea = act.pair;
1032
+ cat.ing = false;
1033
+ try{if(pass && u == (obj_ify(cat.root.graph['~'+pair.pub].auth)||'')[':']){ opt.shuffle = opt.change = pass; } }catch(e){} // migrate UTF8 & Shuffle!
1034
+ opt.change? act.z() : (cb || noop)(at);
1035
+ if(SEA.window && ((gun.back('user')._).opt||opt).remember){
1036
+ // TODO: this needs to be modular.
1037
+ try{var sS = {};
1038
+ sS = SEA.window.sessionStorage; // TODO: FIX BUG putting on `.is`!
1039
+ sS.recall = true;
1040
+ sS.pair = JSON.stringify(pair); // auth using pair is more reliable than alias/pass
1041
+ }catch(e){}
1042
+ }
1043
+ try{
1044
+ if(root._.tag.auth){ // auth handle might not be registered yet
1045
+ (root._).on('auth', at); // TODO: Deprecate this, emit on user instead! Update docs when you do.
1046
+ } else { setTimeout(function(){ (root._).on('auth', at); },1); } // if not, hackily add a timeout.
1047
+ //at.on('auth', at) // Arrgh, this doesn't work without event "merge" code, but "merge" code causes stack overflow and crashes after logging in & trying to write data.
1048
+ }catch(e){
1049
+ Gun.log("Your 'auth' callback crashed with:", e);
1050
+ }
1051
+ };
1052
+ act.h = function(data){
1053
+ if(!data){ return act.b() }
1054
+ alias = data.alias;
1055
+ if(!alias)
1056
+ alias = data.alias = "~" + pair.pub;
1057
+ if(!data.auth){
1058
+ return act.g(pair);
1059
+ }
1060
+ pair = null;
1061
+ act.c((act.data = data).auth);
1062
+ };
1063
+ act.z = function(){
1064
+ // password update so encrypt private key using new pwd + salt
1065
+ act.salt = String.random(64); // pseudo-random
1066
+ SEA.work(opt.change, act.salt, act.y);
1067
+ };
1068
+ act.y = function(proof){
1069
+ SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, proof, act.x, {raw:1});
1070
+ };
1071
+ act.x = function(auth){
1072
+ act.w(JSON.stringify({ek: auth, s: act.salt}));
1073
+ };
1074
+ act.w = function(auth){
1075
+ if(opt.shuffle){ // delete in future!
1076
+ console.log('migrate core account from UTF8 & shuffle');
1077
+ var tmp = {}; Object.keys(act.data).forEach(function(k){ tmp[k] = act.data[k]; });
1078
+ delete tmp._;
1079
+ tmp.auth = auth;
1080
+ root.get('~'+act.pair.pub).put(tmp);
1081
+ } // end delete
1082
+ root.get('~'+act.pair.pub).get('auth').put(auth, cb || noop);
1083
+ };
1084
+ act.err = function(e){
1085
+ var ack = {err: Gun.log(e || 'User cannot be found!')};
1086
+ cat.ing = false;
1087
+ (cb || noop)(ack);
1088
+ };
1089
+ act.plugin = function(name){
1090
+ if(!(act.name = name)){ return act.err() }
1091
+ var tmp = [name];
1092
+ if('~' !== name[0]){
1093
+ tmp[1] = '~'+name;
1094
+ tmp[2] = '~@'+name;
1095
+ }
1096
+ act.b(tmp);
1097
+ };
1098
+ if(pair){
1099
+ if(pair.priv && pair.epriv)
1100
+ act.g(pair);
1101
+ else
1102
+ root.get('~'+pair.pub).once(act.h);
1103
+ } else
1104
+ if(alias){
1105
+ root.get('~@'+alias).once(act.a);
1106
+ } else
1107
+ if(!alias && !pass){
1108
+ SEA.name(act.plugin);
1109
+ }
1110
+ return gun;
1111
+ };
1112
+ function obj_ify(o){
1113
+ if('string' != typeof o){ return o }
1114
+ try{o = JSON.parse(o);
1115
+ }catch(e){o={};} return o;
1116
+ }
1117
+ })(USE, './auth');
1118
+ USE(function(module){
1119
+ var User = USE('./user'), SEA = User.SEA; User.GUN;
1120
+ User.prototype.recall = function(opt, cb){
1121
+ var gun = this, root = gun.back(-1);
1122
+ opt = opt || {};
1123
+ if(opt && opt.sessionStorage){
1124
+ if(SEA.window){
1125
+ try{
1126
+ var sS = {};
1127
+ sS = SEA.window.sessionStorage; // TODO: FIX BUG putting on `.is`!
1128
+ if(sS){
1129
+ (root._).opt.remember = true;
1130
+ ((gun.back('user')._).opt||opt).remember = true;
1131
+ if(sS.recall || sS.pair) root.user().auth(JSON.parse(sS.pair), cb); // pair is more reliable than alias/pass
1132
+ }
1133
+ }catch(e){}
1134
+ }
1135
+ return gun;
1136
+ }
1137
+ /*
1138
+ TODO: copy mhelander's expiry code back in.
1139
+ Although, we should check with community,
1140
+ should expiry be core or a plugin?
1141
+ */
1142
+ return gun;
1143
+ };
1144
+ })(USE, './recall');
1145
+ USE(function(module){
1146
+ var User = USE('./user'), SEA = User.SEA, Gun = User.GUN, noop = function(){};
1147
+ User.prototype.pair = function(){
1148
+ var user = this, proxy; // undeprecated, hiding with proxies.
1149
+ try{ proxy = new Proxy({DANGER:'\u2620'}, {get: function(t,p,r){
1150
+ if(!user.is || !(user._||'').sea){ return }
1151
+ return user._.sea[p];
1152
+ }});}catch(e){}
1153
+ return proxy;
1154
+ };
1155
+ // If authenticated user wants to delete his/her account, let's support it!
1156
+ User.prototype.delete = async function(alias, pass, cb){
1157
+ console.log("user.delete() IS DEPRECATED AND WILL BE MOVED TO A MODULE!!!");
1158
+ var gun = this; gun.back(-1); var user = gun.back('user');
1159
+ try {
1160
+ user.auth(alias, pass, function(ack){
1161
+ var pub = (user.is||{}).pub;
1162
+ // Delete user data
1163
+ user.map().once(function(){ this.put(null); });
1164
+ // Wipe user data from memory
1165
+ user.leave();
1166
+ (cb || noop)({ok: 0});
1167
+ });
1168
+ } catch (e) {
1169
+ Gun.log('User.delete failed! Error:', e);
1170
+ }
1171
+ return gun;
1172
+ };
1173
+ User.prototype.alive = async function(){
1174
+ console.log("user.alive() IS DEPRECATED!!!");
1175
+ const gunRoot = this.back(-1);
1176
+ try {
1177
+ // All is good. Should we do something more with actual recalled data?
1178
+ await authRecall(gunRoot);
1179
+ return gunRoot._.user._
1180
+ } catch (e) {
1181
+ const err = 'No session!';
1182
+ Gun.log(err);
1183
+ throw { err }
1184
+ }
1185
+ };
1186
+ User.prototype.trust = async function(user){
1187
+ console.log("`.trust` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
1188
+ // TODO: BUG!!! SEA `node` read listener needs to be async, which means core needs to be async too.
1189
+ //gun.get('alice').get('age').trust(bob);
1190
+ if (Gun.is(user)) {
1191
+ user.get('pub').get((ctx, ev) => {
1192
+ console.log(ctx, ev);
1193
+ });
1194
+ }
1195
+ user.get('trust').get(path).put(theirPubkey);
1196
+
1197
+ // do a lookup on this gun chain directly (that gets bob's copy of the data)
1198
+ // do a lookup on the metadata trust table for this path (that gets all the pubkeys allowed to write on this path)
1199
+ // do a lookup on each of those pubKeys ON the path (to get the collab data "layers")
1200
+ // THEN you perform Jachen's mix operation
1201
+ // and return the result of that to...
1202
+ };
1203
+ User.prototype.grant = function(to, cb){
1204
+ console.log("`.grant` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
1205
+ var gun = this, user = gun.back(-1).user(), pair = user._.sea, path = '';
1206
+ gun.back(function(at){ if(at.is){ return } path += (at.get||''); });
1207
+ (async function(){
1208
+ var enc, sec = await user.get('grant').get(pair.pub).get(path).then();
1209
+ sec = await SEA.decrypt(sec, pair);
1210
+ if(!sec){
1211
+ sec = SEA.random(16).toString();
1212
+ enc = await SEA.encrypt(sec, pair);
1213
+ user.get('grant').get(pair.pub).get(path).put(enc);
1214
+ }
1215
+ var pub = to.get('pub').then();
1216
+ var epub = to.get('epub').then();
1217
+ pub = await pub; epub = await epub;
1218
+ var dh = await SEA.secret(epub, pair);
1219
+ enc = await SEA.encrypt(sec, dh);
1220
+ user.get('grant').get(pub).get(path).put(enc, cb);
1221
+ }());
1222
+ return gun;
1223
+ };
1224
+ User.prototype.secret = function(data, cb){
1225
+ console.log("`.secret` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
1226
+ var gun = this, user = gun.back(-1).user(), pair = user.pair(), path = '';
1227
+ gun.back(function(at){ if(at.is){ return } path += (at.get||''); });
1228
+ (async function(){
1229
+ var enc, sec = await user.get('trust').get(pair.pub).get(path).then();
1230
+ sec = await SEA.decrypt(sec, pair);
1231
+ if(!sec){
1232
+ sec = SEA.random(16).toString();
1233
+ enc = await SEA.encrypt(sec, pair);
1234
+ user.get('trust').get(pair.pub).get(path).put(enc);
1235
+ }
1236
+ enc = await SEA.encrypt(data, sec);
1237
+ gun.put(enc, cb);
1238
+ }());
1239
+ return gun;
1240
+ };
1241
+
1242
+ /**
1243
+ * returns the decrypted value, encrypted by secret
1244
+ * @returns {Promise<any>}
1245
+ // Mark needs to review 1st before officially supported
1246
+ User.prototype.decrypt = function(cb) {
1247
+ let gun = this,
1248
+ path = ''
1249
+ gun.back(function(at) {
1250
+ if (at.is) {
1251
+ return
1252
+ }
1253
+ path += at.get || ''
1254
+ })
1255
+ return gun
1256
+ .then(async data => {
1257
+ if (data == null) {
1258
+ return
1259
+ }
1260
+ const user = gun.back(-1).user()
1261
+ const pair = user.pair()
1262
+ let sec = await user
1263
+ .get('trust')
1264
+ .get(pair.pub)
1265
+ .get(path)
1266
+ sec = await SEA.decrypt(sec, pair)
1267
+ if (!sec) {
1268
+ return data
1269
+ }
1270
+ let decrypted = await SEA.decrypt(data, sec)
1271
+ return decrypted
1272
+ })
1273
+ .then(res => {
1274
+ cb && cb(res)
1275
+ return res
1276
+ })
1277
+ }
1278
+ */
1279
+ module.exports = User;
1280
+ })(USE, './share');
1281
+ USE(function(module){
1282
+ var SEA = USE('./sea'), S = USE('./settings'), u;
1283
+ var Gun = (SEA.window||'').GUN || USE((''+u === typeof MODULE?'.':'')+'./gun', 1);
1284
+ // After we have a GUN extension to make user registration/login easy, we then need to handle everything else.
1285
+
1286
+ // We do this with a GUN adapter, we first listen to when a gun instance is created (and when its options change)
1287
+ Gun.on('opt', function(at){
1288
+ if(!at.sea){ // only add SEA once per instance, on the "at" context.
1289
+ at.sea = {own: {}};
1290
+ at.on('put', check, at); // SEA now runs its firewall on HAM diffs, not all i/o.
1291
+ }
1292
+ this.to.next(at); // make sure to call the "next" middleware adapter.
1293
+ });
1294
+
1295
+ // Alright, this next adapter gets run at the per node level in the graph database.
1296
+ // correction: 2020 it gets run on each key/value pair in a node upon a HAM diff.
1297
+ // This will let us verify that every property on a node has a value signed by a public key we trust.
1298
+ // If the signature does not match, the data is just `undefined` so it doesn't get passed on.
1299
+ // If it does match, then we transform the in-memory "view" of the data into its plain value (without the signature).
1300
+ // Now NOTE! Some data is "system" data, not user data. Example: List of public keys, aliases, etc.
1301
+ // This data is self-enforced (the value can only match its ID), but that is handled in the `security` function.
1302
+ // From the self-enforced data, we can see all the edges in the graph that belong to a public key.
1303
+ // Example: ~ASDF is the ID of a node with ASDF as its public key, signed alias and salt, and
1304
+ // its encrypted private key, but it might also have other signed values on it like `profile = <ID>` edge.
1305
+ // Using that directed edge's ID, we can then track (in memory) which IDs belong to which keys.
1306
+ // Here is a problem: Multiple public keys can "claim" any node's ID, so this is dangerous!
1307
+ // This means we should ONLY trust our "friends" (our key ring) public keys, not any ones.
1308
+ // I have not yet added that to SEA yet in this alpha release. That is coming soon, but beware in the meanwhile!
1309
+
1310
+ function check(msg){ // REVISE / IMPROVE, NO NEED TO PASS MSG/EVE EACH SUB?
1311
+ var eve = this, at = eve.as, put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], id = msg['#'], tmp;
1312
+ if(!soul || !key){ return }
1313
+ if((msg._||'').faith && (at.opt||'').faith && 'function' == typeof msg._){
1314
+ SEA.opt.pack(put, function(raw){
1315
+ SEA.verify(raw, false, function(data){ // this is synchronous if false
1316
+ put['='] = SEA.opt.unpack(data);
1317
+ eve.to.next(msg);
1318
+ });});
1319
+ return
1320
+ }
1321
+ var no = function(why){ at.on('in', {'@': id, err: msg.err = why}); }; // exploit internal relay stun for now, maybe violates spec, but testing for now. // Note: this may be only the sharded message, not original batch.
1322
+ //var no = function(why){ msg.ack(why) };
1323
+ (msg._||'').DBG && ((msg._||'').DBG.c = +new Date);
1324
+ if(0 <= soul.indexOf('<?')){ // special case for "do not sync data X old" forget
1325
+ // 'a~pub.key/b<?9'
1326
+ tmp = parseFloat(soul.split('<?')[1]||'');
1327
+ if(tmp && (state < (Gun.state() - (tmp * 1000)))){ // sec to ms
1328
+ (tmp = msg._) && (tmp.stun) && (tmp.stun--); // THIS IS BAD CODE! It assumes GUN internals do something that will probably change in future, but hacking in now.
1329
+ return; // omit!
1330
+ }
1331
+ }
1332
+
1333
+ if('~@' === soul){ // special case for shared system data, the list of aliases.
1334
+ check.alias(eve, msg, val, key, soul, at, no); return;
1335
+ }
1336
+ if('~@' === soul.slice(0,2)){ // special case for shared system data, the list of public keys for an alias.
1337
+ check.pubs(eve, msg, val, key, soul, at, no); return;
1338
+ }
1339
+ //if('~' === soul.slice(0,1) && 2 === (tmp = soul.slice(1)).split('.').length){ // special case, account data for a public key.
1340
+ if(tmp = SEA.opt.pub(soul)){ // special case, account data for a public key.
1341
+ check.pub(eve, msg, val, key, soul, at, no, at.user||'', tmp); return;
1342
+ }
1343
+ if(0 <= soul.indexOf('#')){ // special case for content addressing immutable hashed data.
1344
+ check.hash(eve, msg, val, key, soul, at, no); return;
1345
+ }
1346
+ check.any(eve, msg, val, key, soul, at, no, at.user||''); return;
1347
+ }
1348
+ check.hash = function(eve, msg, val, key, soul, at, no){ // mark unbuilt @i001962 's epic hex contrib!
1349
+ SEA.work(val, null, function(data){
1350
+ function hexToBase64(hexStr) {
1351
+ let base64 = "";
1352
+ for(let i = 0; i < hexStr.length; i++) {
1353
+ base64 += !(i - 1 & 1) ? String.fromCharCode(parseInt(hexStr.substring(i - 1, i + 1), 16)) : "";}
1354
+ return btoa(base64);}
1355
+ if(data && data === key.split('#').slice(-1)[0]){ return eve.to.next(msg) }
1356
+ else if (data && data === hexToBase64(key.split('#').slice(-1)[0])){
1357
+ return eve.to.next(msg) }
1358
+ no("Data hash not same as hash!");
1359
+ }, {name: 'SHA-256'});
1360
+ };
1361
+ check.alias = function(eve, msg, val, key, soul, at, no){ // Example: {_:#~@, ~@alice: {#~@alice}}
1362
+ if(!val){ return no("Data must exist!") } // data MUST exist
1363
+ if('~@'+key === link_is(val)){ return eve.to.next(msg) } // in fact, it must be EXACTLY equal to itself
1364
+ no("Alias not same!"); // if it isn't, reject.
1365
+ };
1366
+ check.pubs = function(eve, msg, val, key, soul, at, no){ // Example: {_:#~@alice, ~asdf: {#~asdf}}
1367
+ if(!val){ return no("Alias must exist!") } // data MUST exist
1368
+ if(key === link_is(val)){ return eve.to.next(msg) } // and the ID must be EXACTLY equal to its property
1369
+ no("Alias not same!"); // that way nobody can tamper with the list of public keys.
1370
+ };
1371
+ check.pub = async function(eve, msg, val, key, soul, at, no, user, pub){ var tmp; // Example: {_:#~asdf, hello:'world'~fdsa}}
1372
+ const raw = await S.parse(val) || {};
1373
+ const verify = (certificate, certificant, cb) => {
1374
+ if (certificate.m && certificate.s && certificant && pub)
1375
+ // now verify certificate
1376
+ return SEA.verify(certificate, pub, data => { // check if "pub" (of the graph owner) really issued this cert
1377
+ if (u !== data && u !== data.e && msg.put['>'] && msg.put['>'] > parseFloat(data.e)) return no("Certificate expired.") // certificate expired
1378
+ // "data.c" = a list of certificants/certified users
1379
+ // "data.w" = lex WRITE permission, in the future, there will be "data.r" which means lex READ permission
1380
+ if (u !== data && data.c && data.w && (data.c === certificant || data.c.indexOf('*' ) > -1)) {
1381
+ // ok, now "certificant" is in the "certificants" list, but is "path" allowed? Check path
1382
+ let path = soul.indexOf('/') > -1 ? soul.replace(soul.substring(0, soul.indexOf('/') + 1), '') : '';
1383
+ String.match = String.match || Gun.text.match;
1384
+ const w = Array.isArray(data.w) ? data.w : typeof data.w === 'object' || typeof data.w === 'string' ? [data.w] : [];
1385
+ for (const lex of w) {
1386
+ if ((String.match(path, lex['#']) && String.match(key, lex['.'])) || (!lex['.'] && String.match(path, lex['#'])) || (!lex['#'] && String.match(key, lex['.'])) || String.match((path ? path + '/' + key : key), lex['#'] || lex)) {
1387
+ // is Certificant forced to present in Path
1388
+ if (lex['+'] && lex['+'].indexOf('*') > -1 && path && path.indexOf(certificant) == -1 && key.indexOf(certificant) == -1) return no(`Path "${path}" or key "${key}" must contain string "${certificant}".`)
1389
+ // path is allowed, but is there any WRITE block? Check it out
1390
+ if (data.wb && (typeof data.wb === 'string' || ((data.wb || {})['#']))) { // "data.wb" = path to the WRITE block
1391
+ var root = eve.as.root.$.back(-1);
1392
+ if (typeof data.wb === 'string' && '~' !== data.wb.slice(0, 1)) root = root.get('~' + pub);
1393
+ return root.get(data.wb).get(certificant).once(value => { // TODO: INTENT TO DEPRECATE.
1394
+ if (value && (value === 1 || value === true)) return no(`Certificant ${certificant} blocked.`)
1395
+ return cb(data)
1396
+ })
1397
+ }
1398
+ return cb(data)
1399
+ }
1400
+ }
1401
+ return no("Certificate verification fail.")
1402
+ }
1403
+ })
1404
+ return
1405
+ };
1406
+
1407
+ if ('pub' === key && '~' + pub === soul) {
1408
+ if (val === pub) return eve.to.next(msg) // the account MUST match `pub` property that equals the ID of the public key.
1409
+ return no("Account not same!")
1410
+ }
1411
+
1412
+ if ((tmp = user.is) && tmp.pub && !raw['*'] && !raw['+'] && (pub === tmp.pub || (pub !== tmp.pub && ((msg._.msg || {}).opt || {}).cert))){
1413
+ SEA.opt.pack(msg.put, packed => {
1414
+ SEA.sign(packed, (user._).sea, async function(data) {
1415
+ if (u === data) return no(SEA.err || 'Signature fail.')
1416
+ msg.put[':'] = {':': tmp = SEA.opt.unpack(data.m), '~': data.s};
1417
+ msg.put['='] = tmp;
1418
+
1419
+ // if writing to own graph, just allow it
1420
+ if (pub === user.is.pub) {
1421
+ if (tmp = link_is(val)) (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1;
1422
+ JSON.stringifyAsync(msg.put[':'], function(err,s){
1423
+ if(err){ return no(err || "Stringify error.") }
1424
+ msg.put[':'] = s;
1425
+ return eve.to.next(msg);
1426
+ });
1427
+ return
1428
+ }
1429
+
1430
+ // if writing to other's graph, check if cert exists then try to inject cert into put, also inject self pub so that everyone can verify the put
1431
+ if (pub !== user.is.pub && ((msg._.msg || {}).opt || {}).cert) {
1432
+ const cert = await S.parse(msg._.msg.opt.cert);
1433
+ // even if cert exists, we must verify it
1434
+ if (cert && cert.m && cert.s)
1435
+ verify(cert, user.is.pub, _ => {
1436
+ msg.put[':']['+'] = cert; // '+' is a certificate
1437
+ msg.put[':']['*'] = user.is.pub; // '*' is pub of the user who puts
1438
+ JSON.stringifyAsync(msg.put[':'], function(err,s){
1439
+ if(err){ return no(err || "Stringify error.") }
1440
+ msg.put[':'] = s;
1441
+ return eve.to.next(msg);
1442
+ });
1443
+ return
1444
+ });
1445
+ }
1446
+ }, {raw: 1});
1447
+ });
1448
+ return;
1449
+ }
1450
+
1451
+ SEA.opt.pack(msg.put, packed => {
1452
+ SEA.verify(packed, raw['*'] || pub, function(data){ var tmp;
1453
+ data = SEA.opt.unpack(data);
1454
+ if (u === data) return no("Unverified data.") // make sure the signature matches the account it claims to be on. // reject any updates that are signed with a mismatched account.
1455
+ if ((tmp = link_is(data)) && pub === SEA.opt.pub(tmp)) (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1;
1456
+
1457
+ // check if cert ('+') and putter's pub ('*') exist
1458
+ if (raw['+'] && raw['+']['m'] && raw['+']['s'] && raw['*'])
1459
+ // now verify certificate
1460
+ verify(raw['+'], raw['*'], _ => {
1461
+ msg.put['='] = data;
1462
+ return eve.to.next(msg);
1463
+ });
1464
+ else {
1465
+ msg.put['='] = data;
1466
+ return eve.to.next(msg);
1467
+ }
1468
+ });
1469
+ });
1470
+ return
1471
+ };
1472
+ check.any = function(eve, msg, val, key, soul, at, no, user){ if(at.opt.secure){ return no("Soul missing public key at '" + key + "'.") }
1473
+ // TODO: Ask community if should auto-sign non user-graph data.
1474
+ at.on('secure', function(msg){ this.off();
1475
+ if(!at.opt.secure){ return eve.to.next(msg) }
1476
+ no("Data cannot be changed.");
1477
+ }).on.on('secure', msg);
1478
+ return;
1479
+ };
1480
+
1481
+ var valid = Gun.valid, link_is = function(d,l){ return 'string' == typeof (l = valid(d)) && l }; (Gun.state||'').ify;
1482
+
1483
+ var pubcut = /[^\w_-]/; // anything not alphanumeric or _ -
1484
+ SEA.opt.pub = function(s){
1485
+ if(!s){ return }
1486
+ s = s.split('~');
1487
+ if(!s || !(s = s[1])){ return }
1488
+ s = s.split(pubcut).slice(0,2);
1489
+ if(!s || 2 != s.length){ return }
1490
+ if('@' === (s[0]||'')[0]){ return }
1491
+ s = s.slice(0,2).join('.');
1492
+ return s;
1493
+ };
1494
+ SEA.opt.stringy = function(t){
1495
+ // TODO: encrypt etc. need to check string primitive. Make as breaking change.
1496
+ };
1497
+ SEA.opt.pack = function(d,cb,k, n,s){ var tmp, f; // pack for verifying
1498
+ if(SEA.opt.check(d)){ return cb(d) }
1499
+ if(d && d['#'] && d['.'] && d['>']){ tmp = d[':']; f = 1; }
1500
+ JSON.parseAsync(f? tmp : d, function(err, meta){
1501
+ var sig = ((u !== (meta||'')[':']) && (meta||'')['~']); // or just ~ check?
1502
+ if(!sig){ cb(d); return }
1503
+ cb({m: {'#':s||d['#'],'.':k||d['.'],':':(meta||'')[':'],'>':d['>']||Gun.state.is(n, k)}, s: sig});
1504
+ });
1505
+ };
1506
+ var O = SEA.opt;
1507
+ SEA.opt.unpack = function(d, k, n){ var tmp;
1508
+ if(u === d){ return }
1509
+ if(d && (u !== (tmp = d[':']))){ return tmp }
1510
+ k = k || O.fall_key; if(!n && O.fall_val){ n = {}; n[k] = O.fall_val; }
1511
+ if(!k || !n){ return }
1512
+ if(d === n[k]){ return d }
1513
+ if(!SEA.opt.check(n[k])){ return d }
1514
+ var soul = (n && n._ && n._['#']) || O.fall_soul, s = Gun.state.is(n, k) || O.fall_state;
1515
+ if(d && 4 === d.length && soul === d[0] && k === d[1] && fl(s) === fl(d[3])){
1516
+ return d[2];
1517
+ }
1518
+ if(s < SEA.opt.shuffle_attack){
1519
+ return d;
1520
+ }
1521
+ };
1522
+ SEA.opt.shuffle_attack = 1546329600000; // Jan 1, 2019
1523
+ var fl = Math.floor; // TODO: Still need to fix inconsistent state issue.
1524
+ // TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible.
1525
+
1526
+ })(USE, './index');
1527
+ }());
1528
+ } (sea));
1529
+
1530
+ var seaExports = sea.exports;
1531
+ var SEA = /*@__PURE__*/getDefaultExportFromCjs(seaExports);
16
1532
 
17
1533
  let contractAddresses$1 = {
18
1534
  PROOF_OF_INTEGRITY_ADDRESS: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
@@ -20,15 +1536,15 @@ let contractAddresses$1 = {
20
1536
  };
21
1537
 
22
1538
  if (typeof window === 'undefined') {
23
- const { fileURLToPath } = require('url');
24
- const { dirname } = require('path');
25
- const { readFileSync } = require('fs');
26
- const { join } = require('path');
27
-
28
1539
  try {
1540
+ const { fileURLToPath } = require('url');
1541
+ const { dirname, join } = require('path');
1542
+ const { readFileSync } = require('fs');
1543
+
29
1544
  const __filename = fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('gun-eth.cjs.js', document.baseURI).href)));
30
1545
  const __dirname = dirname(__filename);
31
- const rawdata = readFileSync(join(__dirname, 'contract-address.json'), 'utf8');
1546
+
1547
+ const rawdata = readFileSync(join(__dirname, 'contract-address.json'));
32
1548
  contractAddresses$1 = JSON.parse(rawdata);
33
1549
  console.log("Loaded contract addresses:", contractAddresses$1);
34
1550
  } catch (error) {
@@ -44,56 +1560,6 @@ const LOCAL_CONFIG = {
44
1560
  GUN_PEER: "http://localhost:8765/gun"
45
1561
  };
46
1562
 
47
- // Indirizzi di produzione per diverse chain
48
- const CHAIN_CONFIG = {
49
- optimismSepolia: {
50
- STEALTH_ANNOUNCER_ADDRESS: "",
51
- PROOF_OF_INTEGRITY_ADDRESS: "",
52
- RPC_URL: "https://sepolia.optimism.io",
53
- CHAIN_ID: 11155420
54
- },
55
- arbitrumSepolia: {
56
- STEALTH_ANNOUNCER_ADDRESS: "",
57
- PROOF_OF_INTEGRITY_ADDRESS: "",
58
- RPC_URL: "https://sepolia-rollup.arbitrum.io/rpc",
59
- CHAIN_ID: 421614
60
- },
61
- localhost: {
62
- RPC_URL: "http://127.0.0.1:8545",
63
- CHAIN_ID: 1337
64
- }
65
- };
66
-
67
- // Funzione per ottenere gli indirizzi corretti
68
- function getAddressesForChain(chainName) {
69
- let config;
70
-
71
- // Se è localhost, prova a caricare gli indirizzi locali
72
- if (chainName === 'localhost') {
73
- try {
74
- // Carica gli indirizzi dal file generato dal deploy locale
75
- const localAddresses = require$$0;
76
- config = {
77
- ...CHAIN_CONFIG.localhost,
78
- ...localAddresses
79
- };
80
- console.log("Using local addresses:", config);
81
- return config;
82
- } catch (err) {
83
- console.warn('No local addresses found');
84
- throw new Error('No local addresses found. Did you run local deployment?');
85
- }
86
- }
87
-
88
- // Altrimenti usa gli indirizzi di produzione
89
- config = CHAIN_CONFIG[chainName];
90
- if (!config) {
91
- throw new Error(`Chain ${chainName} not supported. Supported chains: ${Object.keys(CHAIN_CONFIG).join(', ')}`);
92
- }
93
-
94
- return config;
95
- }
96
-
97
1563
  const STEALTH_ANNOUNCER_ABI = [
98
1564
  {
99
1565
  "inputs": [
@@ -425,12 +1891,12 @@ const PROOF_OF_INTEGRITY_ABI = [
425
1891
  "stateMutability": "view",
426
1892
  "type": "function"
427
1893
  }
428
- ];
1894
+ ];
1895
+ const STEALTH_ANNOUNCER_ADDRESS = "...";
429
1896
 
430
- // Ottieni gli indirizzi corretti per la chain
431
- const chainConfig = getAddressesForChain('optimismSepolia'); // o la chain desiderata
432
- const STEALTH_ANNOUNCER_ADDRESS = chainConfig.STEALTH_ANNOUNCER_ADDRESS;
433
- chainConfig.PROOF_OF_INTEGRITY_ADDRESS;
1897
+ // =============================================
1898
+ // IMPORTS AND GLOBAL VARIABLES
1899
+ // =============================================
434
1900
 
435
1901
  let PROOF_CONTRACT_ADDRESS;
436
1902
  let rpcUrl = "";