glow.ts 1.1.4 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/glow.cjs.js +2 -2
- package/dist/glow.cjs.js.map +1 -1
- package/dist/glow.es.js +7 -3
- package/dist/glow.es.js.map +1 -1
- package/dist/index.d.ts +13 -1
- package/package.json +1 -1
package/dist/glow.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("tweetnacl"),t=require("async-mutex");var n=class{static toBase64(e){return typeof btoa<`u`?btoa(this.decode_latin1(e)):Buffer.from(this.decode_latin1(e),`latin1`).toString(`base64`)}static fromBase64(e){return typeof btoa<`u`?this.encode_latin1(atob(e)):this.encode_latin1(Buffer.from(e,`base64`).toString(`latin1`))}static encode_latin1(e){let t=new Uint8Array(e.length);for(let n=0;n<e.length;n++){let r=e.charCodeAt(n);if((r&255)!==r)throw{message:`Cannot encode string in Latin1`,str:e};t[n]=r&255}return t}static decode_latin1(e){let t=[];for(let n=0;n<e.length;n++)t.push(String.fromCharCode(e[n]));return t.join(``)}static toObject(e){return Array.from(e).reduce((e,[t,n])=>Object.assign(e,{[t]:n}),{})}},r=class{constructor(){this.crypto_secretbox_KEYBYTES=e.secretbox.keyLength,this.crypto_box_NONCEBYTES=e.box.nonceLength}async crypto_secretbox_random_nonce(){return(0,e.randomBytes)(e.secretbox.nonceLength)}async crypto_secretbox(t,n,r){return(0,e.secretbox)(t,n,r)}async crypto_secretbox_open(t,n,r){return e.secretbox.open(t,n,r)}async crypto_box(t,n,r,i){return(0,e.box)(t,n,r,i)}async crypto_box_open(t,n,r,i){return e.box.open(t,n,r,i)}async crypto_box_random_nonce(){return(0,e.randomBytes)(e.box.nonceLength)}async crypto_box_keypair(){let t=e.box.keyPair();return{boxPk:t.publicKey,boxSk:t.secretKey}}async crypto_box_keypair_from_raw_sk(t){let n=e.box.keyPair.fromSecretKey(t);return{boxPk:n.publicKey,boxSk:n.secretKey}}async crypto_box_keypair_from_seed(t){return this.crypto_box_keypair_from_raw_sk((0,e.hash)(t).subarray(0,e.box.secretKeyLength))}async crypto_hash_sha256(e){let t=globalThis.crypto;if(!t||!t.subtle)throw Error(`Web Crypto API is not available. Please use a modern browser or Node.js 16+.`);let n=await t.subtle.digest(`SHA-256`,e);return new Uint8Array(n)}async random_bytes(t){return(0,e.randomBytes)(t)}async encode_latin1(e){return n.encode_latin1(e)}async decode_latin1(e){return n.decode_latin1(e)}async encode_utf8(e){return this.encode_latin1(unescape(encodeURIComponent(e)))}async decode_utf8(e){return decodeURIComponent(escape(await this.decode_latin1(e)))}async to_hex(e){let t=[];for(let n=0;n<e.length;n++){let r=`0123456789abcdef`;t.push(r[e[n]>>4&15]),t.push(r[e[n]&15])}return t.join(``)}async from_hex(e){let t=new Uint8Array(e.length/2);for(let n=0;n<e.length/2;n++)t[n]=parseInt(e.substr(2*n,2),16);return t}async h2(e){e instanceof Uint8Array||(e=await this.encode_latin1(e));let t=new Uint8Array(64+e.length);return t.fill(0),t.set(e,64),this.crypto_hash_sha256(await this.crypto_hash_sha256(t))}},i=class{constructor(){}static setDefaultInstance(){return this.setInstance(new r)}static setInstance(e){if(this.driverInstance)throw Error(`[NaCl] NaCl driver has been already set, it is supposed to be set only once`);return this.driverInstance=e,!0}static getInstance(){if(!this.driverInstance)throw Error(`[NaCl] NaCl instance is not yet set`);return this.driverInstance}},a=class{constructor(e){if(this.keys=e,typeof e==`string`){let{boxPk:t,boxSk:r}=JSON.parse(e);this.keyPair={boxPk:n.fromBase64(t),boxSk:n.fromBase64(r)}}else this.keyPair=e}static isEqual(e,t){return e.toString()===t.toString()}toString(){return JSON.stringify({boxPk:this.publicKey,boxSk:this.privateKey})}get publicKey(){return n.toBase64(this.keyPair.boxPk)}get privateKey(){return n.toBase64(this.keyPair.boxSk)}},o=30*1e3,s={NONCE_TAG:`__nc`,SKEY_TAG:`storage_key`,STORAGE_ROOT:`.v1.stor.vlt12`,RELAY_TOKEN_LEN:32,RELAY_TOKEN_TIMEOUT:300*1e3-o,RELAY_SESSION_TIMEOUT:1200*1e3-o,RELAY_AJAX_TIMEOUT:5*1e3},c=class{constructor(e=`storage.`){this.rootTag=`__glow.${e}`}async get(e){return(await this.getMultiple([e]))[0]}async getMultiple(e){return e.map(e=>localStorage.getItem(this.tag(e))||null)}async set(e,t){return this.setMultiple({[e]:t})}async setMultiple(e){for(let t in e){let n=e[t];n!==null&&localStorage.setItem(this.tag(t),n)}}async remove(e){this.removeMultiple([e])}async removeMultiple(e){e.forEach(e=>localStorage.removeItem(this.tag(e)))}tag(e){return`${this.rootTag}.${e}`}},l=class e{constructor(e,t){let n=i.getInstance();this.nacl=n,this.driver=e,this.id=t?`.${t}${s.STORAGE_ROOT}`:s.STORAGE_ROOT}static async new(t){let r=this.getStorageDriver(),i=new e(r,t),a=i.addPrefix(s.SKEY_TAG),o=await r.get(a);if(o){let{key:e}=JSON.parse(o);i.storageKey=n.fromBase64(e)}else i.storageKey=await i.nacl.random_bytes(i.nacl.crypto_secretbox_KEYBYTES),await r.set(a,JSON.stringify({key:n.toBase64(i.storageKey)}));return i}static getStorageDriver(){if(this.storageDriver)return this.storageDriver;throw Error(`[CryptoStorage] StorageDriver instance is not yet set`)}static setDefaultStorageDriver(){return this.setStorageDriver(new c)}static setStorageDriver(e){if(this.storageDriver)throw Error(`[CryptoStorage] StorageDriver has been already set, it is supposed to be set only once`);return this.storageDriver=e,!0}async save(e,t){if(!this.driver)throw Error(`[CryptoStorage] Storage driver is not set`);if(!this.storageKey)throw Error(`[CryptoStorage] Storage key is not set`);let r=JSON.stringify(t),i=await this.nacl.encode_utf8(r),a=await this.nacl.crypto_secretbox_random_nonce(),o=await this.nacl.crypto_secretbox(i,a,this.storageKey);return await this.driver.setMultiple({[this.addPrefix(e)]:n.toBase64(o),[this.addNonceTag(e)]:n.toBase64(a)}),!0}async get(e){if(!this.driver)throw Error(`[CryptoStorage] Storage driver is not set`);if(!this.storageKey)throw Error(`[CryptoStorage] Storage key is not set`);let[t,r]=await this.driver.getMultiple([this.addPrefix(e),this.addNonceTag(e)]);if(!t||!r)return null;let i=n.fromBase64(t),a=n.fromBase64(r),o=await this.nacl.crypto_secretbox_open(i,a,this.storageKey);if(o){let e=await this.nacl.decode_utf8(o);return JSON.parse(e)}else throw Error(`[CryptoStorage] crypto_secretbox_open: decryption error`)}async remove(e){if(!this.driver)throw Error(`[CryptoStorage] Storage driver is not set`);return await this.driver.removeMultiple([this.addPrefix(e),this.addNonceTag(e)]),!0}async selfDestruct(){if(!this.driver)throw Error(`[CryptoStorage] Storage driver is not set`);await this.driver.remove(this.addPrefix(s.SKEY_TAG))}addPrefix(e){return this.id?e+this.id:e}addNonceTag(e){return this.addPrefix(`${s.NONCE_TAG}.${e}`)}},u=`__::commKey::__`,d=class e{static{this.commKeyTag=`comm_key`}static{this.guestRegistryTag=`guest_registry`}constructor(e,t,n){this.guestKeys=new Map,this.nacl=e,this.storage=t,this.commKey=n}static async new(t){let n=i.getInstance(),r=await l.new(t),a=new e(n,r,await e.getCommKeyOrCreate(n,r));return await a.loadGuestKeys(),a}static async fromBackup(t,r){let i=JSON.parse(r),a=n.fromBase64(i[u]),o=await e.new(t);await o.setCommFromSecKey(a);for(let[e,t]of Object.entries(i))e!==u&&await o.addGuest(e,t);return o}getNumberOfGuests(){return this.guestKeys.size}get guests(){return this.guestKeys}getPubCommKey(){return this.commKey.publicKey}getPrivateCommKey(){return this.commKey.privateKey}async getHpk(){let e=await this.nacl.h2(n.fromBase64(this.commKey.publicKey));return n.toBase64(e)}getTagByHpk(e){for(let[t,n]of this.guestKeys)if(n.hpk===e)return t;return null}getGuestKey(e){let t=this.guestKeys.get(e);return t?t.pk:null}async backup(){let e={[u]:this.commKey.privateKey};if(this.getNumberOfGuests()>0)for(let[t,n]of this.guestKeys)t&&n&&(e[t]=n.pk);return JSON.stringify(e)}async setCommFromSeed(t){this.commKey=new a(await this.nacl.crypto_box_keypair_from_seed(t)),await this.storage.save(e.commKeyTag,this.commKey.toString())}async setCommFromSecKey(t){this.commKey=new a(await this.nacl.crypto_box_keypair_from_raw_sk(t)),await this.storage.save(e.commKeyTag,this.commKey.toString())}async addGuest(e,t){let r=n.toBase64(await this.nacl.h2(n.fromBase64(t)));return this.guestKeys.set(e,{pk:t,hpk:r}),await this.saveGuests(),r}async removeGuest(e){return this.guestKeys.has(e)&&(this.guestKeys.delete(e),await this.saveGuests()),!0}async loadGuestKeys(){let t=await this.storage.get(e.guestRegistryTag);if(t)if(typeof t==`object`)this.guestKeys=new Map(Object.entries(t));else throw Error(`[Keyring] Guest keys is not an object`)}async saveGuests(){await this.storage.save(e.guestRegistryTag,n.toObject(this.guestKeys.entries()))}static async getCommKeyOrCreate(t,n){let r=await n.get(e.commKeyTag);if(r&&typeof r==`string`)return new a(r);{let r=new a(await t.crypto_box_keypair());return await n.save(e.commKeyTag,r.toString()),r}}async selfDestruct(){await this.storage.remove(e.guestRegistryTag),await this.storage.remove(e.commKeyTag),await this.storage.selfDestruct()}},f=class e{constructor(){}static async encodeMessage(e,t,r,a){let o=i.getInstance(),s=await this.makeNonce(a),c=await o.crypto_box(e,s,t,r);return{nonce:n.toBase64(s),ctext:n.toBase64(c)}}static async decodeMessage(e,t,n,r){let a=i.getInstance(),o=await a.crypto_box_open(t,e,n,r);return o&&await a.decode_utf8(o)}static async encodeMessageSymmetric(t,r){let a=i.getInstance(),o=await e.makeNonce(),s=await a.crypto_secretbox(t,o,r);return{nonce:n.toBase64(o),ctext:n.toBase64(s)}}static async decodeMessageSymmetric(e,t,r){return await i.getInstance().crypto_secretbox_open(n.fromBase64(t),n.fromBase64(e),r)}static async makeNonce(e){let t=i.getInstance(),n=await t.crypto_box_random_nonce(),r;if(n.length!==t.crypto_box_NONCEBYTES)throw Error(`[Mailbox] Wrong crypto_box nonce length`);r=8;let a=this.itoa(Math.floor(Date.now()/1e3));if(e&&(r+=4),n.fill(0,0,r),n.set(a,8-a.length),e){let t=this.itoa(e);n.set(t,12-t.length)}return n}static itoa(e){let t=e.toString(16);t=t.length&1?`0${t}`:t;let n=t.length/2,r=new Uint8Array(n);for(let e=0,n=0;n<t.length;n+=2,e++)r[e]=parseInt(t[n]+t[n+1],16);return r}},p=function(e){return e.count=`count`,e.upload=`upload`,e.download=`download`,e.messageStatus=`messageStatus`,e.delete=`delete`,e.startFileUpload=`startFileUpload`,e.uploadFileChunk=`uploadFileChunk`,e.downloadFileChunk=`downloadFileChunk`,e.fileStatus=`fileStatus`,e.deleteFile=`deleteFile`,e}({}),m=function(e){return e.message=`message`,e.file=`file`,e.plain=`plain`,e}({}),h=class extends Error{constructor(e){super(`GlowNetworkError status: ${e}`),this.status=e,this.name=`GlowNetworkError`}},g=class{constructor(e){this.url=e,this.difficulty=0,this.nacl=i.getInstance()}get isConnected(){return!!(this.sessionKeys&&this.publicKey)}async openConnection(){this.sessionKeys=new a(await this.nacl.crypto_box_keypair()),this.clientToken=await this.nacl.random_bytes(s.RELAY_TOKEN_LEN);let e=await this.fetchRelayToken();return this.publicKey=await this.fetchRelayPublicKey(e),{h2Signature:await this.getSignature(e,this.sessionKeys),relayPublicKey:this.publicKey}}async fetchRelayToken(){if(!this.clientToken)throw Error(`[Relay] clientToken is required please openConnection first`);let e=await this.httpCall(`start_session`,n.toBase64(this.clientToken));this.scheduleTokenExpiration();let[t,r]=this.parseResponse(`start_session`,e);return this.difficulty=parseInt(r,10),this.difficulty>10&&console.log(`[Relay] ${this.url} requested difficulty ${this.difficulty}. Session handshake may take longer.`),n.fromBase64(t)}async fetchRelayPublicKey(e){if(!this.clientToken)throw Error(`[Relay] clientToken is required please openConnection first`);let t=n.toBase64(await this.nacl.h2(this.clientToken)),r=new Uint8Array([...this.clientToken,...e]),i;i=this.difficulty===0?await this.nacl.h2(r):await this.ensureNonceDifficulty(r);let a=await this.httpCall(`verify_session`,t,n.toBase64(i));return n.fromBase64(a)}async prove(e){if(!this.clientToken)throw Error(`[Relay] clientToken is required please openConnection first`);if(!this.sessionKeys)throw Error(`[Relay] No session key found, open the connection first`);let t=n.toBase64(await this.nacl.h2(this.clientToken)),r=await this.httpCall(`prove`,t,this.sessionKeys.publicKey,e.nonce,e.ctext);return this.scheduleSessionExpiration(),r}async encodeMessage(e){if(!this.publicKey)throw Error(`[Relay] No relay public key found, open the connection first`);if(!this.sessionKeys)throw Error(`[Relay] No session key found, open the connection first`);return await f.encodeMessage(await this.nacl.encode_utf8(e),this.publicKey,n.fromBase64(this.sessionKeys?.privateKey))}async decodeMessage(e,t){let r=this.publicKey;if(!r)throw Error(`[Relay] No relay public key found, open the connection first`);if(!this.sessionKeys)throw Error(`[Relay] No session key found, open the connection first`);let i=await f.decodeMessage(n.fromBase64(e),n.fromBase64(t),r,n.fromBase64(this.sessionKeys.privateKey));if(i===null)throw Error(`[Relay] failed to decode message`);return JSON.parse(i)}async runCmd(e,t,n,r){if(!Object.keys(p).includes(e))throw Error(`[Relay] ${this.url} doesn't support command ${e}`);let i=[t,n.nonce,n.ctext];r&&i.push(r);let a=await this.httpCall(`command`,...i);return this.parseResponse(e,a)}async getSignature(e,t){if(!this.clientToken)throw Error(`[Relay] clientToken is required please openConnection first`);let r=n.fromBase64(t.publicKey),i=new Uint8Array([...r,...e,...this.clientToken]);return await this.nacl.h2(i)}async httpCall(e,...t){let n=new AbortController,r=setTimeout(()=>n.abort(),s.RELAY_AJAX_TIMEOUT),i={method:`POST`,headers:{Accept:`text/plain`,"Content-Type":`text/plain`},body:t.join(`\r
|
|
2
|
-
`),signal:n.signal};try{let t=await fetch(`${this.url}/${e}`,i);if(!t.ok)
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("tweetnacl"),t=require("async-mutex");var n=class{static toBase64(e){return typeof btoa<`u`?btoa(this.decode_latin1(e)):Buffer.from(this.decode_latin1(e),`latin1`).toString(`base64`)}static fromBase64(e){return typeof btoa<`u`?this.encode_latin1(atob(e)):this.encode_latin1(Buffer.from(e,`base64`).toString(`latin1`))}static encode_latin1(e){let t=new Uint8Array(e.length);for(let n=0;n<e.length;n++){let r=e.charCodeAt(n);if((r&255)!==r)throw{message:`Cannot encode string in Latin1`,str:e};t[n]=r&255}return t}static decode_latin1(e){let t=[];for(let n=0;n<e.length;n++)t.push(String.fromCharCode(e[n]));return t.join(``)}static toObject(e){return Array.from(e).reduce((e,[t,n])=>Object.assign(e,{[t]:n}),{})}},r=class{constructor(){this.crypto_secretbox_KEYBYTES=e.secretbox.keyLength,this.crypto_box_NONCEBYTES=e.box.nonceLength}async crypto_secretbox_random_nonce(){return(0,e.randomBytes)(e.secretbox.nonceLength)}async crypto_secretbox(t,n,r){return(0,e.secretbox)(t,n,r)}async crypto_secretbox_open(t,n,r){return e.secretbox.open(t,n,r)}async crypto_box(t,n,r,i){return(0,e.box)(t,n,r,i)}async crypto_box_open(t,n,r,i){return e.box.open(t,n,r,i)}async crypto_box_random_nonce(){return(0,e.randomBytes)(e.box.nonceLength)}async crypto_box_keypair(){let t=e.box.keyPair();return{boxPk:t.publicKey,boxSk:t.secretKey}}async crypto_box_keypair_from_raw_sk(t){let n=e.box.keyPair.fromSecretKey(t);return{boxPk:n.publicKey,boxSk:n.secretKey}}async crypto_box_keypair_from_seed(t){return this.crypto_box_keypair_from_raw_sk((0,e.hash)(t).subarray(0,e.box.secretKeyLength))}async crypto_hash_sha256(e){let t=globalThis.crypto;if(!t||!t.subtle)throw Error(`Web Crypto API is not available. Please use a modern browser or Node.js 16+.`);let n=await t.subtle.digest(`SHA-256`,e);return new Uint8Array(n)}async random_bytes(t){return(0,e.randomBytes)(t)}async encode_latin1(e){return n.encode_latin1(e)}async decode_latin1(e){return n.decode_latin1(e)}async encode_utf8(e){return this.encode_latin1(unescape(encodeURIComponent(e)))}async decode_utf8(e){return decodeURIComponent(escape(await this.decode_latin1(e)))}async to_hex(e){let t=[];for(let n=0;n<e.length;n++){let r=`0123456789abcdef`;t.push(r[e[n]>>4&15]),t.push(r[e[n]&15])}return t.join(``)}async from_hex(e){let t=new Uint8Array(e.length/2);for(let n=0;n<e.length/2;n++)t[n]=parseInt(e.substr(2*n,2),16);return t}async h2(e){e instanceof Uint8Array||(e=await this.encode_latin1(e));let t=new Uint8Array(64+e.length);return t.fill(0),t.set(e,64),this.crypto_hash_sha256(await this.crypto_hash_sha256(t))}},i=class{constructor(){}static setDefaultInstance(){return this.setInstance(new r)}static setInstance(e){if(this.driverInstance)throw Error(`[NaCl] NaCl driver has been already set, it is supposed to be set only once`);return this.driverInstance=e,!0}static getInstance(){if(!this.driverInstance)throw Error(`[NaCl] NaCl instance is not yet set`);return this.driverInstance}},a=class{constructor(e){if(this.keys=e,typeof e==`string`){let{boxPk:t,boxSk:r}=JSON.parse(e);this.keyPair={boxPk:n.fromBase64(t),boxSk:n.fromBase64(r)}}else this.keyPair=e}static isEqual(e,t){return e.toString()===t.toString()}toString(){return JSON.stringify({boxPk:this.publicKey,boxSk:this.privateKey})}get publicKey(){return n.toBase64(this.keyPair.boxPk)}get privateKey(){return n.toBase64(this.keyPair.boxSk)}},o=30*1e3,s={NONCE_TAG:`__nc`,SKEY_TAG:`storage_key`,STORAGE_ROOT:`.v1.stor.vlt12`,RELAY_TOKEN_LEN:32,RELAY_TOKEN_TIMEOUT:300*1e3-o,RELAY_SESSION_TIMEOUT:1200*1e3-o,RELAY_AJAX_TIMEOUT:5*1e3},c=class{constructor(e=`storage.`){this.rootTag=`__glow.${e}`}async get(e){return(await this.getMultiple([e]))[0]}async getMultiple(e){return e.map(e=>localStorage.getItem(this.tag(e))||null)}async set(e,t){return this.setMultiple({[e]:t})}async setMultiple(e){for(let t in e){let n=e[t];n!==null&&localStorage.setItem(this.tag(t),n)}}async remove(e){this.removeMultiple([e])}async removeMultiple(e){e.forEach(e=>localStorage.removeItem(this.tag(e)))}tag(e){return`${this.rootTag}.${e}`}},l=class e{constructor(e,t){let n=i.getInstance();this.nacl=n,this.driver=e,this.id=t?`.${t}${s.STORAGE_ROOT}`:s.STORAGE_ROOT}static async new(t){let r=this.getStorageDriver(),i=new e(r,t),a=i.addPrefix(s.SKEY_TAG),o=await r.get(a);if(o){let{key:e}=JSON.parse(o);i.storageKey=n.fromBase64(e)}else i.storageKey=await i.nacl.random_bytes(i.nacl.crypto_secretbox_KEYBYTES),await r.set(a,JSON.stringify({key:n.toBase64(i.storageKey)}));return i}static getStorageDriver(){if(this.storageDriver)return this.storageDriver;throw Error(`[CryptoStorage] StorageDriver instance is not yet set`)}static setDefaultStorageDriver(){return this.setStorageDriver(new c)}static setStorageDriver(e){if(this.storageDriver)throw Error(`[CryptoStorage] StorageDriver has been already set, it is supposed to be set only once`);return this.storageDriver=e,!0}async save(e,t){if(!this.driver)throw Error(`[CryptoStorage] Storage driver is not set`);if(!this.storageKey)throw Error(`[CryptoStorage] Storage key is not set`);let r=JSON.stringify(t),i=await this.nacl.encode_utf8(r),a=await this.nacl.crypto_secretbox_random_nonce(),o=await this.nacl.crypto_secretbox(i,a,this.storageKey);return await this.driver.setMultiple({[this.addPrefix(e)]:n.toBase64(o),[this.addNonceTag(e)]:n.toBase64(a)}),!0}async get(e){if(!this.driver)throw Error(`[CryptoStorage] Storage driver is not set`);if(!this.storageKey)throw Error(`[CryptoStorage] Storage key is not set`);let[t,r]=await this.driver.getMultiple([this.addPrefix(e),this.addNonceTag(e)]);if(!t||!r)return null;let i=n.fromBase64(t),a=n.fromBase64(r),o=await this.nacl.crypto_secretbox_open(i,a,this.storageKey);if(o){let e=await this.nacl.decode_utf8(o);return JSON.parse(e)}else throw Error(`[CryptoStorage] crypto_secretbox_open: decryption error`)}async remove(e){if(!this.driver)throw Error(`[CryptoStorage] Storage driver is not set`);return await this.driver.removeMultiple([this.addPrefix(e),this.addNonceTag(e)]),!0}async selfDestruct(){if(!this.driver)throw Error(`[CryptoStorage] Storage driver is not set`);await this.driver.remove(this.addPrefix(s.SKEY_TAG))}addPrefix(e){return this.id?e+this.id:e}addNonceTag(e){return this.addPrefix(`${s.NONCE_TAG}.${e}`)}},u=`__::commKey::__`,d=class e{static{this.commKeyTag=`comm_key`}static{this.guestRegistryTag=`guest_registry`}constructor(e,t,n){this.guestKeys=new Map,this.nacl=e,this.storage=t,this.commKey=n}static async new(t){let n=i.getInstance(),r=await l.new(t),a=new e(n,r,await e.getCommKeyOrCreate(n,r));return await a.loadGuestKeys(),a}static async fromBackup(t,r){let i=JSON.parse(r),a=n.fromBase64(i[u]),o=await e.new(t);await o.setCommFromSecKey(a);for(let[e,t]of Object.entries(i))e!==u&&await o.addGuest(e,t);return o}getNumberOfGuests(){return this.guestKeys.size}get guests(){return this.guestKeys}getPubCommKey(){return this.commKey.publicKey}getPrivateCommKey(){return this.commKey.privateKey}async getHpk(){let e=await this.nacl.h2(n.fromBase64(this.commKey.publicKey));return n.toBase64(e)}getTagByHpk(e){for(let[t,n]of this.guestKeys)if(n.hpk===e)return t;return null}getGuestKey(e){let t=this.guestKeys.get(e);return t?t.pk:null}async backup(){let e={[u]:this.commKey.privateKey};if(this.getNumberOfGuests()>0)for(let[t,n]of this.guestKeys)t&&n&&(e[t]=n.pk);return JSON.stringify(e)}async setCommFromSeed(t){this.commKey=new a(await this.nacl.crypto_box_keypair_from_seed(t)),await this.storage.save(e.commKeyTag,this.commKey.toString())}async setCommFromSecKey(t){this.commKey=new a(await this.nacl.crypto_box_keypair_from_raw_sk(t)),await this.storage.save(e.commKeyTag,this.commKey.toString())}async addGuest(e,t){let r=n.toBase64(await this.nacl.h2(n.fromBase64(t)));return this.guestKeys.set(e,{pk:t,hpk:r}),await this.saveGuests(),r}async removeGuest(e){return this.guestKeys.has(e)&&(this.guestKeys.delete(e),await this.saveGuests()),!0}async loadGuestKeys(){let t=await this.storage.get(e.guestRegistryTag);if(t)if(typeof t==`object`)this.guestKeys=new Map(Object.entries(t));else throw Error(`[Keyring] Guest keys is not an object`)}async saveGuests(){await this.storage.save(e.guestRegistryTag,n.toObject(this.guestKeys.entries()))}static async getCommKeyOrCreate(t,n){let r=await n.get(e.commKeyTag);if(r&&typeof r==`string`)return new a(r);{let r=new a(await t.crypto_box_keypair());return await n.save(e.commKeyTag,r.toString()),r}}async selfDestruct(){await this.storage.remove(e.guestRegistryTag),await this.storage.remove(e.commKeyTag),await this.storage.selfDestruct()}},f=class e{constructor(){}static async encodeMessage(e,t,r,a){let o=i.getInstance(),s=await this.makeNonce(a),c=await o.crypto_box(e,s,t,r);return{nonce:n.toBase64(s),ctext:n.toBase64(c)}}static async decodeMessage(e,t,n,r){let a=i.getInstance(),o=await a.crypto_box_open(t,e,n,r);return o&&await a.decode_utf8(o)}static async encodeMessageSymmetric(t,r){let a=i.getInstance(),o=await e.makeNonce(),s=await a.crypto_secretbox(t,o,r);return{nonce:n.toBase64(o),ctext:n.toBase64(s)}}static async decodeMessageSymmetric(e,t,r){return await i.getInstance().crypto_secretbox_open(n.fromBase64(t),n.fromBase64(e),r)}static async makeNonce(e){let t=i.getInstance(),n=await t.crypto_box_random_nonce(),r;if(n.length!==t.crypto_box_NONCEBYTES)throw Error(`[Mailbox] Wrong crypto_box nonce length`);r=8;let a=this.itoa(Math.floor(Date.now()/1e3));if(e&&(r+=4),n.fill(0,0,r),n.set(a,8-a.length),e){let t=this.itoa(e);n.set(t,12-t.length)}return n}static itoa(e){let t=e.toString(16);t=t.length&1?`0${t}`:t;let n=t.length/2,r=new Uint8Array(n);for(let e=0,n=0;n<t.length;n+=2,e++)r[e]=parseInt(t[n]+t[n+1],16);return r}},p=function(e){return e.count=`count`,e.upload=`upload`,e.download=`download`,e.messageStatus=`messageStatus`,e.delete=`delete`,e.startFileUpload=`startFileUpload`,e.uploadFileChunk=`uploadFileChunk`,e.downloadFileChunk=`downloadFileChunk`,e.fileStatus=`fileStatus`,e.deleteFile=`deleteFile`,e}({}),m=function(e){return e.message=`message`,e.file=`file`,e.plain=`plain`,e}({}),h=class extends Error{constructor(e,t,n){super(t?`GlowNetworkError status: ${e} (${t})`:`GlowNetworkError status: ${e}`),this.status=e,this.details=t,this.retryAfter=n,this.name=`GlowNetworkError`}},g=class{constructor(e){this.url=e,this.difficulty=0,this.nacl=i.getInstance()}get isConnected(){return!!(this.sessionKeys&&this.publicKey)}async openConnection(){this.sessionKeys=new a(await this.nacl.crypto_box_keypair()),this.clientToken=await this.nacl.random_bytes(s.RELAY_TOKEN_LEN);let e=await this.fetchRelayToken();return this.publicKey=await this.fetchRelayPublicKey(e),{h2Signature:await this.getSignature(e,this.sessionKeys),relayPublicKey:this.publicKey}}async fetchRelayToken(){if(!this.clientToken)throw Error(`[Relay] clientToken is required please openConnection first`);let e=await this.httpCall(`start_session`,n.toBase64(this.clientToken));this.scheduleTokenExpiration();let[t,r]=this.parseResponse(`start_session`,e);return this.difficulty=parseInt(r,10),this.difficulty>10&&console.log(`[Relay] ${this.url} requested difficulty ${this.difficulty}. Session handshake may take longer.`),n.fromBase64(t)}async fetchRelayPublicKey(e){if(!this.clientToken)throw Error(`[Relay] clientToken is required please openConnection first`);let t=n.toBase64(await this.nacl.h2(this.clientToken)),r=new Uint8Array([...this.clientToken,...e]),i;i=this.difficulty===0?await this.nacl.h2(r):await this.ensureNonceDifficulty(r);let a=await this.httpCall(`verify_session`,t,n.toBase64(i));return n.fromBase64(a)}async prove(e){if(!this.clientToken)throw Error(`[Relay] clientToken is required please openConnection first`);if(!this.sessionKeys)throw Error(`[Relay] No session key found, open the connection first`);let t=n.toBase64(await this.nacl.h2(this.clientToken)),r=await this.httpCall(`prove`,t,this.sessionKeys.publicKey,e.nonce,e.ctext);return this.scheduleSessionExpiration(),r}async encodeMessage(e){if(!this.publicKey)throw Error(`[Relay] No relay public key found, open the connection first`);if(!this.sessionKeys)throw Error(`[Relay] No session key found, open the connection first`);return await f.encodeMessage(await this.nacl.encode_utf8(e),this.publicKey,n.fromBase64(this.sessionKeys?.privateKey))}async decodeMessage(e,t){let r=this.publicKey;if(!r)throw Error(`[Relay] No relay public key found, open the connection first`);if(!this.sessionKeys)throw Error(`[Relay] No session key found, open the connection first`);let i=await f.decodeMessage(n.fromBase64(e),n.fromBase64(t),r,n.fromBase64(this.sessionKeys.privateKey));if(i===null)throw Error(`[Relay] failed to decode message`);return JSON.parse(i)}async runCmd(e,t,n,r){if(!Object.keys(p).includes(e))throw Error(`[Relay] ${this.url} doesn't support command ${e}`);let i=[t,n.nonce,n.ctext];r&&i.push(r);let a=await this.httpCall(`command`,...i);return this.parseResponse(e,a)}async getSignature(e,t){if(!this.clientToken)throw Error(`[Relay] clientToken is required please openConnection first`);let r=n.fromBase64(t.publicKey),i=new Uint8Array([...r,...e,...this.clientToken]);return await this.nacl.h2(i)}async httpCall(e,...t){let n=new AbortController,r=setTimeout(()=>n.abort(),s.RELAY_AJAX_TIMEOUT),i={method:`POST`,headers:{Accept:`text/plain`,"Content-Type":`text/plain`},body:t.join(`\r
|
|
2
|
+
`),signal:n.signal};try{let t=await fetch(`${this.url}/${e}`,i);if(!t.ok){t.status===401&&(this.clearSession(),this.clearToken());let e=t.headers.get(`retry-after`);throw new h(t.status,t.headers.get(`x-error-details`)??void 0,e!==null&&/^\d+$/.test(e)?parseInt(e,10):void 0)}return await t.text()}catch(e){throw e instanceof Error&&e.name===`AbortError`?new h(408):e instanceof h?e:e instanceof TypeError?new h(0):e}finally{clearTimeout(r)}}parseResponse(e,t){let n=t.split(`\r
|
|
3
3
|
`);if(!t||!this.validateResponse(e,n.length))throw console.log(n),Error(`[Relay] ${this.url} - ${e}: Bad response`);return n}validateResponse(e,t){switch(e){case p.upload:case p.messageStatus:case p.delete:return t===1;case p.downloadFileChunk:return t===3;default:return t===2}}scheduleTokenExpiration(){this.tokenExpirationTimeoutHandle&&clearTimeout(this.tokenExpirationTimeoutHandle),this.tokenExpirationTimeoutHandle=setTimeout(()=>this.clearToken(),s.RELAY_TOKEN_TIMEOUT)}clearToken(){this.tokenExpirationTimeoutHandle&&clearTimeout(this.tokenExpirationTimeoutHandle),this.clientToken&&delete this.clientToken}scheduleSessionExpiration(){this.sessionExpirationTimeoutHandle&&clearTimeout(this.sessionExpirationTimeoutHandle),this.sessionExpirationTimeoutHandle=setTimeout(()=>{this.clearSession()},s.RELAY_SESSION_TIMEOUT)}clearSession(){this.sessionExpirationTimeoutHandle&&clearTimeout(this.sessionExpirationTimeoutHandle),this.sessionKeys&&delete this.sessionKeys}async ensureNonceDifficulty(e){let t,n;do t=await this.nacl.random_bytes(32),n=await this.nacl.h2(new Uint8Array([...e,...t]));while(!this.arrayZeroBits(n));return t}arrayZeroBits(e){let t,n=this.difficulty;for(let r=0;r<=1+this.difficulty/8;r++){if(t=e[r],n<=0)return!0;if(n>8){if(n-=8,t>0)return!1}else return this.firstZeroBits(t,n)}return!1}firstZeroBits(e,t){return e===e>>t<<t}},_=class{constructor(){this.relays={}}getInstance(e){let t=this.relays[e];return t||=this.relays[e]=new g(e),t}},v=class e{constructor(e,t,n){this.relayFactory=new _,this.relayConnectionMutexes=new Map,this.nacl=e,this.keyRing=t,this.identity=n}static async new(t){return new e(i.getInstance(),await d.new(t),t)}static async fromSeed(e,t){let r=await this.new(e);return t instanceof Uint8Array||(t=n.encode_latin1(t)),await r.keyRing.setCommFromSeed(t),r}static async fromSecKey(e,t){let r=await this.new(e);return t instanceof Uint8Array||(t=n.fromBase64(t)),await r.keyRing.setCommFromSecKey(t),r}static async fromBackup(t,n){return new e(i.getInstance(),await d.fromBackup(t,n),t)}async upload(e,t,r,i=!0){let a=await this.prepareRelay(e),o=this.getGuestKey(t),s=i?await this.encodeMessage(t,r):r,c=n.toBase64(await this.nacl.h2(n.fromBase64(o))),[l]=await this.runRelayCommand(a,p.upload,{to:c,payload:s});return l}async download(e){let t=await this.prepareRelay(e),n=await this.runRelayCommand(t,p.download),r=await this.decryptResponse(t,n),i=[];for(let e of r){let t=this.keyRing.getTagByHpk(e.from);if(!t)i.push(await this.parsePlainMessage(e));else if(e.kind===`message`)i.push(await this.parseTextMessage(e,t));else if(e.kind===`file`)i.push(await this.parseFileMessage(e,t));else throw Error(`[Mailbox] download - Unknown message type`)}return i}async parsePlainMessage({data:e,time:t,from:n,nonce:r}){return{data:e,time:t,from:n,nonce:r,kind:m.plain}}async parseFileMessage(e,t){let{nonce:n,ctext:r,uploadID:i}=JSON.parse(e.data),a=await this.decodeMessage(t,n,r);if(a===null)throw Error(`[Mailbox] Failed to decode file message`);return{data:JSON.parse(a),time:e.time,senderTag:t,uploadID:i,nonce:n,kind:m.file}}async parseTextMessage(e,t){let n=await this.decodeMessage(t,e.nonce,e.data);return n||=e.data,{data:n,time:e.time,senderTag:t,nonce:e.nonce,kind:m.message}}async count(e){let t=await this.prepareRelay(e),n=await this.runRelayCommand(t,p.count);return await this.decryptResponse(t,n)}async delete(e,t){let n=await this.prepareRelay(e),[r]=await this.runRelayCommand(n,p.delete,{payload:t});return parseInt(r,10)}async messageStatus(e,t){let n=await this.prepareRelay(e),[r]=await this.runRelayCommand(n,p.messageStatus,{token:t});return parseInt(r,10)}async startFileUpload(e,t,r){let i=await this.prepareRelay(e),a=this.getGuestKey(t),o=n.toBase64(await this.nacl.h2(n.fromBase64(a))),s=await this.nacl.random_bytes(this.nacl.crypto_secretbox_KEYBYTES);r.skey=n.toBase64(s);let c=await this.encodeMessage(t,JSON.stringify(r)),l=await this.runRelayCommand(i,p.startFileUpload,{to:o,file_size:r.orig_size,metadata:c}),u=await this.decryptResponse(i,l);return u.skey=s,u}async uploadFileChunk(e,t,n,r,i,a){let o=await this.prepareRelay(e),s=await f.encodeMessageSymmetric(n,a),c=await this.runRelayCommand(o,p.uploadFileChunk,{uploadID:t,part:r,last_chunk:i-1===r,nonce:s.nonce},s.ctext);return await this.decryptResponse(o,c)}async getFileStatus(e,t){let n=await this.prepareRelay(e),r=await this.runRelayCommand(n,p.fileStatus,{uploadID:t});return await this.decryptResponse(n,r)}async getFileMetadata(e,t){return(await this.download(e)).filter(e=>e.kind===`file`).find(e=>e.uploadID===t)?.data}async downloadFileChunk(e,t,r,i){i instanceof Uint8Array||(i=n.fromBase64(i));let a=await this.prepareRelay(e),[o,s,c]=await this.runRelayCommand(a,p.downloadFileChunk,{uploadID:t,part:r}),l=await a.decodeMessage(o,s);return await f.decodeMessageSymmetric(l.nonce,c,i)}async deleteFile(e,t){let n=await this.prepareRelay(e),r=await this.runRelayCommand(n,p.deleteFile,{uploadID:t});return await this.decryptResponse(n,r)}async connectToRelay(e){let t=this.relayFactory.getInstance(e),n=await t.openConnection(),r=await this.encryptSignature(n),i=await t.prove(await t.encodeMessage(JSON.stringify({pub_key:this.keyRing.getPubCommKey(),nonce:r.nonce,ctext:r.ctext})));return parseInt(i,10)}clearSession(e){let t=this.relayFactory.getInstance(e);t.clearToken(),t.clearSession()}async encryptSignature(e){let t=n.fromBase64(this.keyRing.getPrivateCommKey());return await f.encodeMessage(e.h2Signature,e.relayPublicKey,t)}async prepareRelay(e){let t=this.relayFactory.getInstance(e);return await this.getRelayConnectionMutex(e).runExclusive(async()=>{t.isConnected||await this.connectToRelay(e)}),t}async runRelayCommand(e,t,n,r){n={cmd:t,...n};let i=await this.keyRing.getHpk(),a=await e.encodeMessage(JSON.stringify(n));return await e.runCmd(t,i,a,r)}async decryptResponse(e,t){let[n,r]=t;return await e.decodeMessage(n,r)}async encodeMessage(e,t){let r=this.getGuestKey(e),i=this.keyRing.getPrivateCommKey();return await f.encodeMessage(await this.nacl.encode_utf8(t),n.fromBase64(r),n.fromBase64(i))}async decodeMessage(e,t,r){let i=this.getGuestKey(e),a=this.keyRing.getPrivateCommKey(),o;try{o=n.fromBase64(r)}catch{return null}return await f.decodeMessage(n.fromBase64(t),o,n.fromBase64(i),n.fromBase64(a))}getGuestKey(e){let t=this.keyRing.getGuestKey(e);if(!t)throw Error(`[Mailbox] Unknown guest ${e}`);return t}async selfDestruct(){await this.keyRing.selfDestruct()}getRelayConnectionMutex(e){return this.relayConnectionMutexes.has(e)||this.relayConnectionMutexes.set(e,new t.Mutex),this.relayConnectionMutexes.get(e)}},y=class{constructor(){this.storage={}}async get(e){return(await this.getMultiple([e]))[0]}async getMultiple(e){return e.map(e=>this.storage[e])}set(e,t){return this.setMultiple({[e]:t})}async setMultiple(e){for(let t in e)this.storage[t]=e[t]}remove(e){return this.removeMultiple([e])}async removeMultiple(e){e.forEach(e=>delete this.storage[e])}reset(){this.storage={}}};exports.CryptoStorage=l,exports.GlowNetworkError=h,exports.InMemoryStorage=y,exports.JsNaClDriver=r,exports.KeyRing=d,exports.Keys=a,exports.LocalStorageDriver=c,exports.Mailbox=v,exports.NaCl=i,exports.Relay=g,exports.Utils=n,exports.ZaxMessageKind=m;
|
|
4
4
|
//# sourceMappingURL=glow.cjs.js.map
|
package/dist/glow.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glow.cjs.js","names":[],"sources":["../src/utils/utils.ts","../src/nacl/js-nacl-driver.ts","../src/nacl/nacl.ts","../src/keys/keys.ts","../src/config.ts","../src/crypto-storage/local-storage.driver.ts","../src/crypto-storage/crypto-storage.ts","../src/keyring/keyring.ts","../src/nacl/encryption.helper.ts","../src/zax.interface.ts","../src/relay/network-error.ts","../src/relay/relay.ts","../src/relay/relay-factory.ts","../src/mailbox/mailbox.ts","../src/crypto-storage/in-memory-storage.ts"],"sourcesContent":["export type Base64 = string;\n\nexport class Utils {\n static toBase64(input: Uint8Array): Base64 {\n const isBrowser = typeof btoa !== 'undefined';\n return isBrowser ?\n btoa(this.decode_latin1(input)) :\n Buffer.from(this.decode_latin1(input), 'latin1').toString('base64');\n }\n\n static fromBase64(input: Base64): Uint8Array {\n const isBrowser = typeof btoa !== 'undefined';\n return isBrowser ?\n this.encode_latin1(atob(input)) :\n this.encode_latin1(Buffer.from(input, 'base64').toString('latin1'));\n }\n\n static encode_latin1(data: string): Uint8Array {\n const result = new Uint8Array(data.length);\n for (let i = 0; i < data.length; i++) {\n const c = data.charCodeAt(i);\n if ((c & 0xff) !== c) throw { message: 'Cannot encode string in Latin1', str: data };\n result[i] = (c & 0xff);\n }\n return result;\n }\n\n static decode_latin1(data: Uint8Array): string {\n const encoded = [];\n for (let i = 0; i < data.length; i++) {\n encoded.push(String.fromCharCode(data[i]));\n }\n return encoded.join('');\n }\n\n static toObject(pairs: IterableIterator<[string, unknown]>) {\n return Array.from(pairs).reduce(\n (acc, [key, value]) => Object.assign(acc, { [key]: value }),\n {},\n );\n }\n}\n","import { box, BoxKeyPair, randomBytes, secretbox, hash } from 'tweetnacl';\n\nimport { NaClDriver } from './nacl-driver.interface';\nimport { Keypair } from './keypair.interface';\nimport { Utils } from '../utils/utils';\n\n/**\n * Implementation based on TweetNaCl.js: {@link https://github.com/dchest/tweetnacl-js}.\n * SHA256 is implemented using Web Crypto API (SubtleCrypto).\n *\n * All the methods of this class are actually synchronous, but they are defined\n * as async to conform to the NaClDriver interface, which may have other implementations.\n */\nexport class JsNaClDriver implements NaClDriver {\n crypto_secretbox_KEYBYTES = secretbox.keyLength;\n\n async crypto_secretbox_random_nonce(): Promise<Uint8Array> {\n return randomBytes(secretbox.nonceLength);\n }\n\n async crypto_secretbox(message: Uint8Array, nonce: Uint8Array, key: Uint8Array): Promise<Uint8Array> {\n return secretbox(message, nonce, key);\n }\n\n async crypto_secretbox_open(box: Uint8Array, nonce: Uint8Array, key: Uint8Array): Promise<Uint8Array | null> {\n return secretbox.open(box, nonce, key);\n }\n\n crypto_box_NONCEBYTES = box.nonceLength;\n\n async crypto_box(message: Uint8Array, nonce: Uint8Array, pk: Uint8Array, sk: Uint8Array): Promise<Uint8Array> {\n return box(message, nonce, pk, sk);\n }\n\n async crypto_box_open(\n cipher: Uint8Array, nonce: Uint8Array, pk: Uint8Array, sk: Uint8Array): Promise<Uint8Array | null> {\n return box.open(cipher, nonce, pk, sk);\n }\n\n async crypto_box_random_nonce(): Promise<Uint8Array> {\n return randomBytes(box.nonceLength);\n }\n\n async crypto_box_keypair(): Promise<Keypair> {\n const pair: BoxKeyPair = box.keyPair();\n return {\n boxPk: pair.publicKey,\n boxSk: pair.secretKey\n };\n }\n\n async crypto_box_keypair_from_raw_sk(key: Uint8Array): Promise<Keypair> {\n const pair: BoxKeyPair = box.keyPair.fromSecretKey(key);\n return {\n boxPk: pair.publicKey,\n boxSk: pair.secretKey\n };\n }\n\n async crypto_box_keypair_from_seed(seed: Uint8Array): Promise<Keypair> {\n // using SHA512 to hash, per original NaCl docs\n return this.crypto_box_keypair_from_raw_sk(hash(seed).subarray(0, box.secretKeyLength));\n }\n\n async crypto_hash_sha256(data: Uint8Array): Promise<Uint8Array> {\n // In Node.js, globalThis.crypto is implementation of WebCrypto API, available since Node.js 16\n // In browsers, globalThis.crypto is the standard Web Crypto API\n const cryptoProvider = globalThis.crypto;\n if (!cryptoProvider || !cryptoProvider.subtle) {\n throw new Error('Web Crypto API is not available. Please use a modern browser or Node.js 16+.');\n }\n\n const hashBuffer = await cryptoProvider.subtle.digest('SHA-256', data as BufferSource);\n return new Uint8Array(hashBuffer);\n }\n\n async random_bytes(size: number): Promise<Uint8Array> {\n return randomBytes(size);\n }\n\n // Helper methods are based on `js-nacl` implementations\n // https://github.com/tonyg/js-nacl/blob/cc70775cfc9d68a04905ca65c7f179b33a18066e/nacl_cooked.js\n\n async encode_latin1(data: string): Promise<Uint8Array> {\n return Utils.encode_latin1(data);\n }\n\n async decode_latin1(data: Uint8Array): Promise<string> {\n return Utils.decode_latin1(data);\n }\n\n async encode_utf8(data: string): Promise<Uint8Array> {\n return this.encode_latin1(unescape(encodeURIComponent(data)));\n }\n\n async decode_utf8(data: Uint8Array): Promise<string> {\n return decodeURIComponent(escape(await this.decode_latin1(data)));\n }\n\n async to_hex(data: Uint8Array): Promise<string> {\n const encoded = [];\n for (let i = 0; i < data.length; i++) {\n const hexAlphabet = '0123456789abcdef';\n encoded.push(hexAlphabet[(data[i] >> 4) & 15]);\n encoded.push(hexAlphabet[data[i] & 15]);\n }\n return encoded.join('');\n }\n\n async from_hex(data: string): Promise<Uint8Array> {\n const result = new Uint8Array(data.length / 2);\n for (let i = 0; i < data.length / 2; i++) {\n result[i] = parseInt(data.substr(2 * i, 2), 16);\n }\n return result;\n }\n\n /**\n * h2(m) = sha256(sha256(64x0 + m))\n * Zero out initial sha256 block, and double hash 0-padded message\n * http://cs.nyu.edu/~dodis/ps/h-of-h.pdf\n */\n async h2(data: string | Uint8Array): Promise<Uint8Array> {\n if (!(data instanceof Uint8Array)) {\n data = await this.encode_latin1(data);\n }\n\n const zeroPaddingLength = 64;\n const extendedSource = new Uint8Array(zeroPaddingLength + data.length);\n extendedSource.fill(0);\n extendedSource.set(data, zeroPaddingLength);\n return this.crypto_hash_sha256(await this.crypto_hash_sha256(extendedSource));\n }\n}\n","import { NaClDriver } from './nacl-driver.interface';\nimport { JsNaClDriver } from './js-nacl-driver';\n\n/**\n * Facade singleton to access the NaCl driver.\n * Usage: call instance() with a chosen driver, and store the received object to perform further actions.\n */\nexport class NaCl {\n private static driverInstance?: NaClDriver;\n\n private constructor() { }\n\n public static setDefaultInstance() {\n return this.setInstance(new JsNaClDriver());\n }\n\n public static setInstance(driver: NaClDriver): boolean {\n if (this.driverInstance) {\n throw new Error('[NaCl] NaCl driver has been already set, it is supposed to be set only once');\n }\n this.driverInstance = driver;\n\n return true;\n }\n\n public static getInstance(): NaClDriver {\n if (!this.driverInstance) {\n throw new Error('[NaCl] NaCl instance is not yet set');\n }\n\n return this.driverInstance;\n }\n}\n","import { Base64, Utils } from '../utils/utils';\nimport { Keypair } from '../nacl/keypair.interface';\n\n/**\n * A simple wrapper around a public/private keys pair.\n * Stores keys in base64 format.\n */\nexport class Keys {\n private keyPair: Keypair;\n\n constructor(public keys: string | Keypair) {\n if (typeof keys === 'string') {\n const { boxPk, boxSk } = JSON.parse(keys);\n this.keyPair = {\n boxPk: Utils.fromBase64(boxPk),\n boxSk: Utils.fromBase64(boxSk)\n };\n } else {\n this.keyPair = keys;\n }\n }\n\n static isEqual(keys1: Keys, keys2: Keys): boolean {\n return keys1.toString() === keys2.toString();\n }\n\n toString(): string {\n return JSON.stringify({\n boxPk: this.publicKey,\n boxSk: this.privateKey\n });\n }\n\n get publicKey(): Base64 {\n return Utils.toBase64(this.keyPair.boxPk);\n }\n\n get privateKey(): Base64 {\n return Utils.toBase64(this.keyPair.boxSk);\n }\n}\n","const sessionTimeoutBuffer = 30 * 1000;\n\nexport const config = {\n NONCE_TAG: '__nc',\n SKEY_TAG: 'storage_key',\n STORAGE_ROOT: '.v1.stor.vlt12',\n // Relay tokens, keys and hashes are 32 bytes\n RELAY_TOKEN_LEN: 32,\n // 5 min - Token expiration on the server side, matched with config.x.relay.token_timeout on Zax server\n RELAY_TOKEN_TIMEOUT: 5 * 60 * 1000 - sessionTimeoutBuffer,\n // 20 min - Session expiration on the server side, matched with config.x.relay.session_timeout on Zax server\n RELAY_SESSION_TIMEOUT: 20 * 60 * 1000 - sessionTimeoutBuffer,\n // 5 sec - Ajax request timeout\n RELAY_AJAX_TIMEOUT: 5 * 1000\n};\n","import { StorageDriver } from './storage-driver.interface';\n\nexport class LocalStorageDriver implements StorageDriver {\n private rootTag: string;\n\n constructor(root = 'storage.') {\n this.rootTag = `__glow.${root}`;\n }\n\n async get(key: string): Promise<string | null> {\n return (await this.getMultiple([key]))[0];\n }\n\n async getMultiple(keys: string[]): Promise<(string | null)[]> {\n return keys.map(key => localStorage.getItem(this.tag(key)) || null);\n }\n\n async set(key: string, value: string | null): Promise<void> {\n return this.setMultiple({ [key]: value });\n }\n\n async setMultiple(values: { [key: string]: string | null; }): Promise<void> {\n for(const key in values) {\n const value = values[key];\n if (value !== null) {\n localStorage.setItem(this.tag(key), value);\n }\n }\n }\n\n async remove(key: string): Promise<void> {\n this.removeMultiple([key]);\n }\n\n async removeMultiple(keys: string[]): Promise<void> {\n keys.forEach(key => localStorage.removeItem(this.tag(key)));\n }\n\n private tag(key: string) {\n return `${this.rootTag}.${key}`;\n }\n}\n","import { NaCl } from '../nacl/nacl';\nimport { Utils } from '../utils/utils';\nimport { config } from '../config';\nimport { StorageDriver } from './storage-driver.interface';\nimport { NaClDriver } from '../nacl/nacl-driver.interface';\nimport { LocalStorageDriver } from './local-storage.driver';\n\n// TODO: add bulk operations to set(), get() and remove() pairs of values simultaneously\n\n/* CryptoStorage is a handy wrapper around any storage that provides JavaScript interface,\n that allows to store symmetrically encrypted serializable Javascript objects and primitives. */\nexport class CryptoStorage {\n\n private static storageDriver: StorageDriver;\n\n private driver?: StorageDriver;\n private id?: string;\n private storageKey?: Uint8Array;\n private nacl: NaClDriver;\n\n private constructor(storageDriver: StorageDriver, id: string) {\n const nacl = NaCl.getInstance();\n this.nacl = nacl;\n this.driver = storageDriver;\n this.id = id ? `.${id}${config.STORAGE_ROOT}` : config.STORAGE_ROOT;\n }\n\n static async new(id: string): Promise<CryptoStorage> {\n const storageDriver = this.getStorageDriver();\n const storage = new CryptoStorage(storageDriver, id);\n const prefixedStorageKeyTag = storage.addPrefix(config.SKEY_TAG);\n const storageKey = await storageDriver.get(prefixedStorageKeyTag);\n // Either load or generate a storage key\n if (storageKey) {\n const { key } = JSON.parse(storageKey);\n storage.storageKey = Utils.fromBase64(key);\n } else {\n storage.storageKey = await storage.nacl.random_bytes(storage.nacl.crypto_secretbox_KEYBYTES);\n await storageDriver.set(prefixedStorageKeyTag, JSON.stringify({ key: Utils.toBase64(storage.storageKey)}));\n }\n return storage;\n }\n\n static getStorageDriver() {\n if (!this.storageDriver) {\n throw new Error('[CryptoStorage] StorageDriver instance is not yet set');\n } else {\n return this.storageDriver;\n }\n }\n\n static setDefaultStorageDriver() {\n return this.setStorageDriver(new LocalStorageDriver());\n }\n\n static setStorageDriver(driver: StorageDriver) {\n if (this.storageDriver) {\n throw new Error('[CryptoStorage] StorageDriver has been already set, it is supposed to be set only once');\n }\n this.storageDriver = driver;\n return true;\n }\n\n async save(tag: string, data: unknown): Promise<boolean> {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n if (!this.storageKey) {\n throw new Error('[CryptoStorage] Storage key is not set');\n }\n // Convert the data to JSON, then convert that string to a byte array\n const input = JSON.stringify(data);\n const encoded = await this.nacl.encode_utf8(input);\n // For each item in the store we also generate and save its own nonce\n const nonce = await this.nacl.crypto_secretbox_random_nonce();\n const cipherText = await this.nacl.crypto_secretbox(encoded, nonce, this.storageKey);\n // Save the cipher text and nonce\n await this.driver.setMultiple({\n [this.addPrefix(tag)]: Utils.toBase64(cipherText),\n [this.addNonceTag(tag)]: Utils.toBase64(nonce)\n });\n return true;\n }\n\n async get<T>(tag: string): Promise<T | null> {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n if (!this.storageKey) {\n throw new Error('[CryptoStorage] Storage key is not set');\n }\n // Get cipher text and nonce from the storage\n const [data, nonce] = await this.driver.getMultiple([this.addPrefix(tag), this.addNonceTag(tag)]);\n // Nothing to do without cipher text or nonce\n if (!data || !nonce) {\n return null;\n }\n const dataBinary = Utils.fromBase64(data);\n const nonceBinary = Utils.fromBase64(nonce);\n const source = await this.nacl.crypto_secretbox_open(dataBinary, nonceBinary, this.storageKey);\n if (source) {\n const decoded = await this.nacl.decode_utf8(source);\n return JSON.parse(decoded) as T;\n } else {\n throw new Error('[CryptoStorage] crypto_secretbox_open: decryption error');\n }\n }\n\n async remove(tag: string): Promise<boolean> {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n await this.driver.removeMultiple([this.addPrefix(tag), this.addNonceTag(tag)]);\n return true;\n }\n\n async selfDestruct() {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n await this.driver.remove(this.addPrefix(config.SKEY_TAG));\n }\n\n // Keys are tagged in the storage with a versioned prefix\n private addPrefix(key: string): string {\n return this.id ? (key + this.id) : key;\n }\n\n private addNonceTag(tag: string): string {\n return this.addPrefix(`${config.NONCE_TAG}.${tag}`);\n }\n}\n","import { CryptoStorage } from '../crypto-storage/crypto-storage';\nimport { Keys } from '../keys/keys';\nimport { NaCl } from '../nacl/nacl';\nimport { Utils, Base64 } from '../utils/utils';\nimport { NaClDriver } from '../nacl/nacl-driver.interface';\n\ninterface KeyRecord {\n pk: Base64;\n hpk: Base64;\n}\n\nconst commKeyTag = '__::commKey::__';\n\ninterface KeyRingBackup {\n [commKeyTag]: string;\n [key: string]: string | null | undefined\n}\n\n// Manages the set of public keys of counterparties\nexport class KeyRing {\n static readonly commKeyTag = 'comm_key';\n static readonly guestRegistryTag = 'guest_registry';\n\n private commKey: Keys;\n private storage: CryptoStorage;\n private guestKeys: Map<string, KeyRecord> = new Map();\n private nacl: NaClDriver;\n\n private constructor(naclDriver: NaClDriver, cryptoStorage: CryptoStorage, commKey: Keys) {\n this.nacl = naclDriver;\n this.storage = cryptoStorage;\n this.commKey = commKey;\n }\n\n static async new(id: string): Promise<KeyRing> {\n const nacl = NaCl.getInstance();\n const cryptoStorage = await CryptoStorage.new(id);\n const commKey = await KeyRing.getCommKeyOrCreate(nacl, cryptoStorage);\n const keyRing = new KeyRing(nacl, cryptoStorage, commKey);\n\n await keyRing.loadGuestKeys();\n return keyRing;\n }\n\n static async fromBackup(id: string, backupString: string): Promise<KeyRing> {\n const backup: KeyRingBackup = JSON.parse(backupString);\n const secretKey = Utils.fromBase64(backup[commKeyTag]);\n const restoredKeyRing = await KeyRing.new(id);\n await restoredKeyRing.setCommFromSecKey(secretKey);\n for (const [key, value] of Object.entries(backup)) {\n if (key !== commKeyTag) {\n await restoredKeyRing.addGuest(key, value as string);\n }\n }\n return restoredKeyRing;\n }\n\n getNumberOfGuests(): number {\n return this.guestKeys.size;\n }\n\n get guests(): Map<string, KeyRecord> {\n return this.guestKeys;\n }\n\n /**\n * Returns public identity, which is the default communication key.\n * Correspondents can know it, whereas Relays do not need it (other than\n * temporarily for internal use during the ownership proof)\n */\n getPubCommKey(): string {\n return this.commKey.publicKey;\n }\n\n getPrivateCommKey(): string {\n return this.commKey.privateKey;\n }\n\n /**\n * Returns HPK (hash of the public key) of the mailbox. This is what Zax relays\n * uses as the universal address of the mailbox\n */\n async getHpk(): Promise<Base64> {\n const hpk = await this.nacl.h2(Utils.fromBase64(this.commKey.publicKey));\n return Utils.toBase64(hpk);\n }\n\n getTagByHpk(hpk: string): string | null {\n for (const [key, value] of this.guestKeys) {\n if (value.hpk === hpk) {\n return key;\n }\n }\n return null;\n }\n\n getGuestKey(guestTag: string): Base64 | null {\n const keyRecord = this.guestKeys.get(guestTag);\n if (keyRecord) {\n return keyRecord.pk;\n }\n return null;\n }\n\n // Backups\n\n async backup(): Promise<string> {\n const backup: KeyRingBackup = {\n [commKeyTag]: this.commKey.privateKey\n };\n\n if (this.getNumberOfGuests() > 0) {\n for (const [key, value] of this.guestKeys) {\n if (key && value) {\n backup[key] = value.pk;\n }\n }\n }\n return JSON.stringify(backup);\n }\n\n async setCommFromSeed(seed: Uint8Array): Promise<void> {\n this.commKey = new Keys(await this.nacl.crypto_box_keypair_from_seed(seed));\n await this.storage.save(KeyRing.commKeyTag, this.commKey.toString());\n }\n\n async setCommFromSecKey(rawSecretKey: Uint8Array): Promise<void> {\n this.commKey = new Keys(await this.nacl.crypto_box_keypair_from_raw_sk(rawSecretKey));\n await this.storage.save(KeyRing.commKeyTag, this.commKey.toString());\n }\n\n async addGuest(guestTag: string, publicKey: Base64): Promise<string> {\n const b64_h2 = Utils.toBase64(await this.nacl.h2(Utils.fromBase64(publicKey)));\n this.guestKeys.set(guestTag, {\n pk: publicKey,\n hpk: b64_h2\n });\n await this.saveGuests();\n return b64_h2;\n }\n\n async removeGuest(guestTag: string): Promise<boolean> {\n if (this.guestKeys.has(guestTag)) {\n this.guestKeys.delete(guestTag);\n await this.saveGuests();\n }\n return true;\n }\n\n private async loadGuestKeys() {\n const guestKeys = await this.storage.get<{[key:string]: KeyRecord}>(KeyRing.guestRegistryTag);\n if (!guestKeys) {\n return;\n } else if (typeof guestKeys === 'object') {\n this.guestKeys = new Map(Object.entries(guestKeys));\n } else {\n throw new Error('[Keyring] Guest keys is not an object');\n }\n }\n\n private async saveGuests() {\n await this.storage.save(KeyRing.guestRegistryTag, Utils.toObject(this.guestKeys.entries()));\n }\n\n private static async getCommKeyOrCreate(nacl: NaClDriver, storage: CryptoStorage): Promise<Keys> {\n const commKeyRecord = await storage.get(KeyRing.commKeyTag);\n if (commKeyRecord && typeof commKeyRecord === 'string') {\n return new Keys(commKeyRecord);\n } else {\n const keypair = await nacl.crypto_box_keypair();\n const commKey = new Keys(keypair);\n await storage.save(KeyRing.commKeyTag, commKey.toString());\n return commKey;\n }\n }\n\n async selfDestruct() {\n await this.storage.remove(KeyRing.guestRegistryTag);\n await this.storage.remove(KeyRing.commKeyTag);\n await this.storage.selfDestruct();\n }\n}\n","import { NaCl } from './nacl';\nimport { EncryptedMessage } from './nacl-driver.interface';\nimport { Utils, Base64 } from '../utils/utils';\n\n/**\n * Includes several static helpers that are build on top of basic NaCl driver methods,\n * to avoid duplicating code in driver implementations.\n */\nexport class EncryptionHelper {\n\n private constructor() { }\n\n /**\n * Encodes a binary message with `cryptobox`\n */\n static async encodeMessage(message: Uint8Array, pkTo: Uint8Array, skFrom: Uint8Array, nonceData?: number) {\n const nacl = NaCl.getInstance();\n\n const nonce = await this.makeNonce(nonceData);\n const ctext = await nacl.crypto_box(message, nonce, pkTo, skFrom);\n return {\n nonce: Utils.toBase64(nonce),\n ctext: Utils.toBase64(ctext)\n } as EncryptedMessage;\n }\n\n /**\n * Decodes a binary message with `cryptobox_open`\n * @returns null if failed to decode\n */\n static async decodeMessage(nonce: Uint8Array, ctext: Uint8Array, pkFrom: Uint8Array, skTo: Uint8Array) {\n const nacl = NaCl.getInstance();\n const data = await nacl.crypto_box_open(ctext, nonce, pkFrom, skTo);\n if (data) {\n return await nacl.decode_utf8(data);\n }\n\n return data;\n }\n\n /**\n * Encodes a binary message with `secretbox` (used for file chunk encryption)\n */\n static async encodeMessageSymmetric(message: Uint8Array, secretKey: Uint8Array): Promise<EncryptedMessage> {\n const nacl = NaCl.getInstance();\n const nonce = await EncryptionHelper.makeNonce();\n const ctext = await nacl.crypto_secretbox(message, nonce, secretKey);\n return {\n nonce: Utils.toBase64(nonce),\n ctext: Utils.toBase64(ctext)\n };\n }\n\n /**\n * Decodes a binary message with `secretbox_open` (used for file chunk decryption)\n */\n static async decodeMessageSymmetric(nonce: Base64, ctext: Base64, secretKey: Uint8Array): Promise<Uint8Array | null> {\n const nacl = NaCl.getInstance();\n return await nacl.crypto_secretbox_open(Utils.fromBase64(ctext), Utils.fromBase64(nonce), secretKey);\n }\n\n // ---------- Nonce helper ----------\n\n /**\n * Makes a timestamp nonce that a relay expects for any crypto operations.\n * Timestamp is the first 8 bytes, the rest is random, unless custom `data`\n * is specified. `data` will be packed as next 4 bytes after timestamp.\n */\n private static async makeNonce(data?: number): Promise<Uint8Array> {\n const nacl = NaCl.getInstance();\n const nonce = await nacl.crypto_box_random_nonce();\n let headerLen;\n if (nonce.length !== nacl.crypto_box_NONCEBYTES) {\n throw new Error('[Mailbox] Wrong crypto_box nonce length');\n }\n // split timestamp integer as an array of bytes\n headerLen = 8; // max timestamp size\n const aTime = this.itoa(Math.floor(Date.now() / 1000));\n\n if (data) {\n headerLen += 4; // extra 4 bytes for custom data\n }\n\n // zero out nonce header area\n nonce.fill(0, 0, headerLen);\n // copy the timestamp into the first 8 bytes of nonce\n nonce.set(aTime, 8 - aTime.length);\n // copy data if present\n if (data) {\n const aData = this.itoa(data);\n nonce.set(aData, 12 - aData.length);\n }\n return nonce;\n }\n\n /**\n * Splits an integer into an array of bytes\n */\n private static itoa(num: number): Uint8Array {\n // calculate length first\n let hex = num.toString(16);\n hex = hex.length & 1 ? `0${hex}` : hex;\n const len = hex.length / 2;\n const byteArray = new Uint8Array(len);\n\n for (let j = 0, i = 0; i < hex.length; i += 2, j++) {\n byteArray[j] = parseInt(hex[i] + hex[i + 1], 16);\n }\n return byteArray;\n }\n}\n","import { Base64 } from './utils/utils';\n\n/**\n * Interfaces of Zax relay command requests and responses.\n * See https://github.com/vault12/zax for details.\n *\n * Full technical specification:\n * https://s3-us-west-1.amazonaws.com/vault12/crypto_relay.pdf\n */\n\n// In the future versions, plugins could add their own commands to specific relays\nexport enum RelayCommand {\n // Zax message commands\n count = 'count',\n upload = 'upload',\n download = 'download',\n messageStatus = 'messageStatus',\n delete = 'delete',\n // Zax file commands\n startFileUpload = 'startFileUpload',\n uploadFileChunk = 'uploadFileChunk',\n downloadFileChunk = 'downloadFileChunk',\n fileStatus = 'fileStatus',\n deleteFile = 'deleteFile'\n}\n\n/**\n * Status of a previously sent message equals redis TTL.\n * See https://redis.io/commands/ttl.\n * -2 : missing key\n * -1 : key never expires\n * 0+ : key time to live in seconds\n */\nexport enum MessageStatusResponse {\n MissingKey = -2,\n NeverExpires = -1\n}\n\n// -------------- Message formats --------------\n\nexport enum ZaxMessageKind {\n message = 'message',\n file = 'file',\n plain = 'plain'\n}\n\n/**\n * Format of raw messages provided by `download` Zax command\n */\nexport interface ZaxRawMessage {\n data: string;\n time: number;\n from: Base64;\n nonce: Base64;\n kind: ZaxMessageKind.message | ZaxMessageKind.file;\n}\n\nexport interface ZaxTextMessage {\n /**\n * Decoded object after JSON.parse\n */\n data: string;\n time: number;\n senderTag: string;\n nonce: Base64;\n kind: ZaxMessageKind.message;\n}\n\nexport interface ZaxFileMessage {\n data: FileUploadMetadata;\n time: number;\n senderTag: string;\n uploadID: string;\n nonce: Base64;\n kind: ZaxMessageKind.file;\n}\n\nexport interface ZaxPlainMessage {\n data: string;\n time: number;\n from: Base64;\n nonce: Base64;\n senderTag: undefined;\n kind: ZaxMessageKind.plain;\n}\n\nexport type ZaxParsedMessage = ZaxPlainMessage | ZaxFileMessage | ZaxTextMessage;\n\n// -------------- File command responses --------------\n// See https://github.com/vault12/zax/wiki/Zax-2.0-File-Commands\n\nexport interface StartFileUploadResponse {\n uploadID: string;\n max_chunk_size: number;\n storage_token: string;\n skey: Uint8Array;\n}\n\nexport interface UploadFileChunkResponse {\n status: 'OK' | 'NOT_FOUND';\n}\n\nexport interface FileStatusResponse {\n status: 'COMPLETE' | 'UPLOADING' | 'START' | 'NOT_FOUND';\n total_chunks: number;\n file_size: number;\n bytes_stored: number;\n}\n\nexport interface DeleteFileResponse {\n status: 'OK' | 'NOT_FOUND';\n}\n\nexport interface FileUploadMetadata {\n name: string;\n orig_size: number;\n md5?: string;\n created?: number;\n modified?: number;\n attrs?: string;\n skey?: Base64;\n}\n","export class GlowNetworkError extends Error {\n readonly name = 'GlowNetworkError';\n\n constructor(public status: number|undefined) {\n super(`GlowNetworkError status: ${status}`);\n }\n}","import { NaCl } from '../nacl/nacl';\nimport { NaClDriver, EncryptedMessage } from '../nacl/nacl-driver.interface';\nimport { EncryptionHelper } from '../nacl/encryption.helper';\nimport { config } from '../config';\nimport { Base64, Utils } from '../utils/utils';\nimport { Keys } from '../keys/keys';\nimport { RelayCommand } from '../zax.interface';\nimport { GlowNetworkError } from './network-error';\n\nexport interface RelayConnectionData {\n h2Signature: Uint8Array;\n relayPublicKey: Uint8Array;\n}\n\n/**\n * Low-level operations with Zax relay\n */\nexport class Relay {\n private nacl: NaClDriver;\n private difficulty = 0;\n private publicKey?: Uint8Array;\n private clientToken?: Uint8Array;\n private sessionKeys?: Keys;\n private tokenExpirationTimeoutHandle?: ReturnType<typeof setTimeout>;\n private sessionExpirationTimeoutHandle?: ReturnType<typeof setTimeout>;\n\n constructor(public url: string) {\n this.nacl = NaCl.getInstance();\n }\n\n get isConnected() {\n // relay is assumed to be connected when it got session keys and server public\n // sessionKeys are not permanent so relay won't be connected when sessionKeys will be removed within timeout\n return !!(this.sessionKeys && this.publicKey);\n }\n\n // ---------- Connection initialization ----------\n\n /**\n * Exchanges tokens with a relay and gets a temp session key for this relay.\n * Returns h₂(signature) and a relay public key\n */\n async openConnection(): Promise<RelayConnectionData> {\n this.sessionKeys = new Keys(await this.nacl.crypto_box_keypair());\n this.clientToken = await this.nacl.random_bytes(config.RELAY_TOKEN_LEN);\n const relayToken = await this.fetchRelayToken();\n this.publicKey = await this.fetchRelayPublicKey(relayToken);\n return {\n h2Signature: await this.getSignature(relayToken, this.sessionKeys),\n relayPublicKey: this.publicKey\n };\n }\n\n /**\n * Sends a client token to a relay and saves a relay token\n */\n private async fetchRelayToken(): Promise<Uint8Array> {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n const data = await this.httpCall('start_session', Utils.toBase64(this.clientToken));\n // Set a timer to mark a relay instance as having an expired token after a certain time\n this.scheduleTokenExpiration();\n // Relay responds with its own counter token. Until session is established these 2 tokens are handshake id.\n const [token, difficulty] = this.parseResponse('start_session', data);\n\n this.difficulty = parseInt(difficulty, 10);\n if (this.difficulty > 10) {\n console.log(`[Relay] ${this.url} requested difficulty ${this.difficulty}. Session handshake may take longer.`);\n }\n\n return Utils.fromBase64(token);\n }\n\n /**\n * Completes the handshake and saves a relay pubic key\n */\n private async fetchRelayPublicKey(relayToken: Uint8Array) {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n // After clientToken is sent to the relay, we use only h2() of it\n const h2ClientToken = Utils.toBase64(await this.nacl.h2(this.clientToken));\n\n const handshake = new Uint8Array([...this.clientToken, ...relayToken]);\n let sessionHandshake: Uint8Array;\n\n // Compute session handshake based on difficulty level set by the server\n if (this.difficulty === 0) {\n sessionHandshake = await this.nacl.h2(handshake);\n } else {\n sessionHandshake = await this.ensureNonceDifficulty(handshake);\n }\n\n // We confirm handshake by sending back h2(clientToken, relay_token)\n const relayPk = await this.httpCall('verify_session', h2ClientToken, Utils.toBase64(sessionHandshake));\n // Relay gives us back temp session key masked by clientToken we started with\n return Utils.fromBase64(relayPk);\n }\n\n /**\n * Attaches a mailbox and fetches number of messages\n */\n async prove(payload: EncryptedMessage): Promise<string> {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n if (!this.sessionKeys) {\n throw new Error('[Relay] No session key found, open the connection first');\n }\n const h2ClientToken = Utils.toBase64(await this.nacl.h2(this.clientToken));\n const result = await this.httpCall('prove',\n h2ClientToken, this.sessionKeys.publicKey, payload.nonce, payload.ctext);\n this.scheduleSessionExpiration();\n return result;\n }\n\n async encodeMessage(message: string): Promise<EncryptedMessage> {\n if (!this.publicKey) {\n throw new Error('[Relay] No relay public key found, open the connection first');\n }\n if (!this.sessionKeys) {\n throw new Error('[Relay] No session key found, open the connection first');\n }\n\n return await EncryptionHelper.encodeMessage(\n await this.nacl.encode_utf8(message), this.publicKey, Utils.fromBase64(this.sessionKeys?.privateKey));\n }\n\n async decodeMessage<T>(nonce: Base64, ctext: Base64): Promise<T> {\n const relayPk = this.publicKey;\n if (!relayPk) {\n throw new Error('[Relay] No relay public key found, open the connection first');\n }\n if (!this.sessionKeys) {\n throw new Error('[Relay] No session key found, open the connection first');\n }\n\n const decodedData = await EncryptionHelper.decodeMessage(Utils.fromBase64(nonce), Utils.fromBase64(ctext), relayPk,\n Utils.fromBase64(this.sessionKeys.privateKey));\n if (decodedData === null) {\n throw new Error('[Relay] failed to decode message');\n }\n return JSON.parse(decodedData);\n }\n\n // ---------- Low-level server request handling ----------\n\n /**\n * Executes a message/file command on a relay, parses and validates the response\n */\n async runCmd(command: RelayCommand, hpk: Base64, message: EncryptedMessage, ctext?: string): Promise<string[]> {\n if (!Object.keys(RelayCommand).includes(command)) {\n throw new Error(`[Relay] ${this.url} doesn't support command ${command}`);\n }\n\n const payload = [hpk, message.nonce, message.ctext];\n // if we are sending symmetrically encrypted ctext, it goes on the next line in the payload\n if (ctext) {\n payload.push(ctext);\n }\n\n const response = await this.httpCall('command', ...payload);\n return this.parseResponse(command, response);\n }\n\n private async getSignature(relayToken: Uint8Array, sessionKeys: Keys) {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n const clientTempPk = Utils.fromBase64(sessionKeys.publicKey);\n // Alice creates a 32 byte session signature as h₂(a_temp_pk, relayToken, clientToken)\n const signature = new Uint8Array([...clientTempPk, ...relayToken, ...this.clientToken]);\n const h2Signature = await this.nacl.h2(signature);\n return h2Signature;\n }\n\n /**\n * Executes a call to a relay and return raw string response\n */\n // TODO: extract fetching into separate function\n private async httpCall(command: string, ...params: string[]): Promise<string> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), config.RELAY_AJAX_TIMEOUT);\n\n const requestInit: RequestInit = {\n method: 'POST',\n headers: {\n Accept: 'text/plain',\n 'Content-Type': 'text/plain',\n },\n body: params.join('\\r\\n'),\n signal: controller.signal,\n };\n\n // NOTE: Network and server errors are not handled ny Glow itself.\n // They should instead be handled where the library is used\n try {\n const response = await fetch(`${this.url}/${command}`, requestInit);\n if (!response.ok) {\n if (response.status === 401) {\n // clear session if unauthorized\n this.clearSession();\n this.clearToken();\n }\n throw new GlowNetworkError(response.status);\n }\n return await response.text();\n } catch (err: unknown) {\n if (err instanceof Error && err.name === 'AbortError') {\n // Timeout occurred during fetch request or while downloading response body\n throw new GlowNetworkError(408);\n }\n if (err instanceof GlowNetworkError) {\n throw err;\n }\n if (err instanceof TypeError) {\n // Network layer errors (no internet, DNS failure, CORS, etc.)\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch#exceptions\n // Use status 0 to indicate network connectivity issues\n throw new GlowNetworkError(0);\n }\n // For any other unexpected errors, re-throw the original error\n throw err;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Parses relay response and throws an error if its format is unexpected\n */\n private parseResponse(command: string, rawResponse: string): string[] {\n const response = rawResponse.split('\\r\\n');\n\n if (!rawResponse || !this.validateResponse(command, response.length)) {\n console.log(response);\n throw new Error(`[Relay] ${this.url} - ${command}: Bad response`);\n }\n\n return response;\n }\n\n /**\n * Compares the expected number of lines in response with what was actually received from a relay\n */\n private validateResponse(command: string, lines: number): boolean {\n switch (command) {\n case RelayCommand.upload:\n case RelayCommand.messageStatus:\n case RelayCommand.delete:\n return lines === 1;\n case RelayCommand.downloadFileChunk:\n return lines === 3;\n default:\n return lines === 2;\n }\n }\n\n /**\n * delete token locally before it was removed on server so will reconnect without getting unauthorized error\n */\n private scheduleTokenExpiration() {\n if (this.tokenExpirationTimeoutHandle) {\n clearTimeout(this.tokenExpirationTimeoutHandle);\n }\n this.tokenExpirationTimeoutHandle = setTimeout(() => this.clearToken(), config.RELAY_TOKEN_TIMEOUT);\n }\n\n clearToken() {\n if (this.tokenExpirationTimeoutHandle) {\n clearTimeout(this.tokenExpirationTimeoutHandle);\n }\n if (this.clientToken) {\n delete this.clientToken;\n }\n }\n\n /**\n * established session is valid only for some period need to remove it after it's ttl\n * so relay will reconnect without receiving error from server\n */\n private scheduleSessionExpiration() {\n if (this.sessionExpirationTimeoutHandle) {\n clearTimeout(this.sessionExpirationTimeoutHandle);\n }\n this.sessionExpirationTimeoutHandle = setTimeout(() => {\n this.clearSession();\n }, config.RELAY_SESSION_TIMEOUT);\n }\n\n clearSession() {\n if (this.sessionExpirationTimeoutHandle) {\n clearTimeout(this.sessionExpirationTimeoutHandle);\n }\n if (this.sessionKeys) {\n delete this.sessionKeys;\n }\n }\n\n // ---------- Difficulty adjustment ----------\n\n /**\n * Continuously calculates the nonce until one requested by a relay is found\n */\n private async ensureNonceDifficulty(handshake: Uint8Array) {\n let nonce;\n let h2;\n do {\n nonce = await this.nacl.random_bytes(32);\n h2 = await this.nacl.h2(new Uint8Array([...handshake, ...nonce]));\n } while (!this.arrayZeroBits(h2));\n\n return nonce;\n }\n\n /**\n * Returns `true` if the rightmost n bits of a byte are 0.\n * Check whether the rightmost difficulty bits of an Uint8Array are 0, where\n * the lowest indexes of the array represent those rightmost bits. Thus if\n * the difficulty is 17, then array[0] and array[1] should be 0, as should the\n * rightmost bit of array[2]. This is used for our difficulty settings in Zax to\n * reduce burden on a busy server by ensuring clients have to do some\n * additional work during the session handshake\n */\n private arrayZeroBits(array: Uint8Array): boolean {\n let byte;\n let n = this.difficulty;\n for (let i = 0; i <= (1 + this.difficulty / 8); i++) {\n byte = array[i];\n if (n <= 0) {\n return true;\n }\n if (n > 8) {\n n -= 8;\n if (byte > 0) {\n return false;\n }\n } else {\n return this.firstZeroBits(byte, n);\n }\n }\n return false;\n }\n\n /**\n * Returns `true` if the rightmost n bits of a byte are 0\n */\n private firstZeroBits(byte: number, n: number): boolean {\n return byte === ((byte >> n) << n);\n }\n}\n","import { Relay } from './relay';\n\nexport class RelayFactory {\n\n private relays: { [url: string]: Relay } = {};\n\n /**\n * Relay factory, that returns a Relay instance for a given URL,\n * or creates a new one if it hasn't yet been initialized.\n */\n getInstance(url: string) {\n let relay = this.relays[url];\n if (!relay) {\n relay = this.relays[url] = new Relay(url);\n }\n return relay;\n }\n}","import { NaCl } from '../nacl/nacl';\nimport { NaClDriver, EncryptedMessage } from '../nacl/nacl-driver.interface';\nimport { EncryptionHelper } from '../nacl/encryption.helper';\nimport { KeyRing } from '../keyring/keyring';\nimport { Base64, Utils } from '../utils/utils';\nimport { Relay, RelayConnectionData } from '../relay/relay';\nimport {\n RelayCommand,\n StartFileUploadResponse,\n UploadFileChunkResponse,\n FileStatusResponse,\n DeleteFileResponse,\n MessageStatusResponse,\n FileUploadMetadata,\n ZaxMessageKind,\n ZaxRawMessage,\n ZaxFileMessage,\n ZaxPlainMessage,\n ZaxTextMessage,\n ZaxParsedMessage\n} from '../zax.interface';\nimport { RelayFactory } from '../relay/relay-factory';\nimport { Mutex } from 'async-mutex';\n\n\n/**\n * Mailbox class represents a wrapper around a Keyring that allows to exchange\n * encrypted messages with other Mailboxes via a relay\n */\nexport class Mailbox {\n public keyRing: KeyRing;\n public identity: string;\n /**\n * each mailbox use it's own relays for connection\n * this gives possibility to connect different mailboxes to same server simultaneously\n * because they will use different session keys, tokens, server pub keys\n */\n\n private relayFactory = new RelayFactory;\n\n private relayConnectionMutexes = new Map<string, Mutex>();\n\n private nacl: NaClDriver;\n\n private constructor(naclDriver: NaClDriver, keyRing: KeyRing, identity: string) {\n this.nacl = naclDriver;\n this.keyRing = keyRing;\n this.identity = identity;\n }\n\n static async new(identity: string): Promise<Mailbox> {\n return new Mailbox(NaCl.getInstance(), await KeyRing.new(identity), identity);\n }\n\n // ---------- Alternative initializers ----------\n\n /**\n * Create a Mailbox where the secret identity key is derived from a well-known seed\n */\n static async fromSeed(id: string, seed: Uint8Array | string): Promise<Mailbox> {\n const mbx = await this.new(id);\n if (!(seed instanceof Uint8Array)) {\n seed = Utils.encode_latin1(seed);\n }\n await mbx.keyRing.setCommFromSeed(seed);\n return mbx;\n }\n\n /**\n * Create a Mailbox from the known secret identity key\n */\n static async fromSecKey(id: string, rawSecretKey: Uint8Array | Base64): Promise<Mailbox> {\n const mbx = await this.new(id);\n if (!(rawSecretKey instanceof Uint8Array)) {\n rawSecretKey = Utils.fromBase64(rawSecretKey);\n }\n await mbx.keyRing.setCommFromSecKey(rawSecretKey);\n return mbx;\n }\n\n /**\n * Create a Mailbox from the backup string\n */\n static async fromBackup(identity: string, backup: string): Promise<Mailbox> {\n return new Mailbox(NaCl.getInstance(), await KeyRing.fromBackup(identity, backup), identity);\n }\n\n // ---------- Relay message commands (public API) ----------\n\n /**\n * Sends a free-form object to a guest we already have in our keyring. Set `encrypt` to `false` to\n * send a plaintext message. Returns a token that can be used with `messageStatus` command to check\n * the status of the message\n */\n async upload(url: string, guestKey: string, message: string, encrypt = true): Promise<Base64> {\n const relay = await this.prepareRelay(url);\n const guestPk = this.getGuestKey(guestKey);\n const payload = encrypt ? await this.encodeMessage(guestKey, message) : message;\n const toHpk = Utils.toBase64(await this.nacl.h2(Utils.fromBase64(guestPk)));\n\n const [token] = await this.runRelayCommand(relay, RelayCommand.upload, { to: toHpk, payload });\n return token;\n }\n\n /**\n * Downloads all messages from a relay, decrypts them with a relay key,\n * and then parses each message to find out if it's a text message, file message,\n * or if it can't be decrypted because HPK is missing in the keyring.\n * Returns an array of mixed messages\n */\n async download(url: string) {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.download);\n const messages: ZaxRawMessage[] = await this.decryptResponse<ZaxRawMessage[]>(relay, response);\n\n const parsedMessages: ZaxParsedMessage[] = [];\n for (const message of messages) {\n const senderTag = this.keyRing.getTagByHpk(message.from);\n if (!senderTag) {\n parsedMessages.push(await this.parsePlainMessage(message));\n } else if (message.kind === 'message') {\n parsedMessages.push(await this.parseTextMessage(message, senderTag));\n } else if (message.kind === 'file') {\n parsedMessages.push(await this.parseFileMessage(message, senderTag));\n } else {\n throw new Error('[Mailbox] download - Unknown message type');\n }\n }\n return parsedMessages;\n }\n\n /**\n * Marks a raw Zax message as one that can't be decrypted,\n * because sender's HPK is not found in the keyring\n */\n private async parsePlainMessage({ data, time, from, nonce }: ZaxRawMessage) {\n return { data, time, from, nonce, kind: ZaxMessageKind.plain } as ZaxPlainMessage;\n }\n\n /**\n * Decrypts a message that represents uploaded file metadata\n */\n private async parseFileMessage(message: ZaxRawMessage, senderTag: string) {\n const { nonce, ctext, uploadID } = JSON.parse(message.data);\n const rawData = await this.decodeMessage(senderTag, nonce, ctext);\n if (rawData === null) {\n throw new Error('[Mailbox] Failed to decode file message');\n }\n const data = JSON.parse(rawData) as FileUploadMetadata;\n return { data, time: message.time, senderTag, uploadID, nonce, kind: ZaxMessageKind.file } as ZaxFileMessage;\n }\n\n /**\n * Attempts to decrypt a regular encrypted Zax message. Returns plain message if it was sent encrypted\n */\n private async parseTextMessage(message: ZaxRawMessage, senderTag: string): Promise<ZaxTextMessage> {\n let data = await this.decodeMessage(senderTag, message.nonce, message.data);\n // If the message was sent unencrypted, the line above will return `null`\n if (!data) {\n data = message.data;\n }\n return ({ data, time: message.time, senderTag, nonce: message.nonce, kind: ZaxMessageKind.message });\n }\n\n /**\n * Returns the number of messages in the mailbox on a given relay\n */\n async count(url: string): Promise<number> {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.count);\n return await this.decryptResponse(relay, response);\n }\n\n /**\n * Deletes messages from a relay given a list of base64 message nonces,\n * and returns the number of remaining messages\n */\n async delete(url: string, nonceList: Base64[]): Promise<number> {\n const relay = await this.prepareRelay(url);\n const [response] = await this.runRelayCommand(relay, RelayCommand.delete, { payload: nonceList });\n return parseInt(response, 10);\n }\n\n /**\n * Gets the status of a previously sent Zax message by a storage token.\n * Returns \"time to live\" in seconds or a negative value if it's not applicable.\n * See `MessageStatusResponse` values for reference\n */\n async messageStatus(url: string, storageToken: Base64): Promise<MessageStatusResponse | number> {\n const relay = await this.prepareRelay(url);\n const [response] = await this.runRelayCommand(relay, RelayCommand.messageStatus, { token: storageToken });\n const status = parseInt(response, 10);\n return status;\n }\n\n // ---------- Relay file commands (public API) ----------\n\n /**\n * Asks Zax to start a new upload session, and returns a unique file identifier\n * required to upload file chunks.\n */\n async startFileUpload(url: string, guest: string,\n rawMetadata: FileUploadMetadata): Promise<StartFileUploadResponse> {\n const relay = await this.prepareRelay(url);\n const guestPk = this.getGuestKey(guest);\n const toHpk = Utils.toBase64(await this.nacl.h2(Utils.fromBase64(guestPk)));\n\n const secretKey = await this.nacl.random_bytes(this.nacl.crypto_secretbox_KEYBYTES);\n rawMetadata.skey = Utils.toBase64(secretKey);\n\n const metadata = await this.encodeMessage(guest, JSON.stringify(rawMetadata));\n\n const response = await this.runRelayCommand(relay, RelayCommand.startFileUpload, {\n to: toHpk,\n file_size: rawMetadata.orig_size,\n metadata\n });\n\n const decrypted = await this.decryptResponse<StartFileUploadResponse>(relay, response);\n // append symmetric secret key (unique for this upload session) to the server response\n decrypted.skey = secretKey;\n return decrypted;\n }\n\n /**\n * Encrypts the file chunk symmetrically and transfers it to a relay\n */\n async uploadFileChunk(url: string, uploadID: string, chunk: Uint8Array,\n part: number, totalParts: number, skey: Uint8Array): Promise<UploadFileChunkResponse> {\n const relay = await this.prepareRelay(url);\n const encodedChunk = await EncryptionHelper.encodeMessageSymmetric(chunk, skey);\n const response = await this.runRelayCommand(relay, RelayCommand.uploadFileChunk, {\n uploadID,\n part,\n last_chunk: (totalParts - 1 === part), // marker of the last chunk, sent only once\n nonce: encodedChunk.nonce\n }, encodedChunk.ctext); // do not encode file chunk contents, as it's already encoded with symmetric encryption\n return await this.decryptResponse(relay, response);\n }\n\n /**\n * Returns the status of a file upload by its relay-specific uploadID. Uploader can call it\n * to verify the correct transfer, and downloader can check if the file exists and retrieve\n * the number of chunks\n */\n async getFileStatus(url: string, uploadID: string): Promise<FileStatusResponse> {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.fileStatus, { uploadID });\n return await this.decryptResponse(relay, response);\n }\n\n /**\n * Fetches the file metadata by uploadID, which was declared by the uploader\n */\n async getFileMetadata(url: string, uploadID: string): Promise<FileUploadMetadata> {\n const messages = await this.download(url);\n\n const fileMessage = messages\n .filter(message => message.kind === 'file')\n .find(message => (message as ZaxFileMessage).uploadID === uploadID);\n return fileMessage?.data as FileUploadMetadata;\n }\n\n /**\n * Downloads a binary chunk of a file from a relay by a given uploadID. The total number of chunks\n * can be retrieved via a `getFileStatus` request\n */\n async downloadFileChunk(url: string, uploadID: string, part: number, skey: Uint8Array | Base64):\n Promise<Uint8Array | null> {\n if (!(skey instanceof Uint8Array)) {\n skey = Utils.fromBase64(skey);\n }\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.downloadFileChunk, { uploadID, part });\n const [nonce, ctext, fileCtext] = response;\n const decoded = await relay.decodeMessage<{nonce: string}>(nonce, ctext);\n return await EncryptionHelper.decodeMessageSymmetric(decoded.nonce, fileCtext, skey);\n }\n\n /**\n * Deletes a file from the relay (or all chunks uploaded so far, if the upload was not completed).\n * Can be called by either the sender or recipient\n */\n async deleteFile(url: string, uploadID: string): Promise<DeleteFileResponse> {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.deleteFile, { uploadID });\n return await this.decryptResponse(relay, response);\n }\n\n // ---------- Dealing with Relay ----------\n\n /**\n * Establishes a session, exchanges temp keys and proves our ownership of this\n * Mailbox to this specific relay. This is the first function to start\n * communications with any relay. Returns the number of messages in the mailbox\n */\n async connectToRelay(url: string): Promise<number> {\n const relay = this.relayFactory.getInstance(url);\n const connectionData = await relay.openConnection();\n const encryptedSignature = await this.encryptSignature(connectionData);\n\n const messagesNumber = await relay.prove(await relay.encodeMessage(JSON.stringify({\n pub_key: this.keyRing.getPubCommKey(),\n nonce: encryptedSignature.nonce,\n ctext: encryptedSignature.ctext\n })));\n return parseInt(messagesNumber, 10);\n }\n\n clearSession(url: string) {\n const relay = this.relayFactory.getInstance(url);\n relay.clearToken();\n relay.clearSession();\n }\n\n private async encryptSignature(connection: RelayConnectionData) {\n const privateKey = Utils.fromBase64(this.keyRing.getPrivateCommKey());\n return await EncryptionHelper.encodeMessage(connection.h2Signature, connection.relayPublicKey, privateKey);\n }\n\n /**\n * Gets a singleton Relay instance, and reconnects to a relay if a previous token has expired\n */\n private async prepareRelay(url: string): Promise<Relay> {\n const relay = this.relayFactory.getInstance(url);\n /**\n * allow establishing only once connection for pair mailbox-relay\n */\n await this.getRelayConnectionMutex(url).runExclusive(async () => {\n if (!relay.isConnected) {\n await this.connectToRelay(url);\n }\n });\n return relay;\n }\n\n /**\n * Encrypts the payload of the command and sends it to a relay\n */\n private async runRelayCommand(\n relay: Relay, command: RelayCommand, params?: {[key:string]: unknown}, ctext?: string): Promise<string[]> {\n params = { cmd: command, ...params };\n const hpk = await this.keyRing.getHpk();\n const message = await relay.encodeMessage(JSON.stringify(params));\n return await relay.runCmd(command, hpk, message, ctext);\n }\n\n /**\n * Parses relay's response to a command, for those commands that expect an encrypted message in return.\n * Two lines of POST response will be nonce and ctext\n */\n private async decryptResponse<T>(relay: Relay, response: string[]) {\n const [nonce, ctext] = response;\n return await relay.decodeMessage<T>(nonce, ctext);\n }\n\n // ---------- Message encoding / decoding ----------\n\n /**\n * Encodes `message` to the guest key of a guest already\n * added to the keyring\n */\n async encodeMessage(guest: string, message: string): Promise<EncryptedMessage> {\n const guestPk = this.getGuestKey(guest);\n const privateKey = this.keyRing.getPrivateCommKey();\n\n return await EncryptionHelper.encodeMessage(\n await this.nacl.encode_utf8(message), Utils.fromBase64(guestPk), Utils.fromBase64(privateKey));\n }\n\n /**\n * Decodes a ciphertext from a guest key already in our keyring with this nonce\n * @returns null if failed to decode\n */\n async decodeMessage(guest: string, nonce: Base64, ctext: Base64) {\n const guestPk = this.getGuestKey(guest);\n const privateKey = this.keyRing.getPrivateCommKey();\n let uint8ArrayCtext: Uint8Array;\n try {\n uint8ArrayCtext = Utils.fromBase64(ctext);\n } catch {\n // looks like ctext was not encoded\n return null;\n }\n\n return await EncryptionHelper.decodeMessage(Utils.fromBase64(nonce), uint8ArrayCtext,\n Utils.fromBase64(guestPk), Utils.fromBase64(privateKey));\n }\n\n /**\n * Wrapper around `keyring.getGuestKey` that handles unknown guests\n */\n private getGuestKey(guest: string): string {\n const guestPk = this.keyRing.getGuestKey(guest);\n if (!guestPk) {\n throw new Error(`[Mailbox] Unknown guest ${guest}`);\n }\n return guestPk;\n }\n\n // ---------- Destroying Mailbox ----------\n\n /**\n * Deletes a Mailbox and all its data from local CryptoStorage. This is a very\n * destructive operation, use with caution - it will delete the Mailbox\n * keyring along with all stored public keys. To restore that information, you\n * will need to do another key exchange with all the guests on your keyring.\n */\n async selfDestruct() {\n await this.keyRing.selfDestruct();\n }\n\n private getRelayConnectionMutex(url: string) {\n if (!this.relayConnectionMutexes.has(url)) {\n this.relayConnectionMutexes.set(url, new Mutex());\n }\n return this.relayConnectionMutexes.get(url) as Mutex;\n }\n}\n","import { StorageDriver } from './storage-driver.interface';\n\n/**\n * temporary created in-memory storage for testing purposes\n */\nexport class InMemoryStorage implements StorageDriver {\n\n private storage: {[key: string]: string|null} = {};\n\n async get(key: string) {\n return (await this.getMultiple([key]))[0];\n }\n\n async getMultiple(keys: string[]) {\n return keys.map(key => this.storage[key]);\n }\n\n set<T extends string | null>(key: string, value: T) {\n return this.setMultiple({[key]: value});\n }\n\n async setMultiple<T extends string | null>(values: { [key: string]: T; }){\n for (const key in values) {\n this.storage[key] = values[key];\n }\n }\n\n remove(key: string) {\n return this.removeMultiple([key]);\n }\n\n async removeMultiple(keys: string[]) {\n keys.forEach(key => delete this.storage[key]);\n }\n\n reset() {\n this.storage = {};\n }\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"glow.cjs.js","names":[],"sources":["../src/utils/utils.ts","../src/nacl/js-nacl-driver.ts","../src/nacl/nacl.ts","../src/keys/keys.ts","../src/config.ts","../src/crypto-storage/local-storage.driver.ts","../src/crypto-storage/crypto-storage.ts","../src/keyring/keyring.ts","../src/nacl/encryption.helper.ts","../src/zax.interface.ts","../src/relay/network-error.ts","../src/relay/relay.ts","../src/relay/relay-factory.ts","../src/mailbox/mailbox.ts","../src/crypto-storage/in-memory-storage.ts"],"sourcesContent":["export type Base64 = string;\n\nexport class Utils {\n static toBase64(input: Uint8Array): Base64 {\n const isBrowser = typeof btoa !== 'undefined';\n return isBrowser ?\n btoa(this.decode_latin1(input)) :\n Buffer.from(this.decode_latin1(input), 'latin1').toString('base64');\n }\n\n static fromBase64(input: Base64): Uint8Array {\n const isBrowser = typeof btoa !== 'undefined';\n return isBrowser ?\n this.encode_latin1(atob(input)) :\n this.encode_latin1(Buffer.from(input, 'base64').toString('latin1'));\n }\n\n static encode_latin1(data: string): Uint8Array {\n const result = new Uint8Array(data.length);\n for (let i = 0; i < data.length; i++) {\n const c = data.charCodeAt(i);\n if ((c & 0xff) !== c) throw { message: 'Cannot encode string in Latin1', str: data };\n result[i] = (c & 0xff);\n }\n return result;\n }\n\n static decode_latin1(data: Uint8Array): string {\n const encoded = [];\n for (let i = 0; i < data.length; i++) {\n encoded.push(String.fromCharCode(data[i]));\n }\n return encoded.join('');\n }\n\n static toObject(pairs: IterableIterator<[string, unknown]>) {\n return Array.from(pairs).reduce(\n (acc, [key, value]) => Object.assign(acc, { [key]: value }),\n {},\n );\n }\n}\n","import { box, BoxKeyPair, randomBytes, secretbox, hash } from 'tweetnacl';\n\nimport { NaClDriver } from './nacl-driver.interface';\nimport { Keypair } from './keypair.interface';\nimport { Utils } from '../utils/utils';\n\n/**\n * Implementation based on TweetNaCl.js: {@link https://github.com/dchest/tweetnacl-js}.\n * SHA256 is implemented using Web Crypto API (SubtleCrypto).\n *\n * All the methods of this class are actually synchronous, but they are defined\n * as async to conform to the NaClDriver interface, which may have other implementations.\n */\nexport class JsNaClDriver implements NaClDriver {\n crypto_secretbox_KEYBYTES = secretbox.keyLength;\n\n async crypto_secretbox_random_nonce(): Promise<Uint8Array> {\n return randomBytes(secretbox.nonceLength);\n }\n\n async crypto_secretbox(message: Uint8Array, nonce: Uint8Array, key: Uint8Array): Promise<Uint8Array> {\n return secretbox(message, nonce, key);\n }\n\n async crypto_secretbox_open(box: Uint8Array, nonce: Uint8Array, key: Uint8Array): Promise<Uint8Array | null> {\n return secretbox.open(box, nonce, key);\n }\n\n crypto_box_NONCEBYTES = box.nonceLength;\n\n async crypto_box(message: Uint8Array, nonce: Uint8Array, pk: Uint8Array, sk: Uint8Array): Promise<Uint8Array> {\n return box(message, nonce, pk, sk);\n }\n\n async crypto_box_open(\n cipher: Uint8Array, nonce: Uint8Array, pk: Uint8Array, sk: Uint8Array): Promise<Uint8Array | null> {\n return box.open(cipher, nonce, pk, sk);\n }\n\n async crypto_box_random_nonce(): Promise<Uint8Array> {\n return randomBytes(box.nonceLength);\n }\n\n async crypto_box_keypair(): Promise<Keypair> {\n const pair: BoxKeyPair = box.keyPair();\n return {\n boxPk: pair.publicKey,\n boxSk: pair.secretKey\n };\n }\n\n async crypto_box_keypair_from_raw_sk(key: Uint8Array): Promise<Keypair> {\n const pair: BoxKeyPair = box.keyPair.fromSecretKey(key);\n return {\n boxPk: pair.publicKey,\n boxSk: pair.secretKey\n };\n }\n\n async crypto_box_keypair_from_seed(seed: Uint8Array): Promise<Keypair> {\n // using SHA512 to hash, per original NaCl docs\n return this.crypto_box_keypair_from_raw_sk(hash(seed).subarray(0, box.secretKeyLength));\n }\n\n async crypto_hash_sha256(data: Uint8Array): Promise<Uint8Array> {\n // In Node.js, globalThis.crypto is implementation of WebCrypto API, available since Node.js 16\n // In browsers, globalThis.crypto is the standard Web Crypto API\n const cryptoProvider = globalThis.crypto;\n if (!cryptoProvider || !cryptoProvider.subtle) {\n throw new Error('Web Crypto API is not available. Please use a modern browser or Node.js 16+.');\n }\n\n const hashBuffer = await cryptoProvider.subtle.digest('SHA-256', data as BufferSource);\n return new Uint8Array(hashBuffer);\n }\n\n async random_bytes(size: number): Promise<Uint8Array> {\n return randomBytes(size);\n }\n\n // Helper methods are based on `js-nacl` implementations\n // https://github.com/tonyg/js-nacl/blob/cc70775cfc9d68a04905ca65c7f179b33a18066e/nacl_cooked.js\n\n async encode_latin1(data: string): Promise<Uint8Array> {\n return Utils.encode_latin1(data);\n }\n\n async decode_latin1(data: Uint8Array): Promise<string> {\n return Utils.decode_latin1(data);\n }\n\n async encode_utf8(data: string): Promise<Uint8Array> {\n return this.encode_latin1(unescape(encodeURIComponent(data)));\n }\n\n async decode_utf8(data: Uint8Array): Promise<string> {\n return decodeURIComponent(escape(await this.decode_latin1(data)));\n }\n\n async to_hex(data: Uint8Array): Promise<string> {\n const encoded = [];\n for (let i = 0; i < data.length; i++) {\n const hexAlphabet = '0123456789abcdef';\n encoded.push(hexAlphabet[(data[i] >> 4) & 15]);\n encoded.push(hexAlphabet[data[i] & 15]);\n }\n return encoded.join('');\n }\n\n async from_hex(data: string): Promise<Uint8Array> {\n const result = new Uint8Array(data.length / 2);\n for (let i = 0; i < data.length / 2; i++) {\n result[i] = parseInt(data.substr(2 * i, 2), 16);\n }\n return result;\n }\n\n /**\n * h2(m) = sha256(sha256(64x0 + m))\n * Zero out initial sha256 block, and double hash 0-padded message\n * http://cs.nyu.edu/~dodis/ps/h-of-h.pdf\n */\n async h2(data: string | Uint8Array): Promise<Uint8Array> {\n if (!(data instanceof Uint8Array)) {\n data = await this.encode_latin1(data);\n }\n\n const zeroPaddingLength = 64;\n const extendedSource = new Uint8Array(zeroPaddingLength + data.length);\n extendedSource.fill(0);\n extendedSource.set(data, zeroPaddingLength);\n return this.crypto_hash_sha256(await this.crypto_hash_sha256(extendedSource));\n }\n}\n","import { NaClDriver } from './nacl-driver.interface';\nimport { JsNaClDriver } from './js-nacl-driver';\n\n/**\n * Facade singleton to access the NaCl driver.\n * Usage: call instance() with a chosen driver, and store the received object to perform further actions.\n */\nexport class NaCl {\n private static driverInstance?: NaClDriver;\n\n private constructor() { }\n\n public static setDefaultInstance() {\n return this.setInstance(new JsNaClDriver());\n }\n\n public static setInstance(driver: NaClDriver): boolean {\n if (this.driverInstance) {\n throw new Error('[NaCl] NaCl driver has been already set, it is supposed to be set only once');\n }\n this.driverInstance = driver;\n\n return true;\n }\n\n public static getInstance(): NaClDriver {\n if (!this.driverInstance) {\n throw new Error('[NaCl] NaCl instance is not yet set');\n }\n\n return this.driverInstance;\n }\n}\n","import { Base64, Utils } from '../utils/utils';\nimport { Keypair } from '../nacl/keypair.interface';\n\n/**\n * A simple wrapper around a public/private keys pair.\n * Stores keys in base64 format.\n */\nexport class Keys {\n private keyPair: Keypair;\n\n constructor(public keys: string | Keypair) {\n if (typeof keys === 'string') {\n const { boxPk, boxSk } = JSON.parse(keys);\n this.keyPair = {\n boxPk: Utils.fromBase64(boxPk),\n boxSk: Utils.fromBase64(boxSk)\n };\n } else {\n this.keyPair = keys;\n }\n }\n\n static isEqual(keys1: Keys, keys2: Keys): boolean {\n return keys1.toString() === keys2.toString();\n }\n\n toString(): string {\n return JSON.stringify({\n boxPk: this.publicKey,\n boxSk: this.privateKey\n });\n }\n\n get publicKey(): Base64 {\n return Utils.toBase64(this.keyPair.boxPk);\n }\n\n get privateKey(): Base64 {\n return Utils.toBase64(this.keyPair.boxSk);\n }\n}\n","const sessionTimeoutBuffer = 30 * 1000;\n\nexport const config = {\n NONCE_TAG: '__nc',\n SKEY_TAG: 'storage_key',\n STORAGE_ROOT: '.v1.stor.vlt12',\n // Relay tokens, keys and hashes are 32 bytes\n RELAY_TOKEN_LEN: 32,\n // 5 min - Token expiration on the server side, matched with config.x.relay.token_timeout on Zax server\n RELAY_TOKEN_TIMEOUT: 5 * 60 * 1000 - sessionTimeoutBuffer,\n // 20 min - Session expiration on the server side, matched with config.x.relay.session_timeout on Zax server\n RELAY_SESSION_TIMEOUT: 20 * 60 * 1000 - sessionTimeoutBuffer,\n // 5 sec - Ajax request timeout\n RELAY_AJAX_TIMEOUT: 5 * 1000\n};\n","import { StorageDriver } from './storage-driver.interface';\n\nexport class LocalStorageDriver implements StorageDriver {\n private rootTag: string;\n\n constructor(root = 'storage.') {\n this.rootTag = `__glow.${root}`;\n }\n\n async get(key: string): Promise<string | null> {\n return (await this.getMultiple([key]))[0];\n }\n\n async getMultiple(keys: string[]): Promise<(string | null)[]> {\n return keys.map(key => localStorage.getItem(this.tag(key)) || null);\n }\n\n async set(key: string, value: string | null): Promise<void> {\n return this.setMultiple({ [key]: value });\n }\n\n async setMultiple(values: { [key: string]: string | null; }): Promise<void> {\n for(const key in values) {\n const value = values[key];\n if (value !== null) {\n localStorage.setItem(this.tag(key), value);\n }\n }\n }\n\n async remove(key: string): Promise<void> {\n this.removeMultiple([key]);\n }\n\n async removeMultiple(keys: string[]): Promise<void> {\n keys.forEach(key => localStorage.removeItem(this.tag(key)));\n }\n\n private tag(key: string) {\n return `${this.rootTag}.${key}`;\n }\n}\n","import { NaCl } from '../nacl/nacl';\nimport { Utils } from '../utils/utils';\nimport { config } from '../config';\nimport { StorageDriver } from './storage-driver.interface';\nimport { NaClDriver } from '../nacl/nacl-driver.interface';\nimport { LocalStorageDriver } from './local-storage.driver';\n\n// TODO: add bulk operations to set(), get() and remove() pairs of values simultaneously\n\n/* CryptoStorage is a handy wrapper around any storage that provides JavaScript interface,\n that allows to store symmetrically encrypted serializable Javascript objects and primitives. */\nexport class CryptoStorage {\n\n private static storageDriver: StorageDriver;\n\n private driver?: StorageDriver;\n private id?: string;\n private storageKey?: Uint8Array;\n private nacl: NaClDriver;\n\n private constructor(storageDriver: StorageDriver, id: string) {\n const nacl = NaCl.getInstance();\n this.nacl = nacl;\n this.driver = storageDriver;\n this.id = id ? `.${id}${config.STORAGE_ROOT}` : config.STORAGE_ROOT;\n }\n\n static async new(id: string): Promise<CryptoStorage> {\n const storageDriver = this.getStorageDriver();\n const storage = new CryptoStorage(storageDriver, id);\n const prefixedStorageKeyTag = storage.addPrefix(config.SKEY_TAG);\n const storageKey = await storageDriver.get(prefixedStorageKeyTag);\n // Either load or generate a storage key\n if (storageKey) {\n const { key } = JSON.parse(storageKey);\n storage.storageKey = Utils.fromBase64(key);\n } else {\n storage.storageKey = await storage.nacl.random_bytes(storage.nacl.crypto_secretbox_KEYBYTES);\n await storageDriver.set(prefixedStorageKeyTag, JSON.stringify({ key: Utils.toBase64(storage.storageKey)}));\n }\n return storage;\n }\n\n static getStorageDriver() {\n if (!this.storageDriver) {\n throw new Error('[CryptoStorage] StorageDriver instance is not yet set');\n } else {\n return this.storageDriver;\n }\n }\n\n static setDefaultStorageDriver() {\n return this.setStorageDriver(new LocalStorageDriver());\n }\n\n static setStorageDriver(driver: StorageDriver) {\n if (this.storageDriver) {\n throw new Error('[CryptoStorage] StorageDriver has been already set, it is supposed to be set only once');\n }\n this.storageDriver = driver;\n return true;\n }\n\n async save(tag: string, data: unknown): Promise<boolean> {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n if (!this.storageKey) {\n throw new Error('[CryptoStorage] Storage key is not set');\n }\n // Convert the data to JSON, then convert that string to a byte array\n const input = JSON.stringify(data);\n const encoded = await this.nacl.encode_utf8(input);\n // For each item in the store we also generate and save its own nonce\n const nonce = await this.nacl.crypto_secretbox_random_nonce();\n const cipherText = await this.nacl.crypto_secretbox(encoded, nonce, this.storageKey);\n // Save the cipher text and nonce\n await this.driver.setMultiple({\n [this.addPrefix(tag)]: Utils.toBase64(cipherText),\n [this.addNonceTag(tag)]: Utils.toBase64(nonce)\n });\n return true;\n }\n\n async get<T>(tag: string): Promise<T | null> {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n if (!this.storageKey) {\n throw new Error('[CryptoStorage] Storage key is not set');\n }\n // Get cipher text and nonce from the storage\n const [data, nonce] = await this.driver.getMultiple([this.addPrefix(tag), this.addNonceTag(tag)]);\n // Nothing to do without cipher text or nonce\n if (!data || !nonce) {\n return null;\n }\n const dataBinary = Utils.fromBase64(data);\n const nonceBinary = Utils.fromBase64(nonce);\n const source = await this.nacl.crypto_secretbox_open(dataBinary, nonceBinary, this.storageKey);\n if (source) {\n const decoded = await this.nacl.decode_utf8(source);\n return JSON.parse(decoded) as T;\n } else {\n throw new Error('[CryptoStorage] crypto_secretbox_open: decryption error');\n }\n }\n\n async remove(tag: string): Promise<boolean> {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n await this.driver.removeMultiple([this.addPrefix(tag), this.addNonceTag(tag)]);\n return true;\n }\n\n async selfDestruct() {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n await this.driver.remove(this.addPrefix(config.SKEY_TAG));\n }\n\n // Keys are tagged in the storage with a versioned prefix\n private addPrefix(key: string): string {\n return this.id ? (key + this.id) : key;\n }\n\n private addNonceTag(tag: string): string {\n return this.addPrefix(`${config.NONCE_TAG}.${tag}`);\n }\n}\n","import { CryptoStorage } from '../crypto-storage/crypto-storage';\nimport { Keys } from '../keys/keys';\nimport { NaCl } from '../nacl/nacl';\nimport { Utils, Base64 } from '../utils/utils';\nimport { NaClDriver } from '../nacl/nacl-driver.interface';\n\ninterface KeyRecord {\n pk: Base64;\n hpk: Base64;\n}\n\nconst commKeyTag = '__::commKey::__';\n\ninterface KeyRingBackup {\n [commKeyTag]: string;\n [key: string]: string | null | undefined\n}\n\n// Manages the set of public keys of counterparties\nexport class KeyRing {\n static readonly commKeyTag = 'comm_key';\n static readonly guestRegistryTag = 'guest_registry';\n\n private commKey: Keys;\n private storage: CryptoStorage;\n private guestKeys: Map<string, KeyRecord> = new Map();\n private nacl: NaClDriver;\n\n private constructor(naclDriver: NaClDriver, cryptoStorage: CryptoStorage, commKey: Keys) {\n this.nacl = naclDriver;\n this.storage = cryptoStorage;\n this.commKey = commKey;\n }\n\n static async new(id: string): Promise<KeyRing> {\n const nacl = NaCl.getInstance();\n const cryptoStorage = await CryptoStorage.new(id);\n const commKey = await KeyRing.getCommKeyOrCreate(nacl, cryptoStorage);\n const keyRing = new KeyRing(nacl, cryptoStorage, commKey);\n\n await keyRing.loadGuestKeys();\n return keyRing;\n }\n\n static async fromBackup(id: string, backupString: string): Promise<KeyRing> {\n const backup: KeyRingBackup = JSON.parse(backupString);\n const secretKey = Utils.fromBase64(backup[commKeyTag]);\n const restoredKeyRing = await KeyRing.new(id);\n await restoredKeyRing.setCommFromSecKey(secretKey);\n for (const [key, value] of Object.entries(backup)) {\n if (key !== commKeyTag) {\n await restoredKeyRing.addGuest(key, value as string);\n }\n }\n return restoredKeyRing;\n }\n\n getNumberOfGuests(): number {\n return this.guestKeys.size;\n }\n\n get guests(): Map<string, KeyRecord> {\n return this.guestKeys;\n }\n\n /**\n * Returns public identity, which is the default communication key.\n * Correspondents can know it, whereas Relays do not need it (other than\n * temporarily for internal use during the ownership proof)\n */\n getPubCommKey(): string {\n return this.commKey.publicKey;\n }\n\n getPrivateCommKey(): string {\n return this.commKey.privateKey;\n }\n\n /**\n * Returns HPK (hash of the public key) of the mailbox. This is what Zax relays\n * uses as the universal address of the mailbox\n */\n async getHpk(): Promise<Base64> {\n const hpk = await this.nacl.h2(Utils.fromBase64(this.commKey.publicKey));\n return Utils.toBase64(hpk);\n }\n\n getTagByHpk(hpk: string): string | null {\n for (const [key, value] of this.guestKeys) {\n if (value.hpk === hpk) {\n return key;\n }\n }\n return null;\n }\n\n getGuestKey(guestTag: string): Base64 | null {\n const keyRecord = this.guestKeys.get(guestTag);\n if (keyRecord) {\n return keyRecord.pk;\n }\n return null;\n }\n\n // Backups\n\n async backup(): Promise<string> {\n const backup: KeyRingBackup = {\n [commKeyTag]: this.commKey.privateKey\n };\n\n if (this.getNumberOfGuests() > 0) {\n for (const [key, value] of this.guestKeys) {\n if (key && value) {\n backup[key] = value.pk;\n }\n }\n }\n return JSON.stringify(backup);\n }\n\n async setCommFromSeed(seed: Uint8Array): Promise<void> {\n this.commKey = new Keys(await this.nacl.crypto_box_keypair_from_seed(seed));\n await this.storage.save(KeyRing.commKeyTag, this.commKey.toString());\n }\n\n async setCommFromSecKey(rawSecretKey: Uint8Array): Promise<void> {\n this.commKey = new Keys(await this.nacl.crypto_box_keypair_from_raw_sk(rawSecretKey));\n await this.storage.save(KeyRing.commKeyTag, this.commKey.toString());\n }\n\n async addGuest(guestTag: string, publicKey: Base64): Promise<string> {\n const b64_h2 = Utils.toBase64(await this.nacl.h2(Utils.fromBase64(publicKey)));\n this.guestKeys.set(guestTag, {\n pk: publicKey,\n hpk: b64_h2\n });\n await this.saveGuests();\n return b64_h2;\n }\n\n async removeGuest(guestTag: string): Promise<boolean> {\n if (this.guestKeys.has(guestTag)) {\n this.guestKeys.delete(guestTag);\n await this.saveGuests();\n }\n return true;\n }\n\n private async loadGuestKeys() {\n const guestKeys = await this.storage.get<{[key:string]: KeyRecord}>(KeyRing.guestRegistryTag);\n if (!guestKeys) {\n return;\n } else if (typeof guestKeys === 'object') {\n this.guestKeys = new Map(Object.entries(guestKeys));\n } else {\n throw new Error('[Keyring] Guest keys is not an object');\n }\n }\n\n private async saveGuests() {\n await this.storage.save(KeyRing.guestRegistryTag, Utils.toObject(this.guestKeys.entries()));\n }\n\n private static async getCommKeyOrCreate(nacl: NaClDriver, storage: CryptoStorage): Promise<Keys> {\n const commKeyRecord = await storage.get(KeyRing.commKeyTag);\n if (commKeyRecord && typeof commKeyRecord === 'string') {\n return new Keys(commKeyRecord);\n } else {\n const keypair = await nacl.crypto_box_keypair();\n const commKey = new Keys(keypair);\n await storage.save(KeyRing.commKeyTag, commKey.toString());\n return commKey;\n }\n }\n\n async selfDestruct() {\n await this.storage.remove(KeyRing.guestRegistryTag);\n await this.storage.remove(KeyRing.commKeyTag);\n await this.storage.selfDestruct();\n }\n}\n","import { NaCl } from './nacl';\nimport { EncryptedMessage } from './nacl-driver.interface';\nimport { Utils, Base64 } from '../utils/utils';\n\n/**\n * Includes several static helpers that are build on top of basic NaCl driver methods,\n * to avoid duplicating code in driver implementations.\n */\nexport class EncryptionHelper {\n\n private constructor() { }\n\n /**\n * Encodes a binary message with `cryptobox`\n */\n static async encodeMessage(message: Uint8Array, pkTo: Uint8Array, skFrom: Uint8Array, nonceData?: number) {\n const nacl = NaCl.getInstance();\n\n const nonce = await this.makeNonce(nonceData);\n const ctext = await nacl.crypto_box(message, nonce, pkTo, skFrom);\n return {\n nonce: Utils.toBase64(nonce),\n ctext: Utils.toBase64(ctext)\n } as EncryptedMessage;\n }\n\n /**\n * Decodes a binary message with `cryptobox_open`\n * @returns null if failed to decode\n */\n static async decodeMessage(nonce: Uint8Array, ctext: Uint8Array, pkFrom: Uint8Array, skTo: Uint8Array) {\n const nacl = NaCl.getInstance();\n const data = await nacl.crypto_box_open(ctext, nonce, pkFrom, skTo);\n if (data) {\n return await nacl.decode_utf8(data);\n }\n\n return data;\n }\n\n /**\n * Encodes a binary message with `secretbox` (used for file chunk encryption)\n */\n static async encodeMessageSymmetric(message: Uint8Array, secretKey: Uint8Array): Promise<EncryptedMessage> {\n const nacl = NaCl.getInstance();\n const nonce = await EncryptionHelper.makeNonce();\n const ctext = await nacl.crypto_secretbox(message, nonce, secretKey);\n return {\n nonce: Utils.toBase64(nonce),\n ctext: Utils.toBase64(ctext)\n };\n }\n\n /**\n * Decodes a binary message with `secretbox_open` (used for file chunk decryption)\n */\n static async decodeMessageSymmetric(nonce: Base64, ctext: Base64, secretKey: Uint8Array): Promise<Uint8Array | null> {\n const nacl = NaCl.getInstance();\n return await nacl.crypto_secretbox_open(Utils.fromBase64(ctext), Utils.fromBase64(nonce), secretKey);\n }\n\n // ---------- Nonce helper ----------\n\n /**\n * Makes a timestamp nonce that a relay expects for any crypto operations.\n * Timestamp is the first 8 bytes, the rest is random, unless custom `data`\n * is specified. `data` will be packed as next 4 bytes after timestamp.\n */\n private static async makeNonce(data?: number): Promise<Uint8Array> {\n const nacl = NaCl.getInstance();\n const nonce = await nacl.crypto_box_random_nonce();\n let headerLen;\n if (nonce.length !== nacl.crypto_box_NONCEBYTES) {\n throw new Error('[Mailbox] Wrong crypto_box nonce length');\n }\n // split timestamp integer as an array of bytes\n headerLen = 8; // max timestamp size\n const aTime = this.itoa(Math.floor(Date.now() / 1000));\n\n if (data) {\n headerLen += 4; // extra 4 bytes for custom data\n }\n\n // zero out nonce header area\n nonce.fill(0, 0, headerLen);\n // copy the timestamp into the first 8 bytes of nonce\n nonce.set(aTime, 8 - aTime.length);\n // copy data if present\n if (data) {\n const aData = this.itoa(data);\n nonce.set(aData, 12 - aData.length);\n }\n return nonce;\n }\n\n /**\n * Splits an integer into an array of bytes\n */\n private static itoa(num: number): Uint8Array {\n // calculate length first\n let hex = num.toString(16);\n hex = hex.length & 1 ? `0${hex}` : hex;\n const len = hex.length / 2;\n const byteArray = new Uint8Array(len);\n\n for (let j = 0, i = 0; i < hex.length; i += 2, j++) {\n byteArray[j] = parseInt(hex[i] + hex[i + 1], 16);\n }\n return byteArray;\n }\n}\n","import { Base64 } from './utils/utils';\n\n/**\n * Interfaces of Zax relay command requests and responses.\n * See https://github.com/vault12/zax for details.\n *\n * Full technical specification:\n * https://s3-us-west-1.amazonaws.com/vault12/crypto_relay.pdf\n */\n\n// In the future versions, plugins could add their own commands to specific relays\nexport enum RelayCommand {\n // Zax message commands\n count = 'count',\n upload = 'upload',\n download = 'download',\n messageStatus = 'messageStatus',\n delete = 'delete',\n // Zax file commands\n startFileUpload = 'startFileUpload',\n uploadFileChunk = 'uploadFileChunk',\n downloadFileChunk = 'downloadFileChunk',\n fileStatus = 'fileStatus',\n deleteFile = 'deleteFile'\n}\n\n/**\n * Status of a previously sent message equals redis TTL.\n * See https://redis.io/commands/ttl.\n * -2 : missing key\n * -1 : key never expires\n * 0+ : key time to live in seconds\n */\nexport enum MessageStatusResponse {\n MissingKey = -2,\n NeverExpires = -1\n}\n\n// -------------- Message formats --------------\n\nexport enum ZaxMessageKind {\n message = 'message',\n file = 'file',\n plain = 'plain'\n}\n\n/**\n * Format of raw messages provided by `download` Zax command\n */\nexport interface ZaxRawMessage {\n data: string;\n time: number;\n from: Base64;\n nonce: Base64;\n kind: ZaxMessageKind.message | ZaxMessageKind.file;\n}\n\nexport interface ZaxTextMessage {\n /**\n * Decoded object after JSON.parse\n */\n data: string;\n time: number;\n senderTag: string;\n nonce: Base64;\n kind: ZaxMessageKind.message;\n}\n\nexport interface ZaxFileMessage {\n data: FileUploadMetadata;\n time: number;\n senderTag: string;\n uploadID: string;\n nonce: Base64;\n kind: ZaxMessageKind.file;\n}\n\nexport interface ZaxPlainMessage {\n data: string;\n time: number;\n from: Base64;\n nonce: Base64;\n senderTag: undefined;\n kind: ZaxMessageKind.plain;\n}\n\nexport type ZaxParsedMessage = ZaxPlainMessage | ZaxFileMessage | ZaxTextMessage;\n\n// -------------- File command responses --------------\n// See https://github.com/vault12/zax/wiki/Zax-2.0-File-Commands\n\nexport interface StartFileUploadResponse {\n uploadID: string;\n max_chunk_size: number;\n storage_token: string;\n skey: Uint8Array;\n}\n\nexport interface UploadFileChunkResponse {\n status: 'OK' | 'NOT_FOUND';\n}\n\nexport interface FileStatusResponse {\n status: 'COMPLETE' | 'UPLOADING' | 'START' | 'NOT_FOUND';\n total_chunks: number;\n file_size: number;\n bytes_stored: number;\n}\n\nexport interface DeleteFileResponse {\n status: 'OK' | 'NOT_FOUND';\n}\n\nexport interface FileUploadMetadata {\n name: string;\n orig_size: number;\n md5?: string;\n created?: number;\n modified?: number;\n attrs?: string;\n skey?: Base64;\n}\n","export class GlowNetworkError extends Error {\n readonly name = 'GlowNetworkError';\n\n /**\n * @param status HTTP status code, 0 for network-layer failures, 408 for timeouts\n * @param details relay-provided reason from the `X-Error-Details` response header.\n * Most relay rejections share one generic text on purpose; conditions the relay\n * considers safe to reveal (e.g. a full mailbox or an exceeded storage quota)\n * carry a specific message the caller can branch on.\n * @param retryAfter seconds until the relay accepts requests from this sender\n * again, from the `Retry-After` response header on a 429. Zero means a retry\n * is acceptable immediately. Undefined when the relay doesn't send the header.\n */\n constructor(public status: number|undefined, public details?: string, public retryAfter?: number) {\n super(details ? `GlowNetworkError status: ${status} (${details})` : `GlowNetworkError status: ${status}`);\n }\n}\n","import { NaCl } from '../nacl/nacl';\nimport { NaClDriver, EncryptedMessage } from '../nacl/nacl-driver.interface';\nimport { EncryptionHelper } from '../nacl/encryption.helper';\nimport { config } from '../config';\nimport { Base64, Utils } from '../utils/utils';\nimport { Keys } from '../keys/keys';\nimport { RelayCommand } from '../zax.interface';\nimport { GlowNetworkError } from './network-error';\n\nexport interface RelayConnectionData {\n h2Signature: Uint8Array;\n relayPublicKey: Uint8Array;\n}\n\n/**\n * Low-level operations with Zax relay\n */\nexport class Relay {\n private nacl: NaClDriver;\n private difficulty = 0;\n private publicKey?: Uint8Array;\n private clientToken?: Uint8Array;\n private sessionKeys?: Keys;\n private tokenExpirationTimeoutHandle?: ReturnType<typeof setTimeout>;\n private sessionExpirationTimeoutHandle?: ReturnType<typeof setTimeout>;\n\n constructor(public url: string) {\n this.nacl = NaCl.getInstance();\n }\n\n get isConnected() {\n // relay is assumed to be connected when it got session keys and server public\n // sessionKeys are not permanent so relay won't be connected when sessionKeys will be removed within timeout\n return !!(this.sessionKeys && this.publicKey);\n }\n\n // ---------- Connection initialization ----------\n\n /**\n * Exchanges tokens with a relay and gets a temp session key for this relay.\n * Returns h₂(signature) and a relay public key\n */\n async openConnection(): Promise<RelayConnectionData> {\n this.sessionKeys = new Keys(await this.nacl.crypto_box_keypair());\n this.clientToken = await this.nacl.random_bytes(config.RELAY_TOKEN_LEN);\n const relayToken = await this.fetchRelayToken();\n this.publicKey = await this.fetchRelayPublicKey(relayToken);\n return {\n h2Signature: await this.getSignature(relayToken, this.sessionKeys),\n relayPublicKey: this.publicKey\n };\n }\n\n /**\n * Sends a client token to a relay and saves a relay token\n */\n private async fetchRelayToken(): Promise<Uint8Array> {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n const data = await this.httpCall('start_session', Utils.toBase64(this.clientToken));\n // Set a timer to mark a relay instance as having an expired token after a certain time\n this.scheduleTokenExpiration();\n // Relay responds with its own counter token. Until session is established these 2 tokens are handshake id.\n const [token, difficulty] = this.parseResponse('start_session', data);\n\n this.difficulty = parseInt(difficulty, 10);\n if (this.difficulty > 10) {\n console.log(`[Relay] ${this.url} requested difficulty ${this.difficulty}. Session handshake may take longer.`);\n }\n\n return Utils.fromBase64(token);\n }\n\n /**\n * Completes the handshake and saves a relay pubic key\n */\n private async fetchRelayPublicKey(relayToken: Uint8Array) {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n // After clientToken is sent to the relay, we use only h2() of it\n const h2ClientToken = Utils.toBase64(await this.nacl.h2(this.clientToken));\n\n const handshake = new Uint8Array([...this.clientToken, ...relayToken]);\n let sessionHandshake: Uint8Array;\n\n // Compute session handshake based on difficulty level set by the server\n if (this.difficulty === 0) {\n sessionHandshake = await this.nacl.h2(handshake);\n } else {\n sessionHandshake = await this.ensureNonceDifficulty(handshake);\n }\n\n // We confirm handshake by sending back h2(clientToken, relay_token)\n const relayPk = await this.httpCall('verify_session', h2ClientToken, Utils.toBase64(sessionHandshake));\n // Relay gives us back temp session key masked by clientToken we started with\n return Utils.fromBase64(relayPk);\n }\n\n /**\n * Attaches a mailbox and fetches number of messages\n */\n async prove(payload: EncryptedMessage): Promise<string> {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n if (!this.sessionKeys) {\n throw new Error('[Relay] No session key found, open the connection first');\n }\n const h2ClientToken = Utils.toBase64(await this.nacl.h2(this.clientToken));\n const result = await this.httpCall('prove',\n h2ClientToken, this.sessionKeys.publicKey, payload.nonce, payload.ctext);\n this.scheduleSessionExpiration();\n return result;\n }\n\n async encodeMessage(message: string): Promise<EncryptedMessage> {\n if (!this.publicKey) {\n throw new Error('[Relay] No relay public key found, open the connection first');\n }\n if (!this.sessionKeys) {\n throw new Error('[Relay] No session key found, open the connection first');\n }\n\n return await EncryptionHelper.encodeMessage(\n await this.nacl.encode_utf8(message), this.publicKey, Utils.fromBase64(this.sessionKeys?.privateKey));\n }\n\n async decodeMessage<T>(nonce: Base64, ctext: Base64): Promise<T> {\n const relayPk = this.publicKey;\n if (!relayPk) {\n throw new Error('[Relay] No relay public key found, open the connection first');\n }\n if (!this.sessionKeys) {\n throw new Error('[Relay] No session key found, open the connection first');\n }\n\n const decodedData = await EncryptionHelper.decodeMessage(Utils.fromBase64(nonce), Utils.fromBase64(ctext), relayPk,\n Utils.fromBase64(this.sessionKeys.privateKey));\n if (decodedData === null) {\n throw new Error('[Relay] failed to decode message');\n }\n return JSON.parse(decodedData);\n }\n\n // ---------- Low-level server request handling ----------\n\n /**\n * Executes a message/file command on a relay, parses and validates the response\n */\n async runCmd(command: RelayCommand, hpk: Base64, message: EncryptedMessage, ctext?: string): Promise<string[]> {\n if (!Object.keys(RelayCommand).includes(command)) {\n throw new Error(`[Relay] ${this.url} doesn't support command ${command}`);\n }\n\n const payload = [hpk, message.nonce, message.ctext];\n // if we are sending symmetrically encrypted ctext, it goes on the next line in the payload\n if (ctext) {\n payload.push(ctext);\n }\n\n const response = await this.httpCall('command', ...payload);\n return this.parseResponse(command, response);\n }\n\n private async getSignature(relayToken: Uint8Array, sessionKeys: Keys) {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n const clientTempPk = Utils.fromBase64(sessionKeys.publicKey);\n // Alice creates a 32 byte session signature as h₂(a_temp_pk, relayToken, clientToken)\n const signature = new Uint8Array([...clientTempPk, ...relayToken, ...this.clientToken]);\n const h2Signature = await this.nacl.h2(signature);\n return h2Signature;\n }\n\n /**\n * Executes a call to a relay and return raw string response\n */\n // TODO: extract fetching into separate function\n private async httpCall(command: string, ...params: string[]): Promise<string> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), config.RELAY_AJAX_TIMEOUT);\n\n const requestInit: RequestInit = {\n method: 'POST',\n headers: {\n Accept: 'text/plain',\n 'Content-Type': 'text/plain',\n },\n body: params.join('\\r\\n'),\n signal: controller.signal,\n };\n\n // NOTE: Network and server errors are not handled ny Glow itself.\n // They should instead be handled where the library is used\n try {\n const response = await fetch(`${this.url}/${command}`, requestInit);\n if (!response.ok) {\n if (response.status === 401) {\n // clear session if unauthorized\n this.clearSession();\n this.clearToken();\n }\n // the relay names safe-to-reveal rejection reasons in X-Error-Details\n // and the seconds until a rate-limited sender may retry in Retry-After;\n // reading either cross-origin requires the relay to expose it via CORS\n const retryAfter = response.headers.get('retry-after');\n throw new GlowNetworkError(response.status, response.headers.get('x-error-details') ?? undefined,\n // strict delta-seconds (1*DIGIT): zero is valid (\"retry now\"); the\n // HTTP-date form and any other spelling are ignored\n retryAfter !== null && /^\\d+$/.test(retryAfter) ? parseInt(retryAfter, 10) : undefined);\n }\n return await response.text();\n } catch (err: unknown) {\n if (err instanceof Error && err.name === 'AbortError') {\n // Timeout occurred during fetch request or while downloading response body\n throw new GlowNetworkError(408);\n }\n if (err instanceof GlowNetworkError) {\n throw err;\n }\n if (err instanceof TypeError) {\n // Network layer errors (no internet, DNS failure, CORS, etc.)\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch#exceptions\n // Use status 0 to indicate network connectivity issues\n throw new GlowNetworkError(0);\n }\n // For any other unexpected errors, re-throw the original error\n throw err;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Parses relay response and throws an error if its format is unexpected\n */\n private parseResponse(command: string, rawResponse: string): string[] {\n const response = rawResponse.split('\\r\\n');\n\n if (!rawResponse || !this.validateResponse(command, response.length)) {\n console.log(response);\n throw new Error(`[Relay] ${this.url} - ${command}: Bad response`);\n }\n\n return response;\n }\n\n /**\n * Compares the expected number of lines in response with what was actually received from a relay\n */\n private validateResponse(command: string, lines: number): boolean {\n switch (command) {\n case RelayCommand.upload:\n case RelayCommand.messageStatus:\n case RelayCommand.delete:\n return lines === 1;\n case RelayCommand.downloadFileChunk:\n return lines === 3;\n default:\n return lines === 2;\n }\n }\n\n /**\n * delete token locally before it was removed on server so will reconnect without getting unauthorized error\n */\n private scheduleTokenExpiration() {\n if (this.tokenExpirationTimeoutHandle) {\n clearTimeout(this.tokenExpirationTimeoutHandle);\n }\n this.tokenExpirationTimeoutHandle = setTimeout(() => this.clearToken(), config.RELAY_TOKEN_TIMEOUT);\n }\n\n clearToken() {\n if (this.tokenExpirationTimeoutHandle) {\n clearTimeout(this.tokenExpirationTimeoutHandle);\n }\n if (this.clientToken) {\n delete this.clientToken;\n }\n }\n\n /**\n * established session is valid only for some period need to remove it after it's ttl\n * so relay will reconnect without receiving error from server\n */\n private scheduleSessionExpiration() {\n if (this.sessionExpirationTimeoutHandle) {\n clearTimeout(this.sessionExpirationTimeoutHandle);\n }\n this.sessionExpirationTimeoutHandle = setTimeout(() => {\n this.clearSession();\n }, config.RELAY_SESSION_TIMEOUT);\n }\n\n clearSession() {\n if (this.sessionExpirationTimeoutHandle) {\n clearTimeout(this.sessionExpirationTimeoutHandle);\n }\n if (this.sessionKeys) {\n delete this.sessionKeys;\n }\n }\n\n // ---------- Difficulty adjustment ----------\n\n /**\n * Continuously calculates the nonce until one requested by a relay is found\n */\n private async ensureNonceDifficulty(handshake: Uint8Array) {\n let nonce;\n let h2;\n do {\n nonce = await this.nacl.random_bytes(32);\n h2 = await this.nacl.h2(new Uint8Array([...handshake, ...nonce]));\n } while (!this.arrayZeroBits(h2));\n\n return nonce;\n }\n\n /**\n * Returns `true` if the rightmost n bits of a byte are 0.\n * Check whether the rightmost difficulty bits of an Uint8Array are 0, where\n * the lowest indexes of the array represent those rightmost bits. Thus if\n * the difficulty is 17, then array[0] and array[1] should be 0, as should the\n * rightmost bit of array[2]. This is used for our difficulty settings in Zax to\n * reduce burden on a busy server by ensuring clients have to do some\n * additional work during the session handshake\n */\n private arrayZeroBits(array: Uint8Array): boolean {\n let byte;\n let n = this.difficulty;\n for (let i = 0; i <= (1 + this.difficulty / 8); i++) {\n byte = array[i];\n if (n <= 0) {\n return true;\n }\n if (n > 8) {\n n -= 8;\n if (byte > 0) {\n return false;\n }\n } else {\n return this.firstZeroBits(byte, n);\n }\n }\n return false;\n }\n\n /**\n * Returns `true` if the rightmost n bits of a byte are 0\n */\n private firstZeroBits(byte: number, n: number): boolean {\n return byte === ((byte >> n) << n);\n }\n}\n","import { Relay } from './relay';\n\nexport class RelayFactory {\n\n private relays: { [url: string]: Relay } = {};\n\n /**\n * Relay factory, that returns a Relay instance for a given URL,\n * or creates a new one if it hasn't yet been initialized.\n */\n getInstance(url: string) {\n let relay = this.relays[url];\n if (!relay) {\n relay = this.relays[url] = new Relay(url);\n }\n return relay;\n }\n}","import { NaCl } from '../nacl/nacl';\nimport { NaClDriver, EncryptedMessage } from '../nacl/nacl-driver.interface';\nimport { EncryptionHelper } from '../nacl/encryption.helper';\nimport { KeyRing } from '../keyring/keyring';\nimport { Base64, Utils } from '../utils/utils';\nimport { Relay, RelayConnectionData } from '../relay/relay';\nimport {\n RelayCommand,\n StartFileUploadResponse,\n UploadFileChunkResponse,\n FileStatusResponse,\n DeleteFileResponse,\n MessageStatusResponse,\n FileUploadMetadata,\n ZaxMessageKind,\n ZaxRawMessage,\n ZaxFileMessage,\n ZaxPlainMessage,\n ZaxTextMessage,\n ZaxParsedMessage\n} from '../zax.interface';\nimport { RelayFactory } from '../relay/relay-factory';\nimport { Mutex } from 'async-mutex';\n\n\n/**\n * Mailbox class represents a wrapper around a Keyring that allows to exchange\n * encrypted messages with other Mailboxes via a relay\n */\nexport class Mailbox {\n public keyRing: KeyRing;\n public identity: string;\n /**\n * each mailbox use it's own relays for connection\n * this gives possibility to connect different mailboxes to same server simultaneously\n * because they will use different session keys, tokens, server pub keys\n */\n\n private relayFactory = new RelayFactory;\n\n private relayConnectionMutexes = new Map<string, Mutex>();\n\n private nacl: NaClDriver;\n\n private constructor(naclDriver: NaClDriver, keyRing: KeyRing, identity: string) {\n this.nacl = naclDriver;\n this.keyRing = keyRing;\n this.identity = identity;\n }\n\n static async new(identity: string): Promise<Mailbox> {\n return new Mailbox(NaCl.getInstance(), await KeyRing.new(identity), identity);\n }\n\n // ---------- Alternative initializers ----------\n\n /**\n * Create a Mailbox where the secret identity key is derived from a well-known seed\n */\n static async fromSeed(id: string, seed: Uint8Array | string): Promise<Mailbox> {\n const mbx = await this.new(id);\n if (!(seed instanceof Uint8Array)) {\n seed = Utils.encode_latin1(seed);\n }\n await mbx.keyRing.setCommFromSeed(seed);\n return mbx;\n }\n\n /**\n * Create a Mailbox from the known secret identity key\n */\n static async fromSecKey(id: string, rawSecretKey: Uint8Array | Base64): Promise<Mailbox> {\n const mbx = await this.new(id);\n if (!(rawSecretKey instanceof Uint8Array)) {\n rawSecretKey = Utils.fromBase64(rawSecretKey);\n }\n await mbx.keyRing.setCommFromSecKey(rawSecretKey);\n return mbx;\n }\n\n /**\n * Create a Mailbox from the backup string\n */\n static async fromBackup(identity: string, backup: string): Promise<Mailbox> {\n return new Mailbox(NaCl.getInstance(), await KeyRing.fromBackup(identity, backup), identity);\n }\n\n // ---------- Relay message commands (public API) ----------\n\n /**\n * Sends a free-form object to a guest we already have in our keyring. Set `encrypt` to `false` to\n * send a plaintext message. Returns a token that can be used with `messageStatus` command to check\n * the status of the message\n */\n async upload(url: string, guestKey: string, message: string, encrypt = true): Promise<Base64> {\n const relay = await this.prepareRelay(url);\n const guestPk = this.getGuestKey(guestKey);\n const payload = encrypt ? await this.encodeMessage(guestKey, message) : message;\n const toHpk = Utils.toBase64(await this.nacl.h2(Utils.fromBase64(guestPk)));\n\n const [token] = await this.runRelayCommand(relay, RelayCommand.upload, { to: toHpk, payload });\n return token;\n }\n\n /**\n * Downloads all messages from a relay, decrypts them with a relay key,\n * and then parses each message to find out if it's a text message, file message,\n * or if it can't be decrypted because HPK is missing in the keyring.\n * Returns an array of mixed messages\n */\n async download(url: string) {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.download);\n const messages: ZaxRawMessage[] = await this.decryptResponse<ZaxRawMessage[]>(relay, response);\n\n const parsedMessages: ZaxParsedMessage[] = [];\n for (const message of messages) {\n const senderTag = this.keyRing.getTagByHpk(message.from);\n if (!senderTag) {\n parsedMessages.push(await this.parsePlainMessage(message));\n } else if (message.kind === 'message') {\n parsedMessages.push(await this.parseTextMessage(message, senderTag));\n } else if (message.kind === 'file') {\n parsedMessages.push(await this.parseFileMessage(message, senderTag));\n } else {\n throw new Error('[Mailbox] download - Unknown message type');\n }\n }\n return parsedMessages;\n }\n\n /**\n * Marks a raw Zax message as one that can't be decrypted,\n * because sender's HPK is not found in the keyring\n */\n private async parsePlainMessage({ data, time, from, nonce }: ZaxRawMessage) {\n return { data, time, from, nonce, kind: ZaxMessageKind.plain } as ZaxPlainMessage;\n }\n\n /**\n * Decrypts a message that represents uploaded file metadata\n */\n private async parseFileMessage(message: ZaxRawMessage, senderTag: string) {\n const { nonce, ctext, uploadID } = JSON.parse(message.data);\n const rawData = await this.decodeMessage(senderTag, nonce, ctext);\n if (rawData === null) {\n throw new Error('[Mailbox] Failed to decode file message');\n }\n const data = JSON.parse(rawData) as FileUploadMetadata;\n return { data, time: message.time, senderTag, uploadID, nonce, kind: ZaxMessageKind.file } as ZaxFileMessage;\n }\n\n /**\n * Attempts to decrypt a regular encrypted Zax message. Returns plain message if it was sent encrypted\n */\n private async parseTextMessage(message: ZaxRawMessage, senderTag: string): Promise<ZaxTextMessage> {\n let data = await this.decodeMessage(senderTag, message.nonce, message.data);\n // If the message was sent unencrypted, the line above will return `null`\n if (!data) {\n data = message.data;\n }\n return ({ data, time: message.time, senderTag, nonce: message.nonce, kind: ZaxMessageKind.message });\n }\n\n /**\n * Returns the number of messages in the mailbox on a given relay\n */\n async count(url: string): Promise<number> {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.count);\n return await this.decryptResponse(relay, response);\n }\n\n /**\n * Deletes messages from a relay given a list of base64 message nonces,\n * and returns the number of remaining messages\n */\n async delete(url: string, nonceList: Base64[]): Promise<number> {\n const relay = await this.prepareRelay(url);\n const [response] = await this.runRelayCommand(relay, RelayCommand.delete, { payload: nonceList });\n return parseInt(response, 10);\n }\n\n /**\n * Gets the status of a previously sent Zax message by a storage token.\n * Returns \"time to live\" in seconds or a negative value if it's not applicable.\n * See `MessageStatusResponse` values for reference\n */\n async messageStatus(url: string, storageToken: Base64): Promise<MessageStatusResponse | number> {\n const relay = await this.prepareRelay(url);\n const [response] = await this.runRelayCommand(relay, RelayCommand.messageStatus, { token: storageToken });\n const status = parseInt(response, 10);\n return status;\n }\n\n // ---------- Relay file commands (public API) ----------\n\n /**\n * Asks Zax to start a new upload session, and returns a unique file identifier\n * required to upload file chunks.\n */\n async startFileUpload(url: string, guest: string,\n rawMetadata: FileUploadMetadata): Promise<StartFileUploadResponse> {\n const relay = await this.prepareRelay(url);\n const guestPk = this.getGuestKey(guest);\n const toHpk = Utils.toBase64(await this.nacl.h2(Utils.fromBase64(guestPk)));\n\n const secretKey = await this.nacl.random_bytes(this.nacl.crypto_secretbox_KEYBYTES);\n rawMetadata.skey = Utils.toBase64(secretKey);\n\n const metadata = await this.encodeMessage(guest, JSON.stringify(rawMetadata));\n\n const response = await this.runRelayCommand(relay, RelayCommand.startFileUpload, {\n to: toHpk,\n file_size: rawMetadata.orig_size,\n metadata\n });\n\n const decrypted = await this.decryptResponse<StartFileUploadResponse>(relay, response);\n // append symmetric secret key (unique for this upload session) to the server response\n decrypted.skey = secretKey;\n return decrypted;\n }\n\n /**\n * Encrypts the file chunk symmetrically and transfers it to a relay\n */\n async uploadFileChunk(url: string, uploadID: string, chunk: Uint8Array,\n part: number, totalParts: number, skey: Uint8Array): Promise<UploadFileChunkResponse> {\n const relay = await this.prepareRelay(url);\n const encodedChunk = await EncryptionHelper.encodeMessageSymmetric(chunk, skey);\n const response = await this.runRelayCommand(relay, RelayCommand.uploadFileChunk, {\n uploadID,\n part,\n last_chunk: (totalParts - 1 === part), // marker of the last chunk, sent only once\n nonce: encodedChunk.nonce\n }, encodedChunk.ctext); // do not encode file chunk contents, as it's already encoded with symmetric encryption\n return await this.decryptResponse(relay, response);\n }\n\n /**\n * Returns the status of a file upload by its relay-specific uploadID. Uploader can call it\n * to verify the correct transfer, and downloader can check if the file exists and retrieve\n * the number of chunks\n */\n async getFileStatus(url: string, uploadID: string): Promise<FileStatusResponse> {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.fileStatus, { uploadID });\n return await this.decryptResponse(relay, response);\n }\n\n /**\n * Fetches the file metadata by uploadID, which was declared by the uploader\n */\n async getFileMetadata(url: string, uploadID: string): Promise<FileUploadMetadata> {\n const messages = await this.download(url);\n\n const fileMessage = messages\n .filter(message => message.kind === 'file')\n .find(message => (message as ZaxFileMessage).uploadID === uploadID);\n return fileMessage?.data as FileUploadMetadata;\n }\n\n /**\n * Downloads a binary chunk of a file from a relay by a given uploadID. The total number of chunks\n * can be retrieved via a `getFileStatus` request\n */\n async downloadFileChunk(url: string, uploadID: string, part: number, skey: Uint8Array | Base64):\n Promise<Uint8Array | null> {\n if (!(skey instanceof Uint8Array)) {\n skey = Utils.fromBase64(skey);\n }\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.downloadFileChunk, { uploadID, part });\n const [nonce, ctext, fileCtext] = response;\n const decoded = await relay.decodeMessage<{nonce: string}>(nonce, ctext);\n return await EncryptionHelper.decodeMessageSymmetric(decoded.nonce, fileCtext, skey);\n }\n\n /**\n * Deletes a file from the relay (or all chunks uploaded so far, if the upload was not completed).\n * Can be called by either the sender or recipient\n */\n async deleteFile(url: string, uploadID: string): Promise<DeleteFileResponse> {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.deleteFile, { uploadID });\n return await this.decryptResponse(relay, response);\n }\n\n // ---------- Dealing with Relay ----------\n\n /**\n * Establishes a session, exchanges temp keys and proves our ownership of this\n * Mailbox to this specific relay. This is the first function to start\n * communications with any relay. Returns the number of messages in the mailbox\n */\n async connectToRelay(url: string): Promise<number> {\n const relay = this.relayFactory.getInstance(url);\n const connectionData = await relay.openConnection();\n const encryptedSignature = await this.encryptSignature(connectionData);\n\n const messagesNumber = await relay.prove(await relay.encodeMessage(JSON.stringify({\n pub_key: this.keyRing.getPubCommKey(),\n nonce: encryptedSignature.nonce,\n ctext: encryptedSignature.ctext\n })));\n return parseInt(messagesNumber, 10);\n }\n\n clearSession(url: string) {\n const relay = this.relayFactory.getInstance(url);\n relay.clearToken();\n relay.clearSession();\n }\n\n private async encryptSignature(connection: RelayConnectionData) {\n const privateKey = Utils.fromBase64(this.keyRing.getPrivateCommKey());\n return await EncryptionHelper.encodeMessage(connection.h2Signature, connection.relayPublicKey, privateKey);\n }\n\n /**\n * Gets a singleton Relay instance, and reconnects to a relay if a previous token has expired\n */\n private async prepareRelay(url: string): Promise<Relay> {\n const relay = this.relayFactory.getInstance(url);\n /**\n * allow establishing only once connection for pair mailbox-relay\n */\n await this.getRelayConnectionMutex(url).runExclusive(async () => {\n if (!relay.isConnected) {\n await this.connectToRelay(url);\n }\n });\n return relay;\n }\n\n /**\n * Encrypts the payload of the command and sends it to a relay\n */\n private async runRelayCommand(\n relay: Relay, command: RelayCommand, params?: {[key:string]: unknown}, ctext?: string): Promise<string[]> {\n params = { cmd: command, ...params };\n const hpk = await this.keyRing.getHpk();\n const message = await relay.encodeMessage(JSON.stringify(params));\n return await relay.runCmd(command, hpk, message, ctext);\n }\n\n /**\n * Parses relay's response to a command, for those commands that expect an encrypted message in return.\n * Two lines of POST response will be nonce and ctext\n */\n private async decryptResponse<T>(relay: Relay, response: string[]) {\n const [nonce, ctext] = response;\n return await relay.decodeMessage<T>(nonce, ctext);\n }\n\n // ---------- Message encoding / decoding ----------\n\n /**\n * Encodes `message` to the guest key of a guest already\n * added to the keyring\n */\n async encodeMessage(guest: string, message: string): Promise<EncryptedMessage> {\n const guestPk = this.getGuestKey(guest);\n const privateKey = this.keyRing.getPrivateCommKey();\n\n return await EncryptionHelper.encodeMessage(\n await this.nacl.encode_utf8(message), Utils.fromBase64(guestPk), Utils.fromBase64(privateKey));\n }\n\n /**\n * Decodes a ciphertext from a guest key already in our keyring with this nonce\n * @returns null if failed to decode\n */\n async decodeMessage(guest: string, nonce: Base64, ctext: Base64) {\n const guestPk = this.getGuestKey(guest);\n const privateKey = this.keyRing.getPrivateCommKey();\n let uint8ArrayCtext: Uint8Array;\n try {\n uint8ArrayCtext = Utils.fromBase64(ctext);\n } catch {\n // looks like ctext was not encoded\n return null;\n }\n\n return await EncryptionHelper.decodeMessage(Utils.fromBase64(nonce), uint8ArrayCtext,\n Utils.fromBase64(guestPk), Utils.fromBase64(privateKey));\n }\n\n /**\n * Wrapper around `keyring.getGuestKey` that handles unknown guests\n */\n private getGuestKey(guest: string): string {\n const guestPk = this.keyRing.getGuestKey(guest);\n if (!guestPk) {\n throw new Error(`[Mailbox] Unknown guest ${guest}`);\n }\n return guestPk;\n }\n\n // ---------- Destroying Mailbox ----------\n\n /**\n * Deletes a Mailbox and all its data from local CryptoStorage. This is a very\n * destructive operation, use with caution - it will delete the Mailbox\n * keyring along with all stored public keys. To restore that information, you\n * will need to do another key exchange with all the guests on your keyring.\n */\n async selfDestruct() {\n await this.keyRing.selfDestruct();\n }\n\n private getRelayConnectionMutex(url: string) {\n if (!this.relayConnectionMutexes.has(url)) {\n this.relayConnectionMutexes.set(url, new Mutex());\n }\n return this.relayConnectionMutexes.get(url) as Mutex;\n }\n}\n","import { StorageDriver } from './storage-driver.interface';\n\n/**\n * temporary created in-memory storage for testing purposes\n */\nexport class InMemoryStorage implements StorageDriver {\n\n private storage: {[key: string]: string|null} = {};\n\n async get(key: string) {\n return (await this.getMultiple([key]))[0];\n }\n\n async getMultiple(keys: string[]) {\n return keys.map(key => this.storage[key]);\n }\n\n set<T extends string | null>(key: string, value: T) {\n return this.setMultiple({[key]: value});\n }\n\n async setMultiple<T extends string | null>(values: { [key: string]: T; }){\n for (const key in values) {\n this.storage[key] = values[key];\n }\n }\n\n remove(key: string) {\n return this.removeMultiple([key]);\n }\n\n async removeMultiple(keys: string[]) {\n keys.forEach(key => delete this.storage[key]);\n }\n\n reset() {\n this.storage = {};\n }\n}"],"mappings":"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"}
|
package/dist/glow.es.js
CHANGED
|
@@ -368,8 +368,8 @@ var a = class {
|
|
|
368
368
|
}({}), _ = /* @__PURE__ */ function(e) {
|
|
369
369
|
return e.message = "message", e.file = "file", e.plain = "plain", e;
|
|
370
370
|
}({}), v = class extends Error {
|
|
371
|
-
constructor(e) {
|
|
372
|
-
super(`GlowNetworkError status: ${e}`), this.status = e, this.name = "GlowNetworkError";
|
|
371
|
+
constructor(e, t, n) {
|
|
372
|
+
super(t ? `GlowNetworkError status: ${e} (${t})` : `GlowNetworkError status: ${e}`), this.status = e, this.details = t, this.retryAfter = n, this.name = "GlowNetworkError";
|
|
373
373
|
}
|
|
374
374
|
}, y = class {
|
|
375
375
|
constructor(e) {
|
|
@@ -451,7 +451,11 @@ var a = class {
|
|
|
451
451
|
};
|
|
452
452
|
try {
|
|
453
453
|
let t = await fetch(`${this.url}/${e}`, i);
|
|
454
|
-
if (!t.ok)
|
|
454
|
+
if (!t.ok) {
|
|
455
|
+
t.status === 401 && (this.clearSession(), this.clearToken());
|
|
456
|
+
let e = t.headers.get("retry-after");
|
|
457
|
+
throw new v(t.status, t.headers.get("x-error-details") ?? void 0, e !== null && /^\d+$/.test(e) ? parseInt(e, 10) : void 0);
|
|
458
|
+
}
|
|
455
459
|
return await t.text();
|
|
456
460
|
} catch (e) {
|
|
457
461
|
throw e instanceof Error && e.name === "AbortError" ? new v(408) : e instanceof v ? e : e instanceof TypeError ? new v(0) : e;
|
package/dist/glow.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glow.es.js","names":[],"sources":["../src/utils/utils.ts","../src/nacl/js-nacl-driver.ts","../src/nacl/nacl.ts","../src/keys/keys.ts","../src/config.ts","../src/crypto-storage/local-storage.driver.ts","../src/crypto-storage/crypto-storage.ts","../src/keyring/keyring.ts","../src/nacl/encryption.helper.ts","../src/zax.interface.ts","../src/relay/network-error.ts","../src/relay/relay.ts","../src/relay/relay-factory.ts","../src/mailbox/mailbox.ts","../src/crypto-storage/in-memory-storage.ts"],"sourcesContent":["export type Base64 = string;\n\nexport class Utils {\n static toBase64(input: Uint8Array): Base64 {\n const isBrowser = typeof btoa !== 'undefined';\n return isBrowser ?\n btoa(this.decode_latin1(input)) :\n Buffer.from(this.decode_latin1(input), 'latin1').toString('base64');\n }\n\n static fromBase64(input: Base64): Uint8Array {\n const isBrowser = typeof btoa !== 'undefined';\n return isBrowser ?\n this.encode_latin1(atob(input)) :\n this.encode_latin1(Buffer.from(input, 'base64').toString('latin1'));\n }\n\n static encode_latin1(data: string): Uint8Array {\n const result = new Uint8Array(data.length);\n for (let i = 0; i < data.length; i++) {\n const c = data.charCodeAt(i);\n if ((c & 0xff) !== c) throw { message: 'Cannot encode string in Latin1', str: data };\n result[i] = (c & 0xff);\n }\n return result;\n }\n\n static decode_latin1(data: Uint8Array): string {\n const encoded = [];\n for (let i = 0; i < data.length; i++) {\n encoded.push(String.fromCharCode(data[i]));\n }\n return encoded.join('');\n }\n\n static toObject(pairs: IterableIterator<[string, unknown]>) {\n return Array.from(pairs).reduce(\n (acc, [key, value]) => Object.assign(acc, { [key]: value }),\n {},\n );\n }\n}\n","import { box, BoxKeyPair, randomBytes, secretbox, hash } from 'tweetnacl';\n\nimport { NaClDriver } from './nacl-driver.interface';\nimport { Keypair } from './keypair.interface';\nimport { Utils } from '../utils/utils';\n\n/**\n * Implementation based on TweetNaCl.js: {@link https://github.com/dchest/tweetnacl-js}.\n * SHA256 is implemented using Web Crypto API (SubtleCrypto).\n *\n * All the methods of this class are actually synchronous, but they are defined\n * as async to conform to the NaClDriver interface, which may have other implementations.\n */\nexport class JsNaClDriver implements NaClDriver {\n crypto_secretbox_KEYBYTES = secretbox.keyLength;\n\n async crypto_secretbox_random_nonce(): Promise<Uint8Array> {\n return randomBytes(secretbox.nonceLength);\n }\n\n async crypto_secretbox(message: Uint8Array, nonce: Uint8Array, key: Uint8Array): Promise<Uint8Array> {\n return secretbox(message, nonce, key);\n }\n\n async crypto_secretbox_open(box: Uint8Array, nonce: Uint8Array, key: Uint8Array): Promise<Uint8Array | null> {\n return secretbox.open(box, nonce, key);\n }\n\n crypto_box_NONCEBYTES = box.nonceLength;\n\n async crypto_box(message: Uint8Array, nonce: Uint8Array, pk: Uint8Array, sk: Uint8Array): Promise<Uint8Array> {\n return box(message, nonce, pk, sk);\n }\n\n async crypto_box_open(\n cipher: Uint8Array, nonce: Uint8Array, pk: Uint8Array, sk: Uint8Array): Promise<Uint8Array | null> {\n return box.open(cipher, nonce, pk, sk);\n }\n\n async crypto_box_random_nonce(): Promise<Uint8Array> {\n return randomBytes(box.nonceLength);\n }\n\n async crypto_box_keypair(): Promise<Keypair> {\n const pair: BoxKeyPair = box.keyPair();\n return {\n boxPk: pair.publicKey,\n boxSk: pair.secretKey\n };\n }\n\n async crypto_box_keypair_from_raw_sk(key: Uint8Array): Promise<Keypair> {\n const pair: BoxKeyPair = box.keyPair.fromSecretKey(key);\n return {\n boxPk: pair.publicKey,\n boxSk: pair.secretKey\n };\n }\n\n async crypto_box_keypair_from_seed(seed: Uint8Array): Promise<Keypair> {\n // using SHA512 to hash, per original NaCl docs\n return this.crypto_box_keypair_from_raw_sk(hash(seed).subarray(0, box.secretKeyLength));\n }\n\n async crypto_hash_sha256(data: Uint8Array): Promise<Uint8Array> {\n // In Node.js, globalThis.crypto is implementation of WebCrypto API, available since Node.js 16\n // In browsers, globalThis.crypto is the standard Web Crypto API\n const cryptoProvider = globalThis.crypto;\n if (!cryptoProvider || !cryptoProvider.subtle) {\n throw new Error('Web Crypto API is not available. Please use a modern browser or Node.js 16+.');\n }\n\n const hashBuffer = await cryptoProvider.subtle.digest('SHA-256', data as BufferSource);\n return new Uint8Array(hashBuffer);\n }\n\n async random_bytes(size: number): Promise<Uint8Array> {\n return randomBytes(size);\n }\n\n // Helper methods are based on `js-nacl` implementations\n // https://github.com/tonyg/js-nacl/blob/cc70775cfc9d68a04905ca65c7f179b33a18066e/nacl_cooked.js\n\n async encode_latin1(data: string): Promise<Uint8Array> {\n return Utils.encode_latin1(data);\n }\n\n async decode_latin1(data: Uint8Array): Promise<string> {\n return Utils.decode_latin1(data);\n }\n\n async encode_utf8(data: string): Promise<Uint8Array> {\n return this.encode_latin1(unescape(encodeURIComponent(data)));\n }\n\n async decode_utf8(data: Uint8Array): Promise<string> {\n return decodeURIComponent(escape(await this.decode_latin1(data)));\n }\n\n async to_hex(data: Uint8Array): Promise<string> {\n const encoded = [];\n for (let i = 0; i < data.length; i++) {\n const hexAlphabet = '0123456789abcdef';\n encoded.push(hexAlphabet[(data[i] >> 4) & 15]);\n encoded.push(hexAlphabet[data[i] & 15]);\n }\n return encoded.join('');\n }\n\n async from_hex(data: string): Promise<Uint8Array> {\n const result = new Uint8Array(data.length / 2);\n for (let i = 0; i < data.length / 2; i++) {\n result[i] = parseInt(data.substr(2 * i, 2), 16);\n }\n return result;\n }\n\n /**\n * h2(m) = sha256(sha256(64x0 + m))\n * Zero out initial sha256 block, and double hash 0-padded message\n * http://cs.nyu.edu/~dodis/ps/h-of-h.pdf\n */\n async h2(data: string | Uint8Array): Promise<Uint8Array> {\n if (!(data instanceof Uint8Array)) {\n data = await this.encode_latin1(data);\n }\n\n const zeroPaddingLength = 64;\n const extendedSource = new Uint8Array(zeroPaddingLength + data.length);\n extendedSource.fill(0);\n extendedSource.set(data, zeroPaddingLength);\n return this.crypto_hash_sha256(await this.crypto_hash_sha256(extendedSource));\n }\n}\n","import { NaClDriver } from './nacl-driver.interface';\nimport { JsNaClDriver } from './js-nacl-driver';\n\n/**\n * Facade singleton to access the NaCl driver.\n * Usage: call instance() with a chosen driver, and store the received object to perform further actions.\n */\nexport class NaCl {\n private static driverInstance?: NaClDriver;\n\n private constructor() { }\n\n public static setDefaultInstance() {\n return this.setInstance(new JsNaClDriver());\n }\n\n public static setInstance(driver: NaClDriver): boolean {\n if (this.driverInstance) {\n throw new Error('[NaCl] NaCl driver has been already set, it is supposed to be set only once');\n }\n this.driverInstance = driver;\n\n return true;\n }\n\n public static getInstance(): NaClDriver {\n if (!this.driverInstance) {\n throw new Error('[NaCl] NaCl instance is not yet set');\n }\n\n return this.driverInstance;\n }\n}\n","import { Base64, Utils } from '../utils/utils';\nimport { Keypair } from '../nacl/keypair.interface';\n\n/**\n * A simple wrapper around a public/private keys pair.\n * Stores keys in base64 format.\n */\nexport class Keys {\n private keyPair: Keypair;\n\n constructor(public keys: string | Keypair) {\n if (typeof keys === 'string') {\n const { boxPk, boxSk } = JSON.parse(keys);\n this.keyPair = {\n boxPk: Utils.fromBase64(boxPk),\n boxSk: Utils.fromBase64(boxSk)\n };\n } else {\n this.keyPair = keys;\n }\n }\n\n static isEqual(keys1: Keys, keys2: Keys): boolean {\n return keys1.toString() === keys2.toString();\n }\n\n toString(): string {\n return JSON.stringify({\n boxPk: this.publicKey,\n boxSk: this.privateKey\n });\n }\n\n get publicKey(): Base64 {\n return Utils.toBase64(this.keyPair.boxPk);\n }\n\n get privateKey(): Base64 {\n return Utils.toBase64(this.keyPair.boxSk);\n }\n}\n","const sessionTimeoutBuffer = 30 * 1000;\n\nexport const config = {\n NONCE_TAG: '__nc',\n SKEY_TAG: 'storage_key',\n STORAGE_ROOT: '.v1.stor.vlt12',\n // Relay tokens, keys and hashes are 32 bytes\n RELAY_TOKEN_LEN: 32,\n // 5 min - Token expiration on the server side, matched with config.x.relay.token_timeout on Zax server\n RELAY_TOKEN_TIMEOUT: 5 * 60 * 1000 - sessionTimeoutBuffer,\n // 20 min - Session expiration on the server side, matched with config.x.relay.session_timeout on Zax server\n RELAY_SESSION_TIMEOUT: 20 * 60 * 1000 - sessionTimeoutBuffer,\n // 5 sec - Ajax request timeout\n RELAY_AJAX_TIMEOUT: 5 * 1000\n};\n","import { StorageDriver } from './storage-driver.interface';\n\nexport class LocalStorageDriver implements StorageDriver {\n private rootTag: string;\n\n constructor(root = 'storage.') {\n this.rootTag = `__glow.${root}`;\n }\n\n async get(key: string): Promise<string | null> {\n return (await this.getMultiple([key]))[0];\n }\n\n async getMultiple(keys: string[]): Promise<(string | null)[]> {\n return keys.map(key => localStorage.getItem(this.tag(key)) || null);\n }\n\n async set(key: string, value: string | null): Promise<void> {\n return this.setMultiple({ [key]: value });\n }\n\n async setMultiple(values: { [key: string]: string | null; }): Promise<void> {\n for(const key in values) {\n const value = values[key];\n if (value !== null) {\n localStorage.setItem(this.tag(key), value);\n }\n }\n }\n\n async remove(key: string): Promise<void> {\n this.removeMultiple([key]);\n }\n\n async removeMultiple(keys: string[]): Promise<void> {\n keys.forEach(key => localStorage.removeItem(this.tag(key)));\n }\n\n private tag(key: string) {\n return `${this.rootTag}.${key}`;\n }\n}\n","import { NaCl } from '../nacl/nacl';\nimport { Utils } from '../utils/utils';\nimport { config } from '../config';\nimport { StorageDriver } from './storage-driver.interface';\nimport { NaClDriver } from '../nacl/nacl-driver.interface';\nimport { LocalStorageDriver } from './local-storage.driver';\n\n// TODO: add bulk operations to set(), get() and remove() pairs of values simultaneously\n\n/* CryptoStorage is a handy wrapper around any storage that provides JavaScript interface,\n that allows to store symmetrically encrypted serializable Javascript objects and primitives. */\nexport class CryptoStorage {\n\n private static storageDriver: StorageDriver;\n\n private driver?: StorageDriver;\n private id?: string;\n private storageKey?: Uint8Array;\n private nacl: NaClDriver;\n\n private constructor(storageDriver: StorageDriver, id: string) {\n const nacl = NaCl.getInstance();\n this.nacl = nacl;\n this.driver = storageDriver;\n this.id = id ? `.${id}${config.STORAGE_ROOT}` : config.STORAGE_ROOT;\n }\n\n static async new(id: string): Promise<CryptoStorage> {\n const storageDriver = this.getStorageDriver();\n const storage = new CryptoStorage(storageDriver, id);\n const prefixedStorageKeyTag = storage.addPrefix(config.SKEY_TAG);\n const storageKey = await storageDriver.get(prefixedStorageKeyTag);\n // Either load or generate a storage key\n if (storageKey) {\n const { key } = JSON.parse(storageKey);\n storage.storageKey = Utils.fromBase64(key);\n } else {\n storage.storageKey = await storage.nacl.random_bytes(storage.nacl.crypto_secretbox_KEYBYTES);\n await storageDriver.set(prefixedStorageKeyTag, JSON.stringify({ key: Utils.toBase64(storage.storageKey)}));\n }\n return storage;\n }\n\n static getStorageDriver() {\n if (!this.storageDriver) {\n throw new Error('[CryptoStorage] StorageDriver instance is not yet set');\n } else {\n return this.storageDriver;\n }\n }\n\n static setDefaultStorageDriver() {\n return this.setStorageDriver(new LocalStorageDriver());\n }\n\n static setStorageDriver(driver: StorageDriver) {\n if (this.storageDriver) {\n throw new Error('[CryptoStorage] StorageDriver has been already set, it is supposed to be set only once');\n }\n this.storageDriver = driver;\n return true;\n }\n\n async save(tag: string, data: unknown): Promise<boolean> {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n if (!this.storageKey) {\n throw new Error('[CryptoStorage] Storage key is not set');\n }\n // Convert the data to JSON, then convert that string to a byte array\n const input = JSON.stringify(data);\n const encoded = await this.nacl.encode_utf8(input);\n // For each item in the store we also generate and save its own nonce\n const nonce = await this.nacl.crypto_secretbox_random_nonce();\n const cipherText = await this.nacl.crypto_secretbox(encoded, nonce, this.storageKey);\n // Save the cipher text and nonce\n await this.driver.setMultiple({\n [this.addPrefix(tag)]: Utils.toBase64(cipherText),\n [this.addNonceTag(tag)]: Utils.toBase64(nonce)\n });\n return true;\n }\n\n async get<T>(tag: string): Promise<T | null> {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n if (!this.storageKey) {\n throw new Error('[CryptoStorage] Storage key is not set');\n }\n // Get cipher text and nonce from the storage\n const [data, nonce] = await this.driver.getMultiple([this.addPrefix(tag), this.addNonceTag(tag)]);\n // Nothing to do without cipher text or nonce\n if (!data || !nonce) {\n return null;\n }\n const dataBinary = Utils.fromBase64(data);\n const nonceBinary = Utils.fromBase64(nonce);\n const source = await this.nacl.crypto_secretbox_open(dataBinary, nonceBinary, this.storageKey);\n if (source) {\n const decoded = await this.nacl.decode_utf8(source);\n return JSON.parse(decoded) as T;\n } else {\n throw new Error('[CryptoStorage] crypto_secretbox_open: decryption error');\n }\n }\n\n async remove(tag: string): Promise<boolean> {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n await this.driver.removeMultiple([this.addPrefix(tag), this.addNonceTag(tag)]);\n return true;\n }\n\n async selfDestruct() {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n await this.driver.remove(this.addPrefix(config.SKEY_TAG));\n }\n\n // Keys are tagged in the storage with a versioned prefix\n private addPrefix(key: string): string {\n return this.id ? (key + this.id) : key;\n }\n\n private addNonceTag(tag: string): string {\n return this.addPrefix(`${config.NONCE_TAG}.${tag}`);\n }\n}\n","import { CryptoStorage } from '../crypto-storage/crypto-storage';\nimport { Keys } from '../keys/keys';\nimport { NaCl } from '../nacl/nacl';\nimport { Utils, Base64 } from '../utils/utils';\nimport { NaClDriver } from '../nacl/nacl-driver.interface';\n\ninterface KeyRecord {\n pk: Base64;\n hpk: Base64;\n}\n\nconst commKeyTag = '__::commKey::__';\n\ninterface KeyRingBackup {\n [commKeyTag]: string;\n [key: string]: string | null | undefined\n}\n\n// Manages the set of public keys of counterparties\nexport class KeyRing {\n static readonly commKeyTag = 'comm_key';\n static readonly guestRegistryTag = 'guest_registry';\n\n private commKey: Keys;\n private storage: CryptoStorage;\n private guestKeys: Map<string, KeyRecord> = new Map();\n private nacl: NaClDriver;\n\n private constructor(naclDriver: NaClDriver, cryptoStorage: CryptoStorage, commKey: Keys) {\n this.nacl = naclDriver;\n this.storage = cryptoStorage;\n this.commKey = commKey;\n }\n\n static async new(id: string): Promise<KeyRing> {\n const nacl = NaCl.getInstance();\n const cryptoStorage = await CryptoStorage.new(id);\n const commKey = await KeyRing.getCommKeyOrCreate(nacl, cryptoStorage);\n const keyRing = new KeyRing(nacl, cryptoStorage, commKey);\n\n await keyRing.loadGuestKeys();\n return keyRing;\n }\n\n static async fromBackup(id: string, backupString: string): Promise<KeyRing> {\n const backup: KeyRingBackup = JSON.parse(backupString);\n const secretKey = Utils.fromBase64(backup[commKeyTag]);\n const restoredKeyRing = await KeyRing.new(id);\n await restoredKeyRing.setCommFromSecKey(secretKey);\n for (const [key, value] of Object.entries(backup)) {\n if (key !== commKeyTag) {\n await restoredKeyRing.addGuest(key, value as string);\n }\n }\n return restoredKeyRing;\n }\n\n getNumberOfGuests(): number {\n return this.guestKeys.size;\n }\n\n get guests(): Map<string, KeyRecord> {\n return this.guestKeys;\n }\n\n /**\n * Returns public identity, which is the default communication key.\n * Correspondents can know it, whereas Relays do not need it (other than\n * temporarily for internal use during the ownership proof)\n */\n getPubCommKey(): string {\n return this.commKey.publicKey;\n }\n\n getPrivateCommKey(): string {\n return this.commKey.privateKey;\n }\n\n /**\n * Returns HPK (hash of the public key) of the mailbox. This is what Zax relays\n * uses as the universal address of the mailbox\n */\n async getHpk(): Promise<Base64> {\n const hpk = await this.nacl.h2(Utils.fromBase64(this.commKey.publicKey));\n return Utils.toBase64(hpk);\n }\n\n getTagByHpk(hpk: string): string | null {\n for (const [key, value] of this.guestKeys) {\n if (value.hpk === hpk) {\n return key;\n }\n }\n return null;\n }\n\n getGuestKey(guestTag: string): Base64 | null {\n const keyRecord = this.guestKeys.get(guestTag);\n if (keyRecord) {\n return keyRecord.pk;\n }\n return null;\n }\n\n // Backups\n\n async backup(): Promise<string> {\n const backup: KeyRingBackup = {\n [commKeyTag]: this.commKey.privateKey\n };\n\n if (this.getNumberOfGuests() > 0) {\n for (const [key, value] of this.guestKeys) {\n if (key && value) {\n backup[key] = value.pk;\n }\n }\n }\n return JSON.stringify(backup);\n }\n\n async setCommFromSeed(seed: Uint8Array): Promise<void> {\n this.commKey = new Keys(await this.nacl.crypto_box_keypair_from_seed(seed));\n await this.storage.save(KeyRing.commKeyTag, this.commKey.toString());\n }\n\n async setCommFromSecKey(rawSecretKey: Uint8Array): Promise<void> {\n this.commKey = new Keys(await this.nacl.crypto_box_keypair_from_raw_sk(rawSecretKey));\n await this.storage.save(KeyRing.commKeyTag, this.commKey.toString());\n }\n\n async addGuest(guestTag: string, publicKey: Base64): Promise<string> {\n const b64_h2 = Utils.toBase64(await this.nacl.h2(Utils.fromBase64(publicKey)));\n this.guestKeys.set(guestTag, {\n pk: publicKey,\n hpk: b64_h2\n });\n await this.saveGuests();\n return b64_h2;\n }\n\n async removeGuest(guestTag: string): Promise<boolean> {\n if (this.guestKeys.has(guestTag)) {\n this.guestKeys.delete(guestTag);\n await this.saveGuests();\n }\n return true;\n }\n\n private async loadGuestKeys() {\n const guestKeys = await this.storage.get<{[key:string]: KeyRecord}>(KeyRing.guestRegistryTag);\n if (!guestKeys) {\n return;\n } else if (typeof guestKeys === 'object') {\n this.guestKeys = new Map(Object.entries(guestKeys));\n } else {\n throw new Error('[Keyring] Guest keys is not an object');\n }\n }\n\n private async saveGuests() {\n await this.storage.save(KeyRing.guestRegistryTag, Utils.toObject(this.guestKeys.entries()));\n }\n\n private static async getCommKeyOrCreate(nacl: NaClDriver, storage: CryptoStorage): Promise<Keys> {\n const commKeyRecord = await storage.get(KeyRing.commKeyTag);\n if (commKeyRecord && typeof commKeyRecord === 'string') {\n return new Keys(commKeyRecord);\n } else {\n const keypair = await nacl.crypto_box_keypair();\n const commKey = new Keys(keypair);\n await storage.save(KeyRing.commKeyTag, commKey.toString());\n return commKey;\n }\n }\n\n async selfDestruct() {\n await this.storage.remove(KeyRing.guestRegistryTag);\n await this.storage.remove(KeyRing.commKeyTag);\n await this.storage.selfDestruct();\n }\n}\n","import { NaCl } from './nacl';\nimport { EncryptedMessage } from './nacl-driver.interface';\nimport { Utils, Base64 } from '../utils/utils';\n\n/**\n * Includes several static helpers that are build on top of basic NaCl driver methods,\n * to avoid duplicating code in driver implementations.\n */\nexport class EncryptionHelper {\n\n private constructor() { }\n\n /**\n * Encodes a binary message with `cryptobox`\n */\n static async encodeMessage(message: Uint8Array, pkTo: Uint8Array, skFrom: Uint8Array, nonceData?: number) {\n const nacl = NaCl.getInstance();\n\n const nonce = await this.makeNonce(nonceData);\n const ctext = await nacl.crypto_box(message, nonce, pkTo, skFrom);\n return {\n nonce: Utils.toBase64(nonce),\n ctext: Utils.toBase64(ctext)\n } as EncryptedMessage;\n }\n\n /**\n * Decodes a binary message with `cryptobox_open`\n * @returns null if failed to decode\n */\n static async decodeMessage(nonce: Uint8Array, ctext: Uint8Array, pkFrom: Uint8Array, skTo: Uint8Array) {\n const nacl = NaCl.getInstance();\n const data = await nacl.crypto_box_open(ctext, nonce, pkFrom, skTo);\n if (data) {\n return await nacl.decode_utf8(data);\n }\n\n return data;\n }\n\n /**\n * Encodes a binary message with `secretbox` (used for file chunk encryption)\n */\n static async encodeMessageSymmetric(message: Uint8Array, secretKey: Uint8Array): Promise<EncryptedMessage> {\n const nacl = NaCl.getInstance();\n const nonce = await EncryptionHelper.makeNonce();\n const ctext = await nacl.crypto_secretbox(message, nonce, secretKey);\n return {\n nonce: Utils.toBase64(nonce),\n ctext: Utils.toBase64(ctext)\n };\n }\n\n /**\n * Decodes a binary message with `secretbox_open` (used for file chunk decryption)\n */\n static async decodeMessageSymmetric(nonce: Base64, ctext: Base64, secretKey: Uint8Array): Promise<Uint8Array | null> {\n const nacl = NaCl.getInstance();\n return await nacl.crypto_secretbox_open(Utils.fromBase64(ctext), Utils.fromBase64(nonce), secretKey);\n }\n\n // ---------- Nonce helper ----------\n\n /**\n * Makes a timestamp nonce that a relay expects for any crypto operations.\n * Timestamp is the first 8 bytes, the rest is random, unless custom `data`\n * is specified. `data` will be packed as next 4 bytes after timestamp.\n */\n private static async makeNonce(data?: number): Promise<Uint8Array> {\n const nacl = NaCl.getInstance();\n const nonce = await nacl.crypto_box_random_nonce();\n let headerLen;\n if (nonce.length !== nacl.crypto_box_NONCEBYTES) {\n throw new Error('[Mailbox] Wrong crypto_box nonce length');\n }\n // split timestamp integer as an array of bytes\n headerLen = 8; // max timestamp size\n const aTime = this.itoa(Math.floor(Date.now() / 1000));\n\n if (data) {\n headerLen += 4; // extra 4 bytes for custom data\n }\n\n // zero out nonce header area\n nonce.fill(0, 0, headerLen);\n // copy the timestamp into the first 8 bytes of nonce\n nonce.set(aTime, 8 - aTime.length);\n // copy data if present\n if (data) {\n const aData = this.itoa(data);\n nonce.set(aData, 12 - aData.length);\n }\n return nonce;\n }\n\n /**\n * Splits an integer into an array of bytes\n */\n private static itoa(num: number): Uint8Array {\n // calculate length first\n let hex = num.toString(16);\n hex = hex.length & 1 ? `0${hex}` : hex;\n const len = hex.length / 2;\n const byteArray = new Uint8Array(len);\n\n for (let j = 0, i = 0; i < hex.length; i += 2, j++) {\n byteArray[j] = parseInt(hex[i] + hex[i + 1], 16);\n }\n return byteArray;\n }\n}\n","import { Base64 } from './utils/utils';\n\n/**\n * Interfaces of Zax relay command requests and responses.\n * See https://github.com/vault12/zax for details.\n *\n * Full technical specification:\n * https://s3-us-west-1.amazonaws.com/vault12/crypto_relay.pdf\n */\n\n// In the future versions, plugins could add their own commands to specific relays\nexport enum RelayCommand {\n // Zax message commands\n count = 'count',\n upload = 'upload',\n download = 'download',\n messageStatus = 'messageStatus',\n delete = 'delete',\n // Zax file commands\n startFileUpload = 'startFileUpload',\n uploadFileChunk = 'uploadFileChunk',\n downloadFileChunk = 'downloadFileChunk',\n fileStatus = 'fileStatus',\n deleteFile = 'deleteFile'\n}\n\n/**\n * Status of a previously sent message equals redis TTL.\n * See https://redis.io/commands/ttl.\n * -2 : missing key\n * -1 : key never expires\n * 0+ : key time to live in seconds\n */\nexport enum MessageStatusResponse {\n MissingKey = -2,\n NeverExpires = -1\n}\n\n// -------------- Message formats --------------\n\nexport enum ZaxMessageKind {\n message = 'message',\n file = 'file',\n plain = 'plain'\n}\n\n/**\n * Format of raw messages provided by `download` Zax command\n */\nexport interface ZaxRawMessage {\n data: string;\n time: number;\n from: Base64;\n nonce: Base64;\n kind: ZaxMessageKind.message | ZaxMessageKind.file;\n}\n\nexport interface ZaxTextMessage {\n /**\n * Decoded object after JSON.parse\n */\n data: string;\n time: number;\n senderTag: string;\n nonce: Base64;\n kind: ZaxMessageKind.message;\n}\n\nexport interface ZaxFileMessage {\n data: FileUploadMetadata;\n time: number;\n senderTag: string;\n uploadID: string;\n nonce: Base64;\n kind: ZaxMessageKind.file;\n}\n\nexport interface ZaxPlainMessage {\n data: string;\n time: number;\n from: Base64;\n nonce: Base64;\n senderTag: undefined;\n kind: ZaxMessageKind.plain;\n}\n\nexport type ZaxParsedMessage = ZaxPlainMessage | ZaxFileMessage | ZaxTextMessage;\n\n// -------------- File command responses --------------\n// See https://github.com/vault12/zax/wiki/Zax-2.0-File-Commands\n\nexport interface StartFileUploadResponse {\n uploadID: string;\n max_chunk_size: number;\n storage_token: string;\n skey: Uint8Array;\n}\n\nexport interface UploadFileChunkResponse {\n status: 'OK' | 'NOT_FOUND';\n}\n\nexport interface FileStatusResponse {\n status: 'COMPLETE' | 'UPLOADING' | 'START' | 'NOT_FOUND';\n total_chunks: number;\n file_size: number;\n bytes_stored: number;\n}\n\nexport interface DeleteFileResponse {\n status: 'OK' | 'NOT_FOUND';\n}\n\nexport interface FileUploadMetadata {\n name: string;\n orig_size: number;\n md5?: string;\n created?: number;\n modified?: number;\n attrs?: string;\n skey?: Base64;\n}\n","export class GlowNetworkError extends Error {\n readonly name = 'GlowNetworkError';\n\n constructor(public status: number|undefined) {\n super(`GlowNetworkError status: ${status}`);\n }\n}","import { NaCl } from '../nacl/nacl';\nimport { NaClDriver, EncryptedMessage } from '../nacl/nacl-driver.interface';\nimport { EncryptionHelper } from '../nacl/encryption.helper';\nimport { config } from '../config';\nimport { Base64, Utils } from '../utils/utils';\nimport { Keys } from '../keys/keys';\nimport { RelayCommand } from '../zax.interface';\nimport { GlowNetworkError } from './network-error';\n\nexport interface RelayConnectionData {\n h2Signature: Uint8Array;\n relayPublicKey: Uint8Array;\n}\n\n/**\n * Low-level operations with Zax relay\n */\nexport class Relay {\n private nacl: NaClDriver;\n private difficulty = 0;\n private publicKey?: Uint8Array;\n private clientToken?: Uint8Array;\n private sessionKeys?: Keys;\n private tokenExpirationTimeoutHandle?: ReturnType<typeof setTimeout>;\n private sessionExpirationTimeoutHandle?: ReturnType<typeof setTimeout>;\n\n constructor(public url: string) {\n this.nacl = NaCl.getInstance();\n }\n\n get isConnected() {\n // relay is assumed to be connected when it got session keys and server public\n // sessionKeys are not permanent so relay won't be connected when sessionKeys will be removed within timeout\n return !!(this.sessionKeys && this.publicKey);\n }\n\n // ---------- Connection initialization ----------\n\n /**\n * Exchanges tokens with a relay and gets a temp session key for this relay.\n * Returns h₂(signature) and a relay public key\n */\n async openConnection(): Promise<RelayConnectionData> {\n this.sessionKeys = new Keys(await this.nacl.crypto_box_keypair());\n this.clientToken = await this.nacl.random_bytes(config.RELAY_TOKEN_LEN);\n const relayToken = await this.fetchRelayToken();\n this.publicKey = await this.fetchRelayPublicKey(relayToken);\n return {\n h2Signature: await this.getSignature(relayToken, this.sessionKeys),\n relayPublicKey: this.publicKey\n };\n }\n\n /**\n * Sends a client token to a relay and saves a relay token\n */\n private async fetchRelayToken(): Promise<Uint8Array> {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n const data = await this.httpCall('start_session', Utils.toBase64(this.clientToken));\n // Set a timer to mark a relay instance as having an expired token after a certain time\n this.scheduleTokenExpiration();\n // Relay responds with its own counter token. Until session is established these 2 tokens are handshake id.\n const [token, difficulty] = this.parseResponse('start_session', data);\n\n this.difficulty = parseInt(difficulty, 10);\n if (this.difficulty > 10) {\n console.log(`[Relay] ${this.url} requested difficulty ${this.difficulty}. Session handshake may take longer.`);\n }\n\n return Utils.fromBase64(token);\n }\n\n /**\n * Completes the handshake and saves a relay pubic key\n */\n private async fetchRelayPublicKey(relayToken: Uint8Array) {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n // After clientToken is sent to the relay, we use only h2() of it\n const h2ClientToken = Utils.toBase64(await this.nacl.h2(this.clientToken));\n\n const handshake = new Uint8Array([...this.clientToken, ...relayToken]);\n let sessionHandshake: Uint8Array;\n\n // Compute session handshake based on difficulty level set by the server\n if (this.difficulty === 0) {\n sessionHandshake = await this.nacl.h2(handshake);\n } else {\n sessionHandshake = await this.ensureNonceDifficulty(handshake);\n }\n\n // We confirm handshake by sending back h2(clientToken, relay_token)\n const relayPk = await this.httpCall('verify_session', h2ClientToken, Utils.toBase64(sessionHandshake));\n // Relay gives us back temp session key masked by clientToken we started with\n return Utils.fromBase64(relayPk);\n }\n\n /**\n * Attaches a mailbox and fetches number of messages\n */\n async prove(payload: EncryptedMessage): Promise<string> {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n if (!this.sessionKeys) {\n throw new Error('[Relay] No session key found, open the connection first');\n }\n const h2ClientToken = Utils.toBase64(await this.nacl.h2(this.clientToken));\n const result = await this.httpCall('prove',\n h2ClientToken, this.sessionKeys.publicKey, payload.nonce, payload.ctext);\n this.scheduleSessionExpiration();\n return result;\n }\n\n async encodeMessage(message: string): Promise<EncryptedMessage> {\n if (!this.publicKey) {\n throw new Error('[Relay] No relay public key found, open the connection first');\n }\n if (!this.sessionKeys) {\n throw new Error('[Relay] No session key found, open the connection first');\n }\n\n return await EncryptionHelper.encodeMessage(\n await this.nacl.encode_utf8(message), this.publicKey, Utils.fromBase64(this.sessionKeys?.privateKey));\n }\n\n async decodeMessage<T>(nonce: Base64, ctext: Base64): Promise<T> {\n const relayPk = this.publicKey;\n if (!relayPk) {\n throw new Error('[Relay] No relay public key found, open the connection first');\n }\n if (!this.sessionKeys) {\n throw new Error('[Relay] No session key found, open the connection first');\n }\n\n const decodedData = await EncryptionHelper.decodeMessage(Utils.fromBase64(nonce), Utils.fromBase64(ctext), relayPk,\n Utils.fromBase64(this.sessionKeys.privateKey));\n if (decodedData === null) {\n throw new Error('[Relay] failed to decode message');\n }\n return JSON.parse(decodedData);\n }\n\n // ---------- Low-level server request handling ----------\n\n /**\n * Executes a message/file command on a relay, parses and validates the response\n */\n async runCmd(command: RelayCommand, hpk: Base64, message: EncryptedMessage, ctext?: string): Promise<string[]> {\n if (!Object.keys(RelayCommand).includes(command)) {\n throw new Error(`[Relay] ${this.url} doesn't support command ${command}`);\n }\n\n const payload = [hpk, message.nonce, message.ctext];\n // if we are sending symmetrically encrypted ctext, it goes on the next line in the payload\n if (ctext) {\n payload.push(ctext);\n }\n\n const response = await this.httpCall('command', ...payload);\n return this.parseResponse(command, response);\n }\n\n private async getSignature(relayToken: Uint8Array, sessionKeys: Keys) {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n const clientTempPk = Utils.fromBase64(sessionKeys.publicKey);\n // Alice creates a 32 byte session signature as h₂(a_temp_pk, relayToken, clientToken)\n const signature = new Uint8Array([...clientTempPk, ...relayToken, ...this.clientToken]);\n const h2Signature = await this.nacl.h2(signature);\n return h2Signature;\n }\n\n /**\n * Executes a call to a relay and return raw string response\n */\n // TODO: extract fetching into separate function\n private async httpCall(command: string, ...params: string[]): Promise<string> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), config.RELAY_AJAX_TIMEOUT);\n\n const requestInit: RequestInit = {\n method: 'POST',\n headers: {\n Accept: 'text/plain',\n 'Content-Type': 'text/plain',\n },\n body: params.join('\\r\\n'),\n signal: controller.signal,\n };\n\n // NOTE: Network and server errors are not handled ny Glow itself.\n // They should instead be handled where the library is used\n try {\n const response = await fetch(`${this.url}/${command}`, requestInit);\n if (!response.ok) {\n if (response.status === 401) {\n // clear session if unauthorized\n this.clearSession();\n this.clearToken();\n }\n throw new GlowNetworkError(response.status);\n }\n return await response.text();\n } catch (err: unknown) {\n if (err instanceof Error && err.name === 'AbortError') {\n // Timeout occurred during fetch request or while downloading response body\n throw new GlowNetworkError(408);\n }\n if (err instanceof GlowNetworkError) {\n throw err;\n }\n if (err instanceof TypeError) {\n // Network layer errors (no internet, DNS failure, CORS, etc.)\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch#exceptions\n // Use status 0 to indicate network connectivity issues\n throw new GlowNetworkError(0);\n }\n // For any other unexpected errors, re-throw the original error\n throw err;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Parses relay response and throws an error if its format is unexpected\n */\n private parseResponse(command: string, rawResponse: string): string[] {\n const response = rawResponse.split('\\r\\n');\n\n if (!rawResponse || !this.validateResponse(command, response.length)) {\n console.log(response);\n throw new Error(`[Relay] ${this.url} - ${command}: Bad response`);\n }\n\n return response;\n }\n\n /**\n * Compares the expected number of lines in response with what was actually received from a relay\n */\n private validateResponse(command: string, lines: number): boolean {\n switch (command) {\n case RelayCommand.upload:\n case RelayCommand.messageStatus:\n case RelayCommand.delete:\n return lines === 1;\n case RelayCommand.downloadFileChunk:\n return lines === 3;\n default:\n return lines === 2;\n }\n }\n\n /**\n * delete token locally before it was removed on server so will reconnect without getting unauthorized error\n */\n private scheduleTokenExpiration() {\n if (this.tokenExpirationTimeoutHandle) {\n clearTimeout(this.tokenExpirationTimeoutHandle);\n }\n this.tokenExpirationTimeoutHandle = setTimeout(() => this.clearToken(), config.RELAY_TOKEN_TIMEOUT);\n }\n\n clearToken() {\n if (this.tokenExpirationTimeoutHandle) {\n clearTimeout(this.tokenExpirationTimeoutHandle);\n }\n if (this.clientToken) {\n delete this.clientToken;\n }\n }\n\n /**\n * established session is valid only for some period need to remove it after it's ttl\n * so relay will reconnect without receiving error from server\n */\n private scheduleSessionExpiration() {\n if (this.sessionExpirationTimeoutHandle) {\n clearTimeout(this.sessionExpirationTimeoutHandle);\n }\n this.sessionExpirationTimeoutHandle = setTimeout(() => {\n this.clearSession();\n }, config.RELAY_SESSION_TIMEOUT);\n }\n\n clearSession() {\n if (this.sessionExpirationTimeoutHandle) {\n clearTimeout(this.sessionExpirationTimeoutHandle);\n }\n if (this.sessionKeys) {\n delete this.sessionKeys;\n }\n }\n\n // ---------- Difficulty adjustment ----------\n\n /**\n * Continuously calculates the nonce until one requested by a relay is found\n */\n private async ensureNonceDifficulty(handshake: Uint8Array) {\n let nonce;\n let h2;\n do {\n nonce = await this.nacl.random_bytes(32);\n h2 = await this.nacl.h2(new Uint8Array([...handshake, ...nonce]));\n } while (!this.arrayZeroBits(h2));\n\n return nonce;\n }\n\n /**\n * Returns `true` if the rightmost n bits of a byte are 0.\n * Check whether the rightmost difficulty bits of an Uint8Array are 0, where\n * the lowest indexes of the array represent those rightmost bits. Thus if\n * the difficulty is 17, then array[0] and array[1] should be 0, as should the\n * rightmost bit of array[2]. This is used for our difficulty settings in Zax to\n * reduce burden on a busy server by ensuring clients have to do some\n * additional work during the session handshake\n */\n private arrayZeroBits(array: Uint8Array): boolean {\n let byte;\n let n = this.difficulty;\n for (let i = 0; i <= (1 + this.difficulty / 8); i++) {\n byte = array[i];\n if (n <= 0) {\n return true;\n }\n if (n > 8) {\n n -= 8;\n if (byte > 0) {\n return false;\n }\n } else {\n return this.firstZeroBits(byte, n);\n }\n }\n return false;\n }\n\n /**\n * Returns `true` if the rightmost n bits of a byte are 0\n */\n private firstZeroBits(byte: number, n: number): boolean {\n return byte === ((byte >> n) << n);\n }\n}\n","import { Relay } from './relay';\n\nexport class RelayFactory {\n\n private relays: { [url: string]: Relay } = {};\n\n /**\n * Relay factory, that returns a Relay instance for a given URL,\n * or creates a new one if it hasn't yet been initialized.\n */\n getInstance(url: string) {\n let relay = this.relays[url];\n if (!relay) {\n relay = this.relays[url] = new Relay(url);\n }\n return relay;\n }\n}","import { NaCl } from '../nacl/nacl';\nimport { NaClDriver, EncryptedMessage } from '../nacl/nacl-driver.interface';\nimport { EncryptionHelper } from '../nacl/encryption.helper';\nimport { KeyRing } from '../keyring/keyring';\nimport { Base64, Utils } from '../utils/utils';\nimport { Relay, RelayConnectionData } from '../relay/relay';\nimport {\n RelayCommand,\n StartFileUploadResponse,\n UploadFileChunkResponse,\n FileStatusResponse,\n DeleteFileResponse,\n MessageStatusResponse,\n FileUploadMetadata,\n ZaxMessageKind,\n ZaxRawMessage,\n ZaxFileMessage,\n ZaxPlainMessage,\n ZaxTextMessage,\n ZaxParsedMessage\n} from '../zax.interface';\nimport { RelayFactory } from '../relay/relay-factory';\nimport { Mutex } from 'async-mutex';\n\n\n/**\n * Mailbox class represents a wrapper around a Keyring that allows to exchange\n * encrypted messages with other Mailboxes via a relay\n */\nexport class Mailbox {\n public keyRing: KeyRing;\n public identity: string;\n /**\n * each mailbox use it's own relays for connection\n * this gives possibility to connect different mailboxes to same server simultaneously\n * because they will use different session keys, tokens, server pub keys\n */\n\n private relayFactory = new RelayFactory;\n\n private relayConnectionMutexes = new Map<string, Mutex>();\n\n private nacl: NaClDriver;\n\n private constructor(naclDriver: NaClDriver, keyRing: KeyRing, identity: string) {\n this.nacl = naclDriver;\n this.keyRing = keyRing;\n this.identity = identity;\n }\n\n static async new(identity: string): Promise<Mailbox> {\n return new Mailbox(NaCl.getInstance(), await KeyRing.new(identity), identity);\n }\n\n // ---------- Alternative initializers ----------\n\n /**\n * Create a Mailbox where the secret identity key is derived from a well-known seed\n */\n static async fromSeed(id: string, seed: Uint8Array | string): Promise<Mailbox> {\n const mbx = await this.new(id);\n if (!(seed instanceof Uint8Array)) {\n seed = Utils.encode_latin1(seed);\n }\n await mbx.keyRing.setCommFromSeed(seed);\n return mbx;\n }\n\n /**\n * Create a Mailbox from the known secret identity key\n */\n static async fromSecKey(id: string, rawSecretKey: Uint8Array | Base64): Promise<Mailbox> {\n const mbx = await this.new(id);\n if (!(rawSecretKey instanceof Uint8Array)) {\n rawSecretKey = Utils.fromBase64(rawSecretKey);\n }\n await mbx.keyRing.setCommFromSecKey(rawSecretKey);\n return mbx;\n }\n\n /**\n * Create a Mailbox from the backup string\n */\n static async fromBackup(identity: string, backup: string): Promise<Mailbox> {\n return new Mailbox(NaCl.getInstance(), await KeyRing.fromBackup(identity, backup), identity);\n }\n\n // ---------- Relay message commands (public API) ----------\n\n /**\n * Sends a free-form object to a guest we already have in our keyring. Set `encrypt` to `false` to\n * send a plaintext message. Returns a token that can be used with `messageStatus` command to check\n * the status of the message\n */\n async upload(url: string, guestKey: string, message: string, encrypt = true): Promise<Base64> {\n const relay = await this.prepareRelay(url);\n const guestPk = this.getGuestKey(guestKey);\n const payload = encrypt ? await this.encodeMessage(guestKey, message) : message;\n const toHpk = Utils.toBase64(await this.nacl.h2(Utils.fromBase64(guestPk)));\n\n const [token] = await this.runRelayCommand(relay, RelayCommand.upload, { to: toHpk, payload });\n return token;\n }\n\n /**\n * Downloads all messages from a relay, decrypts them with a relay key,\n * and then parses each message to find out if it's a text message, file message,\n * or if it can't be decrypted because HPK is missing in the keyring.\n * Returns an array of mixed messages\n */\n async download(url: string) {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.download);\n const messages: ZaxRawMessage[] = await this.decryptResponse<ZaxRawMessage[]>(relay, response);\n\n const parsedMessages: ZaxParsedMessage[] = [];\n for (const message of messages) {\n const senderTag = this.keyRing.getTagByHpk(message.from);\n if (!senderTag) {\n parsedMessages.push(await this.parsePlainMessage(message));\n } else if (message.kind === 'message') {\n parsedMessages.push(await this.parseTextMessage(message, senderTag));\n } else if (message.kind === 'file') {\n parsedMessages.push(await this.parseFileMessage(message, senderTag));\n } else {\n throw new Error('[Mailbox] download - Unknown message type');\n }\n }\n return parsedMessages;\n }\n\n /**\n * Marks a raw Zax message as one that can't be decrypted,\n * because sender's HPK is not found in the keyring\n */\n private async parsePlainMessage({ data, time, from, nonce }: ZaxRawMessage) {\n return { data, time, from, nonce, kind: ZaxMessageKind.plain } as ZaxPlainMessage;\n }\n\n /**\n * Decrypts a message that represents uploaded file metadata\n */\n private async parseFileMessage(message: ZaxRawMessage, senderTag: string) {\n const { nonce, ctext, uploadID } = JSON.parse(message.data);\n const rawData = await this.decodeMessage(senderTag, nonce, ctext);\n if (rawData === null) {\n throw new Error('[Mailbox] Failed to decode file message');\n }\n const data = JSON.parse(rawData) as FileUploadMetadata;\n return { data, time: message.time, senderTag, uploadID, nonce, kind: ZaxMessageKind.file } as ZaxFileMessage;\n }\n\n /**\n * Attempts to decrypt a regular encrypted Zax message. Returns plain message if it was sent encrypted\n */\n private async parseTextMessage(message: ZaxRawMessage, senderTag: string): Promise<ZaxTextMessage> {\n let data = await this.decodeMessage(senderTag, message.nonce, message.data);\n // If the message was sent unencrypted, the line above will return `null`\n if (!data) {\n data = message.data;\n }\n return ({ data, time: message.time, senderTag, nonce: message.nonce, kind: ZaxMessageKind.message });\n }\n\n /**\n * Returns the number of messages in the mailbox on a given relay\n */\n async count(url: string): Promise<number> {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.count);\n return await this.decryptResponse(relay, response);\n }\n\n /**\n * Deletes messages from a relay given a list of base64 message nonces,\n * and returns the number of remaining messages\n */\n async delete(url: string, nonceList: Base64[]): Promise<number> {\n const relay = await this.prepareRelay(url);\n const [response] = await this.runRelayCommand(relay, RelayCommand.delete, { payload: nonceList });\n return parseInt(response, 10);\n }\n\n /**\n * Gets the status of a previously sent Zax message by a storage token.\n * Returns \"time to live\" in seconds or a negative value if it's not applicable.\n * See `MessageStatusResponse` values for reference\n */\n async messageStatus(url: string, storageToken: Base64): Promise<MessageStatusResponse | number> {\n const relay = await this.prepareRelay(url);\n const [response] = await this.runRelayCommand(relay, RelayCommand.messageStatus, { token: storageToken });\n const status = parseInt(response, 10);\n return status;\n }\n\n // ---------- Relay file commands (public API) ----------\n\n /**\n * Asks Zax to start a new upload session, and returns a unique file identifier\n * required to upload file chunks.\n */\n async startFileUpload(url: string, guest: string,\n rawMetadata: FileUploadMetadata): Promise<StartFileUploadResponse> {\n const relay = await this.prepareRelay(url);\n const guestPk = this.getGuestKey(guest);\n const toHpk = Utils.toBase64(await this.nacl.h2(Utils.fromBase64(guestPk)));\n\n const secretKey = await this.nacl.random_bytes(this.nacl.crypto_secretbox_KEYBYTES);\n rawMetadata.skey = Utils.toBase64(secretKey);\n\n const metadata = await this.encodeMessage(guest, JSON.stringify(rawMetadata));\n\n const response = await this.runRelayCommand(relay, RelayCommand.startFileUpload, {\n to: toHpk,\n file_size: rawMetadata.orig_size,\n metadata\n });\n\n const decrypted = await this.decryptResponse<StartFileUploadResponse>(relay, response);\n // append symmetric secret key (unique for this upload session) to the server response\n decrypted.skey = secretKey;\n return decrypted;\n }\n\n /**\n * Encrypts the file chunk symmetrically and transfers it to a relay\n */\n async uploadFileChunk(url: string, uploadID: string, chunk: Uint8Array,\n part: number, totalParts: number, skey: Uint8Array): Promise<UploadFileChunkResponse> {\n const relay = await this.prepareRelay(url);\n const encodedChunk = await EncryptionHelper.encodeMessageSymmetric(chunk, skey);\n const response = await this.runRelayCommand(relay, RelayCommand.uploadFileChunk, {\n uploadID,\n part,\n last_chunk: (totalParts - 1 === part), // marker of the last chunk, sent only once\n nonce: encodedChunk.nonce\n }, encodedChunk.ctext); // do not encode file chunk contents, as it's already encoded with symmetric encryption\n return await this.decryptResponse(relay, response);\n }\n\n /**\n * Returns the status of a file upload by its relay-specific uploadID. Uploader can call it\n * to verify the correct transfer, and downloader can check if the file exists and retrieve\n * the number of chunks\n */\n async getFileStatus(url: string, uploadID: string): Promise<FileStatusResponse> {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.fileStatus, { uploadID });\n return await this.decryptResponse(relay, response);\n }\n\n /**\n * Fetches the file metadata by uploadID, which was declared by the uploader\n */\n async getFileMetadata(url: string, uploadID: string): Promise<FileUploadMetadata> {\n const messages = await this.download(url);\n\n const fileMessage = messages\n .filter(message => message.kind === 'file')\n .find(message => (message as ZaxFileMessage).uploadID === uploadID);\n return fileMessage?.data as FileUploadMetadata;\n }\n\n /**\n * Downloads a binary chunk of a file from a relay by a given uploadID. The total number of chunks\n * can be retrieved via a `getFileStatus` request\n */\n async downloadFileChunk(url: string, uploadID: string, part: number, skey: Uint8Array | Base64):\n Promise<Uint8Array | null> {\n if (!(skey instanceof Uint8Array)) {\n skey = Utils.fromBase64(skey);\n }\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.downloadFileChunk, { uploadID, part });\n const [nonce, ctext, fileCtext] = response;\n const decoded = await relay.decodeMessage<{nonce: string}>(nonce, ctext);\n return await EncryptionHelper.decodeMessageSymmetric(decoded.nonce, fileCtext, skey);\n }\n\n /**\n * Deletes a file from the relay (or all chunks uploaded so far, if the upload was not completed).\n * Can be called by either the sender or recipient\n */\n async deleteFile(url: string, uploadID: string): Promise<DeleteFileResponse> {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.deleteFile, { uploadID });\n return await this.decryptResponse(relay, response);\n }\n\n // ---------- Dealing with Relay ----------\n\n /**\n * Establishes a session, exchanges temp keys and proves our ownership of this\n * Mailbox to this specific relay. This is the first function to start\n * communications with any relay. Returns the number of messages in the mailbox\n */\n async connectToRelay(url: string): Promise<number> {\n const relay = this.relayFactory.getInstance(url);\n const connectionData = await relay.openConnection();\n const encryptedSignature = await this.encryptSignature(connectionData);\n\n const messagesNumber = await relay.prove(await relay.encodeMessage(JSON.stringify({\n pub_key: this.keyRing.getPubCommKey(),\n nonce: encryptedSignature.nonce,\n ctext: encryptedSignature.ctext\n })));\n return parseInt(messagesNumber, 10);\n }\n\n clearSession(url: string) {\n const relay = this.relayFactory.getInstance(url);\n relay.clearToken();\n relay.clearSession();\n }\n\n private async encryptSignature(connection: RelayConnectionData) {\n const privateKey = Utils.fromBase64(this.keyRing.getPrivateCommKey());\n return await EncryptionHelper.encodeMessage(connection.h2Signature, connection.relayPublicKey, privateKey);\n }\n\n /**\n * Gets a singleton Relay instance, and reconnects to a relay if a previous token has expired\n */\n private async prepareRelay(url: string): Promise<Relay> {\n const relay = this.relayFactory.getInstance(url);\n /**\n * allow establishing only once connection for pair mailbox-relay\n */\n await this.getRelayConnectionMutex(url).runExclusive(async () => {\n if (!relay.isConnected) {\n await this.connectToRelay(url);\n }\n });\n return relay;\n }\n\n /**\n * Encrypts the payload of the command and sends it to a relay\n */\n private async runRelayCommand(\n relay: Relay, command: RelayCommand, params?: {[key:string]: unknown}, ctext?: string): Promise<string[]> {\n params = { cmd: command, ...params };\n const hpk = await this.keyRing.getHpk();\n const message = await relay.encodeMessage(JSON.stringify(params));\n return await relay.runCmd(command, hpk, message, ctext);\n }\n\n /**\n * Parses relay's response to a command, for those commands that expect an encrypted message in return.\n * Two lines of POST response will be nonce and ctext\n */\n private async decryptResponse<T>(relay: Relay, response: string[]) {\n const [nonce, ctext] = response;\n return await relay.decodeMessage<T>(nonce, ctext);\n }\n\n // ---------- Message encoding / decoding ----------\n\n /**\n * Encodes `message` to the guest key of a guest already\n * added to the keyring\n */\n async encodeMessage(guest: string, message: string): Promise<EncryptedMessage> {\n const guestPk = this.getGuestKey(guest);\n const privateKey = this.keyRing.getPrivateCommKey();\n\n return await EncryptionHelper.encodeMessage(\n await this.nacl.encode_utf8(message), Utils.fromBase64(guestPk), Utils.fromBase64(privateKey));\n }\n\n /**\n * Decodes a ciphertext from a guest key already in our keyring with this nonce\n * @returns null if failed to decode\n */\n async decodeMessage(guest: string, nonce: Base64, ctext: Base64) {\n const guestPk = this.getGuestKey(guest);\n const privateKey = this.keyRing.getPrivateCommKey();\n let uint8ArrayCtext: Uint8Array;\n try {\n uint8ArrayCtext = Utils.fromBase64(ctext);\n } catch {\n // looks like ctext was not encoded\n return null;\n }\n\n return await EncryptionHelper.decodeMessage(Utils.fromBase64(nonce), uint8ArrayCtext,\n Utils.fromBase64(guestPk), Utils.fromBase64(privateKey));\n }\n\n /**\n * Wrapper around `keyring.getGuestKey` that handles unknown guests\n */\n private getGuestKey(guest: string): string {\n const guestPk = this.keyRing.getGuestKey(guest);\n if (!guestPk) {\n throw new Error(`[Mailbox] Unknown guest ${guest}`);\n }\n return guestPk;\n }\n\n // ---------- Destroying Mailbox ----------\n\n /**\n * Deletes a Mailbox and all its data from local CryptoStorage. This is a very\n * destructive operation, use with caution - it will delete the Mailbox\n * keyring along with all stored public keys. To restore that information, you\n * will need to do another key exchange with all the guests on your keyring.\n */\n async selfDestruct() {\n await this.keyRing.selfDestruct();\n }\n\n private getRelayConnectionMutex(url: string) {\n if (!this.relayConnectionMutexes.has(url)) {\n this.relayConnectionMutexes.set(url, new Mutex());\n }\n return this.relayConnectionMutexes.get(url) as Mutex;\n }\n}\n","import { StorageDriver } from './storage-driver.interface';\n\n/**\n * temporary created in-memory storage for testing purposes\n */\nexport class InMemoryStorage implements StorageDriver {\n\n private storage: {[key: string]: string|null} = {};\n\n async get(key: string) {\n return (await this.getMultiple([key]))[0];\n }\n\n async getMultiple(keys: string[]) {\n return keys.map(key => this.storage[key]);\n }\n\n set<T extends string | null>(key: string, value: T) {\n return this.setMultiple({[key]: value});\n }\n\n async setMultiple<T extends string | null>(values: { [key: string]: T; }){\n for (const key in values) {\n this.storage[key] = values[key];\n }\n }\n\n remove(key: string) {\n return this.removeMultiple([key]);\n }\n\n async removeMultiple(keys: string[]) {\n keys.forEach(key => delete this.storage[key]);\n }\n\n reset() {\n this.storage = {};\n }\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"glow.es.js","names":[],"sources":["../src/utils/utils.ts","../src/nacl/js-nacl-driver.ts","../src/nacl/nacl.ts","../src/keys/keys.ts","../src/config.ts","../src/crypto-storage/local-storage.driver.ts","../src/crypto-storage/crypto-storage.ts","../src/keyring/keyring.ts","../src/nacl/encryption.helper.ts","../src/zax.interface.ts","../src/relay/network-error.ts","../src/relay/relay.ts","../src/relay/relay-factory.ts","../src/mailbox/mailbox.ts","../src/crypto-storage/in-memory-storage.ts"],"sourcesContent":["export type Base64 = string;\n\nexport class Utils {\n static toBase64(input: Uint8Array): Base64 {\n const isBrowser = typeof btoa !== 'undefined';\n return isBrowser ?\n btoa(this.decode_latin1(input)) :\n Buffer.from(this.decode_latin1(input), 'latin1').toString('base64');\n }\n\n static fromBase64(input: Base64): Uint8Array {\n const isBrowser = typeof btoa !== 'undefined';\n return isBrowser ?\n this.encode_latin1(atob(input)) :\n this.encode_latin1(Buffer.from(input, 'base64').toString('latin1'));\n }\n\n static encode_latin1(data: string): Uint8Array {\n const result = new Uint8Array(data.length);\n for (let i = 0; i < data.length; i++) {\n const c = data.charCodeAt(i);\n if ((c & 0xff) !== c) throw { message: 'Cannot encode string in Latin1', str: data };\n result[i] = (c & 0xff);\n }\n return result;\n }\n\n static decode_latin1(data: Uint8Array): string {\n const encoded = [];\n for (let i = 0; i < data.length; i++) {\n encoded.push(String.fromCharCode(data[i]));\n }\n return encoded.join('');\n }\n\n static toObject(pairs: IterableIterator<[string, unknown]>) {\n return Array.from(pairs).reduce(\n (acc, [key, value]) => Object.assign(acc, { [key]: value }),\n {},\n );\n }\n}\n","import { box, BoxKeyPair, randomBytes, secretbox, hash } from 'tweetnacl';\n\nimport { NaClDriver } from './nacl-driver.interface';\nimport { Keypair } from './keypair.interface';\nimport { Utils } from '../utils/utils';\n\n/**\n * Implementation based on TweetNaCl.js: {@link https://github.com/dchest/tweetnacl-js}.\n * SHA256 is implemented using Web Crypto API (SubtleCrypto).\n *\n * All the methods of this class are actually synchronous, but they are defined\n * as async to conform to the NaClDriver interface, which may have other implementations.\n */\nexport class JsNaClDriver implements NaClDriver {\n crypto_secretbox_KEYBYTES = secretbox.keyLength;\n\n async crypto_secretbox_random_nonce(): Promise<Uint8Array> {\n return randomBytes(secretbox.nonceLength);\n }\n\n async crypto_secretbox(message: Uint8Array, nonce: Uint8Array, key: Uint8Array): Promise<Uint8Array> {\n return secretbox(message, nonce, key);\n }\n\n async crypto_secretbox_open(box: Uint8Array, nonce: Uint8Array, key: Uint8Array): Promise<Uint8Array | null> {\n return secretbox.open(box, nonce, key);\n }\n\n crypto_box_NONCEBYTES = box.nonceLength;\n\n async crypto_box(message: Uint8Array, nonce: Uint8Array, pk: Uint8Array, sk: Uint8Array): Promise<Uint8Array> {\n return box(message, nonce, pk, sk);\n }\n\n async crypto_box_open(\n cipher: Uint8Array, nonce: Uint8Array, pk: Uint8Array, sk: Uint8Array): Promise<Uint8Array | null> {\n return box.open(cipher, nonce, pk, sk);\n }\n\n async crypto_box_random_nonce(): Promise<Uint8Array> {\n return randomBytes(box.nonceLength);\n }\n\n async crypto_box_keypair(): Promise<Keypair> {\n const pair: BoxKeyPair = box.keyPair();\n return {\n boxPk: pair.publicKey,\n boxSk: pair.secretKey\n };\n }\n\n async crypto_box_keypair_from_raw_sk(key: Uint8Array): Promise<Keypair> {\n const pair: BoxKeyPair = box.keyPair.fromSecretKey(key);\n return {\n boxPk: pair.publicKey,\n boxSk: pair.secretKey\n };\n }\n\n async crypto_box_keypair_from_seed(seed: Uint8Array): Promise<Keypair> {\n // using SHA512 to hash, per original NaCl docs\n return this.crypto_box_keypair_from_raw_sk(hash(seed).subarray(0, box.secretKeyLength));\n }\n\n async crypto_hash_sha256(data: Uint8Array): Promise<Uint8Array> {\n // In Node.js, globalThis.crypto is implementation of WebCrypto API, available since Node.js 16\n // In browsers, globalThis.crypto is the standard Web Crypto API\n const cryptoProvider = globalThis.crypto;\n if (!cryptoProvider || !cryptoProvider.subtle) {\n throw new Error('Web Crypto API is not available. Please use a modern browser or Node.js 16+.');\n }\n\n const hashBuffer = await cryptoProvider.subtle.digest('SHA-256', data as BufferSource);\n return new Uint8Array(hashBuffer);\n }\n\n async random_bytes(size: number): Promise<Uint8Array> {\n return randomBytes(size);\n }\n\n // Helper methods are based on `js-nacl` implementations\n // https://github.com/tonyg/js-nacl/blob/cc70775cfc9d68a04905ca65c7f179b33a18066e/nacl_cooked.js\n\n async encode_latin1(data: string): Promise<Uint8Array> {\n return Utils.encode_latin1(data);\n }\n\n async decode_latin1(data: Uint8Array): Promise<string> {\n return Utils.decode_latin1(data);\n }\n\n async encode_utf8(data: string): Promise<Uint8Array> {\n return this.encode_latin1(unescape(encodeURIComponent(data)));\n }\n\n async decode_utf8(data: Uint8Array): Promise<string> {\n return decodeURIComponent(escape(await this.decode_latin1(data)));\n }\n\n async to_hex(data: Uint8Array): Promise<string> {\n const encoded = [];\n for (let i = 0; i < data.length; i++) {\n const hexAlphabet = '0123456789abcdef';\n encoded.push(hexAlphabet[(data[i] >> 4) & 15]);\n encoded.push(hexAlphabet[data[i] & 15]);\n }\n return encoded.join('');\n }\n\n async from_hex(data: string): Promise<Uint8Array> {\n const result = new Uint8Array(data.length / 2);\n for (let i = 0; i < data.length / 2; i++) {\n result[i] = parseInt(data.substr(2 * i, 2), 16);\n }\n return result;\n }\n\n /**\n * h2(m) = sha256(sha256(64x0 + m))\n * Zero out initial sha256 block, and double hash 0-padded message\n * http://cs.nyu.edu/~dodis/ps/h-of-h.pdf\n */\n async h2(data: string | Uint8Array): Promise<Uint8Array> {\n if (!(data instanceof Uint8Array)) {\n data = await this.encode_latin1(data);\n }\n\n const zeroPaddingLength = 64;\n const extendedSource = new Uint8Array(zeroPaddingLength + data.length);\n extendedSource.fill(0);\n extendedSource.set(data, zeroPaddingLength);\n return this.crypto_hash_sha256(await this.crypto_hash_sha256(extendedSource));\n }\n}\n","import { NaClDriver } from './nacl-driver.interface';\nimport { JsNaClDriver } from './js-nacl-driver';\n\n/**\n * Facade singleton to access the NaCl driver.\n * Usage: call instance() with a chosen driver, and store the received object to perform further actions.\n */\nexport class NaCl {\n private static driverInstance?: NaClDriver;\n\n private constructor() { }\n\n public static setDefaultInstance() {\n return this.setInstance(new JsNaClDriver());\n }\n\n public static setInstance(driver: NaClDriver): boolean {\n if (this.driverInstance) {\n throw new Error('[NaCl] NaCl driver has been already set, it is supposed to be set only once');\n }\n this.driverInstance = driver;\n\n return true;\n }\n\n public static getInstance(): NaClDriver {\n if (!this.driverInstance) {\n throw new Error('[NaCl] NaCl instance is not yet set');\n }\n\n return this.driverInstance;\n }\n}\n","import { Base64, Utils } from '../utils/utils';\nimport { Keypair } from '../nacl/keypair.interface';\n\n/**\n * A simple wrapper around a public/private keys pair.\n * Stores keys in base64 format.\n */\nexport class Keys {\n private keyPair: Keypair;\n\n constructor(public keys: string | Keypair) {\n if (typeof keys === 'string') {\n const { boxPk, boxSk } = JSON.parse(keys);\n this.keyPair = {\n boxPk: Utils.fromBase64(boxPk),\n boxSk: Utils.fromBase64(boxSk)\n };\n } else {\n this.keyPair = keys;\n }\n }\n\n static isEqual(keys1: Keys, keys2: Keys): boolean {\n return keys1.toString() === keys2.toString();\n }\n\n toString(): string {\n return JSON.stringify({\n boxPk: this.publicKey,\n boxSk: this.privateKey\n });\n }\n\n get publicKey(): Base64 {\n return Utils.toBase64(this.keyPair.boxPk);\n }\n\n get privateKey(): Base64 {\n return Utils.toBase64(this.keyPair.boxSk);\n }\n}\n","const sessionTimeoutBuffer = 30 * 1000;\n\nexport const config = {\n NONCE_TAG: '__nc',\n SKEY_TAG: 'storage_key',\n STORAGE_ROOT: '.v1.stor.vlt12',\n // Relay tokens, keys and hashes are 32 bytes\n RELAY_TOKEN_LEN: 32,\n // 5 min - Token expiration on the server side, matched with config.x.relay.token_timeout on Zax server\n RELAY_TOKEN_TIMEOUT: 5 * 60 * 1000 - sessionTimeoutBuffer,\n // 20 min - Session expiration on the server side, matched with config.x.relay.session_timeout on Zax server\n RELAY_SESSION_TIMEOUT: 20 * 60 * 1000 - sessionTimeoutBuffer,\n // 5 sec - Ajax request timeout\n RELAY_AJAX_TIMEOUT: 5 * 1000\n};\n","import { StorageDriver } from './storage-driver.interface';\n\nexport class LocalStorageDriver implements StorageDriver {\n private rootTag: string;\n\n constructor(root = 'storage.') {\n this.rootTag = `__glow.${root}`;\n }\n\n async get(key: string): Promise<string | null> {\n return (await this.getMultiple([key]))[0];\n }\n\n async getMultiple(keys: string[]): Promise<(string | null)[]> {\n return keys.map(key => localStorage.getItem(this.tag(key)) || null);\n }\n\n async set(key: string, value: string | null): Promise<void> {\n return this.setMultiple({ [key]: value });\n }\n\n async setMultiple(values: { [key: string]: string | null; }): Promise<void> {\n for(const key in values) {\n const value = values[key];\n if (value !== null) {\n localStorage.setItem(this.tag(key), value);\n }\n }\n }\n\n async remove(key: string): Promise<void> {\n this.removeMultiple([key]);\n }\n\n async removeMultiple(keys: string[]): Promise<void> {\n keys.forEach(key => localStorage.removeItem(this.tag(key)));\n }\n\n private tag(key: string) {\n return `${this.rootTag}.${key}`;\n }\n}\n","import { NaCl } from '../nacl/nacl';\nimport { Utils } from '../utils/utils';\nimport { config } from '../config';\nimport { StorageDriver } from './storage-driver.interface';\nimport { NaClDriver } from '../nacl/nacl-driver.interface';\nimport { LocalStorageDriver } from './local-storage.driver';\n\n// TODO: add bulk operations to set(), get() and remove() pairs of values simultaneously\n\n/* CryptoStorage is a handy wrapper around any storage that provides JavaScript interface,\n that allows to store symmetrically encrypted serializable Javascript objects and primitives. */\nexport class CryptoStorage {\n\n private static storageDriver: StorageDriver;\n\n private driver?: StorageDriver;\n private id?: string;\n private storageKey?: Uint8Array;\n private nacl: NaClDriver;\n\n private constructor(storageDriver: StorageDriver, id: string) {\n const nacl = NaCl.getInstance();\n this.nacl = nacl;\n this.driver = storageDriver;\n this.id = id ? `.${id}${config.STORAGE_ROOT}` : config.STORAGE_ROOT;\n }\n\n static async new(id: string): Promise<CryptoStorage> {\n const storageDriver = this.getStorageDriver();\n const storage = new CryptoStorage(storageDriver, id);\n const prefixedStorageKeyTag = storage.addPrefix(config.SKEY_TAG);\n const storageKey = await storageDriver.get(prefixedStorageKeyTag);\n // Either load or generate a storage key\n if (storageKey) {\n const { key } = JSON.parse(storageKey);\n storage.storageKey = Utils.fromBase64(key);\n } else {\n storage.storageKey = await storage.nacl.random_bytes(storage.nacl.crypto_secretbox_KEYBYTES);\n await storageDriver.set(prefixedStorageKeyTag, JSON.stringify({ key: Utils.toBase64(storage.storageKey)}));\n }\n return storage;\n }\n\n static getStorageDriver() {\n if (!this.storageDriver) {\n throw new Error('[CryptoStorage] StorageDriver instance is not yet set');\n } else {\n return this.storageDriver;\n }\n }\n\n static setDefaultStorageDriver() {\n return this.setStorageDriver(new LocalStorageDriver());\n }\n\n static setStorageDriver(driver: StorageDriver) {\n if (this.storageDriver) {\n throw new Error('[CryptoStorage] StorageDriver has been already set, it is supposed to be set only once');\n }\n this.storageDriver = driver;\n return true;\n }\n\n async save(tag: string, data: unknown): Promise<boolean> {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n if (!this.storageKey) {\n throw new Error('[CryptoStorage] Storage key is not set');\n }\n // Convert the data to JSON, then convert that string to a byte array\n const input = JSON.stringify(data);\n const encoded = await this.nacl.encode_utf8(input);\n // For each item in the store we also generate and save its own nonce\n const nonce = await this.nacl.crypto_secretbox_random_nonce();\n const cipherText = await this.nacl.crypto_secretbox(encoded, nonce, this.storageKey);\n // Save the cipher text and nonce\n await this.driver.setMultiple({\n [this.addPrefix(tag)]: Utils.toBase64(cipherText),\n [this.addNonceTag(tag)]: Utils.toBase64(nonce)\n });\n return true;\n }\n\n async get<T>(tag: string): Promise<T | null> {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n if (!this.storageKey) {\n throw new Error('[CryptoStorage] Storage key is not set');\n }\n // Get cipher text and nonce from the storage\n const [data, nonce] = await this.driver.getMultiple([this.addPrefix(tag), this.addNonceTag(tag)]);\n // Nothing to do without cipher text or nonce\n if (!data || !nonce) {\n return null;\n }\n const dataBinary = Utils.fromBase64(data);\n const nonceBinary = Utils.fromBase64(nonce);\n const source = await this.nacl.crypto_secretbox_open(dataBinary, nonceBinary, this.storageKey);\n if (source) {\n const decoded = await this.nacl.decode_utf8(source);\n return JSON.parse(decoded) as T;\n } else {\n throw new Error('[CryptoStorage] crypto_secretbox_open: decryption error');\n }\n }\n\n async remove(tag: string): Promise<boolean> {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n await this.driver.removeMultiple([this.addPrefix(tag), this.addNonceTag(tag)]);\n return true;\n }\n\n async selfDestruct() {\n if (!this.driver) {\n throw new Error('[CryptoStorage] Storage driver is not set');\n }\n await this.driver.remove(this.addPrefix(config.SKEY_TAG));\n }\n\n // Keys are tagged in the storage with a versioned prefix\n private addPrefix(key: string): string {\n return this.id ? (key + this.id) : key;\n }\n\n private addNonceTag(tag: string): string {\n return this.addPrefix(`${config.NONCE_TAG}.${tag}`);\n }\n}\n","import { CryptoStorage } from '../crypto-storage/crypto-storage';\nimport { Keys } from '../keys/keys';\nimport { NaCl } from '../nacl/nacl';\nimport { Utils, Base64 } from '../utils/utils';\nimport { NaClDriver } from '../nacl/nacl-driver.interface';\n\ninterface KeyRecord {\n pk: Base64;\n hpk: Base64;\n}\n\nconst commKeyTag = '__::commKey::__';\n\ninterface KeyRingBackup {\n [commKeyTag]: string;\n [key: string]: string | null | undefined\n}\n\n// Manages the set of public keys of counterparties\nexport class KeyRing {\n static readonly commKeyTag = 'comm_key';\n static readonly guestRegistryTag = 'guest_registry';\n\n private commKey: Keys;\n private storage: CryptoStorage;\n private guestKeys: Map<string, KeyRecord> = new Map();\n private nacl: NaClDriver;\n\n private constructor(naclDriver: NaClDriver, cryptoStorage: CryptoStorage, commKey: Keys) {\n this.nacl = naclDriver;\n this.storage = cryptoStorage;\n this.commKey = commKey;\n }\n\n static async new(id: string): Promise<KeyRing> {\n const nacl = NaCl.getInstance();\n const cryptoStorage = await CryptoStorage.new(id);\n const commKey = await KeyRing.getCommKeyOrCreate(nacl, cryptoStorage);\n const keyRing = new KeyRing(nacl, cryptoStorage, commKey);\n\n await keyRing.loadGuestKeys();\n return keyRing;\n }\n\n static async fromBackup(id: string, backupString: string): Promise<KeyRing> {\n const backup: KeyRingBackup = JSON.parse(backupString);\n const secretKey = Utils.fromBase64(backup[commKeyTag]);\n const restoredKeyRing = await KeyRing.new(id);\n await restoredKeyRing.setCommFromSecKey(secretKey);\n for (const [key, value] of Object.entries(backup)) {\n if (key !== commKeyTag) {\n await restoredKeyRing.addGuest(key, value as string);\n }\n }\n return restoredKeyRing;\n }\n\n getNumberOfGuests(): number {\n return this.guestKeys.size;\n }\n\n get guests(): Map<string, KeyRecord> {\n return this.guestKeys;\n }\n\n /**\n * Returns public identity, which is the default communication key.\n * Correspondents can know it, whereas Relays do not need it (other than\n * temporarily for internal use during the ownership proof)\n */\n getPubCommKey(): string {\n return this.commKey.publicKey;\n }\n\n getPrivateCommKey(): string {\n return this.commKey.privateKey;\n }\n\n /**\n * Returns HPK (hash of the public key) of the mailbox. This is what Zax relays\n * uses as the universal address of the mailbox\n */\n async getHpk(): Promise<Base64> {\n const hpk = await this.nacl.h2(Utils.fromBase64(this.commKey.publicKey));\n return Utils.toBase64(hpk);\n }\n\n getTagByHpk(hpk: string): string | null {\n for (const [key, value] of this.guestKeys) {\n if (value.hpk === hpk) {\n return key;\n }\n }\n return null;\n }\n\n getGuestKey(guestTag: string): Base64 | null {\n const keyRecord = this.guestKeys.get(guestTag);\n if (keyRecord) {\n return keyRecord.pk;\n }\n return null;\n }\n\n // Backups\n\n async backup(): Promise<string> {\n const backup: KeyRingBackup = {\n [commKeyTag]: this.commKey.privateKey\n };\n\n if (this.getNumberOfGuests() > 0) {\n for (const [key, value] of this.guestKeys) {\n if (key && value) {\n backup[key] = value.pk;\n }\n }\n }\n return JSON.stringify(backup);\n }\n\n async setCommFromSeed(seed: Uint8Array): Promise<void> {\n this.commKey = new Keys(await this.nacl.crypto_box_keypair_from_seed(seed));\n await this.storage.save(KeyRing.commKeyTag, this.commKey.toString());\n }\n\n async setCommFromSecKey(rawSecretKey: Uint8Array): Promise<void> {\n this.commKey = new Keys(await this.nacl.crypto_box_keypair_from_raw_sk(rawSecretKey));\n await this.storage.save(KeyRing.commKeyTag, this.commKey.toString());\n }\n\n async addGuest(guestTag: string, publicKey: Base64): Promise<string> {\n const b64_h2 = Utils.toBase64(await this.nacl.h2(Utils.fromBase64(publicKey)));\n this.guestKeys.set(guestTag, {\n pk: publicKey,\n hpk: b64_h2\n });\n await this.saveGuests();\n return b64_h2;\n }\n\n async removeGuest(guestTag: string): Promise<boolean> {\n if (this.guestKeys.has(guestTag)) {\n this.guestKeys.delete(guestTag);\n await this.saveGuests();\n }\n return true;\n }\n\n private async loadGuestKeys() {\n const guestKeys = await this.storage.get<{[key:string]: KeyRecord}>(KeyRing.guestRegistryTag);\n if (!guestKeys) {\n return;\n } else if (typeof guestKeys === 'object') {\n this.guestKeys = new Map(Object.entries(guestKeys));\n } else {\n throw new Error('[Keyring] Guest keys is not an object');\n }\n }\n\n private async saveGuests() {\n await this.storage.save(KeyRing.guestRegistryTag, Utils.toObject(this.guestKeys.entries()));\n }\n\n private static async getCommKeyOrCreate(nacl: NaClDriver, storage: CryptoStorage): Promise<Keys> {\n const commKeyRecord = await storage.get(KeyRing.commKeyTag);\n if (commKeyRecord && typeof commKeyRecord === 'string') {\n return new Keys(commKeyRecord);\n } else {\n const keypair = await nacl.crypto_box_keypair();\n const commKey = new Keys(keypair);\n await storage.save(KeyRing.commKeyTag, commKey.toString());\n return commKey;\n }\n }\n\n async selfDestruct() {\n await this.storage.remove(KeyRing.guestRegistryTag);\n await this.storage.remove(KeyRing.commKeyTag);\n await this.storage.selfDestruct();\n }\n}\n","import { NaCl } from './nacl';\nimport { EncryptedMessage } from './nacl-driver.interface';\nimport { Utils, Base64 } from '../utils/utils';\n\n/**\n * Includes several static helpers that are build on top of basic NaCl driver methods,\n * to avoid duplicating code in driver implementations.\n */\nexport class EncryptionHelper {\n\n private constructor() { }\n\n /**\n * Encodes a binary message with `cryptobox`\n */\n static async encodeMessage(message: Uint8Array, pkTo: Uint8Array, skFrom: Uint8Array, nonceData?: number) {\n const nacl = NaCl.getInstance();\n\n const nonce = await this.makeNonce(nonceData);\n const ctext = await nacl.crypto_box(message, nonce, pkTo, skFrom);\n return {\n nonce: Utils.toBase64(nonce),\n ctext: Utils.toBase64(ctext)\n } as EncryptedMessage;\n }\n\n /**\n * Decodes a binary message with `cryptobox_open`\n * @returns null if failed to decode\n */\n static async decodeMessage(nonce: Uint8Array, ctext: Uint8Array, pkFrom: Uint8Array, skTo: Uint8Array) {\n const nacl = NaCl.getInstance();\n const data = await nacl.crypto_box_open(ctext, nonce, pkFrom, skTo);\n if (data) {\n return await nacl.decode_utf8(data);\n }\n\n return data;\n }\n\n /**\n * Encodes a binary message with `secretbox` (used for file chunk encryption)\n */\n static async encodeMessageSymmetric(message: Uint8Array, secretKey: Uint8Array): Promise<EncryptedMessage> {\n const nacl = NaCl.getInstance();\n const nonce = await EncryptionHelper.makeNonce();\n const ctext = await nacl.crypto_secretbox(message, nonce, secretKey);\n return {\n nonce: Utils.toBase64(nonce),\n ctext: Utils.toBase64(ctext)\n };\n }\n\n /**\n * Decodes a binary message with `secretbox_open` (used for file chunk decryption)\n */\n static async decodeMessageSymmetric(nonce: Base64, ctext: Base64, secretKey: Uint8Array): Promise<Uint8Array | null> {\n const nacl = NaCl.getInstance();\n return await nacl.crypto_secretbox_open(Utils.fromBase64(ctext), Utils.fromBase64(nonce), secretKey);\n }\n\n // ---------- Nonce helper ----------\n\n /**\n * Makes a timestamp nonce that a relay expects for any crypto operations.\n * Timestamp is the first 8 bytes, the rest is random, unless custom `data`\n * is specified. `data` will be packed as next 4 bytes after timestamp.\n */\n private static async makeNonce(data?: number): Promise<Uint8Array> {\n const nacl = NaCl.getInstance();\n const nonce = await nacl.crypto_box_random_nonce();\n let headerLen;\n if (nonce.length !== nacl.crypto_box_NONCEBYTES) {\n throw new Error('[Mailbox] Wrong crypto_box nonce length');\n }\n // split timestamp integer as an array of bytes\n headerLen = 8; // max timestamp size\n const aTime = this.itoa(Math.floor(Date.now() / 1000));\n\n if (data) {\n headerLen += 4; // extra 4 bytes for custom data\n }\n\n // zero out nonce header area\n nonce.fill(0, 0, headerLen);\n // copy the timestamp into the first 8 bytes of nonce\n nonce.set(aTime, 8 - aTime.length);\n // copy data if present\n if (data) {\n const aData = this.itoa(data);\n nonce.set(aData, 12 - aData.length);\n }\n return nonce;\n }\n\n /**\n * Splits an integer into an array of bytes\n */\n private static itoa(num: number): Uint8Array {\n // calculate length first\n let hex = num.toString(16);\n hex = hex.length & 1 ? `0${hex}` : hex;\n const len = hex.length / 2;\n const byteArray = new Uint8Array(len);\n\n for (let j = 0, i = 0; i < hex.length; i += 2, j++) {\n byteArray[j] = parseInt(hex[i] + hex[i + 1], 16);\n }\n return byteArray;\n }\n}\n","import { Base64 } from './utils/utils';\n\n/**\n * Interfaces of Zax relay command requests and responses.\n * See https://github.com/vault12/zax for details.\n *\n * Full technical specification:\n * https://s3-us-west-1.amazonaws.com/vault12/crypto_relay.pdf\n */\n\n// In the future versions, plugins could add their own commands to specific relays\nexport enum RelayCommand {\n // Zax message commands\n count = 'count',\n upload = 'upload',\n download = 'download',\n messageStatus = 'messageStatus',\n delete = 'delete',\n // Zax file commands\n startFileUpload = 'startFileUpload',\n uploadFileChunk = 'uploadFileChunk',\n downloadFileChunk = 'downloadFileChunk',\n fileStatus = 'fileStatus',\n deleteFile = 'deleteFile'\n}\n\n/**\n * Status of a previously sent message equals redis TTL.\n * See https://redis.io/commands/ttl.\n * -2 : missing key\n * -1 : key never expires\n * 0+ : key time to live in seconds\n */\nexport enum MessageStatusResponse {\n MissingKey = -2,\n NeverExpires = -1\n}\n\n// -------------- Message formats --------------\n\nexport enum ZaxMessageKind {\n message = 'message',\n file = 'file',\n plain = 'plain'\n}\n\n/**\n * Format of raw messages provided by `download` Zax command\n */\nexport interface ZaxRawMessage {\n data: string;\n time: number;\n from: Base64;\n nonce: Base64;\n kind: ZaxMessageKind.message | ZaxMessageKind.file;\n}\n\nexport interface ZaxTextMessage {\n /**\n * Decoded object after JSON.parse\n */\n data: string;\n time: number;\n senderTag: string;\n nonce: Base64;\n kind: ZaxMessageKind.message;\n}\n\nexport interface ZaxFileMessage {\n data: FileUploadMetadata;\n time: number;\n senderTag: string;\n uploadID: string;\n nonce: Base64;\n kind: ZaxMessageKind.file;\n}\n\nexport interface ZaxPlainMessage {\n data: string;\n time: number;\n from: Base64;\n nonce: Base64;\n senderTag: undefined;\n kind: ZaxMessageKind.plain;\n}\n\nexport type ZaxParsedMessage = ZaxPlainMessage | ZaxFileMessage | ZaxTextMessage;\n\n// -------------- File command responses --------------\n// See https://github.com/vault12/zax/wiki/Zax-2.0-File-Commands\n\nexport interface StartFileUploadResponse {\n uploadID: string;\n max_chunk_size: number;\n storage_token: string;\n skey: Uint8Array;\n}\n\nexport interface UploadFileChunkResponse {\n status: 'OK' | 'NOT_FOUND';\n}\n\nexport interface FileStatusResponse {\n status: 'COMPLETE' | 'UPLOADING' | 'START' | 'NOT_FOUND';\n total_chunks: number;\n file_size: number;\n bytes_stored: number;\n}\n\nexport interface DeleteFileResponse {\n status: 'OK' | 'NOT_FOUND';\n}\n\nexport interface FileUploadMetadata {\n name: string;\n orig_size: number;\n md5?: string;\n created?: number;\n modified?: number;\n attrs?: string;\n skey?: Base64;\n}\n","export class GlowNetworkError extends Error {\n readonly name = 'GlowNetworkError';\n\n /**\n * @param status HTTP status code, 0 for network-layer failures, 408 for timeouts\n * @param details relay-provided reason from the `X-Error-Details` response header.\n * Most relay rejections share one generic text on purpose; conditions the relay\n * considers safe to reveal (e.g. a full mailbox or an exceeded storage quota)\n * carry a specific message the caller can branch on.\n * @param retryAfter seconds until the relay accepts requests from this sender\n * again, from the `Retry-After` response header on a 429. Zero means a retry\n * is acceptable immediately. Undefined when the relay doesn't send the header.\n */\n constructor(public status: number|undefined, public details?: string, public retryAfter?: number) {\n super(details ? `GlowNetworkError status: ${status} (${details})` : `GlowNetworkError status: ${status}`);\n }\n}\n","import { NaCl } from '../nacl/nacl';\nimport { NaClDriver, EncryptedMessage } from '../nacl/nacl-driver.interface';\nimport { EncryptionHelper } from '../nacl/encryption.helper';\nimport { config } from '../config';\nimport { Base64, Utils } from '../utils/utils';\nimport { Keys } from '../keys/keys';\nimport { RelayCommand } from '../zax.interface';\nimport { GlowNetworkError } from './network-error';\n\nexport interface RelayConnectionData {\n h2Signature: Uint8Array;\n relayPublicKey: Uint8Array;\n}\n\n/**\n * Low-level operations with Zax relay\n */\nexport class Relay {\n private nacl: NaClDriver;\n private difficulty = 0;\n private publicKey?: Uint8Array;\n private clientToken?: Uint8Array;\n private sessionKeys?: Keys;\n private tokenExpirationTimeoutHandle?: ReturnType<typeof setTimeout>;\n private sessionExpirationTimeoutHandle?: ReturnType<typeof setTimeout>;\n\n constructor(public url: string) {\n this.nacl = NaCl.getInstance();\n }\n\n get isConnected() {\n // relay is assumed to be connected when it got session keys and server public\n // sessionKeys are not permanent so relay won't be connected when sessionKeys will be removed within timeout\n return !!(this.sessionKeys && this.publicKey);\n }\n\n // ---------- Connection initialization ----------\n\n /**\n * Exchanges tokens with a relay and gets a temp session key for this relay.\n * Returns h₂(signature) and a relay public key\n */\n async openConnection(): Promise<RelayConnectionData> {\n this.sessionKeys = new Keys(await this.nacl.crypto_box_keypair());\n this.clientToken = await this.nacl.random_bytes(config.RELAY_TOKEN_LEN);\n const relayToken = await this.fetchRelayToken();\n this.publicKey = await this.fetchRelayPublicKey(relayToken);\n return {\n h2Signature: await this.getSignature(relayToken, this.sessionKeys),\n relayPublicKey: this.publicKey\n };\n }\n\n /**\n * Sends a client token to a relay and saves a relay token\n */\n private async fetchRelayToken(): Promise<Uint8Array> {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n const data = await this.httpCall('start_session', Utils.toBase64(this.clientToken));\n // Set a timer to mark a relay instance as having an expired token after a certain time\n this.scheduleTokenExpiration();\n // Relay responds with its own counter token. Until session is established these 2 tokens are handshake id.\n const [token, difficulty] = this.parseResponse('start_session', data);\n\n this.difficulty = parseInt(difficulty, 10);\n if (this.difficulty > 10) {\n console.log(`[Relay] ${this.url} requested difficulty ${this.difficulty}. Session handshake may take longer.`);\n }\n\n return Utils.fromBase64(token);\n }\n\n /**\n * Completes the handshake and saves a relay pubic key\n */\n private async fetchRelayPublicKey(relayToken: Uint8Array) {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n // After clientToken is sent to the relay, we use only h2() of it\n const h2ClientToken = Utils.toBase64(await this.nacl.h2(this.clientToken));\n\n const handshake = new Uint8Array([...this.clientToken, ...relayToken]);\n let sessionHandshake: Uint8Array;\n\n // Compute session handshake based on difficulty level set by the server\n if (this.difficulty === 0) {\n sessionHandshake = await this.nacl.h2(handshake);\n } else {\n sessionHandshake = await this.ensureNonceDifficulty(handshake);\n }\n\n // We confirm handshake by sending back h2(clientToken, relay_token)\n const relayPk = await this.httpCall('verify_session', h2ClientToken, Utils.toBase64(sessionHandshake));\n // Relay gives us back temp session key masked by clientToken we started with\n return Utils.fromBase64(relayPk);\n }\n\n /**\n * Attaches a mailbox and fetches number of messages\n */\n async prove(payload: EncryptedMessage): Promise<string> {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n if (!this.sessionKeys) {\n throw new Error('[Relay] No session key found, open the connection first');\n }\n const h2ClientToken = Utils.toBase64(await this.nacl.h2(this.clientToken));\n const result = await this.httpCall('prove',\n h2ClientToken, this.sessionKeys.publicKey, payload.nonce, payload.ctext);\n this.scheduleSessionExpiration();\n return result;\n }\n\n async encodeMessage(message: string): Promise<EncryptedMessage> {\n if (!this.publicKey) {\n throw new Error('[Relay] No relay public key found, open the connection first');\n }\n if (!this.sessionKeys) {\n throw new Error('[Relay] No session key found, open the connection first');\n }\n\n return await EncryptionHelper.encodeMessage(\n await this.nacl.encode_utf8(message), this.publicKey, Utils.fromBase64(this.sessionKeys?.privateKey));\n }\n\n async decodeMessage<T>(nonce: Base64, ctext: Base64): Promise<T> {\n const relayPk = this.publicKey;\n if (!relayPk) {\n throw new Error('[Relay] No relay public key found, open the connection first');\n }\n if (!this.sessionKeys) {\n throw new Error('[Relay] No session key found, open the connection first');\n }\n\n const decodedData = await EncryptionHelper.decodeMessage(Utils.fromBase64(nonce), Utils.fromBase64(ctext), relayPk,\n Utils.fromBase64(this.sessionKeys.privateKey));\n if (decodedData === null) {\n throw new Error('[Relay] failed to decode message');\n }\n return JSON.parse(decodedData);\n }\n\n // ---------- Low-level server request handling ----------\n\n /**\n * Executes a message/file command on a relay, parses and validates the response\n */\n async runCmd(command: RelayCommand, hpk: Base64, message: EncryptedMessage, ctext?: string): Promise<string[]> {\n if (!Object.keys(RelayCommand).includes(command)) {\n throw new Error(`[Relay] ${this.url} doesn't support command ${command}`);\n }\n\n const payload = [hpk, message.nonce, message.ctext];\n // if we are sending symmetrically encrypted ctext, it goes on the next line in the payload\n if (ctext) {\n payload.push(ctext);\n }\n\n const response = await this.httpCall('command', ...payload);\n return this.parseResponse(command, response);\n }\n\n private async getSignature(relayToken: Uint8Array, sessionKeys: Keys) {\n if (!this.clientToken) {\n throw new Error('[Relay] clientToken is required please openConnection first');\n }\n const clientTempPk = Utils.fromBase64(sessionKeys.publicKey);\n // Alice creates a 32 byte session signature as h₂(a_temp_pk, relayToken, clientToken)\n const signature = new Uint8Array([...clientTempPk, ...relayToken, ...this.clientToken]);\n const h2Signature = await this.nacl.h2(signature);\n return h2Signature;\n }\n\n /**\n * Executes a call to a relay and return raw string response\n */\n // TODO: extract fetching into separate function\n private async httpCall(command: string, ...params: string[]): Promise<string> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), config.RELAY_AJAX_TIMEOUT);\n\n const requestInit: RequestInit = {\n method: 'POST',\n headers: {\n Accept: 'text/plain',\n 'Content-Type': 'text/plain',\n },\n body: params.join('\\r\\n'),\n signal: controller.signal,\n };\n\n // NOTE: Network and server errors are not handled ny Glow itself.\n // They should instead be handled where the library is used\n try {\n const response = await fetch(`${this.url}/${command}`, requestInit);\n if (!response.ok) {\n if (response.status === 401) {\n // clear session if unauthorized\n this.clearSession();\n this.clearToken();\n }\n // the relay names safe-to-reveal rejection reasons in X-Error-Details\n // and the seconds until a rate-limited sender may retry in Retry-After;\n // reading either cross-origin requires the relay to expose it via CORS\n const retryAfter = response.headers.get('retry-after');\n throw new GlowNetworkError(response.status, response.headers.get('x-error-details') ?? undefined,\n // strict delta-seconds (1*DIGIT): zero is valid (\"retry now\"); the\n // HTTP-date form and any other spelling are ignored\n retryAfter !== null && /^\\d+$/.test(retryAfter) ? parseInt(retryAfter, 10) : undefined);\n }\n return await response.text();\n } catch (err: unknown) {\n if (err instanceof Error && err.name === 'AbortError') {\n // Timeout occurred during fetch request or while downloading response body\n throw new GlowNetworkError(408);\n }\n if (err instanceof GlowNetworkError) {\n throw err;\n }\n if (err instanceof TypeError) {\n // Network layer errors (no internet, DNS failure, CORS, etc.)\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch#exceptions\n // Use status 0 to indicate network connectivity issues\n throw new GlowNetworkError(0);\n }\n // For any other unexpected errors, re-throw the original error\n throw err;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Parses relay response and throws an error if its format is unexpected\n */\n private parseResponse(command: string, rawResponse: string): string[] {\n const response = rawResponse.split('\\r\\n');\n\n if (!rawResponse || !this.validateResponse(command, response.length)) {\n console.log(response);\n throw new Error(`[Relay] ${this.url} - ${command}: Bad response`);\n }\n\n return response;\n }\n\n /**\n * Compares the expected number of lines in response with what was actually received from a relay\n */\n private validateResponse(command: string, lines: number): boolean {\n switch (command) {\n case RelayCommand.upload:\n case RelayCommand.messageStatus:\n case RelayCommand.delete:\n return lines === 1;\n case RelayCommand.downloadFileChunk:\n return lines === 3;\n default:\n return lines === 2;\n }\n }\n\n /**\n * delete token locally before it was removed on server so will reconnect without getting unauthorized error\n */\n private scheduleTokenExpiration() {\n if (this.tokenExpirationTimeoutHandle) {\n clearTimeout(this.tokenExpirationTimeoutHandle);\n }\n this.tokenExpirationTimeoutHandle = setTimeout(() => this.clearToken(), config.RELAY_TOKEN_TIMEOUT);\n }\n\n clearToken() {\n if (this.tokenExpirationTimeoutHandle) {\n clearTimeout(this.tokenExpirationTimeoutHandle);\n }\n if (this.clientToken) {\n delete this.clientToken;\n }\n }\n\n /**\n * established session is valid only for some period need to remove it after it's ttl\n * so relay will reconnect without receiving error from server\n */\n private scheduleSessionExpiration() {\n if (this.sessionExpirationTimeoutHandle) {\n clearTimeout(this.sessionExpirationTimeoutHandle);\n }\n this.sessionExpirationTimeoutHandle = setTimeout(() => {\n this.clearSession();\n }, config.RELAY_SESSION_TIMEOUT);\n }\n\n clearSession() {\n if (this.sessionExpirationTimeoutHandle) {\n clearTimeout(this.sessionExpirationTimeoutHandle);\n }\n if (this.sessionKeys) {\n delete this.sessionKeys;\n }\n }\n\n // ---------- Difficulty adjustment ----------\n\n /**\n * Continuously calculates the nonce until one requested by a relay is found\n */\n private async ensureNonceDifficulty(handshake: Uint8Array) {\n let nonce;\n let h2;\n do {\n nonce = await this.nacl.random_bytes(32);\n h2 = await this.nacl.h2(new Uint8Array([...handshake, ...nonce]));\n } while (!this.arrayZeroBits(h2));\n\n return nonce;\n }\n\n /**\n * Returns `true` if the rightmost n bits of a byte are 0.\n * Check whether the rightmost difficulty bits of an Uint8Array are 0, where\n * the lowest indexes of the array represent those rightmost bits. Thus if\n * the difficulty is 17, then array[0] and array[1] should be 0, as should the\n * rightmost bit of array[2]. This is used for our difficulty settings in Zax to\n * reduce burden on a busy server by ensuring clients have to do some\n * additional work during the session handshake\n */\n private arrayZeroBits(array: Uint8Array): boolean {\n let byte;\n let n = this.difficulty;\n for (let i = 0; i <= (1 + this.difficulty / 8); i++) {\n byte = array[i];\n if (n <= 0) {\n return true;\n }\n if (n > 8) {\n n -= 8;\n if (byte > 0) {\n return false;\n }\n } else {\n return this.firstZeroBits(byte, n);\n }\n }\n return false;\n }\n\n /**\n * Returns `true` if the rightmost n bits of a byte are 0\n */\n private firstZeroBits(byte: number, n: number): boolean {\n return byte === ((byte >> n) << n);\n }\n}\n","import { Relay } from './relay';\n\nexport class RelayFactory {\n\n private relays: { [url: string]: Relay } = {};\n\n /**\n * Relay factory, that returns a Relay instance for a given URL,\n * or creates a new one if it hasn't yet been initialized.\n */\n getInstance(url: string) {\n let relay = this.relays[url];\n if (!relay) {\n relay = this.relays[url] = new Relay(url);\n }\n return relay;\n }\n}","import { NaCl } from '../nacl/nacl';\nimport { NaClDriver, EncryptedMessage } from '../nacl/nacl-driver.interface';\nimport { EncryptionHelper } from '../nacl/encryption.helper';\nimport { KeyRing } from '../keyring/keyring';\nimport { Base64, Utils } from '../utils/utils';\nimport { Relay, RelayConnectionData } from '../relay/relay';\nimport {\n RelayCommand,\n StartFileUploadResponse,\n UploadFileChunkResponse,\n FileStatusResponse,\n DeleteFileResponse,\n MessageStatusResponse,\n FileUploadMetadata,\n ZaxMessageKind,\n ZaxRawMessage,\n ZaxFileMessage,\n ZaxPlainMessage,\n ZaxTextMessage,\n ZaxParsedMessage\n} from '../zax.interface';\nimport { RelayFactory } from '../relay/relay-factory';\nimport { Mutex } from 'async-mutex';\n\n\n/**\n * Mailbox class represents a wrapper around a Keyring that allows to exchange\n * encrypted messages with other Mailboxes via a relay\n */\nexport class Mailbox {\n public keyRing: KeyRing;\n public identity: string;\n /**\n * each mailbox use it's own relays for connection\n * this gives possibility to connect different mailboxes to same server simultaneously\n * because they will use different session keys, tokens, server pub keys\n */\n\n private relayFactory = new RelayFactory;\n\n private relayConnectionMutexes = new Map<string, Mutex>();\n\n private nacl: NaClDriver;\n\n private constructor(naclDriver: NaClDriver, keyRing: KeyRing, identity: string) {\n this.nacl = naclDriver;\n this.keyRing = keyRing;\n this.identity = identity;\n }\n\n static async new(identity: string): Promise<Mailbox> {\n return new Mailbox(NaCl.getInstance(), await KeyRing.new(identity), identity);\n }\n\n // ---------- Alternative initializers ----------\n\n /**\n * Create a Mailbox where the secret identity key is derived from a well-known seed\n */\n static async fromSeed(id: string, seed: Uint8Array | string): Promise<Mailbox> {\n const mbx = await this.new(id);\n if (!(seed instanceof Uint8Array)) {\n seed = Utils.encode_latin1(seed);\n }\n await mbx.keyRing.setCommFromSeed(seed);\n return mbx;\n }\n\n /**\n * Create a Mailbox from the known secret identity key\n */\n static async fromSecKey(id: string, rawSecretKey: Uint8Array | Base64): Promise<Mailbox> {\n const mbx = await this.new(id);\n if (!(rawSecretKey instanceof Uint8Array)) {\n rawSecretKey = Utils.fromBase64(rawSecretKey);\n }\n await mbx.keyRing.setCommFromSecKey(rawSecretKey);\n return mbx;\n }\n\n /**\n * Create a Mailbox from the backup string\n */\n static async fromBackup(identity: string, backup: string): Promise<Mailbox> {\n return new Mailbox(NaCl.getInstance(), await KeyRing.fromBackup(identity, backup), identity);\n }\n\n // ---------- Relay message commands (public API) ----------\n\n /**\n * Sends a free-form object to a guest we already have in our keyring. Set `encrypt` to `false` to\n * send a plaintext message. Returns a token that can be used with `messageStatus` command to check\n * the status of the message\n */\n async upload(url: string, guestKey: string, message: string, encrypt = true): Promise<Base64> {\n const relay = await this.prepareRelay(url);\n const guestPk = this.getGuestKey(guestKey);\n const payload = encrypt ? await this.encodeMessage(guestKey, message) : message;\n const toHpk = Utils.toBase64(await this.nacl.h2(Utils.fromBase64(guestPk)));\n\n const [token] = await this.runRelayCommand(relay, RelayCommand.upload, { to: toHpk, payload });\n return token;\n }\n\n /**\n * Downloads all messages from a relay, decrypts them with a relay key,\n * and then parses each message to find out if it's a text message, file message,\n * or if it can't be decrypted because HPK is missing in the keyring.\n * Returns an array of mixed messages\n */\n async download(url: string) {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.download);\n const messages: ZaxRawMessage[] = await this.decryptResponse<ZaxRawMessage[]>(relay, response);\n\n const parsedMessages: ZaxParsedMessage[] = [];\n for (const message of messages) {\n const senderTag = this.keyRing.getTagByHpk(message.from);\n if (!senderTag) {\n parsedMessages.push(await this.parsePlainMessage(message));\n } else if (message.kind === 'message') {\n parsedMessages.push(await this.parseTextMessage(message, senderTag));\n } else if (message.kind === 'file') {\n parsedMessages.push(await this.parseFileMessage(message, senderTag));\n } else {\n throw new Error('[Mailbox] download - Unknown message type');\n }\n }\n return parsedMessages;\n }\n\n /**\n * Marks a raw Zax message as one that can't be decrypted,\n * because sender's HPK is not found in the keyring\n */\n private async parsePlainMessage({ data, time, from, nonce }: ZaxRawMessage) {\n return { data, time, from, nonce, kind: ZaxMessageKind.plain } as ZaxPlainMessage;\n }\n\n /**\n * Decrypts a message that represents uploaded file metadata\n */\n private async parseFileMessage(message: ZaxRawMessage, senderTag: string) {\n const { nonce, ctext, uploadID } = JSON.parse(message.data);\n const rawData = await this.decodeMessage(senderTag, nonce, ctext);\n if (rawData === null) {\n throw new Error('[Mailbox] Failed to decode file message');\n }\n const data = JSON.parse(rawData) as FileUploadMetadata;\n return { data, time: message.time, senderTag, uploadID, nonce, kind: ZaxMessageKind.file } as ZaxFileMessage;\n }\n\n /**\n * Attempts to decrypt a regular encrypted Zax message. Returns plain message if it was sent encrypted\n */\n private async parseTextMessage(message: ZaxRawMessage, senderTag: string): Promise<ZaxTextMessage> {\n let data = await this.decodeMessage(senderTag, message.nonce, message.data);\n // If the message was sent unencrypted, the line above will return `null`\n if (!data) {\n data = message.data;\n }\n return ({ data, time: message.time, senderTag, nonce: message.nonce, kind: ZaxMessageKind.message });\n }\n\n /**\n * Returns the number of messages in the mailbox on a given relay\n */\n async count(url: string): Promise<number> {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.count);\n return await this.decryptResponse(relay, response);\n }\n\n /**\n * Deletes messages from a relay given a list of base64 message nonces,\n * and returns the number of remaining messages\n */\n async delete(url: string, nonceList: Base64[]): Promise<number> {\n const relay = await this.prepareRelay(url);\n const [response] = await this.runRelayCommand(relay, RelayCommand.delete, { payload: nonceList });\n return parseInt(response, 10);\n }\n\n /**\n * Gets the status of a previously sent Zax message by a storage token.\n * Returns \"time to live\" in seconds or a negative value if it's not applicable.\n * See `MessageStatusResponse` values for reference\n */\n async messageStatus(url: string, storageToken: Base64): Promise<MessageStatusResponse | number> {\n const relay = await this.prepareRelay(url);\n const [response] = await this.runRelayCommand(relay, RelayCommand.messageStatus, { token: storageToken });\n const status = parseInt(response, 10);\n return status;\n }\n\n // ---------- Relay file commands (public API) ----------\n\n /**\n * Asks Zax to start a new upload session, and returns a unique file identifier\n * required to upload file chunks.\n */\n async startFileUpload(url: string, guest: string,\n rawMetadata: FileUploadMetadata): Promise<StartFileUploadResponse> {\n const relay = await this.prepareRelay(url);\n const guestPk = this.getGuestKey(guest);\n const toHpk = Utils.toBase64(await this.nacl.h2(Utils.fromBase64(guestPk)));\n\n const secretKey = await this.nacl.random_bytes(this.nacl.crypto_secretbox_KEYBYTES);\n rawMetadata.skey = Utils.toBase64(secretKey);\n\n const metadata = await this.encodeMessage(guest, JSON.stringify(rawMetadata));\n\n const response = await this.runRelayCommand(relay, RelayCommand.startFileUpload, {\n to: toHpk,\n file_size: rawMetadata.orig_size,\n metadata\n });\n\n const decrypted = await this.decryptResponse<StartFileUploadResponse>(relay, response);\n // append symmetric secret key (unique for this upload session) to the server response\n decrypted.skey = secretKey;\n return decrypted;\n }\n\n /**\n * Encrypts the file chunk symmetrically and transfers it to a relay\n */\n async uploadFileChunk(url: string, uploadID: string, chunk: Uint8Array,\n part: number, totalParts: number, skey: Uint8Array): Promise<UploadFileChunkResponse> {\n const relay = await this.prepareRelay(url);\n const encodedChunk = await EncryptionHelper.encodeMessageSymmetric(chunk, skey);\n const response = await this.runRelayCommand(relay, RelayCommand.uploadFileChunk, {\n uploadID,\n part,\n last_chunk: (totalParts - 1 === part), // marker of the last chunk, sent only once\n nonce: encodedChunk.nonce\n }, encodedChunk.ctext); // do not encode file chunk contents, as it's already encoded with symmetric encryption\n return await this.decryptResponse(relay, response);\n }\n\n /**\n * Returns the status of a file upload by its relay-specific uploadID. Uploader can call it\n * to verify the correct transfer, and downloader can check if the file exists and retrieve\n * the number of chunks\n */\n async getFileStatus(url: string, uploadID: string): Promise<FileStatusResponse> {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.fileStatus, { uploadID });\n return await this.decryptResponse(relay, response);\n }\n\n /**\n * Fetches the file metadata by uploadID, which was declared by the uploader\n */\n async getFileMetadata(url: string, uploadID: string): Promise<FileUploadMetadata> {\n const messages = await this.download(url);\n\n const fileMessage = messages\n .filter(message => message.kind === 'file')\n .find(message => (message as ZaxFileMessage).uploadID === uploadID);\n return fileMessage?.data as FileUploadMetadata;\n }\n\n /**\n * Downloads a binary chunk of a file from a relay by a given uploadID. The total number of chunks\n * can be retrieved via a `getFileStatus` request\n */\n async downloadFileChunk(url: string, uploadID: string, part: number, skey: Uint8Array | Base64):\n Promise<Uint8Array | null> {\n if (!(skey instanceof Uint8Array)) {\n skey = Utils.fromBase64(skey);\n }\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.downloadFileChunk, { uploadID, part });\n const [nonce, ctext, fileCtext] = response;\n const decoded = await relay.decodeMessage<{nonce: string}>(nonce, ctext);\n return await EncryptionHelper.decodeMessageSymmetric(decoded.nonce, fileCtext, skey);\n }\n\n /**\n * Deletes a file from the relay (or all chunks uploaded so far, if the upload was not completed).\n * Can be called by either the sender or recipient\n */\n async deleteFile(url: string, uploadID: string): Promise<DeleteFileResponse> {\n const relay = await this.prepareRelay(url);\n const response = await this.runRelayCommand(relay, RelayCommand.deleteFile, { uploadID });\n return await this.decryptResponse(relay, response);\n }\n\n // ---------- Dealing with Relay ----------\n\n /**\n * Establishes a session, exchanges temp keys and proves our ownership of this\n * Mailbox to this specific relay. This is the first function to start\n * communications with any relay. Returns the number of messages in the mailbox\n */\n async connectToRelay(url: string): Promise<number> {\n const relay = this.relayFactory.getInstance(url);\n const connectionData = await relay.openConnection();\n const encryptedSignature = await this.encryptSignature(connectionData);\n\n const messagesNumber = await relay.prove(await relay.encodeMessage(JSON.stringify({\n pub_key: this.keyRing.getPubCommKey(),\n nonce: encryptedSignature.nonce,\n ctext: encryptedSignature.ctext\n })));\n return parseInt(messagesNumber, 10);\n }\n\n clearSession(url: string) {\n const relay = this.relayFactory.getInstance(url);\n relay.clearToken();\n relay.clearSession();\n }\n\n private async encryptSignature(connection: RelayConnectionData) {\n const privateKey = Utils.fromBase64(this.keyRing.getPrivateCommKey());\n return await EncryptionHelper.encodeMessage(connection.h2Signature, connection.relayPublicKey, privateKey);\n }\n\n /**\n * Gets a singleton Relay instance, and reconnects to a relay if a previous token has expired\n */\n private async prepareRelay(url: string): Promise<Relay> {\n const relay = this.relayFactory.getInstance(url);\n /**\n * allow establishing only once connection for pair mailbox-relay\n */\n await this.getRelayConnectionMutex(url).runExclusive(async () => {\n if (!relay.isConnected) {\n await this.connectToRelay(url);\n }\n });\n return relay;\n }\n\n /**\n * Encrypts the payload of the command and sends it to a relay\n */\n private async runRelayCommand(\n relay: Relay, command: RelayCommand, params?: {[key:string]: unknown}, ctext?: string): Promise<string[]> {\n params = { cmd: command, ...params };\n const hpk = await this.keyRing.getHpk();\n const message = await relay.encodeMessage(JSON.stringify(params));\n return await relay.runCmd(command, hpk, message, ctext);\n }\n\n /**\n * Parses relay's response to a command, for those commands that expect an encrypted message in return.\n * Two lines of POST response will be nonce and ctext\n */\n private async decryptResponse<T>(relay: Relay, response: string[]) {\n const [nonce, ctext] = response;\n return await relay.decodeMessage<T>(nonce, ctext);\n }\n\n // ---------- Message encoding / decoding ----------\n\n /**\n * Encodes `message` to the guest key of a guest already\n * added to the keyring\n */\n async encodeMessage(guest: string, message: string): Promise<EncryptedMessage> {\n const guestPk = this.getGuestKey(guest);\n const privateKey = this.keyRing.getPrivateCommKey();\n\n return await EncryptionHelper.encodeMessage(\n await this.nacl.encode_utf8(message), Utils.fromBase64(guestPk), Utils.fromBase64(privateKey));\n }\n\n /**\n * Decodes a ciphertext from a guest key already in our keyring with this nonce\n * @returns null if failed to decode\n */\n async decodeMessage(guest: string, nonce: Base64, ctext: Base64) {\n const guestPk = this.getGuestKey(guest);\n const privateKey = this.keyRing.getPrivateCommKey();\n let uint8ArrayCtext: Uint8Array;\n try {\n uint8ArrayCtext = Utils.fromBase64(ctext);\n } catch {\n // looks like ctext was not encoded\n return null;\n }\n\n return await EncryptionHelper.decodeMessage(Utils.fromBase64(nonce), uint8ArrayCtext,\n Utils.fromBase64(guestPk), Utils.fromBase64(privateKey));\n }\n\n /**\n * Wrapper around `keyring.getGuestKey` that handles unknown guests\n */\n private getGuestKey(guest: string): string {\n const guestPk = this.keyRing.getGuestKey(guest);\n if (!guestPk) {\n throw new Error(`[Mailbox] Unknown guest ${guest}`);\n }\n return guestPk;\n }\n\n // ---------- Destroying Mailbox ----------\n\n /**\n * Deletes a Mailbox and all its data from local CryptoStorage. This is a very\n * destructive operation, use with caution - it will delete the Mailbox\n * keyring along with all stored public keys. To restore that information, you\n * will need to do another key exchange with all the guests on your keyring.\n */\n async selfDestruct() {\n await this.keyRing.selfDestruct();\n }\n\n private getRelayConnectionMutex(url: string) {\n if (!this.relayConnectionMutexes.has(url)) {\n this.relayConnectionMutexes.set(url, new Mutex());\n }\n return this.relayConnectionMutexes.get(url) as Mutex;\n }\n}\n","import { StorageDriver } from './storage-driver.interface';\n\n/**\n * temporary created in-memory storage for testing purposes\n */\nexport class InMemoryStorage implements StorageDriver {\n\n private storage: {[key: string]: string|null} = {};\n\n async get(key: string) {\n return (await this.getMultiple([key]))[0];\n }\n\n async getMultiple(keys: string[]) {\n return keys.map(key => this.storage[key]);\n }\n\n set<T extends string | null>(key: string, value: T) {\n return this.setMultiple({[key]: value});\n }\n\n async setMultiple<T extends string | null>(values: { [key: string]: T; }){\n for (const key in values) {\n this.storage[key] = values[key];\n }\n }\n\n remove(key: string) {\n return this.removeMultiple([key]);\n }\n\n async removeMultiple(keys: string[]) {\n keys.forEach(key => delete this.storage[key]);\n }\n\n reset() {\n this.storage = {};\n }\n}"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -47,8 +47,20 @@ declare interface FileUploadMetadata {
|
|
|
47
47
|
|
|
48
48
|
export declare class GlowNetworkError extends Error {
|
|
49
49
|
status: number | undefined;
|
|
50
|
+
details?: string | undefined;
|
|
51
|
+
retryAfter?: number | undefined;
|
|
50
52
|
readonly name = "GlowNetworkError";
|
|
51
|
-
|
|
53
|
+
/**
|
|
54
|
+
* @param status HTTP status code, 0 for network-layer failures, 408 for timeouts
|
|
55
|
+
* @param details relay-provided reason from the `X-Error-Details` response header.
|
|
56
|
+
* Most relay rejections share one generic text on purpose; conditions the relay
|
|
57
|
+
* considers safe to reveal (e.g. a full mailbox or an exceeded storage quota)
|
|
58
|
+
* carry a specific message the caller can branch on.
|
|
59
|
+
* @param retryAfter seconds until the relay accepts requests from this sender
|
|
60
|
+
* again, from the `Retry-After` response header on a 429. Zero means a retry
|
|
61
|
+
* is acceptable immediately. Undefined when the relay doesn't send the header.
|
|
62
|
+
*/
|
|
63
|
+
constructor(status: number | undefined, details?: string | undefined, retryAfter?: number | undefined);
|
|
52
64
|
}
|
|
53
65
|
|
|
54
66
|
/**
|