diff --git a/libs/common/src/enums/cipherType.ts b/libs/common/src/enums/cipherType.ts index cce7874d66..d43618b0bc 100644 --- a/libs/common/src/enums/cipherType.ts +++ b/libs/common/src/enums/cipherType.ts @@ -3,4 +3,5 @@ export enum CipherType { SecureNote = 2, Card = 3, Identity = 4, + Fido2Key = 5, } diff --git a/libs/common/src/models/api/fido2-key.api.ts b/libs/common/src/models/api/fido2-key.api.ts new file mode 100644 index 0000000000..08d4715d0e --- /dev/null +++ b/libs/common/src/models/api/fido2-key.api.ts @@ -0,0 +1,20 @@ +import { BaseResponse } from "../response/base.response"; + +export class Fido2KeyApi extends BaseResponse { + key: string; + rpId: string; + origin: string; + userHandle: string; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + + this.key = this.getResponseProperty("Key"); + this.rpId = this.getResponseProperty("RpId"); + this.origin = this.getResponseProperty("Origin"); + this.userHandle = this.getResponseProperty("UserHandle"); + } +} diff --git a/libs/common/src/models/data/cipher.data.ts b/libs/common/src/models/data/cipher.data.ts index 4cc57559c2..66f3dd1799 100644 --- a/libs/common/src/models/data/cipher.data.ts +++ b/libs/common/src/models/data/cipher.data.ts @@ -4,6 +4,7 @@ import { CipherResponse } from "../response/cipher.response"; import { AttachmentData } from "./attachment.data"; import { CardData } from "./card.data"; +import { Fido2KeyData } from "./fido2-key.data"; import { FieldData } from "./field.data"; import { IdentityData } from "./identity.data"; import { LoginData } from "./login.data"; @@ -26,6 +27,7 @@ export class CipherData { secureNote?: SecureNoteData; card?: CardData; identity?: IdentityData; + fido2Key?: Fido2KeyData; fields?: FieldData[]; attachments?: AttachmentData[]; passwordHistory?: PasswordHistoryData[]; @@ -68,6 +70,9 @@ export class CipherData { case CipherType.Identity: this.identity = new IdentityData(response.identity); break; + case CipherType.Fido2Key: + this.fido2Key = new Fido2KeyData(response.fido2Key); + break; default: break; } diff --git a/libs/common/src/models/data/fido2-key.data.ts b/libs/common/src/models/data/fido2-key.data.ts new file mode 100644 index 0000000000..6392339836 --- /dev/null +++ b/libs/common/src/models/data/fido2-key.data.ts @@ -0,0 +1,19 @@ +import { Fido2KeyApi } from "../api/fido2-key.api"; + +export class Fido2KeyData { + key: string; + rpId: string; + origin: string; + userHandle: string; + + constructor(data?: Fido2KeyApi) { + if (data == null) { + return; + } + + this.key = data.key; + this.rpId = data.rpId; + this.origin = data.origin; + this.userHandle = data.userHandle; + } +} diff --git a/libs/common/src/models/domain/cipher.ts b/libs/common/src/models/domain/cipher.ts index 7b4a4d4b1f..46f57bc799 100644 --- a/libs/common/src/models/domain/cipher.ts +++ b/libs/common/src/models/domain/cipher.ts @@ -12,6 +12,7 @@ import { Attachment } from "./attachment"; import { Card } from "./card"; import Domain from "./domain-base"; import { EncString } from "./enc-string"; +import { Fido2Key } from "./fido2-key"; import { Field } from "./field"; import { Identity } from "./identity"; import { Login } from "./login"; @@ -38,6 +39,7 @@ export class Cipher extends Domain implements Decryptable { identity: Identity; card: Card; secureNote: SecureNote; + fido2Key: Fido2Key; attachments: Attachment[]; fields: Field[]; passwordHistory: Password[]; @@ -94,6 +96,9 @@ export class Cipher extends Domain implements Decryptable { case CipherType.Identity: this.identity = new Identity(obj.identity); break; + case CipherType.Fido2Key: + this.fido2Key = new Fido2Key(obj.fido2Key); + break; default: break; } @@ -143,6 +148,9 @@ export class Cipher extends Domain implements Decryptable { case CipherType.Identity: model.identity = await this.identity.decrypt(this.organizationId, encKey); break; + case CipherType.Fido2Key: + model.fido2Key = await this.fido2Key.decrypt(this.organizationId, encKey); + break; default: break; } @@ -228,6 +236,9 @@ export class Cipher extends Domain implements Decryptable { case CipherType.Identity: c.identity = this.identity.toIdentityData(); break; + case CipherType.Fido2Key: + c.fido2Key = this.fido2Key.toFido2KeyData(); + break; default: break; } @@ -281,6 +292,9 @@ export class Cipher extends Domain implements Decryptable { case CipherType.SecureNote: domain.secureNote = SecureNote.fromJSON(obj.secureNote); break; + case CipherType.Fido2Key: + domain.fido2Key = Fido2Key.fromJSON(obj.fido2Key); + break; default: break; } diff --git a/libs/common/src/models/domain/fido2-key.ts b/libs/common/src/models/domain/fido2-key.ts new file mode 100644 index 0000000000..b1fd93e672 --- /dev/null +++ b/libs/common/src/models/domain/fido2-key.ts @@ -0,0 +1,78 @@ +import { Jsonify } from "type-fest"; + +import { Fido2KeyData } from "../data/fido2-key.data"; +import { Fido2KeyView } from "../view/fido2-key.view"; + +import Domain from "./domain-base"; +import { EncString } from "./enc-string"; +import { SymmetricCryptoKey } from "./symmetric-crypto-key"; + +export class Fido2Key extends Domain { + key: EncString; // PCKS#8 + rpId: EncString; + origin: EncString; + userHandle: EncString; + // extensions: Record; + + constructor(obj?: Fido2KeyData) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + key: null, + rpId: null, + origin: null, + userHandle: null, + }, + [] + ); + } + + decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + return this.decryptObj( + new Fido2KeyView(), + { + key: null, + rpId: null, + origin: null, + userHandle: null, + }, + orgId, + encKey + ); + } + + toFido2KeyData(): Fido2KeyData { + const i = new Fido2KeyData(); + this.buildDataModel(this, i, { + key: null, + rpId: null, + origin: null, + userHandle: null, + }); + return i; + } + + static fromJSON(obj: Jsonify): Fido2Key { + if (obj == null) { + return null; + } + + const key = EncString.fromJSON(obj.key); + const rpId = EncString.fromJSON(obj.rpId); + const origin = EncString.fromJSON(obj.origin); + const userHandle = EncString.fromJSON(obj.userHandle); + + return Object.assign(new Fido2Key(), obj, { + key, + rpId, + origin, + userHandle, + }); + } +} diff --git a/libs/common/src/models/response/cipher.response.ts b/libs/common/src/models/response/cipher.response.ts index 2c95c6a385..65bc984819 100644 --- a/libs/common/src/models/response/cipher.response.ts +++ b/libs/common/src/models/response/cipher.response.ts @@ -1,5 +1,6 @@ import { CipherRepromptType } from "../../enums/cipherRepromptType"; import { CardApi } from "../api/card.api"; +import { Fido2KeyApi } from "../api/fido2-key.api"; import { FieldApi } from "../api/field.api"; import { IdentityApi } from "../api/identity.api"; import { LoginApi } from "../api/login.api"; @@ -21,6 +22,7 @@ export class CipherResponse extends BaseResponse { card: CardApi; identity: IdentityApi; secureNote: SecureNoteApi; + fido2Key: Fido2KeyApi; favorite: boolean; edit: boolean; viewPassword: boolean; @@ -74,6 +76,11 @@ export class CipherResponse extends BaseResponse { this.secureNote = new SecureNoteApi(secureNote); } + const fido2Key = this.getResponseProperty("Fido2Key"); + if (fido2Key != null) { + this.fido2Key = new Fido2KeyApi(fido2Key); + } + const fields = this.getResponseProperty("Fields"); if (fields != null) { this.fields = fields.map((f: any) => new FieldApi(f)); diff --git a/libs/common/src/models/view/cipher.view.ts b/libs/common/src/models/view/cipher.view.ts index c3c69b3b9e..c7968e9722 100644 --- a/libs/common/src/models/view/cipher.view.ts +++ b/libs/common/src/models/view/cipher.view.ts @@ -10,6 +10,7 @@ import { Cipher } from "../domain/cipher"; import { AttachmentView } from "./attachment.view"; import { CardView } from "./card.view"; +import { Fido2KeyView } from "./fido2-key.view"; import { FieldView } from "./field.view"; import { IdentityView } from "./identity.view"; import { LoginView } from "./login.view"; @@ -35,6 +36,7 @@ export class CipherView implements View, InitializerMetadata { identity = new IdentityView(); card = new CardView(); secureNote = new SecureNoteView(); + fido2Key = new Fido2KeyView(); attachments: AttachmentView[] = null; fields: FieldView[] = null; passwordHistory: PasswordHistoryView[] = null; diff --git a/libs/common/src/models/view/fido2-key.view.ts b/libs/common/src/models/view/fido2-key.view.ts new file mode 100644 index 0000000000..96ad0b3642 --- /dev/null +++ b/libs/common/src/models/view/fido2-key.view.ts @@ -0,0 +1,7 @@ +export class Fido2KeyView { + id: string; + key: string; + rpId: string; + origin: string; + userHandle: string; +} diff --git a/libs/common/src/services/fido2/fido2.service.ts b/libs/common/src/services/fido2/fido2.service.ts index 6fc8f7ea2f..d9163b5b3d 100644 --- a/libs/common/src/services/fido2/fido2.service.ts +++ b/libs/common/src/services/fido2/fido2.service.ts @@ -84,7 +84,7 @@ export class Fido2Service implements Fido2ServiceAbstraction { }) ); - this.credentials.set(credentialId.encoded, { + await this.saveCredential({ credentialId, keyPair, origin: params.origin, @@ -170,6 +170,10 @@ export class Fido2Service implements Fido2ServiceAbstraction { return credential; } + private async saveCredential(credential: BitCredential): Promise { + this.credentials.set(credential.credentialId.encoded, credential); + } + private getCredentialByRp(rpId: string): BitCredential | undefined { for (const credential of this.credentials.values()) { if (credential.rpId === rpId) {