mirror of
https://github.com/bitwarden/browser
synced 2025-12-27 21:53:25 +00:00
Add support for Emergency Access (#204)
* Add support for Emergency Access * Resolve review comments
This commit is contained in:
@@ -19,6 +19,11 @@ import { CollectionRequest } from '../models/request/collectionRequest';
|
||||
import { DeleteRecoverRequest } from '../models/request/deleteRecoverRequest';
|
||||
import { EmailRequest } from '../models/request/emailRequest';
|
||||
import { EmailTokenRequest } from '../models/request/emailTokenRequest';
|
||||
import { EmergencyAccessAcceptRequest } from '../models/request/emergencyAccessAcceptRequest';
|
||||
import { EmergencyAccessConfirmRequest } from '../models/request/emergencyAccessConfirmRequest';
|
||||
import { EmergencyAccessInviteRequest } from '../models/request/emergencyAccessInviteRequest';
|
||||
import { EmergencyAccessPasswordRequest } from '../models/request/emergencyAccessPasswordRequest';
|
||||
import { EmergencyAccessUpdateRequest } from '../models/request/emergencyAccessUpdateRequest';
|
||||
import { EventRequest } from '../models/request/eventRequest';
|
||||
import { FolderRequest } from '../models/request/folderRequest';
|
||||
import { GroupRequest } from '../models/request/groupRequest';
|
||||
@@ -77,6 +82,12 @@ import {
|
||||
CollectionResponse,
|
||||
} from '../models/response/collectionResponse';
|
||||
import { DomainsResponse } from '../models/response/domainsResponse';
|
||||
import {
|
||||
EmergencyAccessGranteeDetailsResponse,
|
||||
EmergencyAccessGrantorDetailsResponse,
|
||||
EmergencyAccessTakeoverResponse,
|
||||
EmergencyAccessViewResponse
|
||||
} from '../models/response/emergencyAccessResponse';
|
||||
import { ErrorResponse } from '../models/response/errorResponse';
|
||||
import { EventResponse } from '../models/response/eventResponse';
|
||||
import { FolderResponse } from '../models/response/folderResponse';
|
||||
@@ -901,6 +912,73 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
return this.send('POST', '/two-factor/send-email-login', request, false, false);
|
||||
}
|
||||
|
||||
// Emergency Access APIs
|
||||
|
||||
async getEmergencyAccessTrusted(): Promise<ListResponse<EmergencyAccessGranteeDetailsResponse>> {
|
||||
const r = await this.send('GET', '/emergency-access/trusted', null, true, true);
|
||||
return new ListResponse(r, EmergencyAccessGranteeDetailsResponse);
|
||||
}
|
||||
|
||||
async getEmergencyAccessGranted(): Promise<ListResponse<EmergencyAccessGrantorDetailsResponse>> {
|
||||
const r = await this.send('GET', '/emergency-access/granted', null, true, true);
|
||||
return new ListResponse(r, EmergencyAccessGrantorDetailsResponse);
|
||||
}
|
||||
|
||||
async getEmergencyAccess(id: string): Promise<EmergencyAccessGranteeDetailsResponse> {
|
||||
const r = await this.send('GET', '/emergency-access/' + id, null, true, true);
|
||||
return new EmergencyAccessGranteeDetailsResponse(r);
|
||||
}
|
||||
|
||||
putEmergencyAccess(id: string, request: EmergencyAccessUpdateRequest): Promise<any> {
|
||||
return this.send('PUT', '/emergency-access/' + id, request, true, false);
|
||||
}
|
||||
|
||||
deleteEmergencyAccess(id: string): Promise<any> {
|
||||
return this.send('DELETE', '/emergency-access/' + id, null, true, false);
|
||||
}
|
||||
|
||||
postEmergencyAccessInvite(request: EmergencyAccessInviteRequest): Promise<any> {
|
||||
return this.send('POST', '/emergency-access/invite', request, true, false);
|
||||
}
|
||||
|
||||
postEmergencyAccessReinvite(id: string): Promise<any> {
|
||||
return this.send('POST', '/emergency-access/' + id + '/reinvite', null, true, false);
|
||||
}
|
||||
|
||||
postEmergencyAccessAccept(id: string, request: EmergencyAccessAcceptRequest): Promise<any> {
|
||||
return this.send('POST', '/emergency-access/' + id + '/accept', request, true, false);
|
||||
}
|
||||
|
||||
postEmergencyAccessConfirm(id: string, request: EmergencyAccessConfirmRequest): Promise<any> {
|
||||
return this.send('POST', '/emergency-access/' + id + '/confirm', request, true, false);
|
||||
}
|
||||
|
||||
postEmergencyAccessInitiate(id: string): Promise<any> {
|
||||
return this.send('POST', '/emergency-access/' + id + '/initiate', null, true, false);
|
||||
}
|
||||
|
||||
postEmergencyAccessApprove(id: string): Promise<any> {
|
||||
return this.send('POST', '/emergency-access/' + id + '/approve', null, true, false);
|
||||
}
|
||||
|
||||
postEmergencyAccessReject(id: string): Promise<any> {
|
||||
return this.send('POST', '/emergency-access/' + id + '/reject', null, true, false);
|
||||
}
|
||||
|
||||
async postEmergencyAccessTakeover(id: string): Promise<EmergencyAccessTakeoverResponse> {
|
||||
const r = await this.send('POST', '/emergency-access/' + id + '/takeover', null, true, true);
|
||||
return new EmergencyAccessTakeoverResponse(r);
|
||||
}
|
||||
|
||||
async postEmergencyAccessPassword(id: string, request: EmergencyAccessPasswordRequest): Promise<any> {
|
||||
const r = await this.send('POST', '/emergency-access/' + id + '/password', request, true, true);
|
||||
}
|
||||
|
||||
async postEmergencyAccessView(id: string): Promise<EmergencyAccessViewResponse> {
|
||||
const r = await this.send('POST', '/emergency-access/' + id + '/view', null, true, true);
|
||||
return new EmergencyAccessViewResponse(r);
|
||||
}
|
||||
|
||||
// Organization APIs
|
||||
|
||||
async getOrganization(id: string): Promise<OrganizationResponse> {
|
||||
|
||||
@@ -380,8 +380,10 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return this.buildEncKey(theKey, encKey);
|
||||
}
|
||||
|
||||
async remakeEncKey(key: SymmetricCryptoKey): Promise<[SymmetricCryptoKey, CipherString]> {
|
||||
const encKey = await this.getEncKey();
|
||||
async remakeEncKey(key: SymmetricCryptoKey, encKey?: SymmetricCryptoKey): Promise<[SymmetricCryptoKey, CipherString]> {
|
||||
if (encKey == null) {
|
||||
encKey = await this.getEncKey();
|
||||
}
|
||||
return this.buildEncKey(key, encKey.key);
|
||||
}
|
||||
|
||||
@@ -434,6 +436,58 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return new CipherString(EncryptionType.Rsa2048_OaepSha1_B64, Utils.fromBufferToB64(encBytes));
|
||||
}
|
||||
|
||||
async rsaDecrypt(encValue: string): Promise<ArrayBuffer> {
|
||||
const headerPieces = encValue.split('.');
|
||||
let encType: EncryptionType = null;
|
||||
let encPieces: string[];
|
||||
|
||||
if (headerPieces.length === 1) {
|
||||
encType = EncryptionType.Rsa2048_OaepSha256_B64;
|
||||
encPieces = [headerPieces[0]];
|
||||
} else if (headerPieces.length === 2) {
|
||||
try {
|
||||
encType = parseInt(headerPieces[0], null);
|
||||
encPieces = headerPieces[1].split('|');
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
switch (encType) {
|
||||
case EncryptionType.Rsa2048_OaepSha256_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha1_B64:
|
||||
// HmacSha256 types are deprecated
|
||||
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
|
||||
break;
|
||||
default:
|
||||
throw new Error('encType unavailable.');
|
||||
}
|
||||
|
||||
if (encPieces == null || encPieces.length <= 0) {
|
||||
throw new Error('encPieces unavailable.');
|
||||
}
|
||||
|
||||
const data = Utils.fromB64ToArray(encPieces[0]).buffer;
|
||||
const privateKey = await this.getPrivateKey();
|
||||
if (privateKey == null) {
|
||||
throw new Error('No private key.');
|
||||
}
|
||||
|
||||
let alg: 'sha1' | 'sha256' = 'sha1';
|
||||
switch (encType) {
|
||||
case EncryptionType.Rsa2048_OaepSha256_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64:
|
||||
alg = 'sha256';
|
||||
break;
|
||||
case EncryptionType.Rsa2048_OaepSha1_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
|
||||
break;
|
||||
default:
|
||||
throw new Error('encType unavailable.');
|
||||
}
|
||||
|
||||
return this.cryptoFunctionService.rsaDecrypt(data, privateKey, alg);
|
||||
}
|
||||
|
||||
async decryptToBytes(cipherString: CipherString, key?: SymmetricCryptoKey): Promise<ArrayBuffer> {
|
||||
const iv = Utils.fromB64ToArray(cipherString.iv).buffer;
|
||||
const data = Utils.fromB64ToArray(cipherString.data).buffer;
|
||||
@@ -604,58 +658,6 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return await this.cryptoFunctionService.aesDecrypt(data, iv, theKey.encKey);
|
||||
}
|
||||
|
||||
private async rsaDecrypt(encValue: string): Promise<ArrayBuffer> {
|
||||
const headerPieces = encValue.split('.');
|
||||
let encType: EncryptionType = null;
|
||||
let encPieces: string[];
|
||||
|
||||
if (headerPieces.length === 1) {
|
||||
encType = EncryptionType.Rsa2048_OaepSha256_B64;
|
||||
encPieces = [headerPieces[0]];
|
||||
} else if (headerPieces.length === 2) {
|
||||
try {
|
||||
encType = parseInt(headerPieces[0], null);
|
||||
encPieces = headerPieces[1].split('|');
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
switch (encType) {
|
||||
case EncryptionType.Rsa2048_OaepSha256_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha1_B64:
|
||||
// HmacSha256 types are deprecated
|
||||
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
|
||||
break;
|
||||
default:
|
||||
throw new Error('encType unavailable.');
|
||||
}
|
||||
|
||||
if (encPieces == null || encPieces.length <= 0) {
|
||||
throw new Error('encPieces unavailable.');
|
||||
}
|
||||
|
||||
const data = Utils.fromB64ToArray(encPieces[0]).buffer;
|
||||
const privateKey = await this.getPrivateKey();
|
||||
if (privateKey == null) {
|
||||
throw new Error('No private key.');
|
||||
}
|
||||
|
||||
let alg: 'sha1' | 'sha256' = 'sha1';
|
||||
switch (encType) {
|
||||
case EncryptionType.Rsa2048_OaepSha256_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64:
|
||||
alg = 'sha256';
|
||||
break;
|
||||
case EncryptionType.Rsa2048_OaepSha1_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
|
||||
break;
|
||||
default:
|
||||
throw new Error('encType unavailable.');
|
||||
}
|
||||
|
||||
return this.cryptoFunctionService.rsaDecrypt(data, privateKey, alg);
|
||||
}
|
||||
|
||||
private async getKeyForEncryption(key?: SymmetricCryptoKey): Promise<SymmetricCryptoKey> {
|
||||
if (key != null) {
|
||||
return key;
|
||||
|
||||
Reference in New Issue
Block a user