import Nanobus from 'nanobus'; import { hexToArray, bytes } from './utils'; export default class FileReceiver extends Nanobus { constructor(url, k) { super('FileReceiver'); this.key = window.crypto.subtle.importKey( 'jwk', { k, kty: 'oct', alg: 'A128GCM', ext: true }, { name: 'AES-GCM' }, false, ['decrypt'] ); this.url = url; this.msg = 'fileSizeProgress'; this.progress = [0, 1]; } get progressRatio() { return this.progress[0] / this.progress[1]; } get sizes() { return { partialSize: bytes(this.progress[0]), totalSize: bytes(this.progress[1]) }; } cancel() { // TODO } downloadFile() { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.onprogress = event => { if (event.lengthComputable && event.target.status !== 404) { this.progress = [event.loaded, event.total]; this.emit('progress', this.progress); } }; xhr.onload = function(event) { if (xhr.status === 404) { reject(new Error('notfound')); return; } const blob = new Blob([this.response]); const meta = JSON.parse(xhr.getResponseHeader('X-File-Metadata')); const fileReader = new FileReader(); fileReader.onload = function() { resolve({ data: this.result, name: meta.filename, type: meta.mimeType, iv: meta.id }); }; fileReader.readAsArrayBuffer(blob); }; xhr.open('get', this.url); xhr.responseType = 'blob'; xhr.send(); }); } async download() { const key = await this.key; const file = await this.downloadFile(); this.msg = 'decryptingFile'; this.emit('decrypting'); const plaintext = await window.crypto.subtle.decrypt( { name: 'AES-GCM', iv: hexToArray(file.iv), tagLength: 128 }, key, file.data ); this.msg = 'downloadFinish'; return { plaintext, name: decodeURIComponent(file.name), type: file.type }; } }