sea.js 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315
  1. ;(function(){
  2. /* UNBUILD */
  3. var root;
  4. if(typeof window !== "undefined"){ root = window }
  5. if(typeof global !== "undefined"){ root = global }
  6. root = root || {};
  7. var console = root.console || {log: function(){}};
  8. function USE(arg, req){
  9. return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
  10. arg(mod = {exports: {}});
  11. USE[R(path)] = mod.exports;
  12. }
  13. function R(p){
  14. return p.split('/').slice(-1).toString().replace('.js','');
  15. }
  16. }
  17. if(typeof module !== "undefined"){ var common = module }
  18. /* UNBUILD */
  19. ;USE(function(module){
  20. // Security, Encryption, and Authorization: SEA.js
  21. // MANDATORY READING: https://gun.eco/explainers/data/security.html
  22. // IT IS IMPLEMENTED IN A POLYFILL/SHIM APPROACH.
  23. // THIS IS AN EARLY ALPHA!
  24. if(typeof window !== "undefined"){ module.window = window }
  25. var tmp = module.window || module;
  26. var SEA = tmp.SEA || {};
  27. if(SEA.window = module.window){ SEA.window.SEA = SEA }
  28. try{ if(typeof common !== "undefined"){ common.exports = SEA } }catch(e){}
  29. module.exports = SEA;
  30. })(USE, './root');
  31. ;USE(function(module){
  32. var SEA = USE('./root');
  33. try{ if(SEA.window){
  34. if(location.protocol.indexOf('s') < 0
  35. && location.host.indexOf('localhost') < 0
  36. && location.protocol.indexOf('file:') < 0){
  37. location.protocol = 'https:'; // WebCrypto does NOT work without HTTPS!
  38. }
  39. } }catch(e){}
  40. })(USE, './https');
  41. ;USE(function(module){
  42. // This is Array extended to have .toString(['utf8'|'hex'|'base64'])
  43. function SeaArray() {}
  44. Object.assign(SeaArray, { from: Array.from })
  45. SeaArray.prototype = Object.create(Array.prototype)
  46. SeaArray.prototype.toString = function(enc, start, end) { enc = enc || 'utf8'; start = start || 0;
  47. const length = this.length
  48. if (enc === 'hex') {
  49. const buf = new Uint8Array(this)
  50. return [ ...Array(((end && (end + 1)) || length) - start).keys()]
  51. .map((i) => buf[ i + start ].toString(16).padStart(2, '0')).join('')
  52. }
  53. if (enc === 'utf8') {
  54. return Array.from(
  55. { length: (end || length) - start },
  56. (_, i) => String.fromCharCode(this[ i + start])
  57. ).join('')
  58. }
  59. if (enc === 'base64') {
  60. return btoa(this)
  61. }
  62. }
  63. module.exports = SeaArray;
  64. })(USE, './array');
  65. ;USE(function(module){
  66. // This is Buffer implementation used in SEA. Functionality is mostly
  67. // compatible with NodeJS 'safe-buffer' and is used for encoding conversions
  68. // between binary and 'hex' | 'utf8' | 'base64'
  69. // See documentation and validation for safe implementation in:
  70. // https://github.com/feross/safe-buffer#update
  71. var SeaArray = USE('./array');
  72. function SafeBuffer(...props) {
  73. console.warn('new SafeBuffer() is depreciated, please use SafeBuffer.from()')
  74. return SafeBuffer.from(...props)
  75. }
  76. SafeBuffer.prototype = Object.create(Array.prototype)
  77. Object.assign(SafeBuffer, {
  78. // (data, enc) where typeof data === 'string' then enc === 'utf8'|'hex'|'base64'
  79. from() {
  80. if (!Object.keys(arguments).length) {
  81. throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')
  82. }
  83. const input = arguments[0]
  84. let buf
  85. if (typeof input === 'string') {
  86. const enc = arguments[1] || 'utf8'
  87. if (enc === 'hex') {
  88. const bytes = input.match(/([\da-fA-F]{2})/g)
  89. .map((byte) => parseInt(byte, 16))
  90. if (!bytes || !bytes.length) {
  91. throw new TypeError('Invalid first argument for type \'hex\'.')
  92. }
  93. buf = SeaArray.from(bytes)
  94. } else if (enc === 'utf8') {
  95. const length = input.length
  96. const words = new Uint16Array(length)
  97. Array.from({ length: length }, (_, i) => words[i] = input.charCodeAt(i))
  98. buf = SeaArray.from(words)
  99. } else if (enc === 'base64') {
  100. const dec = atob(input)
  101. const length = dec.length
  102. const bytes = new Uint8Array(length)
  103. Array.from({ length: length }, (_, i) => bytes[i] = dec.charCodeAt(i))
  104. buf = SeaArray.from(bytes)
  105. } else if (enc === 'binary') {
  106. buf = SeaArray.from(input)
  107. } else {
  108. console.info('SafeBuffer.from unknown encoding: '+enc)
  109. }
  110. return buf
  111. }
  112. const byteLength = input.byteLength // what is going on here? FOR MARTTI
  113. const length = input.byteLength ? input.byteLength : input.length
  114. if (length) {
  115. let buf
  116. if (input instanceof ArrayBuffer) {
  117. buf = new Uint8Array(input)
  118. }
  119. return SeaArray.from(buf || input)
  120. }
  121. },
  122. // This is 'safe-buffer.alloc' sans encoding support
  123. alloc(length, fill = 0 /*, enc*/ ) {
  124. return SeaArray.from(new Uint8Array(Array.from({ length: length }, () => fill)))
  125. },
  126. // This is normal UNSAFE 'buffer.alloc' or 'new Buffer(length)' - don't use!
  127. allocUnsafe(length) {
  128. return SeaArray.from(new Uint8Array(Array.from({ length : length })))
  129. },
  130. // This puts together array of array like members
  131. concat(arr) { // octet array
  132. if (!Array.isArray(arr)) {
  133. throw new TypeError('First argument must be Array containing ArrayBuffer or Uint8Array instances.')
  134. }
  135. return SeaArray.from(arr.reduce((ret, item) => ret.concat(Array.from(item)), []))
  136. }
  137. })
  138. SafeBuffer.prototype.from = SafeBuffer.from
  139. SafeBuffer.prototype.toString = SeaArray.prototype.toString
  140. module.exports = SafeBuffer;
  141. })(USE, './buffer');
  142. ;USE(function(module){
  143. const SEA = USE('./root')
  144. const Buffer = USE('./buffer')
  145. const api = {Buffer: Buffer}
  146. var o = {};
  147. if(SEA.window){
  148. api.crypto = window.crypto || window.msCrypto;
  149. api.subtle = (api.crypto||o).subtle || (api.crypto||o).webkitSubtle;
  150. api.TextEncoder = window.TextEncoder;
  151. api.TextDecoder = window.TextDecoder;
  152. api.random = (len) => Buffer.from(api.crypto.getRandomValues(new Uint8Array(Buffer.alloc(len))))
  153. }
  154. if(!api.crypto){try{
  155. var crypto = USE('crypto', 1);
  156. const { TextEncoder, TextDecoder } = USE('text-encoding', 1)
  157. Object.assign(api, {
  158. crypto,
  159. //subtle,
  160. TextEncoder,
  161. TextDecoder,
  162. random: (len) => Buffer.from(crypto.randomBytes(len))
  163. });
  164. //try{
  165. const WebCrypto = USE('node-webcrypto-ossl', 1);
  166. api.ossl = api.subtle = new WebCrypto({directory: 'ossl'}).subtle // ECDH
  167. //}catch(e){
  168. //console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed.");
  169. //}
  170. }catch(e){
  171. console.log("node-webcrypto-ossl and text-encoding may not be included by default, please add it to your package.json!");
  172. OSSL_WEBCRYPTO_OR_TEXT_ENCODING_NOT_INSTALLED;
  173. }}
  174. module.exports = api
  175. })(USE, './shim');
  176. ;USE(function(module){
  177. var SEA = USE('./root');
  178. var Buffer = USE('./buffer');
  179. var s = {};
  180. s.pbkdf2 = {hash: 'SHA-256', iter: 100000, ks: 64};
  181. s.ecdsa = {
  182. pair: {name: 'ECDSA', namedCurve: 'P-256'},
  183. sign: {name: 'ECDSA', hash: {name: 'SHA-256'}}
  184. };
  185. s.ecdh = {name: 'ECDH', namedCurve: 'P-256'};
  186. // This creates Web Cryptography API compliant JWK for sign/verify purposes
  187. s.jwk = function(pub, d){ // d === priv
  188. pub = pub.split('.');
  189. var x = pub[0], y = pub[1];
  190. var jwk = {kty: "EC", crv: "P-256", x: x, y: y, ext: true};
  191. jwk.key_ops = d ? ['sign'] : ['verify'];
  192. if(d){ jwk.d = d }
  193. return jwk;
  194. };
  195. s.recall = {
  196. validity: 12 * 60 * 60, // internally in seconds : 12 hours
  197. hook: function(props){ return props } // { iat, exp, alias, remember } // or return new Promise((resolve, reject) => resolve(props)
  198. };
  199. s.check = function(t){ return (typeof t == 'string') && ('SEA{' === t.slice(0,4)) }
  200. s.parse = function p(t){ try {
  201. var yes = (typeof t == 'string');
  202. if(yes && 'SEA{' === t.slice(0,4)){ t = t.slice(3) }
  203. return yes ? JSON.parse(t) : t;
  204. } catch (e) {}
  205. return t;
  206. }
  207. SEA.opt = s;
  208. module.exports = s
  209. })(USE, './settings');
  210. ;USE(function(module){
  211. var shim = USE('./shim');
  212. module.exports = async function(d, o){
  213. var t = (typeof d == 'string')? d : JSON.stringify(d);
  214. var hash = await shim.subtle.digest({name: o||'SHA-256'}, new shim.TextEncoder().encode(t));
  215. return shim.Buffer.from(hash);
  216. }
  217. })(USE, './sha256');
  218. ;USE(function(module){
  219. // This internal func returns SHA-1 hashed data for KeyID generation
  220. const __shim = USE('./shim')
  221. const subtle = __shim.subtle
  222. const ossl = __shim.ossl ? __shim.ossl : subtle
  223. const sha1hash = (b) => ossl.digest({name: 'SHA-1'}, new ArrayBuffer(b))
  224. module.exports = sha1hash
  225. })(USE, './sha1');
  226. ;USE(function(module){
  227. var SEA = USE('./root');
  228. var shim = USE('./shim');
  229. var S = USE('./settings');
  230. var sha = USE('./sha256');
  231. var u;
  232. SEA.work = SEA.work || (async (data, pair, cb, opt) => { try { // used to be named `proof`
  233. var salt = (pair||{}).epub || pair; // epub not recommended, salt should be random!
  234. var opt = opt || {};
  235. if(salt instanceof Function){
  236. cb = salt;
  237. salt = u;
  238. }
  239. salt = salt || shim.random(9);
  240. data = (typeof data == 'string')? data : JSON.stringify(data);
  241. if('sha' === (opt.name||'').toLowerCase().slice(0,3)){
  242. var rsha = shim.Buffer.from(await sha(data, opt.name), 'binary').toString(opt.encode || 'base64')
  243. if(cb){ try{ cb(rsha) }catch(e){console.log(e)} }
  244. return rsha;
  245. }
  246. var key = await (shim.ossl || shim.subtle).importKey('raw', new shim.TextEncoder().encode(data), {name: opt.name || 'PBKDF2'}, false, ['deriveBits']);
  247. var work = await (shim.ossl || shim.subtle).deriveBits({
  248. name: opt.name || 'PBKDF2',
  249. iterations: opt.iterations || S.pbkdf2.iter,
  250. salt: new shim.TextEncoder().encode(opt.salt || salt),
  251. hash: opt.hash || S.pbkdf2.hash,
  252. }, key, opt.length || (S.pbkdf2.ks * 8))
  253. data = shim.random(data.length) // Erase data in case of passphrase
  254. var r = shim.Buffer.from(work, 'binary').toString(opt.encode || 'base64')
  255. if(cb){ try{ cb(r) }catch(e){console.log(e)} }
  256. return r;
  257. } catch(e) {
  258. console.log(e);
  259. SEA.err = e;
  260. if(SEA.throw){ throw e }
  261. if(cb){ cb() }
  262. return;
  263. }});
  264. module.exports = SEA.work;
  265. })(USE, './work');
  266. ;USE(function(module){
  267. var SEA = USE('./root');
  268. var shim = USE('./shim');
  269. var S = USE('./settings');
  270. SEA.name = SEA.name || (async (cb, opt) => { try {
  271. if(cb){ try{ cb() }catch(e){console.log(e)} }
  272. return;
  273. } catch(e) {
  274. console.log(e);
  275. SEA.err = e;
  276. if(SEA.throw){ throw e }
  277. if(cb){ cb() }
  278. return;
  279. }});
  280. //SEA.pair = async (data, proof, cb) => { try {
  281. SEA.pair = SEA.pair || (async (cb, opt) => { try {
  282. var ecdhSubtle = shim.ossl || shim.subtle;
  283. // First: ECDSA keys for signing/verifying...
  284. var sa = await shim.subtle.generateKey(S.ecdsa.pair, true, [ 'sign', 'verify' ])
  285. .then(async (keys) => {
  286. // privateKey scope doesn't leak out from here!
  287. //const { d: priv } = await shim.subtle.exportKey('jwk', keys.privateKey)
  288. var key = {};
  289. key.priv = (await shim.subtle.exportKey('jwk', keys.privateKey)).d;
  290. var pub = await shim.subtle.exportKey('jwk', keys.publicKey);
  291. //const pub = Buff.from([ x, y ].join(':')).toString('base64') // old
  292. key.pub = pub.x+'.'+pub.y; // new
  293. // x and y are already base64
  294. // pub is UTF8 but filename/URL safe (https://www.ietf.org/rfc/rfc3986.txt)
  295. // but split on a non-base64 letter.
  296. return key;
  297. })
  298. // To include PGPv4 kind of keyId:
  299. // const pubId = await SEA.keyid(keys.pub)
  300. // Next: ECDH keys for encryption/decryption...
  301. try{
  302. var dh = await ecdhSubtle.generateKey(S.ecdh, true, ['deriveKey'])
  303. .then(async (keys) => {
  304. // privateKey scope doesn't leak out from here!
  305. var key = {};
  306. key.epriv = (await ecdhSubtle.exportKey('jwk', keys.privateKey)).d;
  307. var pub = await ecdhSubtle.exportKey('jwk', keys.publicKey);
  308. //const epub = Buff.from([ ex, ey ].join(':')).toString('base64') // old
  309. key.epub = pub.x+'.'+pub.y; // new
  310. // ex and ey are already base64
  311. // epub is UTF8 but filename/URL safe (https://www.ietf.org/rfc/rfc3986.txt)
  312. // but split on a non-base64 letter.
  313. return key;
  314. })
  315. }catch(e){
  316. if(SEA.window){ throw e }
  317. if(e == 'Error: ECDH is not a supported algorithm'){ console.log('Ignoring ECDH...') }
  318. else { throw e }
  319. } dh = dh || {};
  320. var r = { pub: sa.pub, priv: sa.priv, /* pubId, */ epub: dh.epub, epriv: dh.epriv }
  321. if(cb){ try{ cb(r) }catch(e){console.log(e)} }
  322. return r;
  323. } catch(e) {
  324. console.log(e);
  325. SEA.err = e;
  326. if(SEA.throw){ throw e }
  327. if(cb){ cb() }
  328. return;
  329. }});
  330. module.exports = SEA.pair;
  331. })(USE, './pair');
  332. ;USE(function(module){
  333. var SEA = USE('./root');
  334. var shim = USE('./shim');
  335. var S = USE('./settings');
  336. var sha = USE('./sha256');
  337. var u;
  338. SEA.sign = SEA.sign || (async (data, pair, cb, opt) => { try {
  339. opt = opt || {};
  340. if(!(pair||opt).priv){
  341. pair = await SEA.I(null, {what: data, how: 'sign', why: opt.why});
  342. }
  343. if(u === data){ throw '`undefined` not allowed.' }
  344. var json = S.parse(data);
  345. var check = opt.check = opt.check || json;
  346. if(SEA.verify && (SEA.opt.check(check) || (check && check.s && check.m))
  347. && u !== await SEA.verify(check, pair)){ // don't sign if we already signed it.
  348. var r = S.parse(check);
  349. if(!opt.raw){ r = 'SEA'+JSON.stringify(r) }
  350. if(cb){ try{ cb(r) }catch(e){console.log(e)} }
  351. return r;
  352. }
  353. var pub = pair.pub;
  354. var priv = pair.priv;
  355. var jwk = S.jwk(pub, priv);
  356. var hash = await sha(json);
  357. var sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['sign'])
  358. .then((key) => (shim.ossl || shim.subtle).sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here!
  359. var r = {m: json, s: shim.Buffer.from(sig, 'binary').toString(opt.encode || 'base64')}
  360. if(!opt.raw){ r = 'SEA'+JSON.stringify(r) }
  361. if(cb){ try{ cb(r) }catch(e){console.log(e)} }
  362. return r;
  363. } catch(e) {
  364. console.log(e);
  365. SEA.err = e;
  366. if(SEA.throw){ throw e }
  367. if(cb){ cb() }
  368. return;
  369. }});
  370. module.exports = SEA.sign;
  371. })(USE, './sign');
  372. ;USE(function(module){
  373. var SEA = USE('./root');
  374. var shim = USE('./shim');
  375. var S = USE('./settings');
  376. var sha = USE('./sha256');
  377. var u;
  378. SEA.verify = SEA.verify || (async (data, pair, cb, opt) => { try {
  379. var json = S.parse(data);
  380. if(false === pair){ // don't verify!
  381. var raw = S.parse(json.m);
  382. if(cb){ try{ cb(raw) }catch(e){console.log(e)} }
  383. return raw;
  384. }
  385. opt = opt || {};
  386. // SEA.I // verify is free! Requires no user permission.
  387. var pub = pair.pub || pair;
  388. var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['verify']);
  389. var hash = await sha(json.m);
  390. var buf, sig, check, tmp; try{
  391. buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT!
  392. sig = new Uint8Array(buf);
  393. check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash));
  394. if(!check){ throw "Signature did not match." }
  395. }catch(e){
  396. if(SEA.opt.fallback){
  397. return await SEA.opt.fall_verify(data, pair, cb, opt);
  398. }
  399. }
  400. var r = check? S.parse(json.m) : u;
  401. if(cb){ try{ cb(r) }catch(e){console.log(e)} }
  402. return r;
  403. } catch(e) {
  404. console.log(e); // mismatched owner FOR MARTTI
  405. SEA.err = e;
  406. if(SEA.throw){ throw e }
  407. if(cb){ cb() }
  408. return;
  409. }});
  410. module.exports = SEA.verify;
  411. // legacy & ossl leak mitigation:
  412. var knownKeys = {};
  413. var keyForPair = SEA.opt.slow_leak = pair => {
  414. if (knownKeys[pair]) return knownKeys[pair];
  415. var jwk = S.jwk(pair);
  416. knownKeys[pair] = (shim.ossl || shim.subtle).importKey("jwk", jwk, S.ecdsa.pair, false, ["verify"]);
  417. return knownKeys[pair];
  418. };
  419. SEA.opt.fall_verify = async function(data, pair, cb, opt, f){
  420. if(f === SEA.opt.fallback){ throw "Signature did not match" } f = f || 1;
  421. var json = S.parse(data), pub = pair.pub || pair, key = await SEA.opt.slow_leak(pub);
  422. var hash = (f <= SEA.opt.fallback)? shim.Buffer.from(await shim.subtle.digest({name: 'SHA-256'}, new shim.TextEncoder().encode(S.parse(json.m)))) : await sha(json.m); // this line is old bad buggy code but necessary for old compatibility.
  423. var buf; var sig; var check; try{
  424. buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT!
  425. sig = new Uint8Array(buf)
  426. check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
  427. if(!check){ throw "Signature did not match." }
  428. }catch(e){
  429. buf = shim.Buffer.from(json.s, 'utf8') // AUTO BACKWARD OLD UTF8 DATA!
  430. sig = new Uint8Array(buf)
  431. check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
  432. if(!check){ throw "Signature did not match." }
  433. }
  434. var r = check? S.parse(json.m) : u;
  435. if(cb){ try{ cb(r) }catch(e){console.log(e)} }
  436. return r;
  437. }
  438. SEA.opt.fallback = 2;
  439. })(USE, './verify');
  440. ;USE(function(module){
  441. var shim = USE('./shim');
  442. var sha256hash = USE('./sha256');
  443. const importGen = async (key, salt, opt) => {
  444. //const combo = shim.Buffer.concat([shim.Buffer.from(key, 'utf8'), salt || shim.random(8)]).toString('utf8') // old
  445. var opt = opt || {};
  446. const combo = key + (salt || shim.random(8)).toString('utf8'); // new
  447. const hash = shim.Buffer.from(await sha256hash(combo), 'binary')
  448. return await shim.subtle.importKey('raw', new Uint8Array(hash), opt.name || 'AES-GCM', false, ['encrypt', 'decrypt'])
  449. }
  450. module.exports = importGen;
  451. })(USE, './aeskey');
  452. ;USE(function(module){
  453. var SEA = USE('./root');
  454. var shim = USE('./shim');
  455. var S = USE('./settings');
  456. var aeskey = USE('./aeskey');
  457. var u;
  458. SEA.encrypt = SEA.encrypt || (async (data, pair, cb, opt) => { try {
  459. opt = opt || {};
  460. var key = (pair||opt).epriv || pair;
  461. if(u === data){ throw '`undefined` not allowed.' }
  462. if(!key){
  463. pair = await SEA.I(null, {what: data, how: 'encrypt', why: opt.why});
  464. key = pair.epriv || pair;
  465. }
  466. var msg = (typeof data == 'string')? data : JSON.stringify(data);
  467. var rand = {s: shim.random(8), iv: shim.random(16)};
  468. var ct = await aeskey(key, rand.s, opt).then((aes) => (/*shim.ossl ||*/ shim.subtle).encrypt({ // Keeping the AES key scope as private as possible...
  469. name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
  470. }, aes, new shim.TextEncoder().encode(msg)));
  471. var r = {
  472. ct: shim.Buffer.from(ct, 'binary').toString(opt.encode || 'base64'),
  473. iv: rand.iv.toString(opt.encode || 'base64'),
  474. s: rand.s.toString(opt.encode || 'base64')
  475. }
  476. if(!opt.raw){ r = 'SEA'+JSON.stringify(r) }
  477. if(cb){ try{ cb(r) }catch(e){console.log(e)} }
  478. return r;
  479. } catch(e) {
  480. console.log(e);
  481. SEA.err = e;
  482. if(SEA.throw){ throw e }
  483. if(cb){ cb() }
  484. return;
  485. }});
  486. module.exports = SEA.encrypt;
  487. })(USE, './encrypt');
  488. ;USE(function(module){
  489. var SEA = USE('./root');
  490. var shim = USE('./shim');
  491. var S = USE('./settings');
  492. var aeskey = USE('./aeskey');
  493. SEA.decrypt = SEA.decrypt || (async (data, pair, cb, opt) => { try {
  494. opt = opt || {};
  495. var key = (pair||opt).epriv || pair;
  496. if(!key){
  497. pair = await SEA.I(null, {what: data, how: 'decrypt', why: opt.why});
  498. key = pair.epriv || pair;
  499. }
  500. var json = S.parse(data);
  501. var buf, bufiv, bufct; try{
  502. buf = shim.Buffer.from(json.s, opt.encode || 'base64');
  503. bufiv = shim.Buffer.from(json.iv, opt.encode || 'base64');
  504. bufct = shim.Buffer.from(json.ct, opt.encode || 'base64');
  505. var ct = await aeskey(key, buf, opt).then((aes) => (/*shim.ossl ||*/ shim.subtle).decrypt({ // Keeping aesKey scope as private as possible...
  506. name: opt.name || 'AES-GCM', iv: new Uint8Array(bufiv)
  507. }, aes, new Uint8Array(bufct)));
  508. }catch(e){
  509. if('utf8' === opt.encode){ throw "Could not decrypt" }
  510. if(SEA.opt.fallback){
  511. opt.encode = 'utf8';
  512. return await SEA.decrypt(data, pair, cb, opt);
  513. }
  514. }
  515. var r = S.parse(new shim.TextDecoder('utf8').decode(ct));
  516. if(cb){ try{ cb(r) }catch(e){console.log(e)} }
  517. return r;
  518. } catch(e) {
  519. console.log(e);
  520. SEA.err = e;
  521. if(SEA.throw){ throw e }
  522. if(cb){ cb() }
  523. return;
  524. }});
  525. module.exports = SEA.decrypt;
  526. })(USE, './decrypt');
  527. ;USE(function(module){
  528. var SEA = USE('./root');
  529. var shim = USE('./shim');
  530. var S = USE('./settings');
  531. // Derive shared secret from other's pub and my epub/epriv
  532. SEA.secret = SEA.secret || (async (key, pair, cb, opt) => { try {
  533. opt = opt || {};
  534. if(!pair || !pair.epriv || !pair.epub){
  535. pair = await SEA.I(null, {what: key, how: 'secret', why: opt.why});
  536. }
  537. var pub = key.epub || key;
  538. var epub = pair.epub;
  539. var epriv = pair.epriv;
  540. var ecdhSubtle = shim.ossl || shim.subtle;
  541. var pubKeyData = keysToEcdhJwk(pub);
  542. var props = Object.assign(S.ecdh, { public: await ecdhSubtle.importKey(...pubKeyData, true, []) });
  543. var privKeyData = keysToEcdhJwk(epub, epriv);
  544. var derived = await ecdhSubtle.importKey(...privKeyData, false, ['deriveKey']).then(async (privKey) => {
  545. // privateKey scope doesn't leak out from here!
  546. var derivedKey = await ecdhSubtle.deriveKey(props, privKey, { name: 'AES-GCM', length: 256 }, true, [ 'encrypt', 'decrypt' ]);
  547. return ecdhSubtle.exportKey('jwk', derivedKey).then(({ k }) => k);
  548. })
  549. var r = derived;
  550. if(cb){ try{ cb(r) }catch(e){console.log(e)} }
  551. return r;
  552. } catch(e) {
  553. console.log(e);
  554. SEA.err = e;
  555. if(SEA.throw){ throw e }
  556. if(cb){ cb() }
  557. return;
  558. }});
  559. // can this be replaced with settings.jwk?
  560. var keysToEcdhJwk = (pub, d) => { // d === priv
  561. //var [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') // old
  562. var [ x, y ] = pub.split('.') // new
  563. var jwk = d ? { d: d } : {}
  564. return [ // Use with spread returned value...
  565. 'jwk',
  566. Object.assign(
  567. jwk,
  568. { x: x, y: y, kty: 'EC', crv: 'P-256', ext: true }
  569. ), // ??? refactor
  570. S.ecdh
  571. ]
  572. }
  573. module.exports = SEA.secret;
  574. })(USE, './secret');
  575. ;USE(function(module){
  576. var shim = USE('./shim');
  577. // Practical examples about usage found from ./test/common.js
  578. var SEA = USE('./root');
  579. SEA.work = USE('./work');
  580. SEA.sign = USE('./sign');
  581. SEA.verify = USE('./verify');
  582. SEA.encrypt = USE('./encrypt');
  583. SEA.decrypt = USE('./decrypt');
  584. SEA.random = SEA.random || shim.random;
  585. // This is Buffer used in SEA and usable from Gun/SEA application also.
  586. // For documentation see https://nodejs.org/api/buffer.html
  587. SEA.Buffer = SEA.Buffer || USE('./buffer');
  588. // These SEA functions support now ony Promises or
  589. // async/await (compatible) code, use those like Promises.
  590. //
  591. // Creates a wrapper library around Web Crypto API
  592. // for various AES, ECDSA, PBKDF2 functions we called above.
  593. // Calculate public key KeyID aka PGPv4 (result: 8 bytes as hex string)
  594. SEA.keyid = SEA.keyid || (async (pub) => {
  595. try {
  596. // base64('base64(x):base64(y)') => Buffer(xy)
  597. const pb = Buffer.concat(
  598. pub.replace(/-/g, '+').replace(/_/g, '/').split('.')
  599. .map((t) => Buffer.from(t, 'base64'))
  600. )
  601. // id is PGPv4 compliant raw key
  602. const id = Buffer.concat([
  603. Buffer.from([0x99, pb.length / 0x100, pb.length % 0x100]), pb
  604. ])
  605. const sha1 = await sha1hash(id)
  606. const hash = Buffer.from(sha1, 'binary')
  607. return hash.toString('hex', hash.length - 8) // 16-bit ID as hex
  608. } catch (e) {
  609. console.log(e)
  610. throw e
  611. }
  612. });
  613. // all done!
  614. // Obviously it is missing MANY necessary features. This is only an alpha release.
  615. // Please experiment with it, audit what I've done so far, and complain about what needs to be added.
  616. // SEA should be a full suite that is easy and seamless to use.
  617. // Again, scroll naer the top, where I provide an EXAMPLE of how to create a user and sign in.
  618. // Once logged in, the rest of the code you just read handled automatically signing/validating data.
  619. // But all other behavior needs to be equally easy, like opinionated ways of
  620. // Adding friends (trusted public keys), sending private messages, etc.
  621. // Cheers! Tell me what you think.
  622. var Gun = (SEA.window||{}).Gun || USE('./gun', 1);
  623. Gun.SEA = SEA;
  624. SEA.GUN = SEA.Gun = Gun;
  625. module.exports = SEA
  626. })(USE, './sea');
  627. ;USE(function(module){
  628. var Gun = USE('./sea').Gun;
  629. Gun.chain.then = function(cb){
  630. var gun = this, p = (new Promise(function(res, rej){
  631. gun.once(res);
  632. }));
  633. return cb? p.then(cb) : p;
  634. }
  635. })(USE, './then');
  636. ;USE(function(module){
  637. var SEA = USE('./sea');
  638. var Gun = SEA.Gun;
  639. var then = USE('./then');
  640. function User(root){
  641. this._ = {$: this};
  642. }
  643. User.prototype = (function(){ function F(){}; F.prototype = Gun.chain; return new F() }()) // Object.create polyfill
  644. User.prototype.constructor = User;
  645. // let's extend the gun chain with a `user` function.
  646. // only one user can be logged in at a time, per gun instance.
  647. Gun.chain.user = function(pub){
  648. var gun = this, root = gun.back(-1), user;
  649. if(pub){ return root.get('~'+pub) }
  650. if(user = root.back('user')){ return user }
  651. var root = (root._), at = root, uuid = at.opt.uuid || Gun.state.lex;
  652. (at = (user = at.user = gun.chain(new User))._).opt = {};
  653. at.opt.uuid = function(cb){
  654. var id = uuid(), pub = root.user;
  655. if(!pub || !(pub = pub.is) || !(pub = pub.pub)){ return id }
  656. id = id + '~' + pub + '.';
  657. if(cb && cb.call){ cb(null, id) }
  658. return id;
  659. }
  660. return user;
  661. }
  662. Gun.User = User;
  663. module.exports = User;
  664. })(USE, './user');
  665. ;USE(function(module){
  666. // TODO: This needs to be split into all separate functions.
  667. // Not just everything thrown into 'create'.
  668. var SEA = USE('./sea');
  669. var User = USE('./user');
  670. var authsettings = USE('./settings');
  671. var Gun = SEA.Gun;
  672. var noop = function(){};
  673. // Well first we have to actually create a user. That is what this function does.
  674. User.prototype.create = function(alias, pass, cb, opt){
  675. var gun = this, cat = (gun._), root = gun.back(-1);
  676. cb = cb || noop;
  677. if(cat.ing){
  678. cb({err: Gun.log("User is already being created or authenticated!"), wait: true});
  679. return gun;
  680. }
  681. cat.ing = true;
  682. opt = opt || {};
  683. var act = {}, u;
  684. act.a = function(pubs){
  685. act.pubs = pubs;
  686. if(pubs && !opt.already){
  687. // If we can enforce that a user name is already taken, it might be nice to try, but this is not guaranteed.
  688. var ack = {err: Gun.log('User already created!')};
  689. cat.ing = false;
  690. cb(ack);
  691. gun.leave();
  692. return;
  693. }
  694. act.salt = Gun.text.random(64); // pseudo-randomly create a salt, then use PBKDF2 function to extend the password with it.
  695. SEA.work(pass, act.salt, act.b); // this will take some short amount of time to produce a proof, which slows brute force attacks.
  696. }
  697. act.b = function(proof){
  698. act.proof = proof;
  699. SEA.pair(act.c); // now we have generated a brand new ECDSA key pair for the user account.
  700. }
  701. act.c = function(pair){ var tmp;
  702. act.pair = pair || {};
  703. if(tmp = cat.root.user){
  704. tmp._.sea = pair;
  705. tmp.is = {pub: pair.pub, epub: pair.epub, alias: alias};
  706. }
  707. // 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!
  708. act.data = {pub: pair.pub};
  709. act.d();
  710. }
  711. act.d = function(){
  712. act.data.alias = alias;
  713. act.e();
  714. }
  715. act.e = function(){
  716. act.data.epub = act.pair.epub;
  717. 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!
  718. }
  719. act.f = function(auth){
  720. act.data.auth = JSON.stringify({ek: auth, s: act.salt});
  721. act.g(act.data.auth);
  722. }
  723. act.g = function(auth){ var tmp;
  724. act.data.auth = act.data.auth || auth;
  725. root.get(tmp = '~'+act.pair.pub).put(act.data); // awesome, now we can actually save the user with their public key as their ID.
  726. root.get('~@'+alias).put(Gun.obj.put({}, tmp, Gun.val.link.ify(tmp))); // next up, we want to associate the alias with the public key. So we add it to the alias list.
  727. setTimeout(function(){ // we should be able to delete this now, right?
  728. cat.ing = false;
  729. 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)
  730. if(noop === cb){ gun.auth(alias, pass) } // if no callback is passed, auto-login after signing up.
  731. },10);
  732. }
  733. root.get('~@'+alias).once(act.a);
  734. return gun;
  735. }
  736. // now that we have created a user, we want to authenticate them!
  737. User.prototype.auth = function(alias, pass, cb, opt){
  738. var gun = this, cat = (gun._), root = gun.back(-1);
  739. cb = cb || function(){};
  740. if(cat.ing){
  741. cb({err: Gun.log("User is already being created or authenticated!"), wait: true});
  742. return gun;
  743. }
  744. cat.ing = true;
  745. opt = opt || {};
  746. var pair = (alias && (alias.pub || alias.epub))? alias : (pass && (pass.pub || pass.epub))? pass : null;
  747. var act = {}, u;
  748. act.a = function(data){
  749. if(!data){ return act.b() }
  750. if(!data.pub){
  751. var tmp = [];
  752. Gun.node.is(data, function(v){ tmp.push(v) })
  753. return act.b(tmp);
  754. }
  755. if(act.name){ return act.f(data) }
  756. act.c((act.data = data).auth);
  757. }
  758. act.b = function(list){
  759. var get = (act.list = (act.list||[]).concat(list||[])).shift();
  760. if(u === get){
  761. 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.') }
  762. return act.err('Wrong user or password.')
  763. }
  764. root.get(get).once(act.a);
  765. }
  766. act.c = function(auth){
  767. if(u === auth){ return act.b() }
  768. if(Gun.text.is(auth)){ return act.c(Gun.obj.ify(auth)) } // in case of legacy
  769. 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.
  770. }
  771. act.d = function(proof){
  772. SEA.decrypt(act.auth.ek, proof, act.e, act.enc);
  773. }
  774. act.e = function(half){
  775. if(u === half){
  776. if(!act.enc){ // try old format
  777. act.enc = {encode: 'utf8'};
  778. return act.c(act.auth);
  779. } act.enc = null; // end backwards
  780. return act.b();
  781. }
  782. act.half = half;
  783. act.f(act.data);
  784. }
  785. act.f = function(data){
  786. if(!data || !data.pub){ return act.b() }
  787. var tmp = act.half || {};
  788. act.g({pub: data.pub, epub: data.epub, priv: tmp.priv, epriv: tmp.epriv});
  789. }
  790. act.g = function(pair){
  791. act.pair = pair;
  792. var user = (root._).user, at = (user._);
  793. var tmp = at.tag;
  794. var upt = at.opt;
  795. at = user._ = root.get('~'+pair.pub)._;
  796. at.opt = upt;
  797. // add our credentials in-memory only to our root user instance
  798. user.is = {pub: pair.pub, epub: pair.epub, alias: alias};
  799. at.sea = act.pair;
  800. cat.ing = false;
  801. try{if(pass && !Gun.obj.has(Gun.obj.ify(cat.root.graph['~'+pair.pub].auth), ':')){ opt.shuffle = opt.change = pass; } }catch(e){} // migrate UTF8 & Shuffle!
  802. opt.change? act.z() : cb(at);
  803. if(SEA.window && ((gun.back('user')._).opt||opt).remember){
  804. // TODO: this needs to be modular.
  805. try{var sS = {};
  806. sS = window.sessionStorage;
  807. sS.recall = true;
  808. sS.alias = alias;
  809. sS.tmp = pass;
  810. }catch(e){}
  811. }
  812. try{
  813. (root._).on('auth', at) // TODO: Deprecate this, emit on user instead! Update docs when you do.
  814. //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.
  815. }catch(e){
  816. Gun.log("Your 'auth' callback crashed with:", e);
  817. }
  818. }
  819. act.z = function(){
  820. // password update so encrypt private key using new pwd + salt
  821. act.salt = Gun.text.random(64); // pseudo-random
  822. SEA.work(opt.change, act.salt, act.y);
  823. }
  824. act.y = function(proof){
  825. SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, proof, act.x, {raw:1});
  826. }
  827. act.x = function(auth){
  828. act.w(JSON.stringify({ek: auth, s: act.salt}));
  829. }
  830. act.w = function(auth){
  831. if(opt.shuffle){ // delete in future!
  832. console.log('migrate core account from UTF8 & shuffle');
  833. var tmp = Gun.obj.to(act.data);
  834. Gun.obj.del(tmp, '_');
  835. tmp.auth = auth;
  836. root.get('~'+act.pair.pub).put(tmp);
  837. } // end delete
  838. root.get('~'+act.pair.pub).get('auth').put(auth, cb);
  839. }
  840. act.err = function(e){
  841. var ack = {err: Gun.log(e || 'User cannot be found!')};
  842. cat.ing = false;
  843. cb(ack);
  844. }
  845. act.plugin = function(name){
  846. if(!(act.name = name)){ return act.err() }
  847. var tmp = [name];
  848. if('~' !== name[0]){
  849. tmp[1] = '~'+name;
  850. tmp[2] = '~@'+name;
  851. }
  852. act.b(tmp);
  853. }
  854. if(pair){
  855. act.g(pair);
  856. } else
  857. if(alias){
  858. root.get('~@'+alias).once(act.a);
  859. } else
  860. if(!alias && !pass){
  861. SEA.name(act.plugin);
  862. }
  863. return gun;
  864. }
  865. User.prototype.pair = function(){
  866. console.log("user.pair() IS DEPRECATED AND WILL BE DELETED!!!");
  867. var user = this;
  868. if(!user.is){ return false }
  869. return user._.sea;
  870. }
  871. User.prototype.leave = function(opt, cb){
  872. var gun = this, user = (gun.back(-1)._).user;
  873. if(user){
  874. delete user.is;
  875. delete user._.is;
  876. delete user._.sea;
  877. }
  878. if(SEA.window){
  879. try{var sS = {};
  880. sS = window.sessionStorage;
  881. delete sS.alias;
  882. delete sS.tmp;
  883. delete sS.recall;
  884. }catch(e){};
  885. }
  886. return gun;
  887. }
  888. // If authenticated user wants to delete his/her account, let's support it!
  889. User.prototype.delete = async function(alias, pass, cb){
  890. var gun = this, root = gun.back(-1), user = gun.back('user');
  891. try {
  892. user.auth(alias, pass, function(ack){
  893. var pub = (user.is||{}).pub;
  894. // Delete user data
  895. user.map().once(function(){ this.put(null) });
  896. // Wipe user data from memory
  897. user.leave();
  898. (cb || noop)({ok: 0});
  899. });
  900. } catch (e) {
  901. Gun.log('User.delete failed! Error:', e);
  902. }
  903. return gun;
  904. }
  905. User.prototype.recall = function(opt, cb){
  906. var gun = this, root = gun.back(-1), tmp;
  907. opt = opt || {};
  908. if(opt && opt.sessionStorage){
  909. if(SEA.window){
  910. try{var sS = {};
  911. sS = window.sessionStorage;
  912. if(sS){
  913. (root._).opt.remember = true;
  914. ((gun.back('user')._).opt||opt).remember = true;
  915. if(sS.recall || (sS.alias && sS.tmp)){
  916. root.user().auth(sS.alias, sS.tmp, cb);
  917. }
  918. }
  919. }catch(e){}
  920. }
  921. return gun;
  922. }
  923. /*
  924. TODO: copy mhelander's expiry code back in.
  925. Although, we should check with community,
  926. should expiry be core or a plugin?
  927. */
  928. return gun;
  929. }
  930. User.prototype.alive = async function(){
  931. const gunRoot = this.back(-1)
  932. try {
  933. // All is good. Should we do something more with actual recalled data?
  934. await authRecall(gunRoot)
  935. return gunRoot._.user._
  936. } catch (e) {
  937. const err = 'No session!'
  938. Gun.log(err)
  939. throw { err }
  940. }
  941. }
  942. User.prototype.trust = async function(user){
  943. // TODO: BUG!!! SEA `node` read listener needs to be async, which means core needs to be async too.
  944. //gun.get('alice').get('age').trust(bob);
  945. if (Gun.is(user)) {
  946. user.get('pub').get((ctx, ev) => {
  947. console.log(ctx, ev)
  948. })
  949. }
  950. }
  951. User.prototype.grant = function(to, cb){
  952. console.log("`.grant` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
  953. var gun = this, user = gun.back(-1).user(), pair = user.pair(), path = '';
  954. gun.back(function(at){ if(at.is){ return } path += (at.get||'') });
  955. (async function(){
  956. var enc, sec = await user.get('trust').get(pair.pub).get(path).then();
  957. sec = await SEA.decrypt(sec, pair);
  958. if(!sec){
  959. sec = SEA.random(16).toString();
  960. enc = await SEA.encrypt(sec, pair);
  961. user.get('trust').get(pair.pub).get(path).put(enc);
  962. }
  963. var pub = to.get('pub').then();
  964. var epub = to.get('epub').then();
  965. pub = await pub; epub = await epub;
  966. var dh = await SEA.secret(epub, pair);
  967. enc = await SEA.encrypt(sec, dh);
  968. user.get('trust').get(pub).get(path).put(enc, cb);
  969. }());
  970. return gun;
  971. }
  972. User.prototype.secret = function(data, cb){
  973. console.log("`.secret` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
  974. var gun = this, user = gun.back(-1).user(), pair = user.pair(), path = '';
  975. gun.back(function(at){ if(at.is){ return } path += (at.get||'') });
  976. (async function(){
  977. var enc, sec = await user.get('trust').get(pair.pub).get(path).then();
  978. sec = await SEA.decrypt(sec, pair);
  979. if(!sec){
  980. sec = SEA.random(16).toString();
  981. enc = await SEA.encrypt(sec, pair);
  982. user.get('trust').get(pair.pub).get(path).put(enc);
  983. }
  984. enc = await SEA.encrypt(data, sec);
  985. gun.put(enc, cb);
  986. }());
  987. return gun;
  988. }
  989. module.exports = User
  990. })(USE, './create');
  991. ;USE(function(module){
  992. const SEA = USE('./sea')
  993. const Gun = SEA.Gun;
  994. // After we have a GUN extension to make user registration/login easy, we then need to handle everything else.
  995. // We do this with a GUN adapter, we first listen to when a gun instance is created (and when its options change)
  996. Gun.on('opt', function(at){
  997. if(!at.sea){ // only add SEA once per instance, on the "at" context.
  998. at.sea = {own: {}};
  999. at.on('in', security, at); // now listen to all input data, acting as a firewall.
  1000. at.on('out', signature, at); // and output listeners, to encrypt outgoing data.
  1001. at.on('node', each, at);
  1002. }
  1003. this.to.next(at); // make sure to call the "next" middleware adapter.
  1004. });
  1005. // Alright, this next adapter gets run at the per node level in the graph database.
  1006. // This will let us verify that every property on a node has a value signed by a public key we trust.
  1007. // If the signature does not match, the data is just `undefined` so it doesn't get passed on.
  1008. // If it does match, then we transform the in-memory "view" of the data into its plain value (without the signature).
  1009. // Now NOTE! Some data is "system" data, not user data. Example: List of public keys, aliases, etc.
  1010. // This data is self-enforced (the value can only match its ID), but that is handled in the `security` function.
  1011. // From the self-enforced data, we can see all the edges in the graph that belong to a public key.
  1012. // Example: ~ASDF is the ID of a node with ASDF as its public key, signed alias and salt, and
  1013. // its encrypted private key, but it might also have other signed values on it like `profile = <ID>` edge.
  1014. // Using that directed edge's ID, we can then track (in memory) which IDs belong to which keys.
  1015. // Here is a problem: Multiple public keys can "claim" any node's ID, so this is dangerous!
  1016. // This means we should ONLY trust our "friends" (our key ring) public keys, not any ones.
  1017. // I have not yet added that to SEA yet in this alpha release. That is coming soon, but beware in the meanwhile!
  1018. function each(msg){ // TODO: Warning: Need to switch to `gun.on('node')`! Do not use `Gun.on('node'` in your apps!
  1019. // NOTE: THE SECURITY FUNCTION HAS ALREADY VERIFIED THE DATA!!!
  1020. // WE DO NOT NEED TO RE-VERIFY AGAIN, JUST TRANSFORM IT TO PLAINTEXT.
  1021. var to = this.to, vertex = (msg.$._).put, c = 0, d;
  1022. Gun.node.is(msg.put, function(val, key, node){
  1023. // only process if SEA formatted?
  1024. var tmp = Gun.obj.ify(val) || noop;
  1025. if(u !== tmp[':']){
  1026. node[key] = SEA.opt.unpack(tmp);
  1027. return;
  1028. }
  1029. if(!SEA.opt.check(val)){ return }
  1030. c++; // for each property on the node
  1031. SEA.verify(val, false, function(data){ c--; // false just extracts the plain data.
  1032. node[key] = SEA.opt.unpack(data, key, node);; // transform to plain value.
  1033. if(d && !c && (c = -1)){ to.next(msg) }
  1034. });
  1035. });
  1036. if((d = true) && !c){ to.next(msg) }
  1037. }
  1038. // signature handles data output, it is a proxy to the security function.
  1039. function signature(msg){
  1040. if(msg.user){
  1041. return this.to.next(msg);
  1042. }
  1043. var ctx = this.as;
  1044. msg.user = ctx.user;
  1045. security.call(this, msg);
  1046. }
  1047. // okay! The security function handles all the heavy lifting.
  1048. // It needs to deal read and write of input and output of system data, account/public key data, and regular data.
  1049. // This is broken down into some pretty clear edge cases, let's go over them:
  1050. function security(msg){
  1051. var at = this.as, sea = at.sea, to = this.to;
  1052. if(msg.get){
  1053. // if there is a request to read data from us, then...
  1054. var soul = msg.get['#'];
  1055. if(soul){ // for now, only allow direct IDs to be read.
  1056. if(typeof soul !== 'string'){ return to.next(msg) } // do not handle lexical cursors.
  1057. if('alias' === soul){ // Allow reading the list of usernames/aliases in the system?
  1058. return to.next(msg); // yes.
  1059. } else
  1060. if('~@' === soul.slice(0,2)){ // Allow reading the list of public keys associated with an alias?
  1061. return to.next(msg); // yes.
  1062. } else { // Allow reading everything?
  1063. return to.next(msg); // yes // TODO: No! Make this a callback/event that people can filter on.
  1064. }
  1065. }
  1066. }
  1067. if(msg.put){
  1068. // potentially parallel async operations!!!
  1069. var check = {}, each = {}, u;
  1070. each.node = function(node, soul){
  1071. if(Gun.obj.empty(node, '_')){ return check['node'+soul] = 0 } // ignore empty updates, don't reject them.
  1072. Gun.obj.map(node, each.way, {soul: soul, node: node});
  1073. };
  1074. each.way = function(val, key){
  1075. var soul = this.soul, node = this.node, tmp;
  1076. if('_' === key){ return } // ignore meta data
  1077. if('~@' === soul){ // special case for shared system data, the list of aliases.
  1078. each.alias(val, key, node, soul); return;
  1079. }
  1080. if('~@' === soul.slice(0,2)){ // special case for shared system data, the list of public keys for an alias.
  1081. each.pubs(val, key, node, soul); return;
  1082. }
  1083. if('~' === soul.slice(0,1) && 2 === (tmp = soul.slice(1)).split('.').length){ // special case, account data for a public key.
  1084. each.pub(val, key, node, soul, tmp, msg.user); return;
  1085. }
  1086. each.any(val, key, node, soul, msg.user); return;
  1087. return each.end({err: "No other data allowed!"});
  1088. };
  1089. each.alias = function(val, key, node, soul){ // Example: {_:#~@, ~@alice: {#~@alice}}
  1090. if(!val){ return each.end({err: "Data must exist!"}) } // data MUST exist
  1091. if('~@'+key === Gun.val.link.is(val)){ return check['alias'+key] = 0 } // in fact, it must be EXACTLY equal to itself
  1092. each.end({err: "Mismatching alias."}); // if it isn't, reject.
  1093. };
  1094. each.pubs = function(val, key, node, soul){ // Example: {_:#~@alice, ~asdf: {#~asdf}}
  1095. if(!val){ return each.end({err: "Alias must exist!"}) } // data MUST exist
  1096. if(key === Gun.val.link.is(val)){ return check['pubs'+soul+key] = 0 } // and the ID must be EXACTLY equal to its property
  1097. each.end({err: "Alias must match!"}); // that way nobody can tamper with the list of public keys.
  1098. };
  1099. each.pub = function(val, key, node, soul, pub, user){ var tmp; // Example: {_:#~asdf, hello:'world'~fdsa}}
  1100. if('pub' === key){
  1101. if(val === pub){ return (check['pub'+soul+key] = 0) } // the account MUST match `pub` property that equals the ID of the public key.
  1102. return each.end({err: "Account must match!"});
  1103. }
  1104. check['user'+soul+key] = 1;
  1105. if(msg.I && user && user.is && pub === user.is.pub){
  1106. SEA.sign(SEA.opt.prep(tmp = SEA.opt.parse(val), key, node, soul), (user._).sea, function(data){ var rel;
  1107. if(u === data){ return each.end({err: SEA.err || 'Pub signature fail.'}) }
  1108. if(rel = Gun.val.link.is(val)){
  1109. (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
  1110. }
  1111. node[key] = JSON.stringify({':': SEA.opt.unpack(data.m), '~': data.s});
  1112. check['user'+soul+key] = 0;
  1113. each.end({ok: 1});
  1114. }, {check: SEA.opt.pack(tmp, key, node, soul), raw: 1});
  1115. return;
  1116. }
  1117. SEA.verify(SEA.opt.pack(val,key,node,soul), pub, function(data){ var rel, tmp;
  1118. data = SEA.opt.unpack(data, key, node);
  1119. if(u === data){ // make sure the signature matches the account it claims to be on.
  1120. return each.end({err: "Unverified data."}); // reject any updates that are signed with a mismatched account.
  1121. }
  1122. if((rel = Gun.val.link.is(data)) && pub === SEA.opt.pub(rel)){
  1123. (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
  1124. }
  1125. check['user'+soul+key] = 0;
  1126. each.end({ok: 1});
  1127. });
  1128. };
  1129. each.any = function(val, key, node, soul, user){ var tmp, pub;
  1130. if(!user || !user.is){
  1131. if(tmp = SEA.opt.pub(soul)){
  1132. check['any'+soul+key] = 1;
  1133. SEA.verify(SEA.opt.pack(val,key,node,soul), pub = tmp, function(data){ var rel;
  1134. data = SEA.opt.unpack(data, key, node);
  1135. if(u === data){ return each.end({err: "Mismatched owner on '" + key + "'."}) } // thanks @rogowski !
  1136. if((rel = Gun.val.link.is(data)) && pub === SEA.opt.pub(rel)){
  1137. (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
  1138. }
  1139. check['any'+soul+key] = 0;
  1140. each.end({ok: 1});
  1141. });
  1142. return;
  1143. }
  1144. check['any'+soul+key] = 1;
  1145. at.on('secure', function(msg){ this.off();
  1146. check['any'+soul+key] = 0;
  1147. if(at.opt.secure){ msg = null }
  1148. each.end(msg || {err: "Data cannot be modified."});
  1149. }).on.on('secure', msg);
  1150. //each.end({err: "Data cannot be modified."});
  1151. return;
  1152. }
  1153. if(!(tmp = SEA.opt.pub(soul))){
  1154. if(at.opt.secure){
  1155. each.end({err: "Soul is missing public key at '" + key + "'."});
  1156. return;
  1157. }
  1158. // TODO: Ask community if should auto-sign non user-graph data.
  1159. check['any'+soul+key] = 0;
  1160. each.end({ok: 1});
  1161. return;
  1162. }
  1163. if(!msg.I){ // only sign data put out from this instance.
  1164. each.any(val, key, node, soul);
  1165. return;
  1166. }
  1167. if((pub = tmp) !== (user.is||noop).pub){
  1168. each.any(val, key, node, soul);
  1169. return;
  1170. }
  1171. /*var other = Gun.obj.map(at.sea.own[soul], function(v, p){
  1172. if((user.is||{}).pub !== p){ return p }
  1173. });
  1174. if(other){
  1175. each.any(val, key, node, soul);
  1176. return;
  1177. }*/
  1178. check['any'+soul+key] = 1;
  1179. SEA.sign(SEA.opt.prep(tmp = SEA.opt.parse(val), key, node, soul), (user._).sea, function(data){
  1180. if(u === data){ return each.end({err: 'My signature fail.'}) }
  1181. node[key] = JSON.stringify({':': SEA.opt.unpack(data.m), '~': data.s});
  1182. check['any'+soul+key] = 0;
  1183. each.end({ok: 1});
  1184. }, {check: SEA.opt.pack(tmp, key, node, soul), raw: 1});
  1185. }
  1186. each.end = function(ctx){ // TODO: Can't you just switch this to each.end = cb?
  1187. if(each.err){ return }
  1188. if((each.err = ctx.err) || ctx.no){
  1189. console.log('NO!', each.err, msg.put); // 451 mistmached data FOR MARTTI
  1190. return;
  1191. }
  1192. if(!each.end.ed){ return }
  1193. if(Gun.obj.map(check, function(no){
  1194. if(no){ return true }
  1195. })){ return }
  1196. msg.user = at.user; // already been through firewall, does not need to again on out.
  1197. to.next(msg);
  1198. };
  1199. Gun.obj.map(msg.put, each.node);
  1200. each.end({end: each.end.ed = true});
  1201. return; // need to manually call next after async.
  1202. }
  1203. to.next(msg); // pass forward any data we do not know how to handle or process (this allows custom security protocols).
  1204. }
  1205. SEA.opt.pub = function(s){
  1206. if(!s){ return }
  1207. s = s.split('~');
  1208. if(!s || !(s = s[1])){ return }
  1209. s = s.split('.');
  1210. if(!s || 2 > s.length){ return }
  1211. s = s.slice(0,2).join('.');
  1212. return s;
  1213. }
  1214. SEA.opt.prep = function(d,k, n,s){ // prep for signing
  1215. return {'#':s,'.':k,':':SEA.opt.parse(d),'>':Gun.state.is(n, k)};
  1216. }
  1217. SEA.opt.pack = function(d,k, n,s){ // pack for verifying
  1218. if(SEA.opt.check(d)){ return d }
  1219. var meta = (Gun.obj.ify(d)||noop), sig = meta['~'];
  1220. return sig? {m: {'#':s,'.':k,':':meta[':'],'>':Gun.state.is(n, k)}, s: sig} : d;
  1221. }
  1222. SEA.opt.unpack = function(d, k, n){ var tmp;
  1223. if(u === d){ return }
  1224. if(d && (u !== (tmp = d[':']))){ return tmp }
  1225. if(!k || !n){ return }
  1226. if(d === n[k]){ return d }
  1227. if(!SEA.opt.check(n[k])){ return d }
  1228. var soul = Gun.node.soul(n), s = Gun.state.is(n, k);
  1229. if(d && 4 === d.length && soul === d[0] && k === d[1] && fl(s) === fl(d[3])){
  1230. return d[2];
  1231. }
  1232. if(s < SEA.opt.shuffle_attack){
  1233. return d;
  1234. }
  1235. }
  1236. SEA.opt.shuffle_attack = 1546329600000; // Jan 1, 2019
  1237. var noop = {}, u;
  1238. var fl = Math.floor; // TODO: Still need to fix inconsistent state issue.
  1239. var rel_is = Gun.val.rel.is;
  1240. // TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible.
  1241. })(USE, './index');
  1242. }());