diff --git a/common/src/abstractions/keyConnector.service.ts b/common/src/abstractions/keyConnector.service.ts index ca57bbf6..db2392ff 100644 --- a/common/src/abstractions/keyConnector.service.ts +++ b/common/src/abstractions/keyConnector.service.ts @@ -6,6 +6,7 @@ export abstract class KeyConnectorService { getUsesKeyConnector: () => Promise; migrateUser: () => Promise; userNeedsMigration: () => Promise; + convertNewSsoUserToKeyConnector: (kdf: number, kdfIterations: number, url: string, orgId: string) => void; setUsesKeyConnector: (enabled: boolean) => Promise; setConvertAccountRequired: (status: boolean) => Promise; getConvertAccountRequired: () => Promise; diff --git a/common/src/services/auth.service.ts b/common/src/services/auth.service.ts index 4ea1d932..0a43d3f5 100644 --- a/common/src/services/auth.service.ts +++ b/common/src/services/auth.service.ts @@ -291,7 +291,7 @@ export class AuthService implements AuthServiceAbstraction { await this.cryptoService.setEncPrivateKey(tokenResponse.privateKey); } } else if (tokenResponse.keyConnectorUrl != null) { - await this.convertNewUserToKeyConnector(tokenResponse, orgId); + await this.keyConnectorService.convertNewSsoUserToKeyConnector(tokenResponse.kdf, tokenResponse.kdfIterations, tokenResponse.keyConnectorUrl, orgId); } } @@ -355,43 +355,6 @@ export class AuthService implements AuthServiceAbstraction { }); } - private async convertNewUserToKeyConnector(tokenResponse: IdentityTokenResponse, orgId: string) { - const password = await this.cryptoFunctionService.randomBytes(64); - - const k = await this.cryptoService.makeKey( - Utils.fromBufferToB64(password), - await this.tokenService.getEmail(), - tokenResponse.kdf, - tokenResponse.kdfIterations - ); - const keyConnectorRequest = new KeyConnectorUserKeyRequest(k.encKeyB64); - await this.cryptoService.setKey(k); - - const encKey = await this.cryptoService.makeEncKey(k); - await this.cryptoService.setEncKey(encKey[1].encryptedString); - - const [pubKey, privKey] = await this.cryptoService.makeKeyPair(); - - try { - await this.apiService.postUserKeyToKeyConnector( - tokenResponse.keyConnectorUrl, - keyConnectorRequest - ); - } catch (e) { - throw new Error("Unable to reach key connector"); - } - - const keys = new KeysRequest(pubKey, privKey.encryptedString); - const setPasswordRequest = new SetKeyConnectorKeyRequest( - encKey[1].encryptedString, - tokenResponse.kdf, - tokenResponse.kdfIterations, - orgId, - keys - ); - await this.apiService.postSetKeyConnectorKey(setPasswordRequest); - } - private async createKeyPair() { try { const keyPair = await this.cryptoService.makeKeyPair(); diff --git a/common/src/services/keyConnector.service.ts b/common/src/services/keyConnector.service.ts index a258a0e3..431984fe 100644 --- a/common/src/services/keyConnector.service.ts +++ b/common/src/services/keyConnector.service.ts @@ -1,5 +1,6 @@ import { ApiService } from "../abstractions/api.service"; import { CryptoService } from "../abstractions/crypto.service"; +import { CryptoFunctionService } from '../abstractions/cryptoFunction.service'; import { KeyConnectorService as KeyConnectorServiceAbstraction } from "../abstractions/keyConnector.service"; import { LogService } from "../abstractions/log.service"; import { OrganizationService } from "../abstractions/organization.service"; @@ -11,8 +12,10 @@ import { OrganizationUserType } from "../enums/organizationUserType"; import { Utils } from "../misc/utils"; import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { SetKeyConnectorKeyRequest } from '../models/request/account/setKeyConnectorKeyRequest'; import { KeyConnectorUserKeyRequest } from "../models/request/keyConnectorUserKeyRequest"; +import { KeysRequest } from '../models/request/keysRequest'; export class KeyConnectorService implements KeyConnectorServiceAbstraction { constructor( @@ -21,7 +24,8 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction { private apiService: ApiService, private tokenService: TokenService, private logService: LogService, - private organizationService: OrganizationService + private organizationService: OrganizationService, + private cryptoFunctionService: CryptoFunctionService, ) {} setUsesKeyConnector(usesKeyConnector: boolean) { @@ -80,6 +84,43 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction { ); } + async convertNewSsoUserToKeyConnector(kdf: number, kdfIterations: number, url: string, orgId: string) { + const password = await this.cryptoFunctionService.randomBytes(64); + + const k = await this.cryptoService.makeKey( + Utils.fromBufferToB64(password), + await this.tokenService.getEmail(), + kdf, + kdfIterations + ); + const keyConnectorRequest = new KeyConnectorUserKeyRequest(k.encKeyB64); + await this.cryptoService.setKey(k); + + const encKey = await this.cryptoService.makeEncKey(k); + await this.cryptoService.setEncKey(encKey[1].encryptedString); + + const [pubKey, privKey] = await this.cryptoService.makeKeyPair(); + + try { + await this.apiService.postUserKeyToKeyConnector( + url, + keyConnectorRequest + ); + } catch (e) { + throw new Error("Unable to reach key connector"); + } + + const keys = new KeysRequest(pubKey, privKey.encryptedString); + const setPasswordRequest = new SetKeyConnectorKeyRequest( + encKey[1].encryptedString, + kdf, + kdfIterations, + orgId, + keys + ); + await this.apiService.postSetKeyConnectorKey(setPasswordRequest); + } + async setConvertAccountRequired(status: boolean) { await this.stateService.setConvertAccountToKeyConnector(status); } diff --git a/spec/common/services/auth.service.spec.ts b/spec/common/services/auth.service.spec.ts index 275659e1..88bddb98 100644 --- a/spec/common/services/auth.service.spec.ts +++ b/spec/common/services/auth.service.spec.ts @@ -220,7 +220,7 @@ describe("Cipher Service", () => { // Negative tests apiService.didNotReceive().postAccountKeys(Arg.any()); // Did not generate new private key pair keyConnectorService.didNotReceive().getAndSetKey(Arg.any()); // Did not fetch Key Connector key - apiService.didNotReceive().postUserKeyToKeyConnector(Arg.any(), Arg.any()); // Did not send key to KC + keyConnectorService.didNotReceive().convertNewSsoUserToKeyConnector(Arg.all()); // Did not send key to KC tokenService.didNotReceive().setTwoFactorToken(Arg.any()); // Did not save 2FA token // Return result: @@ -447,7 +447,7 @@ describe("Cipher Service", () => { cryptoService.didNotReceive().setKeyHash(localHashedPassword); apiService.didNotReceive().postAccountKeys(Arg.any()); // Did not generate new private key pair keyConnectorService.didNotReceive().getAndSetKey(Arg.any()); // Did not fetch Key Connector key - apiService.didNotReceive().postUserKeyToKeyConnector(Arg.any(), Arg.any()); // Did not send key to KC + keyConnectorService.didNotReceive().convertNewSsoUserToKeyConnector(Arg.all()); // Did not send key to KC tokenService.didNotReceive().setTwoFactorToken(Arg.any()); // Did not save 2FA token // Return result: @@ -514,22 +514,7 @@ describe("Cipher Service", () => { const result = await authService.logInSso(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId); commonSuccessAssertions(); - cryptoService.received(1).setKey(preloginKey); - cryptoService.received(1).setEncKey(Arg.any()); - apiService.received(1).postUserKeyToKeyConnector(keyConnectorUrl, Arg.any()); - apiService - .received(1) - .postSetKeyConnectorKey( - Arg.is( - (r) => - r.kdf === kdf && - r.kdfIterations === kdfIterations && - r.key === realEncKey[1].encryptedString && - r.orgIdentifier === ssoOrgId && - r.keys.encryptedPrivateKey === privKey.encryptedString && - r.keys.publicKey === pubKey - ) - ); + keyConnectorService.received(1).convertNewSsoUserToKeyConnector(kdf, kdfIterations, keyConnectorUrl, ssoOrgId); }); // API @@ -587,7 +572,7 @@ describe("Cipher Service", () => { // Negative tests apiService.didNotReceive().postAccountKeys(Arg.any()); // Did not generate new private key pair keyConnectorService.didNotReceive().getAndSetKey(Arg.any()); // Did not fetch Key Connector key - apiService.didNotReceive().postUserKeyToKeyConnector(Arg.any(), Arg.any()); // Did not send key to KC + keyConnectorService.didNotReceive().convertNewSsoUserToKeyConnector(Arg.all()); // Did not send key to KC tokenService.didNotReceive().setTwoFactorToken(Arg.any()); // Did not save 2FA token // Return result: