Skip to content

Commit

Permalink
Use REST endpoint for backup device communication
Browse files Browse the repository at this point in the history
  • Loading branch information
Blobonat committed Jul 26, 2020
1 parent 5903cea commit 51a9bc0
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 115 deletions.
11 changes: 2 additions & 9 deletions dist/chromium/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,8 @@
</div>
<div id="psk">
<span>PSK Options</span>
<div>
Backup File Sync: <input type="file" accept="application/json" name="files[]" id="backupFile" />
</div>
<div>
Delegation File Sync: <input type="file" accept="application/json" name="files[]" id="delegationFile" />
</div>
<div>
<input type="button" id="recovery" value="Create Recovery keys"/>
</div>
<button type="button" id="Setup">Setup</button>
<button type="button" id="Recovery">Recover</button>
</div>
</body>

Expand Down
29 changes: 27 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@types/loglevel": "^1.6.3",
"@types/webappsec-credential-management": "^0.3.11",
"asn1js": "^2.0.26",
"axios": "^0.19.2",
"bn.js": "^5.1.2",
"cbor": "^4.3.0",
"elliptic": "^6.5.3",
Expand Down
32 changes: 11 additions & 21 deletions src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import {disabledIcons, enabledIcons} from './constants';
import {getLogger} from './logging';
import {getOriginFromUrl, webauthnParse, webauthnStringify} from './utils';
import {processCredentialRequest, processCredentialCreation} from './webauthn';
import {RecoveryKey, syncBackupKeys, syncDelegation} from "./recovery";
import {BackupDeviceBaseUrl, RecoveryKey, pskSetup, pskRecovery} from "./recovery";
import * as axios from 'axios';

const log = getLogger('background');

Expand Down Expand Up @@ -34,22 +35,14 @@ const requestPin = async (tabId: number, origin: string, newPin: boolean = true)
return pin;
};

const syncBackup = async (backupContent) => {
log.debug('Sync Backup called');

await syncBackupKeys(backupContent);
};

const syncDel = async (delegationContent) => {
log.debug('Sync Delegation called');

await syncDelegation(delegationContent);
const setup = async () => {
log.debug('Setup called');
await pskSetup();
};

const recovery = async (n) => {
log.debug('Create recovery keys called')

await RecoveryKey.generate(n);
const recovery = async () => {
log.debug('Recovery called!')
await pskRecovery();
}

const create = async (msg, sender: chrome.runtime.MessageSender) => {
Expand Down Expand Up @@ -122,14 +115,11 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
delete (pinProtectedCallbacks[msg.tabId]);
}
break;
case 'syncBackup':
syncBackup(msg.backup).then(() => alert("Backup file processed"));
break;
case 'syncDelegation':
syncDel(msg.delegation).then(() => alert("Delegation file processed"));
case 'setup':
setup().then(() => alert("Backup keys synchronized successfully!"));
break;
case 'recovery':
recovery(msg.amount).then(() => alert("Creating recovery keys finished"))
recovery().then(() => alert("Recovery finished successfully!"))
break;
default:
sendResponse(null);
Expand Down
48 changes: 5 additions & 43 deletions src/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,54 +11,16 @@ $(() => {
return;
}

$('#delegationFile').on('change', function(evt: Event) {
const files = (<HTMLInputElement>evt.target).files; // FileList object

// use the 1st file from the list
const f = files[0];

const reader = new FileReader();

// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
chrome.runtime.sendMessage({
delegation: e.target.result,
type: 'syncDelegation',
});
};
})(f);

// Read in the image file as a data URL.
reader.readAsText(f);
});
$('#backupFile').on('change', function(evt: Event) {
$('#Setup').on('click', function(evt: Event) {
evt.preventDefault();
const files = (<HTMLInputElement>evt.target).files; // FileList object

// use the 1st file from the list
const f = files[0];

const reader = new FileReader();

// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
chrome.runtime.sendMessage({
backup: e.target.result,
type: 'syncBackup',
});
};
})(f);

// Read in the image file as a data URL.
reader.readAsText(f);
chrome.runtime.sendMessage({
type: 'setup',
});
});

