1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00

[EC-598] feat: implement passwordless vault auth

This commit is contained in:
Andreas Coroiu
2023-01-04 13:02:21 +01:00
parent f56971aa89
commit 1eab09b79d

View File

@@ -34,8 +34,6 @@ interface BitCredential {
const KeyUsages: KeyUsage[] = ["sign"]; const KeyUsages: KeyUsage[] = ["sign"];
export class Fido2Service implements Fido2ServiceAbstraction { export class Fido2Service implements Fido2ServiceAbstraction {
private credentials = new Map<string, BitCredential>();
constructor( constructor(
private fido2UserInterfaceService: Fido2UserInterfaceService, private fido2UserInterfaceService: Fido2UserInterfaceService,
private cipherService: CipherService private cipherService: CipherService
@@ -120,17 +118,19 @@ export class Fido2Service implements Fido2ServiceAbstraction {
if (params.allowedCredentialIds && params.allowedCredentialIds.length > 0) { if (params.allowedCredentialIds && params.allowedCredentialIds.length > 0) {
// We're looking for regular non-resident keys // We're looking for regular non-resident keys
credential = await this.getCredential(params.allowedCredentialIds); credential = await this.getCredential(params.allowedCredentialIds);
console.log("Found credential: ", credential);
} else { } else {
// We're looking for a resident key // We're looking for a resident key
credential = this.getCredentialByRp(params.rpId); credential = await this.getCredentialByRp(params.rpId);
} }
console.log("Found credential: ", credential);
if (credential === undefined) { if (credential === undefined) {
throw new NoCredentialFoundError(); throw new NoCredentialFoundError();
} }
if (credential.origin !== params.origin) { if (credential.origin !== params.origin) {
console.error(`${params.origin} tried to use credential created by ${credential.origin}`);
throw new OriginMismatchError(); throw new OriginMismatchError();
} }
@@ -177,36 +177,12 @@ export class Fido2Service implements Fido2ServiceAbstraction {
} }
} }
if (cipher == null) { if (cipher == undefined) {
return undefined; return undefined;
} }
const cipherView = await cipher.decrypt(); const cipherView = await cipher.decrypt();
const keyBuffer = Fido2Utils.stringToBuffer(cipherView.fido2Key.key); return await mapCipherViewToBitCredential(cipherView);
let privateKey;
try {
privateKey = await crypto.subtle.importKey(
"pkcs8",
keyBuffer,
{
name: "ECDSA",
namedCurve: "P-256",
},
true,
KeyUsages
);
} catch (err) {
console.log("Error importing key", { err });
throw err;
}
return {
credentialId: new CredentialId(cipherView.id),
privateKey,
origin: cipherView.fido2Key.origin,
rpId: cipherView.fido2Key.rpId,
userHandle: Fido2Utils.stringToBuffer(cipherView.fido2Key.userHandle),
};
} }
private async saveCredential( private async saveCredential(
@@ -223,6 +199,8 @@ export class Fido2Service implements Fido2ServiceAbstraction {
view.fido2Key.rpId = credential.rpId; view.fido2Key.rpId = credential.rpId;
view.fido2Key.userHandle = Fido2Utils.bufferToString(credential.userHandle); view.fido2Key.userHandle = Fido2Utils.bufferToString(credential.userHandle);
console.log("saving credential", { view, credential });
const cipher = await this.cipherService.encrypt(view); const cipher = await this.cipherService.encrypt(view);
await this.cipherService.createWithServer(cipher); await this.cipherService.createWithServer(cipher);
@@ -230,13 +208,17 @@ export class Fido2Service implements Fido2ServiceAbstraction {
return new CredentialId(cipher.id); return new CredentialId(cipher.id);
} }
private getCredentialByRp(rpId: string): BitCredential | undefined { private async getCredentialByRp(rpId: string): Promise<BitCredential | undefined> {
for (const credential of this.credentials.values()) { const allCipherViews = await this.cipherService.getAllDecrypted();
if (credential.rpId === rpId) { const cipherView = allCipherViews.find(
return credential; (cv) => cv.type === CipherType.Fido2Key && cv.fido2Key?.rpId === rpId
} );
if (cipherView == undefined) {
return undefined;
} }
return undefined;
return await mapCipherViewToBitCredential(cipherView);
} }
} }
@@ -249,6 +231,34 @@ interface AuthDataParams {
attestationFormat?: "packed" | "fido-u2f"; attestationFormat?: "packed" | "fido-u2f";
} }
async function mapCipherViewToBitCredential(cipherView: CipherView): Promise<BitCredential> {
const keyBuffer = Fido2Utils.stringToBuffer(cipherView.fido2Key.key);
let privateKey;
try {
privateKey = await crypto.subtle.importKey(
"pkcs8",
keyBuffer,
{
name: "ECDSA",
namedCurve: "P-256",
},
true,
KeyUsages
);
} catch (err) {
console.log("Error importing key", { err });
throw err;
}
return {
credentialId: new CredentialId(cipherView.id),
privateKey,
origin: cipherView.fido2Key.origin,
rpId: cipherView.fido2Key.rpId,
userHandle: Fido2Utils.stringToBuffer(cipherView.fido2Key.userHandle),
};
}
async function generateAuthData(params: AuthDataParams) { async function generateAuthData(params: AuthDataParams) {
const encoder = new TextEncoder(); const encoder = new TextEncoder();