1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-24 08:33:29 +00:00

[PM-30785|BEEEP] Remove deprecated master key login with device flow (#17943)

* Remove deprecated master key login with device flow

* Resolve conflicts / cleanup

* Linting

* Fix lint

* Run prettier
This commit is contained in:
Bernd Schoolmann
2026-02-20 19:22:05 +01:00
committed by GitHub
parent 0569ec9517
commit 3a56f2e832
14 changed files with 34 additions and 346 deletions

View File

@@ -13,16 +13,9 @@
## Standard Auth Request Flows
### Flow 1: Unauthed user requests approval from device; Approving device has a masterKey in memory
### Flow 1: This flow was removed
1. Unauthed user clicks "Login with device"
2. Navigates to `/login-with-device` which creates a `StandardAuthRequest`
3. Receives approval from a device with authRequestPublicKey(masterKey)
4. Decrypts masterKey
5. Decrypts userKey
6. Proceeds to vault
### Flow 2: Unauthed user requests approval from device; Approving device does NOT have a masterKey in memory
### Flow 2: Unauthed user requests approval from device; Approving device does NOT need to have a masterKey in memory
1. Unauthed user clicks "Login with device"
2. Navigates to `/login-with-device` which creates a `StandardAuthRequest`
@@ -33,28 +26,18 @@
**Note:** This flow is an uncommon scenario and relates to TDE off-boarding. The following describes how a user could
get into this flow:
1. An SSO TD user logs into a device via an Admin auth request approval, therefore this device does NOT have a masterKey
1. An SSO TD user logs into a device via an Admin auth request approval, therefore this device does NOT need to have a masterKey
in memory
2. The org admin:
- Changes the member decryption options from "Trusted devices" to "Master password" AND
- Turns off the "Require single sign-on authentication" policy
3. On another device, the user clicks "Login with device", which they can do because the org no longer requires SSO
4. The user approves from the device they had previously logged into with SSO TD, which does NOT have a masterKey in
4. The user approves from the device they had previously logged into with SSO TD, which does NOT need to have a masterKey in
memory
### Flow 3: Authed SSO TD user requests approval from device; Approving device has a masterKey in memory
### Flow 3: This flow was removed
1. SSO TD user authenticates via SSO
2. Navigates to `/login-initiated`
3. Clicks "Approve from your other device"
4. Navigates to `/login-with-device` which creates a `StandardAuthRequest`
5. Receives approval from device with authRequestPublicKey(masterKey)
6. Decrypts masterKey
7. Decrypts userKey
8. Establishes trust (if required)
9. Proceeds to vault
### Flow 4: Authed SSO TD user requests approval from device; Approving device does NOT have a masterKey in memory
### Flow 4: Authed SSO TD user requests approval from device; Approving device does NOT need to have a masterKey in memory
1. SSO TD user authenticates via SSO
2. Navigates to `/login-initiated`
@@ -89,9 +72,7 @@ userKey. This is how admins are able to send over the authRequestPublicKey(userK
| Flow | Auth Status | Clicks Button [active route] | Navigates to | Approving device has masterKey in memory\* |
| --------------- | ----------- | ----------------------------------------------------- | --------------------------- | ------------------------------------------------- |
| Standard Flow 1 | unauthed | "Login with device" [`/login`] | `/login-with-device` | yes |
| Standard Flow 2 | unauthed | "Login with device" [`/login`] | `/login-with-device` | no |
| Standard Flow 3 | authed | "Approve from your other device" [`/login-initiated`] | `/login-with-device` | yes |
| Standard Flow 4 | authed | "Approve from your other device" [`/login-initiated`] | `/login-with-device` | no |
| Admin Flow | authed | "Request admin approval"<br>[`/login-initiated`] | `/admin-approval-requested` | NA - admin requests always send encrypted userKey |

View File

@@ -605,10 +605,10 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
if (authRequestResponse.requestApproved) {
const userHasAuthenticatedViaSSO = this.authStatus === AuthenticationStatus.Locked;
if (userHasAuthenticatedViaSSO) {
// [Standard Flow 3-4] Handle authenticated SSO TD user flows
// [Standard Flow 4] Handle authenticated SSO TD user flows
return await this.handleAuthenticatedFlows(authRequestResponse);
} else {
// [Standard Flow 1-2] Handle unauthenticated user flows
// [Standard Flow 2] Handle unauthenticated user flows
return await this.handleUnauthenticatedFlows(authRequestResponse, requestId);
}
}
@@ -629,7 +629,7 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
}
private async handleAuthenticatedFlows(authRequestResponse: AuthRequestResponse) {
// [Standard Flow 3-4] Handle authenticated SSO TD user flows
// [Standard Flow 4] Handle authenticated SSO TD user flows
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
if (!userId) {
this.logService.error(
@@ -654,7 +654,7 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
authRequestResponse: AuthRequestResponse,
requestId: string,
) {
// [Standard Flow 1-2] Handle unauthenticated user flows
// [Standard Flow 2] Handle unauthenticated user flows
const authRequestLoginCredentials = await this.buildAuthRequestLoginCredentials(
requestId,
authRequestResponse,
@@ -679,27 +679,12 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
privateKey: Uint8Array,
userId: UserId,
): Promise<void> {
/**
* [Flow Type Detection]
* We determine the type of `key` based on the presence or absence of `masterPasswordHash`:
* - If `masterPasswordHash` exists: Standard Flow 1 or 3 (device has masterKey)
* - If no `masterPasswordHash`: Standard Flow 2, 4, or Admin Flow (device sends userKey)
*/
if (authRequestResponse.masterPasswordHash) {
// [Standard Flow 1 or 3] Device has masterKey
await this.authRequestService.setKeysAfterDecryptingSharedMasterKeyAndHash(
authRequestResponse,
privateKey,
userId,
);
} else {
// [Standard Flow 2, 4, or Admin Flow] Device sends userKey
await this.authRequestService.setUserKeyAfterDecryptingSharedUserKey(
authRequestResponse,
privateKey,
userId,
);
}
// [Standard Flow 2, 4, or Admin Flow] Device sends userKey
await this.authRequestService.setUserKeyAfterDecryptingSharedUserKey(
authRequestResponse,
privateKey,
userId,
);
// [Admin Flow Cleanup] Clear one-time use admin auth request
// clear the admin auth request from state so it cannot be used again (it's a one time use)
@@ -758,43 +743,13 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
/**
* See verifyAndHandleApprovedAuthReq() for flow details.
*
* We determine the type of `key` based on the presence or absence of `masterPasswordHash`:
* - If `masterPasswordHash` has a value, we receive the `key` as an authRequestPublicKey(masterKey) [plus we have authRequestPublicKey(masterPasswordHash)]
* - If `masterPasswordHash` does not have a value, we receive the `key` as an authRequestPublicKey(userKey)
*/
if (authRequestResponse.masterPasswordHash) {
// ...in Standard Auth Request Flow 1
const { masterKey, masterKeyHash } =
await this.authRequestService.decryptPubKeyEncryptedMasterKeyAndHash(
authRequestResponse.key,
authRequestResponse.masterPasswordHash,
this.authRequestKeyPair.privateKey,
);
return new AuthRequestLoginCredentials(
this.email,
this.accessCode,
requestId,
null, // no userKey
masterKey,
masterKeyHash,
);
} else {
// ...in Standard Auth Request Flow 2
const userKey = await this.authRequestService.decryptPubKeyEncryptedUserKey(
authRequestResponse.key,
this.authRequestKeyPair.privateKey,
);
return new AuthRequestLoginCredentials(
this.email,
this.accessCode,
requestId,
userKey,
null, // no masterKey
null, // no masterKeyHash
);
}
// ...in Standard Auth Request Flow 2
const userKey = await this.authRequestService.decryptPubKeyEncryptedUserKey(
authRequestResponse.key,
this.authRequestKeyPair.privateKey,
);
return new AuthRequestLoginCredentials(this.email, this.accessCode, requestId, userKey);
}
private async clearExistingAdminAuthRequestAndStartNewRequest(userId: UserId) {

View File

@@ -4,7 +4,7 @@ import { AdminAuthRequestStorable } from "@bitwarden/common/auth/models/domain/a
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response";
import { UserId } from "@bitwarden/common/types/guid";
import { UserKey, MasterKey } from "@bitwarden/common/types/key";
import { UserKey } from "@bitwarden/common/types/key";
export abstract class AuthRequestServiceAbstraction {
/** Emits an auth request id when an auth request has been approved. */
@@ -75,17 +75,6 @@ export abstract class AuthRequestServiceAbstraction {
authReqPrivateKey: Uint8Array,
userId: UserId,
): Promise<void>;
/**
* Sets the `MasterKey` and `MasterKeyHash` from an auth request. Auth request must have a `MasterKey` and `MasterKeyHash`.
* @param authReqResponse The auth request.
* @param authReqPrivateKey The private key corresponding to the public key sent in the auth request.
* @param userId The ID of the user for whose account we will set the keys.
*/
abstract setKeysAfterDecryptingSharedMasterKeyAndHash(
authReqResponse: AuthRequestResponse,
authReqPrivateKey: Uint8Array,
userId: UserId,
): Promise<void>;
/**
* Decrypts a `UserKey` from a public key encrypted `UserKey`.
* @param pubKeyEncryptedUserKey The public key encrypted `UserKey`.
@@ -96,18 +85,6 @@ export abstract class AuthRequestServiceAbstraction {
pubKeyEncryptedUserKey: string,
privateKey: Uint8Array,
): Promise<UserKey>;
/**
* Decrypts a `MasterKey` and `MasterKeyHash` from a public key encrypted `MasterKey` and `MasterKeyHash`.
* @param pubKeyEncryptedMasterKey The public key encrypted `MasterKey`.
* @param pubKeyEncryptedMasterKeyHash The public key encrypted `MasterKeyHash`.
* @param privateKey The private key corresponding to the public key used to encrypt the `MasterKey` and `MasterKeyHash`.
* @returns The decrypted `MasterKey` and `MasterKeyHash`.
*/
abstract decryptPubKeyEncryptedMasterKeyAndHash(
pubKeyEncryptedMasterKey: string,
pubKeyEncryptedMasterKeyHash: string,
privateKey: Uint8Array,
): Promise<{ masterKey: MasterKey; masterKeyHash: string }>;
/**
* Handles incoming auth request push server notifications.

View File

@@ -26,7 +26,7 @@ import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/sym
import { makeEncString, FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
import { CsprngArray } from "@bitwarden/common/types/csprng";
import { UserId } from "@bitwarden/common/types/guid";
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
import { UserKey } from "@bitwarden/common/types/key";
import { KdfConfigService, KeyService } from "@bitwarden/key-management";
import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction";
@@ -73,11 +73,7 @@ describe("AuthRequestLoginStrategy", () => {
const email = "EMAIL";
const accessCode = "ACCESS_CODE";
const authRequestId = "AUTH_REQUEST_ID";
const decMasterKey = new SymmetricCryptoKey(
new Uint8Array(64).buffer as CsprngArray,
) as MasterKey;
const decUserKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as UserKey;
const decMasterKeyHash = "LOCAL_PASSWORD_HASH";
beforeEach(async () => {
keyService = mock<KeyService>();
@@ -150,42 +146,6 @@ describe("AuthRequestLoginStrategy", () => {
);
});
it("sets keys after a successful authentication when masterKey and masterKeyHash provided in login credentials", async () => {
credentials = new AuthRequestLoginCredentials(
email,
accessCode,
authRequestId,
null,
decMasterKey,
decMasterKeyHash,
);
const masterKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as MasterKey;
const userKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as UserKey;
masterPasswordService.masterKeySubject.next(masterKey);
masterPasswordService.mock.decryptUserKeyWithMasterKey.mockResolvedValue(userKey);
tokenService.decodeAccessToken.mockResolvedValue({ sub: mockUserId });
await authRequestLoginStrategy.logIn(credentials);
expect(masterPasswordService.mock.setMasterKey).toHaveBeenCalledWith(masterKey, mockUserId);
expect(masterPasswordService.mock.setMasterKeyHash).toHaveBeenCalledWith(
decMasterKeyHash,
mockUserId,
);
expect(masterPasswordService.mock.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(
tokenResponse.key,
mockUserId,
);
expect(keyService.setUserKey).toHaveBeenCalledWith(userKey, mockUserId);
expect(deviceTrustService.trustDeviceIfRequired).toHaveBeenCalled();
expect(accountCryptographicStateService.setAccountCryptographicState).toHaveBeenCalledWith(
{ V1: { private_key: tokenResponse.privateKey } },
mockUserId,
);
});
it("sets keys after a successful authentication when only userKey provided in login credentials", async () => {
// Initialize credentials with only userKey
credentials = new AuthRequestLoginCredentials(
@@ -193,8 +153,6 @@ describe("AuthRequestLoginStrategy", () => {
accessCode,
authRequestId,
decUserKey, // Pass userKey
null, // No masterKey
null, // No masterKeyHash
);
// Call logIn
@@ -240,7 +198,6 @@ describe("AuthRequestLoginStrategy", () => {
};
apiService.postIdentityToken.mockResolvedValue(tokenResponse);
masterPasswordService.masterKeySubject.next(decMasterKey);
masterPasswordService.mock.decryptUserKeyWithMasterKey.mockResolvedValue(decUserKey);
await authRequestLoginStrategy.logIn(credentials);

View File

@@ -72,20 +72,7 @@ export class AuthRequestLoginStrategy extends LoginStrategy {
}
protected override async setMasterKey(response: IdentityTokenResponse, userId: UserId) {
const authRequestCredentials = this.cache.value.authRequestCredentials;
if (
authRequestCredentials.decryptedMasterKey &&
authRequestCredentials.decryptedMasterKeyHash
) {
await this.masterPasswordService.setMasterKey(
authRequestCredentials.decryptedMasterKey,
userId,
);
await this.masterPasswordService.setMasterKeyHash(
authRequestCredentials.decryptedMasterKeyHash,
userId,
);
}
// This login strategy does not use a master key
}
protected override async setUserKey(

View File

@@ -416,24 +416,6 @@ describe("SsoLoginStrategy", () => {
);
});
it("sets the user key using master key and hash from approved admin request if exists", async () => {
apiService.postIdentityToken.mockResolvedValue(tokenResponse);
keyService.hasUserKey.mockResolvedValue(true);
const adminAuthResponse = {
id: "1",
publicKey: "PRIVATE" as any,
key: "KEY" as any,
masterPasswordHash: "HASH" as any,
requestApproved: true,
};
apiService.getAuthRequest.mockResolvedValue(adminAuthResponse as AuthRequestResponse);
await ssoLoginStrategy.logIn(credentials);
expect(authRequestService.setKeysAfterDecryptingSharedMasterKeyAndHash).toHaveBeenCalled();
expect(deviceTrustService.decryptUserKeyWithDeviceKey).not.toHaveBeenCalled();
});
it("sets the user key from approved admin request if exists", async () => {
apiService.postIdentityToken.mockResolvedValue(tokenResponse);
keyService.hasUserKey.mockResolvedValue(true);
@@ -475,9 +457,6 @@ describe("SsoLoginStrategy", () => {
await ssoLoginStrategy.logIn(credentials);
expect(authRequestService.clearAdminAuthRequest).toHaveBeenCalled();
expect(
authRequestService.setKeysAfterDecryptingSharedMasterKeyAndHash,
).not.toHaveBeenCalled();
expect(authRequestService.setUserKeyAfterDecryptingSharedUserKey).not.toHaveBeenCalled();
expect(deviceTrustService.trustDeviceIfRequired).not.toHaveBeenCalled();
});

View File

@@ -239,23 +239,11 @@ export class SsoLoginStrategy extends LoginStrategy {
}
if (adminAuthReqResponse?.requestApproved) {
// if masterPasswordHash has a value, we will always receive authReqResponse.key
// as authRequestPublicKey(masterKey) + authRequestPublicKey(masterPasswordHash)
if (adminAuthReqResponse.masterPasswordHash) {
await this.authRequestService.setKeysAfterDecryptingSharedMasterKeyAndHash(
adminAuthReqResponse,
adminAuthReqStorable.privateKey,
userId,
);
} else {
// if masterPasswordHash is null, we will always receive authReqResponse.key
// as authRequestPublicKey(userKey)
await this.authRequestService.setUserKeyAfterDecryptingSharedUserKey(
adminAuthReqResponse,
adminAuthReqStorable.privateKey,
userId,
);
}
await this.authRequestService.setUserKeyAfterDecryptingSharedUserKey(
adminAuthReqResponse,
adminAuthReqStorable.privateKey,
userId,
);
if (await this.keyService.hasUserKey(userId)) {
// Now that we have a decrypted user key in memory, we can check if we

View File

@@ -7,7 +7,7 @@ import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication-
import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request";
import { WebAuthnLoginAssertionResponseRequest } from "@bitwarden/common/auth/services/webauthn-login/request/webauthn-login-assertion-response.request";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { UserKey, MasterKey } from "@bitwarden/common/types/key";
import { UserKey } from "@bitwarden/common/types/key";
export class PasswordLoginCredentials {
readonly type = AuthenticationType.Password;
@@ -54,8 +54,6 @@ export class AuthRequestLoginCredentials {
public accessCode: string,
public authRequestId: string,
public decryptedUserKey: UserKey | null,
public decryptedMasterKey: MasterKey | null,
public decryptedMasterKeyHash: string | null,
public twoFactor?: TokenTwoFactorRequest,
) {}
@@ -66,8 +64,6 @@ export class AuthRequestLoginCredentials {
json.accessCode,
json.authRequestId,
null,
null,
json.decryptedMasterKeyHash,
json.twoFactor
? new TokenTwoFactorRequest(
json.twoFactor.provider,
@@ -78,7 +74,6 @@ export class AuthRequestLoginCredentials {
),
{
decryptedUserKey: SymmetricCryptoKey.fromJSON(json.decryptedUserKey) as UserKey,
decryptedMasterKey: SymmetricCryptoKey.fromJSON(json.decryptedMasterKey) as MasterKey,
},
);
}

View File

@@ -13,7 +13,7 @@ import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.ser
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { StateProvider } from "@bitwarden/common/platform/state";
import { UserId } from "@bitwarden/common/types/guid";
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
import { UserKey } from "@bitwarden/common/types/key";
import { newGuid } from "@bitwarden/guid";
import { KeyService } from "@bitwarden/key-management";
@@ -154,60 +154,6 @@ describe("AuthRequestService", () => {
});
});
describe("setKeysAfterDecryptingSharedMasterKeyAndHash", () => {
it("decrypts and sets master key and hash and user key when given valid auth request response and private key", async () => {
// Arrange
const mockAuthReqResponse = {
key: "authReqPublicKeyEncryptedMasterKey",
masterPasswordHash: "authReqPublicKeyEncryptedMasterKeyHash",
} as AuthRequestResponse;
const mockDecryptedMasterKey = {} as MasterKey;
const mockDecryptedMasterKeyHash = "mockDecryptedMasterKeyHash";
const mockDecryptedUserKey = {} as UserKey;
jest.spyOn(sut, "decryptPubKeyEncryptedMasterKeyAndHash").mockResolvedValueOnce({
masterKey: mockDecryptedMasterKey,
masterKeyHash: mockDecryptedMasterKeyHash,
});
masterPasswordService.masterKeySubject.next(undefined);
masterPasswordService.masterKeyHashSubject.next(undefined);
masterPasswordService.mock.decryptUserKeyWithMasterKey.mockResolvedValue(
mockDecryptedUserKey,
);
keyService.setUserKey.mockResolvedValueOnce(undefined);
// Act
await sut.setKeysAfterDecryptingSharedMasterKeyAndHash(
mockAuthReqResponse,
mockPrivateKey,
mockUserId,
);
// Assert
expect(sut.decryptPubKeyEncryptedMasterKeyAndHash).toBeCalledWith(
mockAuthReqResponse.key,
mockAuthReqResponse.masterPasswordHash,
mockPrivateKey,
);
expect(masterPasswordService.mock.setMasterKey).toHaveBeenCalledWith(
mockDecryptedMasterKey,
mockUserId,
);
expect(masterPasswordService.mock.setMasterKeyHash).toHaveBeenCalledWith(
mockDecryptedMasterKeyHash,
mockUserId,
);
expect(masterPasswordService.mock.decryptUserKeyWithMasterKey).toHaveBeenCalledWith(
mockDecryptedMasterKey,
mockUserId,
undefined,
);
expect(keyService.setUserKey).toHaveBeenCalledWith(mockDecryptedUserKey, mockUserId);
});
});
describe("decryptAuthReqPubKeyEncryptedUserKey", () => {
it("returns a decrypted user key when given valid public key encrypted user key and an auth req private key", async () => {
// Arrange

View File

@@ -16,14 +16,13 @@ import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import {
AUTH_REQUEST_DISK_LOCAL,
StateProvider,
UserKeyDefinition,
} from "@bitwarden/common/platform/state";
import { UserId } from "@bitwarden/common/types/guid";
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
import { UserKey } from "@bitwarden/common/types/key";
import { KeyService } from "@bitwarden/key-management";
import { AuthRequestApiServiceAbstraction } from "../../abstractions/auth-request-api.service";
@@ -163,27 +162,6 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
await this.keyService.setUserKey(userKey, userId);
}
async setKeysAfterDecryptingSharedMasterKeyAndHash(
authReqResponse: AuthRequestResponse,
authReqPrivateKey: Uint8Array,
userId: UserId,
) {
const { masterKey, masterKeyHash } = await this.decryptPubKeyEncryptedMasterKeyAndHash(
authReqResponse.key,
authReqResponse.masterPasswordHash,
authReqPrivateKey,
);
// Decrypt and set user key in state
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey, userId);
// Set masterKey + masterKeyHash in state after decryption (in case decryption fails)
await this.masterPasswordService.setMasterKey(masterKey, userId);
await this.masterPasswordService.setMasterKeyHash(masterKeyHash, userId);
await this.keyService.setUserKey(userKey, userId);
}
// Decryption helpers
async decryptPubKeyEncryptedUserKey(
pubKeyEncryptedUserKey: string,
@@ -197,30 +175,6 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
return decryptedUserKey as UserKey;
}
async decryptPubKeyEncryptedMasterKeyAndHash(
pubKeyEncryptedMasterKey: string,
pubKeyEncryptedMasterKeyHash: string,
privateKey: Uint8Array,
): Promise<{ masterKey: MasterKey; masterKeyHash: string }> {
const decryptedMasterKeyArrayBuffer = await this.encryptService.rsaDecrypt(
new EncString(pubKeyEncryptedMasterKey),
privateKey,
);
const decryptedMasterKeyHashArrayBuffer = await this.encryptService.rsaDecrypt(
new EncString(pubKeyEncryptedMasterKeyHash),
privateKey,
);
const masterKey = new SymmetricCryptoKey(decryptedMasterKeyArrayBuffer) as MasterKey;
const masterKeyHash = Utils.fromBufferToUtf8(decryptedMasterKeyHashArrayBuffer);
return {
masterKey,
masterKeyHash,
};
}
sendAuthRequestPushNotification(notification: AuthRequestPushNotification): void {
if (notification.id != null) {
this.authRequestPushNotificationSubject.next(notification.id);

View File

@@ -93,8 +93,6 @@ describe("LOGIN_STRATEGY_CACHE_KEY", () => {
"ACCESS_CODE",
"AUTH_REQUEST_ID",
new SymmetricCryptoKey(new Uint8Array(64)) as UserKey,
new SymmetricCryptoKey(new Uint8Array(64)) as MasterKey,
"MASTER_KEY_HASH",
);
const result = sut.deserializer(JSON.parse(JSON.stringify(actual)));

View File

@@ -11,8 +11,7 @@ export class AuthRequestResponse extends BaseResponse {
requestDeviceIdentifier: string;
requestIpAddress: string;
requestCountryName: string;
key: string; // could be either an encrypted MasterKey or an encrypted UserKey
masterPasswordHash: string; // if hash is present, the `key` above is an encrypted MasterKey (else `key` is an encrypted UserKey)
key: string; // Auth-request public-key encrypted user-key. Note: No sender authenticity provided!
creationDate: string;
requestApproved?: boolean;
responseDate?: string;
@@ -30,7 +29,6 @@ export class AuthRequestResponse extends BaseResponse {
this.requestIpAddress = this.getResponseProperty("RequestIpAddress");
this.requestCountryName = this.getResponseProperty("RequestCountryName");
this.key = this.getResponseProperty("Key");
this.masterPasswordHash = this.getResponseProperty("MasterPasswordHash");
this.creationDate = this.getResponseProperty("CreationDate");
this.requestApproved = this.getResponseProperty("RequestApproved");
this.responseDate = this.getResponseProperty("ResponseDate");

View File

@@ -161,13 +161,6 @@ export abstract class EncryptService {
decapsulationKey: Uint8Array,
): Promise<SymmetricCryptoKey>;
/**
* @deprecated Use @see {@link decapsulateKeyUnsigned} instead
* @param data - The ciphertext to decrypt
* @param privateKey - The privateKey to decrypt with
*/
abstract rsaDecrypt(data: EncString, privateKey: Uint8Array): Promise<Uint8Array>;
/**
* Generates a base64-encoded hash of the given value
* @param value The value to hash

View File

@@ -219,24 +219,4 @@ export class EncryptServiceImplementation implements EncryptService {
);
return new SymmetricCryptoKey(keyBytes);
}
async rsaDecrypt(data: EncString, privateKey: Uint8Array): Promise<Uint8Array> {
if (data == null) {
throw new Error("[Encrypt service] rsaDecrypt: No data provided for decryption.");
}
switch (data.encryptionType) {
case EncryptionType.Rsa2048_OaepSha1_B64:
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
break;
default:
throw new Error("Invalid encryption type.");
}
if (privateKey == null) {
throw new Error("[Encrypt service] rsaDecrypt: No private key provided for decryption.");
}
return this.cryptoFunctionService.rsaDecrypt(data.dataBytes, privateKey, "sha1");
}
}