$('#recovery').on('click', function(evt: Event) {
$('#Recovery').on('click', function(evt: Event) {
evt.preventDefault();
chrome.runtime.sendMessage({
amount: 5, // ToDo Read real input
type: 'recovery',
});
});
Expand Down
104 changes: 64 additions & 40 deletions src/recovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,80 @@ import {getLogger} from './logging';
import {getCompatibleKeyFromCryptoKey, ICOSECompatibleKey} from './crypto';
import {base64ToByteArray, byteArrayToBase64, getDomainFromOrigin} from './utils';
import {fetchExportContainer, saveExportContainer, saveKey} from './storage';
import * as axios from "axios";

const log = getLogger('recovery');

export const PSK: string = 'psk'

export const BackupDeviceBaseUrl = 'http://localhost:8005' // ToDo Load from a config file

export type ExportContainerType = string
const BACKUP: ExportContainerType = 'backup'
const RECOVERY: ExportContainerType = 'recovery'
const DELEGATION: ExportContainerType = 'delegation'

export async function syncBackupKeys (content) {
const jwk = JSON.parse(content);
let i;
const container = new Array<ExportContainer>();
for (i = 0; i < jwk.length; ++i) {
const parsedKey = await parseJWK(jwk[i], []);
const id = base64ToByteArray(jwk[i].kid, true);
const encId = byteArrayToBase64(id, true);
const bckpKey = new BackupKey(parsedKey, encId);
const expBckpKey = await bckpKey.export();
container.push(expBckpKey);
}
log.debug('Loaded backup keys', container);

await saveExportContainer(BACKUP, container);
export async function pskSetup () {
const authId = prompt("Please enter a name for your authenticator", "MyAuth");
const keyAmount: number = +prompt("How many backup keys should be created?", "5");

await axios.default.post(BackupDeviceBaseUrl + '/setup', {auth_id: authId, key_amount: keyAmount})
.then(async function (response) {
log.debug(response)
const jwk = response.data;
let i;
const container = new Array<ExportContainer>();
for (i = 0; i < jwk.length; ++i) {
const parsedKey = await parseJWK(jwk[i], []);
const id = base64ToByteArray(jwk[i].kid, true);
const encId = byteArrayToBase64(id, true);
const bckpKey = new BackupKey(parsedKey, encId);
const expBckpKey = await bckpKey.export();
container.push(expBckpKey);
}
log.debug('Loaded backup keys', container);

await saveExportContainer(BACKUP, container);
})
.catch(function (error) {
log.error(error);
})
}

export async function syncDelegation (content) {
const rawDelegations = JSON.parse(content);
let i;
const container = new Array<ExportContainer>();
for (i = 0; i < rawDelegations.length; ++i) {
const sign = rawDelegations[i].sign;
const srcCredId = base64ToByteArray(rawDelegations[i].src_cred_id, true);
const encSrcCredId = byteArrayToBase64(srcCredId, true);
const del = new Delegation(sign, encSrcCredId, rawDelegations[i].pub_rk);
container.push(del.export());
}
log.debug("Loaded delegation", container);
await saveExportContainer(DELEGATION, container);
export async function pskRecovery () {
const authId = prompt("Which authenticator you want to replace?", "MyAuth");

await axios.default.get(BackupDeviceBaseUrl + '/recovery?authId=' + authId)
.then(async function (response1) {
log.debug(response1);
const keyAmount = response1.data.key_amount;

const rkPub = await RecoveryKey.generate(keyAmount);

await axios.default.post(BackupDeviceBaseUrl + '/recovery', {recovery_keys: rkPub, auth_id: authId})
.then(async function (response2) {
log.debug(response2);
const rawDelegations = response2.data;

let i;
const container = new Array<ExportContainer>();
for (i = 0; i < rawDelegations.length; ++i) {
const sign = rawDelegations[i].sign;
const srcCredId = base64ToByteArray(rawDelegations[i].src_cred_id, true);
const encSrcCredId = byteArrayToBase64(srcCredId, true);
const del = new Delegation(sign, encSrcCredId, rawDelegations[i].pub_rk);
container.push(del.export());
}
log.debug("Loaded delegation", container);
await saveExportContainer(DELEGATION, container);
})
.catch(function (error) {
log.error(error);
})
})
.catch(function (error) {
log.error(error);
})
}

export class ExportContainer {
Expand Down Expand Up @@ -115,7 +149,7 @@ export class RecoveryKey {
return new RecoveryKey(key, backupKey);
}

static async generate(n: number) {
static async generate(n: number): Promise<Array<JsonWebKey>> {
const jwk = new Array<JsonWebKey>();
const container = new Array<ExportContainer>();
let i;
Expand All @@ -137,17 +171,7 @@ export class RecoveryKey {

await saveExportContainer(RECOVERY, container);

// Download recovery public keys as file
let json = [JSON.stringify(jwk)];
let blob1 = new Blob(json, { type: "text/plain;charset=utf-8" });
let link = (window.URL ? URL : webkitURL).createObjectURL(blob1);
let a = document.createElement("a");
a.download = "recoveryKeys.json";
a.href = link;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
log.debug("Downloading recovery keys completed");
return jwk;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export const fetchKey = async (key: string, pin: string): Promise<CryptoKey> =>
if (resp[key] == null) {
return rej("Key not found");
}
log.info('PIN', pin);
const payload = base64ToByteArray(resp[key]);
const saltByteLength = payload[0];
const ivByteLength = payload[1];
Expand Down

0 comments on commit 51a9bc0

Please sign in to comment.