use webcrypto-liner to support Safari 10

This commit is contained in:
Danny Coates 2017-08-03 14:07:22 -07:00
parent 4d4098b7c9
commit fb41a40128
No known key found for this signature in database
GPG Key ID: 4C442633C62E00CB
7 changed files with 770 additions and 657 deletions

View File

@ -1,35 +1,30 @@
window.Raven = require('raven-js'); const testPilotGA = require('testpilot-ga');
const { sendEvent } = require('./utils');
const Raven = require('raven-js');
if (navigator.doNotTrack !== '1' && window.RAVEN_CONFIG) { if (navigator.doNotTrack !== '1' && window.RAVEN_CONFIG) {
window.Raven.config(window.SENTRY_ID, window.RAVEN_CONFIG).install(); Raven.config(window.SENTRY_ID, window.RAVEN_CONFIG).install();
} }
const testPilotGA = require('testpilot-ga'); const analytics = new testPilotGA({
const { gcmCompliant, sendEvent } = require('./utils');
window.analytics = new testPilotGA({
an: 'Firefox Send', an: 'Firefox Send',
ds: 'web', ds: 'web',
tid: window.GOOGLE_ANALYTICS_ID tid: window.GOOGLE_ANALYTICS_ID
}); });
const isSender = !location.pathname.includes('/download');
const ec = isSender ? 'sender' : 'recipient';
const ua = navigator.userAgent.toLowerCase(); const ua = navigator.userAgent.toLowerCase();
gcmCompliant().catch(err => {
sendEvent(ec, 'unsupported', {
cd6: err
}).then(() => {
location.replace('/unsupported/gcm');
});
});
if ( if (
ua.indexOf('firefox') > -1 && ua.indexOf('firefox') > -1 &&
parseInt(ua.match(/firefox\/*([^\n\r]*)\./)[1], 10) <= 49 parseInt(ua.match(/firefox\/*([^\n\r]*)\./)[1], 10) <= 49
) { ) {
const isSender = !location.pathname.includes('/download');
const ec = isSender ? 'sender' : 'recipient';
sendEvent(ec, 'unsupported', { sendEvent(ec, 'unsupported', {
cd6: new Error('Firefox is outdated.') cd6: new Error('Firefox is outdated.')
}).then(() => { }).then(() => {
location.replace('/unsupported/outdated'); location.replace('/unsupported/outdated');
}); });
} }
window.analytics = analytics;
window.Raven = Raven;

View File

@ -1,6 +1,6 @@
require('./common'); require('./common');
const FileReceiver = require('./fileReceiver'); const FileReceiver = require('./fileReceiver');
const { notify, findMetric, sendEvent } = require('./utils'); const { notify, findMetric, sendEvent, gcmCompliant } = require('./utils');
const bytes = require('bytes'); const bytes = require('bytes');
const Storage = require('./storage'); const Storage = require('./storage');
const storage = new Storage(localStorage); const storage = new Storage(localStorage);
@ -11,171 +11,181 @@ require('jquery-circle-progress');
const Raven = window.Raven; const Raven = window.Raven;
$(document).ready(function() { $(document).ready(function() {
//link back to homepage gcmCompliant()
$('.send-new').attr('href', window.location.origin); .then(function() {
//link back to homepage
$('.send-new').attr('href', window.location.origin);
$('.send-new').click(function() { $('.send-new').click(function() {
sendEvent('recipient', 'restarted', { sendEvent('recipient', 'restarted', {
cd2: 'completed'
});
});
$('.legal-links a, .social-links a, #dl-firefox').click(function(target) {
const metric = findMetric(target.currentTarget.href);
// record exited event by recipient
sendEvent('recipient', 'exited', {
cd3: metric
});
});
const filename = $('#dl-filename').text();
const bytelength = Number($('#dl-bytelength').text());
const timeToExpiry = Number($('#dl-ttl').text());
//initiate progress bar
$('#dl-progress').circleProgress({
value: 0.0,
startAngle: -Math.PI / 2,
fill: '#3B9DFF',
size: 158,
animation: { duration: 300 }
});
$('#download-btn').click(download);
function download() {
storage.totalDownloads += 1;
const fileReceiver = new FileReceiver();
const unexpiredFiles = storage.numFiles;
fileReceiver.on('progress', progress => {
window.onunload = function() {
storage.referrer = 'cancelled-download';
// record download-stopped (cancelled by tab close or reload)
sendEvent('recipient', 'download-stopped', {
cm1: bytelength,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads,
cd2: 'cancelled'
});
};
$('#download-page-one').attr('hidden', true);
$('#download-progress').removeAttr('hidden');
const percent = progress[0] / progress[1];
// update progress bar
$('#dl-progress').circleProgress('value', percent);
$('.percent-number').text(`${Math.floor(percent * 100)}`);
$('.progress-text').text(
`${filename} (${bytes(progress[0], {
decimalPlaces: 1,
fixedDecimals: true
})} of ${bytes(progress[1], { decimalPlaces: 1 })})`
);
});
let downloadEnd;
fileReceiver.on('decrypting', isStillDecrypting => {
// The file is being decrypted
if (isStillDecrypting) {
fileReceiver.removeAllListeners('progress');
window.onunload = null;
document.l10n.formatValue('decryptingFile').then(decryptingFile => {
$('.progress-text').text(decryptingFile);
});
} else {
downloadEnd = Date.now();
}
});
fileReceiver.on('hashing', isStillHashing => {
// The file is being hashed to make sure a malicious user hasn't tampered with it
if (isStillHashing) {
document.l10n.formatValue('verifyingFile').then(verifyingFile => {
$('.progress-text').text(verifyingFile);
});
} else {
$('.progress-text').text(' ');
document.l10n
.formatValues('downloadNotification', 'downloadFinish')
.then(translated => {
notify(translated[0]);
$('.title').text(translated[1]);
});
}
});
const startTime = Date.now();
// record download-started by recipient
sendEvent('recipient', 'download-started', {
cm1: bytelength,
cm4: timeToExpiry,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads
});
fileReceiver
.download()
.catch(err => {
// record download-stopped (errored) by recipient
sendEvent('recipient', 'download-stopped', {
cm1: bytelength,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads,
cd2: 'errored',
cd6: err
});
if (err.message === 'notfound') {
location.reload();
} else {
document.l10n.formatValue('errorPageHeader').then(translated => {
$('.title').text(translated);
});
$('#download-btn').attr('hidden', true);
$('#expired-img').removeAttr('hidden');
}
throw err;
})
.then(([decrypted, fname]) => {
const endTime = Date.now();
const totalTime = endTime - startTime;
const downloadTime = endTime - downloadEnd;
const downloadSpeed = bytelength / (downloadTime / 1000);
storage.referrer = 'completed-download';
// record download-stopped (completed) by recipient
sendEvent('recipient', 'download-stopped', {
cm1: bytelength,
cm2: totalTime,
cm3: downloadSpeed,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads,
cd2: 'completed' cd2: 'completed'
}); });
const dataView = new DataView(decrypted);
const blob = new Blob([dataView]);
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
if (window.navigator.msSaveBlob) {
// if we are in microsoft edge or IE
window.navigator.msSaveBlob(blob, fname);
return;
}
a.download = fname;
document.body.appendChild(a);
a.click();
})
.catch(err => {
Raven.captureException(err);
return Promise.reject(err);
}); });
}
$('.legal-links a, .social-links a, #dl-firefox').click(function(target) {
const metric = findMetric(target.currentTarget.href);
// record exited event by recipient
sendEvent('recipient', 'exited', {
cd3: metric
});
});
const filename = $('#dl-filename').text();
const bytelength = Number($('#dl-bytelength').text());
const timeToExpiry = Number($('#dl-ttl').text());
//initiate progress bar
$('#dl-progress').circleProgress({
value: 0.0,
startAngle: -Math.PI / 2,
fill: '#3B9DFF',
size: 158,
animation: { duration: 300 }
});
$('#download-btn').click(download);
function download() {
storage.totalDownloads += 1;
const fileReceiver = new FileReceiver();
const unexpiredFiles = storage.numFiles;
fileReceiver.on('progress', progress => {
window.onunload = function() {
storage.referrer = 'cancelled-download';
// record download-stopped (cancelled by tab close or reload)
sendEvent('recipient', 'download-stopped', {
cm1: bytelength,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads,
cd2: 'cancelled'
});
};
$('#download-page-one').attr('hidden', true);
$('#download-progress').removeAttr('hidden');
const percent = progress[0] / progress[1];
// update progress bar
$('#dl-progress').circleProgress('value', percent);
$('.percent-number').text(`${Math.floor(percent * 100)}`);
$('.progress-text').text(
`${filename} (${bytes(progress[0], {
decimalPlaces: 1,
fixedDecimals: true
})} of ${bytes(progress[1], { decimalPlaces: 1 })})`
);
});
let downloadEnd;
fileReceiver.on('decrypting', isStillDecrypting => {
// The file is being decrypted
if (isStillDecrypting) {
fileReceiver.removeAllListeners('progress');
window.onunload = null;
document.l10n.formatValue('decryptingFile').then(decryptingFile => {
$('.progress-text').text(decryptingFile);
});
} else {
downloadEnd = Date.now();
}
});
fileReceiver.on('hashing', isStillHashing => {
// The file is being hashed to make sure a malicious user hasn't tampered with it
if (isStillHashing) {
document.l10n.formatValue('verifyingFile').then(verifyingFile => {
$('.progress-text').text(verifyingFile);
});
} else {
$('.progress-text').text(' ');
document.l10n
.formatValues('downloadNotification', 'downloadFinish')
.then(translated => {
notify(translated[0]);
$('.title').text(translated[1]);
});
}
});
const startTime = Date.now();
// record download-started by recipient
sendEvent('recipient', 'download-started', {
cm1: bytelength,
cm4: timeToExpiry,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads
});
fileReceiver
.download()
.catch(err => {
// record download-stopped (errored) by recipient
sendEvent('recipient', 'download-stopped', {
cm1: bytelength,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads,
cd2: 'errored',
cd6: err
});
if (err.message === 'notfound') {
location.reload();
} else {
document.l10n.formatValue('errorPageHeader').then(translated => {
$('.title').text(translated);
});
$('#download-btn').attr('hidden', true);
$('#expired-img').removeAttr('hidden');
}
throw err;
})
.then(([decrypted, fname]) => {
const endTime = Date.now();
const totalTime = endTime - startTime;
const downloadTime = endTime - downloadEnd;
const downloadSpeed = bytelength / (downloadTime / 1000);
storage.referrer = 'completed-download';
// record download-stopped (completed) by recipient
sendEvent('recipient', 'download-stopped', {
cm1: bytelength,
cm2: totalTime,
cm3: downloadSpeed,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads,
cd2: 'completed'
});
const dataView = new DataView(decrypted);
const blob = new Blob([dataView]);
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
if (window.navigator.msSaveBlob) {
// if we are in microsoft edge or IE
window.navigator.msSaveBlob(blob, fname);
return;
}
a.download = fname;
document.body.appendChild(a);
a.click();
})
.catch(err => {
Raven.captureException(err);
return Promise.reject(err);
});
}
})
.catch(err => {
sendEvent('sender', 'unsupported', {
cd6: err
}).then(() => {
location.replace('/unsupported/gcm');
});
});
}); });

File diff suppressed because it is too large Load Diff

View File

@ -60,16 +60,22 @@ function gcmCompliant() {
) )
.then(() => { .then(() => {
return Promise.resolve(); return Promise.resolve();
})
.catch(err => {
return Promise.reject();
}); });
}) })
.catch(err => { .catch(err => {
return Promise.reject(); return loadShim();
}); });
} catch (err) { } catch (err) {
return Promise.reject(); return loadShim();
}
function loadShim() {
return new Promise((resolve, reject) => {
const shim = document.createElement('script');
shim.src = '/cryptofill.js';
shim.addEventListener('load', resolve);
shim.addEventListener('error', reject);
document.head.appendChild(shim);
});
} }
} }

55
package-lock.json generated
View File

@ -4,6 +4,12 @@
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@types/node": {
"version": "7.0.39",
"resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.39.tgz",
"integrity": "sha512-KQHAZeVsk4UIT9XaR6cn4WpHZzimK6UBD1UomQKfQQFmTlUHaNBzeuov+TM4+kigLO0IJt4I5OOsshcCyA9gSA==",
"dev": true
},
"accepts": { "accepts": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz",
@ -181,6 +187,12 @@
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
}, },
"asmcrypto.js": {
"version": "0.0.11",
"resolved": "https://registry.npmjs.org/asmcrypto.js/-/asmcrypto.js-0.0.11.tgz",
"integrity": "sha1-dLshuq/Z3OFoTcBvFShoOzUMHKA=",
"dev": true
},
"asn1.js": { "asn1.js": {
"version": "4.9.1", "version": "4.9.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz",
@ -5254,6 +5266,12 @@
"integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=",
"dev": true "dev": true
}, },
"tslib": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.7.1.tgz",
"integrity": "sha1-vIAEFkaRkjp5/oN4u+s9ogF1OOw=",
"dev": true
},
"tty-browserify": { "tty-browserify": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
@ -5290,6 +5308,12 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true "dev": true
}, },
"typescript": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.3.4.tgz",
"integrity": "sha1-PTgyGCgjHkNPKHUUlZw3qCtin0I=",
"dev": true
},
"uglify-js": { "uglify-js": {
"version": "2.8.29", "version": "2.8.29",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
@ -5440,6 +5464,37 @@
"indexof": "0.0.1" "indexof": "0.0.1"
} }
}, },
"webcrypto-core": {
"version": "0.1.16",
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-0.1.16.tgz",
"integrity": "sha1-3kkUtZFI23Moe8T45hz0+y9W8CA=",
"dev": true,
"requires": {
"@types/node": "6.0.85",
"tslib": "1.7.1"
},
"dependencies": {
"@types/node": {
"version": "6.0.85",
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.85.tgz",
"integrity": "sha512-6qLZpfQFO/g5Ns2e7RsW6brk0Q6Xzwiw7kVVU/XiQNOiJXSojhX76GP457PBYIsNMH2WfcGgcnZB4awFDHrwpA==",
"dev": true
}
}
},
"webcrypto-liner": {
"version": "0.1.25",
"resolved": "https://registry.npmjs.org/webcrypto-liner/-/webcrypto-liner-0.1.25.tgz",
"integrity": "sha512-p/wtjIvs10nZvruEJlglTf5Qjb9W/sDvfEFuw5RjM+axGaXj8mBGF7tFJb5sjVbSTqv1gsxktYWzG/PZfn2A8Q==",
"dev": true,
"requires": {
"@types/node": "7.0.39",
"asmcrypto.js": "0.0.11",
"elliptic": "6.4.0",
"typescript": "2.3.4",
"webcrypto-core": "0.1.16"
}
},
"which": { "which": {
"version": "1.2.14", "version": "1.2.14",
"resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",

View File

@ -17,6 +17,7 @@
"redis": "^2.7.1" "redis": "^2.7.1"
}, },
"devDependencies": { "devDependencies": {
"asmcrypto.js": "0.0.11",
"babel-polyfill": "^6.23.0", "babel-polyfill": "^6.23.0",
"browserify": "^14.4.0", "browserify": "^14.4.0",
"eslint": "^4.3.0", "eslint": "^4.3.0",
@ -38,7 +39,8 @@
"stylelint-config-standard": "^17.0.0", "stylelint-config-standard": "^17.0.0",
"supertest": "^3.0.0", "supertest": "^3.0.0",
"testpilot-ga": "^0.3.0", "testpilot-ga": "^0.3.0",
"uglifyify": "^4.0.3" "uglifyify": "^4.0.3",
"webcrypto-liner": "^0.1.25"
}, },
"engines": { "engines": {
"node": ">=8.0.0" "node": ">=8.0.0"

22
public/cryptofill.js Normal file

File diff suppressed because one or more lines are too long