diff --git a/packages/encryption/src/Domain/Service/RootKey/RootKeyServiceEvent.ts b/packages/encryption/src/Domain/Service/RootKey/RootKeyServiceEvent.ts deleted file mode 100644 index 4889f650c..000000000 --- a/packages/encryption/src/Domain/Service/RootKey/RootKeyServiceEvent.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum RootKeyServiceEvent { - RootKeyStatusChanged = 'RootKeyStatusChanged', -} diff --git a/packages/encryption/src/Domain/index.ts b/packages/encryption/src/Domain/index.ts index 15f668b51..569c72a2f 100644 --- a/packages/encryption/src/Domain/index.ts +++ b/packages/encryption/src/Domain/index.ts @@ -36,7 +36,6 @@ export * from './Service/Encryption/EncryptionProviderInterface' export * from './Service/KeySystemKeyManagerInterface' export * from './Service/Functions' export * from './Service/RootKey/KeyMode' -export * from './Service/RootKey/RootKeyServiceEvent' export * from './Split/AbstractKeySplit' export * from './Split/EncryptionSplit' diff --git a/packages/services/src/Domain/Encryption/EncryptionService.ts b/packages/services/src/Domain/Encryption/EncryptionService.ts index 0892f544f..358de08d4 100644 --- a/packages/services/src/Domain/Encryption/EncryptionService.ts +++ b/packages/services/src/Domain/Encryption/EncryptionService.ts @@ -1,3 +1,5 @@ +import { InternalEventInterface } from './../Internal/InternalEventInterface' +import { InternalEventHandlerInterface } from './../Internal/InternalEventHandlerInterface' import { MutatorClientInterface } from './../Mutator/MutatorClientInterface' import { CreateAnyKeyParams, @@ -16,9 +18,6 @@ import { LegacyAttachedData, OperatorManager, RootKeyEncryptedAuthenticatedData, - RootKeyServiceEvent, - SNRootKey, - SNRootKeyParams, SplitPayloadsByEncryptionType, V001Algorithm, V002Algorithm, @@ -47,6 +46,7 @@ import { KeySystemRootKeyInterface, KeySystemRootKeyParamsInterface, TrustedContactInterface, + RootKeyParamsInterface, } from '@standardnotes/models' import { ClientDisplayableError } from '@standardnotes/responses' import { PkcKeyPair, PureCryptoInterface } from '@standardnotes/sncrypto-common' @@ -78,10 +78,20 @@ import { DeviceInterface } from '../Device/DeviceInterface' import { StorageServiceInterface } from '../Storage/StorageServiceInterface' import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface' import { SyncEvent } from '../Event/SyncEvent' -import { RootKeyEncryptionService } from './RootKeyEncryption' import { DecryptBackupFileUseCase } from './DecryptBackupFileUseCase' import { EncryptionServiceEvent } from './EncryptionServiceEvent' import { DecryptedParameters } from '@standardnotes/encryption/src/Domain/Types/DecryptedParameters' +import { RootKeyManager } from './RootKey/RootKeyManager' +import { RootKeyManagerEvent } from './RootKey/RootKeyManagerEvent' +import { CreateNewItemsKeyWithRollbackUseCase } from './UseCase/ItemsKey/CreateNewItemsKeyWithRollback' +import { DecryptErroredRootPayloadsUseCase } from './UseCase/RootEncryption/DecryptErroredPayloads' +import { CreateNewDefaultItemsKeyUseCase } from './UseCase/ItemsKey/CreateNewDefaultItemsKey' +import { RootKeyDecryptPayloadUseCase } from './UseCase/RootEncryption/DecryptPayload' +import { RootKeyDecryptPayloadWithKeyLookupUseCase } from './UseCase/RootEncryption/DecryptPayloadWithKeyLookup' +import { RootKeyEncryptPayloadWithKeyLookupUseCase } from './UseCase/RootEncryption/EncryptPayloadWithKeyLookup' +import { RootKeyEncryptPayloadUseCase } from './UseCase/RootEncryption/EncryptPayload' +import { ValidateAccountPasswordResult } from './RootKey/ValidateAccountPasswordResult' +import { ValidatePasscodeResult } from './RootKey/ValidatePasscodeResult' /** * The encryption service is responsible for the encryption and decryption of payloads, and @@ -110,75 +120,73 @@ import { DecryptedParameters } from '@standardnotes/encryption/src/Domain/Types/ * It also exposes public methods that allows consumers to retrieve an items key * for a particular payload, and also retrieve all available items keys. */ -export class EncryptionService extends AbstractService implements EncryptionProviderInterface { - private operatorManager: OperatorManager +export class EncryptionService + extends AbstractService + implements EncryptionProviderInterface, InternalEventHandlerInterface +{ + private operators: OperatorManager private readonly itemsEncryption: ItemsEncryptionService - private readonly rootKeyEncryption: RootKeyEncryptionService - private rootKeyObserverDisposer: () => void + private readonly rootKeyManager: RootKeyManager constructor( - private itemManager: ItemManagerInterface, + private items: ItemManagerInterface, private mutator: MutatorClientInterface, - private payloadManager: PayloadManagerInterface, - public deviceInterface: DeviceInterface, - private storageService: StorageServiceInterface, + private payloads: PayloadManagerInterface, + public device: DeviceInterface, + private storage: StorageServiceInterface, public readonly keys: KeySystemKeyManagerInterface, - private identifier: ApplicationIdentifier, + identifier: ApplicationIdentifier, public crypto: PureCryptoInterface, protected override internalEventBus: InternalEventBusInterface, ) { super(internalEventBus) this.crypto = crypto - this.operatorManager = new OperatorManager(crypto) + this.operators = new OperatorManager(crypto) - this.itemsEncryption = new ItemsEncryptionService( - itemManager, - payloadManager, - storageService, - this.operatorManager, - keys, + this.rootKeyManager = new RootKeyManager( + device, + storage, + items, + mutator, + this.operators, + identifier, internalEventBus, ) - this.rootKeyEncryption = new RootKeyEncryptionService( - this.itemManager, - this.mutator, - this.operatorManager, - this.deviceInterface, - this.storageService, - this.payloadManager, - keys, - this.identifier, - this.internalEventBus, - ) + internalEventBus.addEventHandler(this, RootKeyManagerEvent.RootKeyManagerKeyStatusChanged) - this.rootKeyObserverDisposer = this.rootKeyEncryption.addEventObserver((event) => { - this.itemsEncryption.userVersion = this.getUserVersion() - if (event === RootKeyServiceEvent.RootKeyStatusChanged) { - void this.notifyEvent(EncryptionServiceEvent.RootKeyStatusChanged) - } - }) + this.itemsEncryption = new ItemsEncryptionService(items, payloads, storage, this.operators, keys, internalEventBus) UuidGenerator.SetGenerator(this.crypto.generateUUID) } - public override deinit(): void { - ;(this.itemManager as unknown) = undefined - ;(this.payloadManager as unknown) = undefined - ;(this.deviceInterface as unknown) = undefined - ;(this.storageService as unknown) = undefined - ;(this.crypto as unknown) = undefined - ;(this.operatorManager as unknown) = undefined + async handleEvent(event: InternalEventInterface): Promise { + if (event.type === RootKeyManagerEvent.RootKeyManagerKeyStatusChanged) { + this.itemsEncryption.userVersion = this.getUserVersion() + void this.notifyEvent(EncryptionServiceEvent.RootKeyStatusChanged) + } + } - this.rootKeyObserverDisposer() - ;(this.rootKeyObserverDisposer as unknown) = undefined + public override async blockDeinit(): Promise { + await Promise.all([this.rootKeyManager.blockDeinit(), this.itemsEncryption.blockDeinit()]) + + return super.blockDeinit() + } + + public override deinit(): void { + ;(this.items as unknown) = undefined + ;(this.payloads as unknown) = undefined + ;(this.device as unknown) = undefined + ;(this.storage as unknown) = undefined + ;(this.crypto as unknown) = undefined + ;(this.operators as unknown) = undefined this.itemsEncryption.deinit() ;(this.itemsEncryption as unknown) = undefined - this.rootKeyEncryption.deinit() - ;(this.rootKeyEncryption as unknown) = undefined + this.rootKeyManager.deinit() + ;(this.rootKeyManager as unknown) = undefined super.deinit() } @@ -210,17 +218,17 @@ export class EncryptionService extends AbstractService i } public async initialize() { - await this.rootKeyEncryption.initialize() + await this.rootKeyManager.initialize() } /** * Returns encryption protocol display name for active account/wrapper */ public async getEncryptionDisplayName(): Promise { - const version = await this.rootKeyEncryption.getEncryptionSourceVersion() + const version = await this.rootKeyManager.getEncryptionSourceVersion() if (version) { - return this.operatorManager.operatorForVersion(version).getEncryptionDisplayName() + return this.operators.operatorForVersion(version).getEncryptionDisplayName() } throw Error('Attempting to access encryption display name wtihout source') @@ -231,15 +239,15 @@ export class EncryptionService extends AbstractService i } public hasAccount() { - return this.rootKeyEncryption.hasAccount() + return this.rootKeyManager.hasAccount() } public hasRootKeyEncryptionSource(): boolean { - return this.rootKeyEncryption.hasRootKeyEncryptionSource() + return this.rootKeyManager.hasRootKeyEncryptionSource() } public getUserVersion(): ProtocolVersion | undefined { - return this.rootKeyEncryption.getUserVersion() + return this.rootKeyManager.getUserVersion() } public async upgradeAvailable() { @@ -257,19 +265,34 @@ export class EncryptionService extends AbstractService i } public async reencryptApplicableItemsAfterUserRootKeyChange(): Promise { - await this.rootKeyEncryption.reencryptApplicableItemsAfterUserRootKeyChange() + await this.rootKeyManager.reencryptApplicableItemsAfterUserRootKeyChange() } - public reencryptKeySystemItemsKeysForVault(keySystemIdentifier: KeySystemIdentifier): Promise { - return this.rootKeyEncryption.reencryptKeySystemItemsKeysForVault(keySystemIdentifier) + /** + * When the key system root key changes, we must re-encrypt all vault items keys + * with this new key system root key (by simply re-syncing). + */ + public async reencryptKeySystemItemsKeysForVault(keySystemIdentifier: KeySystemIdentifier): Promise { + const keySystemItemsKeys = this.keys.getKeySystemItemsKeys(keySystemIdentifier) + if (keySystemItemsKeys.length > 0) { + await this.mutator.setItemsDirty(keySystemItemsKeys) + } } public async createNewItemsKeyWithRollback(): Promise<() => Promise> { - return this.rootKeyEncryption.createNewItemsKeyWithRollback() + const usecase = new CreateNewItemsKeyWithRollbackUseCase( + this.mutator, + this.items, + this.operators, + this.rootKeyManager, + ) + return usecase.execute() } public async decryptErroredPayloads(): Promise { - await this.rootKeyEncryption.decryptErroredRootPayloads() + const usecase = new DecryptErroredRootPayloadsUseCase(this.payloads, this.operators, this.keys, this.rootKeyManager) + await usecase.execute() + await this.itemsEncryption.decryptErroredItemPayloads() } @@ -302,10 +325,18 @@ export class EncryptionService extends AbstractService i usesKeySystemRootKeyWithKeyLookup, } = split + const rootKeyEncryptWithKeyLookupUsecase = new RootKeyEncryptPayloadWithKeyLookupUseCase( + this.operators, + this.keys, + this.rootKeyManager, + ) + + const rootKeyEncryptUsecase = new RootKeyEncryptPayloadUseCase(this.operators) + const signingKeyPair = this.hasSigningKeyPair() ? this.getSigningKeyPair() : undefined if (usesRootKey) { - const rootKeyEncrypted = await this.rootKeyEncryption.encryptPayloads( + const rootKeyEncrypted = await rootKeyEncryptUsecase.executeMany( usesRootKey.items, usesRootKey.key, signingKeyPair, @@ -314,7 +345,7 @@ export class EncryptionService extends AbstractService i } if (usesRootKeyWithKeyLookup) { - const rootKeyEncrypted = await this.rootKeyEncryption.encryptPayloadsWithKeyLookup( + const rootKeyEncrypted = await rootKeyEncryptWithKeyLookupUsecase.executeMany( usesRootKeyWithKeyLookup.items, signingKeyPair, ) @@ -322,7 +353,7 @@ export class EncryptionService extends AbstractService i } if (usesKeySystemRootKey) { - const keySystemRootKeyEncrypted = await this.rootKeyEncryption.encryptPayloads( + const keySystemRootKeyEncrypted = await rootKeyEncryptUsecase.executeMany( usesKeySystemRootKey.items, usesKeySystemRootKey.key, signingKeyPair, @@ -331,7 +362,7 @@ export class EncryptionService extends AbstractService i } if (usesKeySystemRootKeyWithKeyLookup) { - const keySystemRootKeyEncrypted = await this.rootKeyEncryption.encryptPayloadsWithKeyLookup( + const keySystemRootKeyEncrypted = await rootKeyEncryptWithKeyLookupUsecase.executeMany( usesKeySystemRootKeyWithKeyLookup.items, signingKeyPair, ) @@ -391,26 +422,32 @@ export class EncryptionService extends AbstractService i usesKeySystemRootKeyWithKeyLookup, } = split + const rootKeyDecryptUseCase = new RootKeyDecryptPayloadUseCase(this.operators) + + const rootKeyDecryptWithKeyLookupUsecase = new RootKeyDecryptPayloadWithKeyLookupUseCase( + this.operators, + this.keys, + this.rootKeyManager, + ) + if (usesRootKey) { - const rootKeyDecrypted = await this.rootKeyEncryption.decryptPayloads(usesRootKey.items, usesRootKey.key) + const rootKeyDecrypted = await rootKeyDecryptUseCase.executeMany(usesRootKey.items, usesRootKey.key) extendArray(resultParams, rootKeyDecrypted) } if (usesRootKeyWithKeyLookup) { - const rootKeyDecrypted = await this.rootKeyEncryption.decryptPayloadsWithKeyLookup( - usesRootKeyWithKeyLookup.items, - ) + const rootKeyDecrypted = await rootKeyDecryptWithKeyLookupUsecase.executeMany(usesRootKeyWithKeyLookup.items) extendArray(resultParams, rootKeyDecrypted) } if (usesKeySystemRootKey) { - const keySystemRootKeyDecrypted = await this.rootKeyEncryption.decryptPayloads( + const keySystemRootKeyDecrypted = await rootKeyDecryptUseCase.executeMany( usesKeySystemRootKey.items, usesKeySystemRootKey.key, ) extendArray(resultParams, keySystemRootKeyDecrypted) } if (usesKeySystemRootKeyWithKeyLookup) { - const keySystemRootKeyDecrypted = await this.rootKeyEncryption.decryptPayloadsWithKeyLookup( + const keySystemRootKeyDecrypted = await rootKeyDecryptWithKeyLookupUsecase.executeMany( usesKeySystemRootKeyWithKeyLookup.items, ) extendArray(resultParams, keySystemRootKeyDecrypted) @@ -492,14 +529,14 @@ export class EncryptionService extends AbstractService i * Returns true if the user's account protocol version is not equal to the latest version. */ public async passcodeUpgradeAvailable(): Promise { - return this.rootKeyEncryption.passcodeUpgradeAvailable() + return this.rootKeyManager.passcodeUpgradeAvailable() } /** * Determines whether the current environment is capable of supporting * key derivation. */ - public platformSupportsKeyDerivation(keyParams: SNRootKeyParams) { + public platformSupportsKeyDerivation(keyParams: RootKeyParamsInterface) { /** * If the version is 003 or lower, key derivation is supported unless the browser is * IE or Edge (or generally, where WebCrypto is not available) or React Native environment is detected. @@ -548,8 +585,11 @@ export class EncryptionService extends AbstractService i * Computes a root key given a password and key params. * Delegates computation to respective protocol operator. */ - public async computeRootKey(password: string, keyParams: SNRootKeyParams): Promise { - return this.rootKeyEncryption.computeRootKey(password, keyParams) + public async computeRootKey( + password: string, + keyParams: RootKeyParamsInterface, + ): Promise { + return this.rootKeyManager.computeRootKey(password, keyParams) } /** @@ -561,7 +601,7 @@ export class EncryptionService extends AbstractService i origination: KeyParamsOrigination, version?: ProtocolVersion, ): Promise { - return this.rootKeyEncryption.createRootKey(identifier, password, origination, version) + return this.rootKeyManager.createRootKey(identifier, password, origination, version) } createRandomizedKeySystemRootKey(dto: { @@ -569,7 +609,7 @@ export class EncryptionService extends AbstractService i systemName: string systemDescription?: string }): KeySystemRootKeyInterface { - return this.operatorManager.defaultOperator().createRandomizedKeySystemRootKey(dto) + return this.operators.defaultOperator().createRandomizedKeySystemRootKey(dto) } createUserInputtedKeySystemRootKey(dto: { @@ -578,14 +618,14 @@ export class EncryptionService extends AbstractService i systemDescription?: string userInputtedPassword: string }): KeySystemRootKeyInterface { - return this.operatorManager.defaultOperator().createUserInputtedKeySystemRootKey(dto) + return this.operators.defaultOperator().createUserInputtedKeySystemRootKey(dto) } deriveUserInputtedKeySystemRootKey(dto: { keyParams: KeySystemRootKeyParamsInterface userInputtedPassword: string }): KeySystemRootKeyInterface { - return this.operatorManager.defaultOperator().deriveUserInputtedKeySystemRootKey(dto) + return this.operators.defaultOperator().deriveUserInputtedKeySystemRootKey(dto) } createKeySystemItemsKey( @@ -594,7 +634,7 @@ export class EncryptionService extends AbstractService i sharedVaultUuid: string | undefined, rootKeyToken: string, ): KeySystemItemsKeyInterface { - return this.operatorManager + return this.operators .defaultOperator() .createKeySystemItemsKey(uuid, keySystemIdentifier, sharedVaultUuid, rootKeyToken) } @@ -605,7 +645,7 @@ export class EncryptionService extends AbstractService i senderSigningKeyPair: PkcKeyPair recipientPublicKey: string }): AsymmetricallyEncryptedString { - const operator = this.operatorManager.defaultOperator() + const operator = this.operators.defaultOperator() const encrypted = operator.asymmetricEncrypt({ stringToEncrypt: JSON.stringify(dto.message), senderKeyPair: dto.senderKeyPair, @@ -620,9 +660,9 @@ export class EncryptionService extends AbstractService i trustedSender: TrustedContactInterface | undefined privateKey: string }): M | undefined { - const defaultOperator = this.operatorManager.defaultOperator() + const defaultOperator = this.operators.defaultOperator() const version = defaultOperator.versionForAsymmetricallyEncryptedString(dto.encryptedString) - const keyOperator = this.operatorManager.operatorForVersion(version) + const keyOperator = this.operators.operatorForVersion(version) const decryptedResult = keyOperator.asymmetricDecrypt({ stringToDecrypt: dto.encryptedString, recipientSecretKey: dto.privateKey, @@ -652,17 +692,17 @@ export class EncryptionService extends AbstractService i asymmetricSignatureVerifyDetached( encryptedString: AsymmetricallyEncryptedString, ): AsymmetricSignatureVerificationDetachedResult { - const defaultOperator = this.operatorManager.defaultOperator() + const defaultOperator = this.operators.defaultOperator() const version = defaultOperator.versionForAsymmetricallyEncryptedString(encryptedString) - const keyOperator = this.operatorManager.operatorForVersion(version) + const keyOperator = this.operators.operatorForVersion(version) return keyOperator.asymmetricSignatureVerifyDetached(encryptedString) } getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: AsymmetricallyEncryptedString): PublicKeySet { - const defaultOperator = this.operatorManager.defaultOperator() + const defaultOperator = this.operators.defaultOperator() const version = defaultOperator.versionForAsymmetricallyEncryptedString(string) - const keyOperator = this.operatorManager.operatorForVersion(version) + const keyOperator = this.operators.operatorForVersion(version) return keyOperator.getSenderPublicKeySetFromAsymmetricallyEncryptedString(string) } @@ -684,7 +724,7 @@ export class EncryptionService extends AbstractService i } public async createEncryptedBackupFile(): Promise { - const payloads = this.itemManager.items.map((item) => item.payload) + const payloads = this.items.items.map((item) => item.payload) const split = SplitPayloadsByEncryptionType(payloads) @@ -705,7 +745,7 @@ export class EncryptionService extends AbstractService i } public createDecryptedBackupFile(): BackupFile { - const payloads = this.payloadManager.nonDeletedItems.filter((item) => item.content_type !== ContentType.ItemsKey) + const payloads = this.payloads.nonDeletedItems.filter((item) => item.content_type !== ContentType.ItemsKey) const data: BackupFile = { version: ProtocolVersionLatest, @@ -725,22 +765,22 @@ export class EncryptionService extends AbstractService i } public hasPasscode(): boolean { - return this.rootKeyEncryption.hasPasscode() + return this.rootKeyManager.hasPasscode() } /** * @returns True if the root key has not yet been unwrapped (passcode locked). */ public async isPasscodeLocked() { - return (await this.rootKeyEncryption.hasRootKeyWrapper()) && this.rootKeyEncryption.getRootKey() == undefined + return (await this.rootKeyManager.hasRootKeyWrapper()) && this.rootKeyManager.getRootKey() == undefined } public getRootKeyParams() { - return this.rootKeyEncryption.getRootKeyParams() + return this.rootKeyManager.getRootKeyParams() } public getAccountKeyParams() { - return this.rootKeyEncryption.memoizedRootKeyParams + return this.rootKeyManager.getMemoizedRootKeyParams() } /** @@ -748,7 +788,7 @@ export class EncryptionService extends AbstractService i * Wrapping key params are read from disk. */ public async computeWrappingKey(passcode: string) { - const keyParams = this.rootKeyEncryption.getSureRootKeyWrapperKeyParams() + const keyParams = this.rootKeyManager.getSureRootKeyWrapperKeyParams() const key = await this.computeRootKey(passcode, keyParams) return key } @@ -760,7 +800,7 @@ export class EncryptionService extends AbstractService i * After unwrapping, the root key is automatically loaded. */ public async unwrapRootKey(wrappingKey: RootKeyInterface) { - return this.rootKeyEncryption.unwrapRootKey(wrappingKey) + return this.rootKeyManager.unwrapRootKey(wrappingKey) } /** * Encrypts rootKey and saves it in storage instead of keychain, and then @@ -768,42 +808,42 @@ export class EncryptionService extends AbstractService i * payloads in the keychain. If the root key is not wrapped, it is stored * in plain form in the user's secure keychain. */ - public async setNewRootKeyWrapper(wrappingKey: SNRootKey) { - return this.rootKeyEncryption.setNewRootKeyWrapper(wrappingKey) + public async setNewRootKeyWrapper(wrappingKey: RootKeyInterface) { + return this.rootKeyManager.setNewRootKeyWrapper(wrappingKey) } public async removePasscode(): Promise { - await this.rootKeyEncryption.removeRootKeyWrapper() + await this.rootKeyManager.removeRootKeyWrapper() } - public async setRootKey(key: RootKeyInterface, wrappingKey?: SNRootKey) { - await this.rootKeyEncryption.setRootKey(key, wrappingKey) + public async setRootKey(key: RootKeyInterface, wrappingKey?: RootKeyInterface) { + await this.rootKeyManager.setRootKey(key, wrappingKey) } /** * Returns the in-memory root key value. */ public getRootKey(): RootKeyInterface | undefined { - return this.rootKeyEncryption.getRootKey() + return this.rootKeyManager.getRootKey() } public getSureRootKey(): RootKeyInterface { - return this.rootKeyEncryption.getRootKey() as RootKeyInterface + return this.rootKeyManager.getRootKey() as RootKeyInterface } /** * Deletes root key and wrapper from keychain. Used when signing out of application. */ public async deleteWorkspaceSpecificKeyStateFromDevice() { - await this.rootKeyEncryption.deleteWorkspaceSpecificKeyStateFromDevice() + await this.rootKeyManager.deleteWorkspaceSpecificKeyStateFromDevice() } - public async validateAccountPassword(password: string) { - return this.rootKeyEncryption.validateAccountPassword(password) + public async validateAccountPassword(password: string): Promise { + return this.rootKeyManager.validateAccountPassword(password) } - public async validatePasscode(passcode: string) { - return this.rootKeyEncryption.validatePasscode(passcode) + public async validatePasscode(passcode: string): Promise { + return this.rootKeyManager.validatePasscode(passcode) } public getEmbeddedPayloadAuthenticatedData( @@ -814,7 +854,7 @@ export class EncryptionService extends AbstractService i return undefined } - const operator = this.operatorManager.operatorForVersion(version) + const operator = this.operators.operatorForVersion(version) const authenticatedData = operator.getPayloadAuthenticatedDataForExternalUse( encryptedInputParametersFromPayload(payload), @@ -824,7 +864,7 @@ export class EncryptionService extends AbstractService i } /** Returns the key params attached to this key's encrypted payload */ - public getKeyEmbeddedKeyParamsFromItemsKey(key: EncryptedPayloadInterface): SNRootKeyParams | undefined { + public getKeyEmbeddedKeyParamsFromItemsKey(key: EncryptedPayloadInterface): RootKeyParamsInterface | undefined { const authenticatedData = this.getEmbeddedPayloadAuthenticatedData(key) if (!authenticatedData) { return undefined @@ -847,7 +887,7 @@ export class EncryptionService extends AbstractService i return false } - const rootKey = this.rootKeyEncryption.getRootKey() + const rootKey = this.rootKeyManager.getRootKey() if (!rootKey) { return false } @@ -872,7 +912,8 @@ export class EncryptionService extends AbstractService i } public async createNewDefaultItemsKey(): Promise { - return this.rootKeyEncryption.createNewDefaultItemsKey() + const usecase = new CreateNewDefaultItemsKeyUseCase(this.mutator, this.items, this.operators, this.rootKeyManager) + return usecase.execute() } public getPasswordCreatedDate(): Date | undefined { @@ -943,7 +984,7 @@ export class EncryptionService extends AbstractService i } if (this.itemsEncryption.getItemsKeys().length === 0) { - await this.rootKeyEncryption.createNewDefaultItemsKey() + await this.createNewDefaultItemsKey() } } } @@ -951,7 +992,7 @@ export class EncryptionService extends AbstractService i const userVersion = this.getUserVersion() const accountVersionedKey = this.itemsEncryption.getItemsKeys().find((key) => key.keyVersion === userVersion) if (isNullOrUndefined(accountVersionedKey)) { - await this.rootKeyEncryption.createNewDefaultItemsKey() + await this.createNewDefaultItemsKey() } this.syncUnsyncedItemsKeys() @@ -961,8 +1002,8 @@ export class EncryptionService extends AbstractService i /** Always create a new items key after full sync, if no items key is found */ const currentItemsKey = findDefaultItemsKey(this.itemsEncryption.getItemsKeys()) if (!currentItemsKey) { - await this.rootKeyEncryption.createNewDefaultItemsKey() - if (this.rootKeyEncryption.keyMode === KeyMode.WrapperOnly) { + await this.createNewDefaultItemsKey() + if (this.rootKeyManager.getKeyMode() === KeyMode.WrapperOnly) { return this.itemsEncryption.repersistAllItems() } } diff --git a/packages/services/src/Domain/Encryption/RootKey/RootKeyManager.ts b/packages/services/src/Domain/Encryption/RootKey/RootKeyManager.ts new file mode 100644 index 000000000..3720cd027 --- /dev/null +++ b/packages/services/src/Domain/Encryption/RootKey/RootKeyManager.ts @@ -0,0 +1,491 @@ +import { + AnyKeyParamsContent, + ApplicationIdentifier, + KeyParamsOrigination, + ProtocolVersion, + ProtocolVersionLatest, +} from '@standardnotes/common' +import { + KeyMode, + CreateNewRootKey, + CreateAnyKeyParams, + SNRootKey, + isErrorDecryptingParameters, + OperatorManager, +} from '@standardnotes/encryption' +import { + ContentTypesUsingRootKeyEncryption, + DecryptedPayload, + DecryptedTransferPayload, + EncryptedPayload, + EncryptedTransferPayload, + FillItemContentSpecialized, + NamespacedRootKeyInKeychain, + RootKeyContent, + RootKeyInterface, + RootKeyParamsInterface, +} from '@standardnotes/models' +import { DeviceInterface } from '../../Device/DeviceInterface' +import { InternalEventBusInterface } from '../../Internal/InternalEventBusInterface' +import { StorageKey } from '../../Storage/StorageKeys' +import { StorageServiceInterface } from '../../Storage/StorageServiceInterface' +import { StorageValueModes } from '../../Storage/StorageTypes' +import { RootKeyEncryptPayloadUseCase } from '../UseCase/RootEncryption/EncryptPayload' +import { RootKeyDecryptPayloadUseCase } from '../UseCase/RootEncryption/DecryptPayload' +import { AbstractService } from '../../Service/AbstractService' +import { ItemManagerInterface } from '../../Item/ItemManagerInterface' +import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface' +import { RootKeyManagerEvent } from './RootKeyManagerEvent' +import { ValidatePasscodeResult } from './ValidatePasscodeResult' +import { ValidateAccountPasswordResult } from './ValidateAccountPasswordResult' + +export class RootKeyManager extends AbstractService { + private rootKey?: RootKeyInterface + private keyMode = KeyMode.RootKeyNone + private memoizedRootKeyParams?: RootKeyParamsInterface + + constructor( + private device: DeviceInterface, + private storage: StorageServiceInterface, + private items: ItemManagerInterface, + private mutator: MutatorClientInterface, + private operators: OperatorManager, + private identifier: ApplicationIdentifier, + eventBus: InternalEventBusInterface, + ) { + super(eventBus) + } + + override deinit() { + super.deinit() + this.rootKey = undefined + this.memoizedRootKeyParams = undefined + } + + public async initialize(): Promise { + const wrappedRootKey = this.getWrappedRootKey() + const accountKeyParams = this.recomputeAccountKeyParams() + const hasWrapper = await this.hasRootKeyWrapper() + const hasRootKey = wrappedRootKey != undefined || accountKeyParams != undefined + + if (hasWrapper && hasRootKey) { + this.keyMode = KeyMode.RootKeyPlusWrapper + } else if (hasWrapper && !hasRootKey) { + this.keyMode = KeyMode.WrapperOnly + } else if (!hasWrapper && hasRootKey) { + this.keyMode = KeyMode.RootKeyOnly + } else if (!hasWrapper && !hasRootKey) { + this.keyMode = KeyMode.RootKeyNone + } else { + throw 'Invalid key mode condition' + } + + if (this.keyMode === KeyMode.RootKeyOnly) { + this.setRootKeyInstance(await this.getRootKeyFromKeychain()) + await this.handleKeyStatusChange() + } + } + + public getMemoizedRootKeyParams(): RootKeyParamsInterface | undefined { + return this.memoizedRootKeyParams + } + + public getKeyMode(): KeyMode { + return this.keyMode + } + + public async hasRootKeyWrapper(): Promise { + const wrapper = this.getRootKeyWrapperKeyParams() + return wrapper != undefined + } + + public getRootKeyWrapperKeyParams(): RootKeyParamsInterface | undefined { + const rawKeyParams = this.storage.getValue(StorageKey.RootKeyWrapperKeyParams, StorageValueModes.Nonwrapped) + + if (!rawKeyParams) { + return undefined + } + + return CreateAnyKeyParams(rawKeyParams as AnyKeyParamsContent) + } + + public async passcodeUpgradeAvailable(): Promise { + const passcodeParams = this.getRootKeyWrapperKeyParams() + if (!passcodeParams) { + return false + } + return passcodeParams.version !== ProtocolVersionLatest + } + + public hasAccount(): boolean { + switch (this.keyMode) { + case KeyMode.RootKeyNone: + case KeyMode.WrapperOnly: + return false + case KeyMode.RootKeyOnly: + case KeyMode.RootKeyPlusWrapper: + return true + default: + throw Error(`Unhandled keyMode value '${this.keyMode}'.`) + } + } + + public getUserVersion(): ProtocolVersion | undefined { + const keyParams = this.memoizedRootKeyParams + return keyParams?.version + } + + public hasRootKeyEncryptionSource(): boolean { + return this.hasAccount() || this.hasPasscode() + } + + public async computeRootKey( + password: string, + keyParams: RootKeyParamsInterface, + ): Promise { + const version = keyParams.version + const operator = this.operators.operatorForVersion(version) + return operator.computeRootKey(password, keyParams) + } + + /** + * Deletes root key and wrapper from keychain. Used when signing out of application. + */ + public async deleteWorkspaceSpecificKeyStateFromDevice(): Promise { + await this.device.clearNamespacedKeychainValue(this.identifier) + + await this.storage.removeValue(StorageKey.WrappedRootKey, StorageValueModes.Nonwrapped) + await this.storage.removeValue(StorageKey.RootKeyWrapperKeyParams, StorageValueModes.Nonwrapped) + await this.storage.removeValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped) + + this.keyMode = KeyMode.RootKeyNone + this.setRootKeyInstance(undefined) + + await this.handleKeyStatusChange() + } + + public async createRootKey( + identifier: string, + password: string, + origination: KeyParamsOrigination, + version?: ProtocolVersion, + ): Promise { + const operator = version ? this.operators.operatorForVersion(version) : this.operators.defaultOperator() + return operator.createRootKey(identifier, password, origination) + } + + public async validateAccountPassword(password: string): Promise { + const key = await this.computeRootKey(password, this.memoizedRootKeyParams as RootKeyParamsInterface) + const valid = this.getSureRootKey().compare(key) + if (valid) { + return { valid, artifacts: { rootKey: key } } + } else { + return { valid: false } + } + } + + public async validatePasscode(passcode: string): Promise { + const keyParams = this.getSureRootKeyWrapperKeyParams() + const key = await this.computeRootKey(passcode, keyParams) + const valid = await this.validateWrappingKey(key) + if (valid) { + return { valid, artifacts: { wrappingKey: key } } + } else { + return { valid: false } + } + } + + public async getEncryptionSourceVersion(): Promise { + if (this.hasAccount()) { + return this.getSureUserVersion() + } else if (this.hasPasscode()) { + const passcodeParams = this.getSureRootKeyWrapperKeyParams() + return passcodeParams.version + } + + throw Error('Attempting to access encryption source version without source') + } + + public getSureUserVersion(): ProtocolVersion { + const keyParams = this.memoizedRootKeyParams as RootKeyParamsInterface + return keyParams.version + } + + private async handleKeyStatusChange(): Promise { + this.recomputeAccountKeyParams() + void this.notifyEvent(RootKeyManagerEvent.RootKeyManagerKeyStatusChanged) + } + + public hasPasscode(): boolean { + return this.keyMode === KeyMode.WrapperOnly || this.keyMode === KeyMode.RootKeyPlusWrapper + } + + public recomputeAccountKeyParams(): RootKeyParamsInterface | undefined { + const rawKeyParams = this.storage.getValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped) + + if (!rawKeyParams) { + return + } + + this.memoizedRootKeyParams = CreateAnyKeyParams(rawKeyParams as AnyKeyParamsContent) + return this.memoizedRootKeyParams + } + + public getSureRootKeyWrapperKeyParams() { + return this.getRootKeyWrapperKeyParams() as RootKeyParamsInterface + } + + /** + * Wraps the current in-memory root key value using the wrappingKey, + * then persists the wrapped value to disk. + */ + public async wrapAndPersistRootKey(wrappingKey: RootKeyInterface): Promise { + const rootKey = this.getSureRootKey() + + const value: DecryptedTransferPayload = { + ...rootKey.payload.ejected(), + content: FillItemContentSpecialized(rootKey.persistableValueWhenWrapping()), + } + + const payload = new DecryptedPayload(value) + + const usecase = new RootKeyEncryptPayloadUseCase(this.operators) + const wrappedKey = await usecase.executeOne(payload, wrappingKey) + const wrappedKeyPayload = new EncryptedPayload({ + ...payload.ejected(), + ...wrappedKey, + errorDecrypting: false, + waitingForKey: false, + }) + + this.storage.setValue(StorageKey.WrappedRootKey, wrappedKeyPayload.ejected(), StorageValueModes.Nonwrapped) + } + + public async unwrapRootKey(wrappingKey: RootKeyInterface): Promise { + if (this.keyMode === KeyMode.WrapperOnly) { + this.setRootKeyInstance(wrappingKey) + return + } + + if (this.keyMode !== KeyMode.RootKeyPlusWrapper) { + throw 'Invalid key mode condition for unwrapping.' + } + + const wrappedKey = this.getWrappedRootKey() + const payload = new EncryptedPayload(wrappedKey) + const usecase = new RootKeyDecryptPayloadUseCase(this.operators) + const decrypted = await usecase.executeOne(payload, wrappingKey) + + if (isErrorDecryptingParameters(decrypted)) { + throw Error('Unable to decrypt root key with provided wrapping key.') + } else { + const decryptedPayload = new DecryptedPayload({ + ...payload.ejected(), + ...decrypted, + }) + this.setRootKeyInstance(new SNRootKey(decryptedPayload)) + await this.handleKeyStatusChange() + } + } + /** + * Encrypts rootKey and saves it in storage instead of keychain, and then + * clears keychain. This is because we don't want to store large encrypted + * payloads in the keychain. If the root key is not wrapped, it is stored + * in plain form in the user's secure keychain. + */ + public async setNewRootKeyWrapper(wrappingKey: RootKeyInterface) { + if (this.keyMode === KeyMode.RootKeyNone) { + this.keyMode = KeyMode.WrapperOnly + } else if (this.keyMode === KeyMode.RootKeyOnly) { + this.keyMode = KeyMode.RootKeyPlusWrapper + } else { + throw Error('Attempting to set wrapper on already wrapped key.') + } + + await this.device.clearNamespacedKeychainValue(this.identifier) + + if (this.keyMode === KeyMode.WrapperOnly || this.keyMode === KeyMode.RootKeyPlusWrapper) { + if (this.keyMode === KeyMode.WrapperOnly) { + this.setRootKeyInstance(wrappingKey) + await this.reencryptApplicableItemsAfterUserRootKeyChange() + } else { + await this.wrapAndPersistRootKey(wrappingKey) + } + + this.storage.setValue( + StorageKey.RootKeyWrapperKeyParams, + wrappingKey.keyParams.getPortableValue(), + StorageValueModes.Nonwrapped, + ) + + await this.handleKeyStatusChange() + } else { + throw Error('Invalid keyMode on setNewRootKeyWrapper') + } + } + + /** + * Removes root key wrapper from local storage and stores root key bare in secure keychain. + */ + public async removeRootKeyWrapper(): Promise { + if (this.keyMode !== KeyMode.WrapperOnly && this.keyMode !== KeyMode.RootKeyPlusWrapper) { + throw Error('Attempting to remove root key wrapper on unwrapped key.') + } + + if (this.keyMode === KeyMode.WrapperOnly) { + this.keyMode = KeyMode.RootKeyNone + this.setRootKeyInstance(undefined) + } else if (this.keyMode === KeyMode.RootKeyPlusWrapper) { + this.keyMode = KeyMode.RootKeyOnly + } + + await this.storage.removeValue(StorageKey.WrappedRootKey, StorageValueModes.Nonwrapped) + await this.storage.removeValue(StorageKey.RootKeyWrapperKeyParams, StorageValueModes.Nonwrapped) + + if (this.keyMode === KeyMode.RootKeyOnly) { + await this.saveRootKeyToKeychain() + } + + await this.handleKeyStatusChange() + } + + public async setRootKey(key: RootKeyInterface, wrappingKey?: RootKeyInterface) { + if (!key.keyParams) { + throw Error('keyParams must be supplied if setting root key.') + } + + if (this.getRootKey() === key) { + throw Error('Attempting to set root key as same current value.') + } + + if (this.keyMode === KeyMode.WrapperOnly) { + this.keyMode = KeyMode.RootKeyPlusWrapper + } else if (this.keyMode === KeyMode.RootKeyNone) { + this.keyMode = KeyMode.RootKeyOnly + } else if (this.keyMode === KeyMode.RootKeyOnly || this.keyMode === KeyMode.RootKeyPlusWrapper) { + /** Root key is simply changing, mode stays the same */ + /** this.keyMode = this.keyMode; */ + } else { + throw Error(`Unhandled key mode for setNewRootKey ${this.keyMode}`) + } + + this.setRootKeyInstance(key) + + this.storage.setValue(StorageKey.RootKeyParams, key.keyParams.getPortableValue(), StorageValueModes.Nonwrapped) + + if (this.keyMode === KeyMode.RootKeyOnly) { + await this.saveRootKeyToKeychain() + } else if (this.keyMode === KeyMode.RootKeyPlusWrapper) { + if (!wrappingKey) { + throw Error('wrappingKey must be supplied') + } + await this.wrapAndPersistRootKey(wrappingKey) + } + + await this.handleKeyStatusChange() + } + + public getRootKeyParams(): RootKeyParamsInterface | undefined { + if (this.keyMode === KeyMode.WrapperOnly) { + return this.getRootKeyWrapperKeyParams() + } else if (this.keyMode === KeyMode.RootKeyOnly || this.keyMode === KeyMode.RootKeyPlusWrapper) { + return this.recomputeAccountKeyParams() + } else if (this.keyMode === KeyMode.RootKeyNone) { + return undefined + } else { + throw `Unhandled key mode for getRootKeyParams ${this.keyMode}` + } + } + + public getSureRootKeyParams(): RootKeyParamsInterface { + return this.getRootKeyParams() as RootKeyParamsInterface + } + + public async saveRootKeyToKeychain() { + if (this.getRootKey() == undefined) { + throw 'Attempting to non-existent root key to the keychain.' + } + if (this.keyMode !== KeyMode.RootKeyOnly) { + throw 'Should not be persisting wrapped key to keychain.' + } + + const rawKey = this.getSureRootKey().getKeychainValue() + + return this.executeCriticalFunction(() => { + return this.device.setNamespacedKeychainValue(rawKey, this.identifier) + }) + } + + /** + * We know a wrappingKey is correct if it correctly decrypts + * wrapped root key. + */ + public async validateWrappingKey(wrappingKey: RootKeyInterface): Promise { + const wrappedRootKey = this.getWrappedRootKey() + + /** If wrapper only, storage is encrypted directly with wrappingKey */ + if (this.keyMode === KeyMode.WrapperOnly) { + return this.storage.canDecryptWithKey(wrappingKey) + } else if (this.keyMode === KeyMode.RootKeyOnly || this.keyMode === KeyMode.RootKeyPlusWrapper) { + /** + * In these modes, storage is encrypted with account keys, and + * account keys are encrypted with wrappingKey. Here we validate + * by attempting to decrypt account keys. + */ + const wrappedKeyPayload = new EncryptedPayload(wrappedRootKey) + const usecase = new RootKeyDecryptPayloadUseCase(this.operators) + const decrypted = await usecase.executeOne(wrappedKeyPayload, wrappingKey) + return !isErrorDecryptingParameters(decrypted) + } else { + throw 'Unhandled case in validateWrappingKey' + } + } + + private getWrappedRootKey(): EncryptedTransferPayload { + return this.storage.getValue(StorageKey.WrappedRootKey, StorageValueModes.Nonwrapped) + } + + public setRootKeyInstance(rootKey: RootKeyInterface | undefined): void { + this.rootKey = rootKey + } + + public getRootKey(): RootKeyInterface | undefined { + return this.rootKey + } + + public getSureRootKey(): RootKeyInterface { + return this.rootKey as RootKeyInterface + } + + public async getRootKeyFromKeychain(): Promise { + const rawKey = (await this.device.getNamespacedKeychainValue(this.identifier)) as + | NamespacedRootKeyInKeychain + | undefined + + if (rawKey == undefined) { + return undefined + } + + const keyParams = this.getSureRootKeyParams() + + return CreateNewRootKey({ + ...rawKey, + keyParams: keyParams.getPortableValue(), + }) + } + + /** + * When the root key changes, we must re-encrypt all relevant items with this new root key (by simply re-syncing). + */ + public async reencryptApplicableItemsAfterUserRootKeyChange(): Promise { + const items = this.items.getItems(ContentTypesUsingRootKeyEncryption()) + if (items.length > 0) { + /** + * Do not call sync after marking dirty. + * Re-encrypting items keys is called by consumers who have specific flows who + * will sync on their own timing + */ + await this.mutator.setItemsDirty(items) + } + } +} diff --git a/packages/services/src/Domain/Encryption/RootKey/RootKeyManagerEvent.ts b/packages/services/src/Domain/Encryption/RootKey/RootKeyManagerEvent.ts new file mode 100644 index 000000000..1fc3dd564 --- /dev/null +++ b/packages/services/src/Domain/Encryption/RootKey/RootKeyManagerEvent.ts @@ -0,0 +1,3 @@ +export enum RootKeyManagerEvent { + RootKeyManagerKeyStatusChanged = 'RootKeyManagerKeyStatusChanged', +} diff --git a/packages/services/src/Domain/Encryption/RootKey/ValidateAccountPasswordResult.ts b/packages/services/src/Domain/Encryption/RootKey/ValidateAccountPasswordResult.ts new file mode 100644 index 000000000..7e1f33136 --- /dev/null +++ b/packages/services/src/Domain/Encryption/RootKey/ValidateAccountPasswordResult.ts @@ -0,0 +1,13 @@ +import { RootKeyInterface } from '@standardnotes/models' + +export type ValidateAccountPasswordResult = + | { + valid: true + artifacts: { + rootKey: RootKeyInterface + } + } + | { + valid: boolean + artifacts?: undefined + } diff --git a/packages/services/src/Domain/Encryption/RootKey/ValidatePasscodeResult.ts b/packages/services/src/Domain/Encryption/RootKey/ValidatePasscodeResult.ts new file mode 100644 index 000000000..a345fbdb6 --- /dev/null +++ b/packages/services/src/Domain/Encryption/RootKey/ValidatePasscodeResult.ts @@ -0,0 +1,13 @@ +import { RootKeyInterface } from '@standardnotes/models' + +export type ValidatePasscodeResult = + | { + valid: true + artifacts: { + wrappingKey: RootKeyInterface + } + } + | { + valid: boolean + artifacts?: undefined + } diff --git a/packages/services/src/Domain/Encryption/RootKeyEncryption.ts b/packages/services/src/Domain/Encryption/RootKeyEncryption.ts deleted file mode 100644 index c5994840f..000000000 --- a/packages/services/src/Domain/Encryption/RootKeyEncryption.ts +++ /dev/null @@ -1,716 +0,0 @@ -import { MutatorClientInterface } from './../Mutator/MutatorClientInterface' -import { - ApplicationIdentifier, - ProtocolVersionLatest, - ProtocolVersion, - AnyKeyParamsContent, - KeyParamsOrigination, - compareVersions, - ProtocolVersionLastNonrootItemsKey, - ContentType, -} from '@standardnotes/common' -import { - RootKeyServiceEvent, - KeyMode, - SNRootKeyParams, - OperatorManager, - CreateNewRootKey, - CreateAnyKeyParams, - SNRootKey, - isErrorDecryptingParameters, - ErrorDecryptingParameters, - findDefaultItemsKey, - ItemsKeyMutator, - encryptPayload, - decryptPayload, - EncryptedOutputParameters, - DecryptedParameters, - KeySystemKeyManagerInterface, -} from '@standardnotes/encryption' -import { - ContentTypeUsesKeySystemRootKeyEncryption, - ContentTypesUsingRootKeyEncryption, - ContentTypeUsesRootKeyEncryption, - CreateDecryptedItemFromPayload, - DecryptedPayload, - DecryptedPayloadInterface, - DecryptedTransferPayload, - EncryptedPayload, - EncryptedPayloadInterface, - EncryptedTransferPayload, - FillItemContentSpecialized, - KeySystemRootKeyInterface, - ItemContent, - ItemsKeyContent, - ItemsKeyContentSpecialized, - ItemsKeyInterface, - NamespacedRootKeyInKeychain, - PayloadEmitSource, - PayloadTimestampDefaults, - RootKeyContent, - RootKeyInterface, - SureFindPayload, - KeySystemIdentifier, -} from '@standardnotes/models' -import { UuidGenerator } from '@standardnotes/utils' -import { DeviceInterface } from '../Device/DeviceInterface' -import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface' -import { ItemManagerInterface } from '../Item/ItemManagerInterface' -import { AbstractService } from '../Service/AbstractService' -import { StorageKey } from '../Storage/StorageKeys' -import { StorageServiceInterface } from '../Storage/StorageServiceInterface' -import { StorageValueModes } from '../Storage/StorageTypes' -import { PayloadManagerInterface } from '../Payloads/PayloadManagerInterface' -import { PkcKeyPair } from '@standardnotes/sncrypto-common' - -export class RootKeyEncryptionService extends AbstractService { - private rootKey?: RootKeyInterface - public keyMode = KeyMode.RootKeyNone - public memoizedRootKeyParams?: SNRootKeyParams - - constructor( - private items: ItemManagerInterface, - private mutator: MutatorClientInterface, - private operatorManager: OperatorManager, - public deviceInterface: DeviceInterface, - private storageService: StorageServiceInterface, - private payloadManager: PayloadManagerInterface, - private keys: KeySystemKeyManagerInterface, - private identifier: ApplicationIdentifier, - protected override internalEventBus: InternalEventBusInterface, - ) { - super(internalEventBus) - } - - public override deinit(): void { - ;(this.items as unknown) = undefined - ;(this.operatorManager as unknown) = undefined - ;(this.deviceInterface as unknown) = undefined - ;(this.storageService as unknown) = undefined - ;(this.payloadManager as unknown) = undefined - ;(this.keys as unknown) = undefined - - this.rootKey = undefined - this.memoizedRootKeyParams = undefined - super.deinit() - } - - public async initialize() { - const wrappedRootKey = this.getWrappedRootKey() - const accountKeyParams = await this.recomputeAccountKeyParams() - const hasWrapper = await this.hasRootKeyWrapper() - const hasRootKey = wrappedRootKey != undefined || accountKeyParams != undefined - - if (hasWrapper && hasRootKey) { - this.keyMode = KeyMode.RootKeyPlusWrapper - } else if (hasWrapper && !hasRootKey) { - this.keyMode = KeyMode.WrapperOnly - } else if (!hasWrapper && hasRootKey) { - this.keyMode = KeyMode.RootKeyOnly - } else if (!hasWrapper && !hasRootKey) { - this.keyMode = KeyMode.RootKeyNone - } else { - throw 'Invalid key mode condition' - } - - if (this.keyMode === KeyMode.RootKeyOnly) { - this.setRootKeyInstance(await this.getRootKeyFromKeychain()) - await this.handleKeyStatusChange() - } - } - - private async handleKeyStatusChange() { - await this.recomputeAccountKeyParams() - void this.notifyEvent(RootKeyServiceEvent.RootKeyStatusChanged) - } - - public async passcodeUpgradeAvailable() { - const passcodeParams = await this.getRootKeyWrapperKeyParams() - if (!passcodeParams) { - return false - } - return passcodeParams.version !== ProtocolVersionLatest - } - - public async hasRootKeyWrapper() { - const wrapper = await this.getRootKeyWrapperKeyParams() - return wrapper != undefined - } - - public hasAccount() { - switch (this.keyMode) { - case KeyMode.RootKeyNone: - case KeyMode.WrapperOnly: - return false - case KeyMode.RootKeyOnly: - case KeyMode.RootKeyPlusWrapper: - return true - default: - throw Error(`Unhandled keyMode value '${this.keyMode}'.`) - } - } - - public hasRootKeyEncryptionSource(): boolean { - return this.hasAccount() || this.hasPasscode() - } - - public hasPasscode() { - return this.keyMode === KeyMode.WrapperOnly || this.keyMode === KeyMode.RootKeyPlusWrapper - } - - public async getEncryptionSourceVersion(): Promise { - if (this.hasAccount()) { - return this.getSureUserVersion() - } else if (this.hasPasscode()) { - const passcodeParams = this.getSureRootKeyWrapperKeyParams() - return passcodeParams.version - } - - throw Error('Attempting to access encryption source version without source') - } - - public getUserVersion(): ProtocolVersion | undefined { - const keyParams = this.memoizedRootKeyParams - return keyParams?.version - } - - private getSureUserVersion(): ProtocolVersion { - const keyParams = this.memoizedRootKeyParams as SNRootKeyParams - return keyParams.version - } - - private async getRootKeyFromKeychain() { - const rawKey = (await this.deviceInterface.getNamespacedKeychainValue(this.identifier)) as - | NamespacedRootKeyInKeychain - | undefined - - if (rawKey == undefined) { - return undefined - } - - const keyParams = this.getSureRootKeyParams() - - return CreateNewRootKey({ - ...rawKey, - keyParams: keyParams.getPortableValue(), - }) - } - - private async saveRootKeyToKeychain() { - if (this.getRootKey() == undefined) { - throw 'Attempting to non-existent root key to the keychain.' - } - if (this.keyMode !== KeyMode.RootKeyOnly) { - throw 'Should not be persisting wrapped key to keychain.' - } - - const rawKey = this.getSureRootKey().getKeychainValue() - - return this.executeCriticalFunction(() => { - return this.deviceInterface.setNamespacedKeychainValue(rawKey, this.identifier) - }) - } - - public getRootKeyWrapperKeyParams(): SNRootKeyParams | undefined { - const rawKeyParams = this.storageService.getValue(StorageKey.RootKeyWrapperKeyParams, StorageValueModes.Nonwrapped) - - if (!rawKeyParams) { - return undefined - } - - return CreateAnyKeyParams(rawKeyParams as AnyKeyParamsContent) - } - - public getSureRootKeyWrapperKeyParams() { - return this.getRootKeyWrapperKeyParams() as SNRootKeyParams - } - - public getRootKeyParams(): SNRootKeyParams | undefined { - if (this.keyMode === KeyMode.WrapperOnly) { - return this.getRootKeyWrapperKeyParams() - } else if (this.keyMode === KeyMode.RootKeyOnly || this.keyMode === KeyMode.RootKeyPlusWrapper) { - return this.recomputeAccountKeyParams() - } else if (this.keyMode === KeyMode.RootKeyNone) { - return undefined - } else { - throw `Unhandled key mode for getRootKeyParams ${this.keyMode}` - } - } - - public getSureRootKeyParams(): SNRootKeyParams { - return this.getRootKeyParams() as SNRootKeyParams - } - - public async computeRootKey(password: string, keyParams: SNRootKeyParams): Promise { - const version = keyParams.version - const operator = this.operatorManager.operatorForVersion(version) - return operator.computeRootKey(password, keyParams) - } - - public async createRootKey( - identifier: string, - password: string, - origination: KeyParamsOrigination, - version?: ProtocolVersion, - ): Promise { - const operator = version ? this.operatorManager.operatorForVersion(version) : this.operatorManager.defaultOperator() - return operator.createRootKey(identifier, password, origination) - } - - private getSureMemoizedRootKeyParams(): SNRootKeyParams { - return this.memoizedRootKeyParams as SNRootKeyParams - } - - public async validateAccountPassword(password: string) { - const key = await this.computeRootKey(password, this.getSureMemoizedRootKeyParams()) - const valid = this.getSureRootKey().compare(key) - if (valid) { - return { valid, artifacts: { rootKey: key } } - } else { - return { valid: false } - } - } - - public async validatePasscode(passcode: string) { - const keyParams = await this.getSureRootKeyWrapperKeyParams() - const key = await this.computeRootKey(passcode, keyParams) - const valid = await this.validateWrappingKey(key) - if (valid) { - return { valid, artifacts: { wrappingKey: key } } - } else { - return { valid: false } - } - } - - /** - * We know a wrappingKey is correct if it correctly decrypts - * wrapped root key. - */ - public async validateWrappingKey(wrappingKey: SNRootKey) { - const wrappedRootKey = this.getWrappedRootKey() - - /** If wrapper only, storage is encrypted directly with wrappingKey */ - if (this.keyMode === KeyMode.WrapperOnly) { - return this.storageService.canDecryptWithKey(wrappingKey) - } else if (this.keyMode === KeyMode.RootKeyOnly || this.keyMode === KeyMode.RootKeyPlusWrapper) { - /** - * In these modes, storage is encrypted with account keys, and - * account keys are encrypted with wrappingKey. Here we validate - * by attempting to decrypt account keys. - */ - const wrappedKeyPayload = new EncryptedPayload(wrappedRootKey) - const decrypted = await this.decryptPayload(wrappedKeyPayload, wrappingKey) - return !isErrorDecryptingParameters(decrypted) - } else { - throw 'Unhandled case in validateWrappingKey' - } - } - - private recomputeAccountKeyParams(): SNRootKeyParams | undefined { - const rawKeyParams = this.storageService.getValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped) - - if (!rawKeyParams) { - return - } - - this.memoizedRootKeyParams = CreateAnyKeyParams(rawKeyParams as AnyKeyParamsContent) - return this.memoizedRootKeyParams - } - - /** - * Wraps the current in-memory root key value using the wrappingKey, - * then persists the wrapped value to disk. - */ - private async wrapAndPersistRootKey(wrappingKey: SNRootKey) { - const rootKey = this.getSureRootKey() - - const value: DecryptedTransferPayload = { - ...rootKey.payload.ejected(), - content: FillItemContentSpecialized(rootKey.persistableValueWhenWrapping()), - } - - const payload = new DecryptedPayload(value) - - const wrappedKey = await this.encryptPayload(payload, wrappingKey) - const wrappedKeyPayload = new EncryptedPayload({ - ...payload.ejected(), - ...wrappedKey, - errorDecrypting: false, - waitingForKey: false, - }) - - this.storageService.setValue(StorageKey.WrappedRootKey, wrappedKeyPayload.ejected(), StorageValueModes.Nonwrapped) - } - - public async unwrapRootKey(wrappingKey: RootKeyInterface) { - if (this.keyMode === KeyMode.WrapperOnly) { - this.setRootKeyInstance(wrappingKey) - return - } - - if (this.keyMode !== KeyMode.RootKeyPlusWrapper) { - throw 'Invalid key mode condition for unwrapping.' - } - - const wrappedKey = this.getWrappedRootKey() - const payload = new EncryptedPayload(wrappedKey) - const decrypted = await this.decryptPayload(payload, wrappingKey) - - if (isErrorDecryptingParameters(decrypted)) { - throw Error('Unable to decrypt root key with provided wrapping key.') - } else { - const decryptedPayload = new DecryptedPayload({ - ...payload.ejected(), - ...decrypted, - }) - this.setRootKeyInstance(new SNRootKey(decryptedPayload)) - await this.handleKeyStatusChange() - } - } - - /** - * Encrypts rootKey and saves it in storage instead of keychain, and then - * clears keychain. This is because we don't want to store large encrypted - * payloads in the keychain. If the root key is not wrapped, it is stored - * in plain form in the user's secure keychain. - */ - public async setNewRootKeyWrapper(wrappingKey: SNRootKey) { - if (this.keyMode === KeyMode.RootKeyNone) { - this.keyMode = KeyMode.WrapperOnly - } else if (this.keyMode === KeyMode.RootKeyOnly) { - this.keyMode = KeyMode.RootKeyPlusWrapper - } else { - throw Error('Attempting to set wrapper on already wrapped key.') - } - - await this.deviceInterface.clearNamespacedKeychainValue(this.identifier) - - if (this.keyMode === KeyMode.WrapperOnly || this.keyMode === KeyMode.RootKeyPlusWrapper) { - if (this.keyMode === KeyMode.WrapperOnly) { - this.setRootKeyInstance(wrappingKey) - await this.reencryptApplicableItemsAfterUserRootKeyChange() - } else { - await this.wrapAndPersistRootKey(wrappingKey) - } - - this.storageService.setValue( - StorageKey.RootKeyWrapperKeyParams, - wrappingKey.keyParams.getPortableValue(), - StorageValueModes.Nonwrapped, - ) - - await this.handleKeyStatusChange() - } else { - throw Error('Invalid keyMode on setNewRootKeyWrapper') - } - } - - /** - * Removes root key wrapper from local storage and stores root key bare in secure keychain. - */ - public async removeRootKeyWrapper(): Promise { - if (this.keyMode !== KeyMode.WrapperOnly && this.keyMode !== KeyMode.RootKeyPlusWrapper) { - throw Error('Attempting to remove root key wrapper on unwrapped key.') - } - - if (this.keyMode === KeyMode.WrapperOnly) { - this.keyMode = KeyMode.RootKeyNone - this.setRootKeyInstance(undefined) - } else if (this.keyMode === KeyMode.RootKeyPlusWrapper) { - this.keyMode = KeyMode.RootKeyOnly - } - - await this.storageService.removeValue(StorageKey.WrappedRootKey, StorageValueModes.Nonwrapped) - await this.storageService.removeValue(StorageKey.RootKeyWrapperKeyParams, StorageValueModes.Nonwrapped) - - if (this.keyMode === KeyMode.RootKeyOnly) { - await this.saveRootKeyToKeychain() - } - - await this.handleKeyStatusChange() - } - - public async setRootKey(key: SNRootKey, wrappingKey?: SNRootKey) { - if (!key.keyParams) { - throw Error('keyParams must be supplied if setting root key.') - } - - if (this.getRootKey() === key) { - throw Error('Attempting to set root key as same current value.') - } - - if (this.keyMode === KeyMode.WrapperOnly) { - this.keyMode = KeyMode.RootKeyPlusWrapper - } else if (this.keyMode === KeyMode.RootKeyNone) { - this.keyMode = KeyMode.RootKeyOnly - } else if (this.keyMode === KeyMode.RootKeyOnly || this.keyMode === KeyMode.RootKeyPlusWrapper) { - /** Root key is simply changing, mode stays the same */ - /** this.keyMode = this.keyMode; */ - } else { - throw Error(`Unhandled key mode for setNewRootKey ${this.keyMode}`) - } - - this.setRootKeyInstance(key) - - this.storageService.setValue( - StorageKey.RootKeyParams, - key.keyParams.getPortableValue(), - StorageValueModes.Nonwrapped, - ) - - if (this.keyMode === KeyMode.RootKeyOnly) { - await this.saveRootKeyToKeychain() - } else if (this.keyMode === KeyMode.RootKeyPlusWrapper) { - if (!wrappingKey) { - throw Error('wrappingKey must be supplied') - } - await this.wrapAndPersistRootKey(wrappingKey) - } - - await this.handleKeyStatusChange() - } - - /** - * Deletes root key and wrapper from keychain. Used when signing out of application. - */ - public async deleteWorkspaceSpecificKeyStateFromDevice() { - await this.deviceInterface.clearNamespacedKeychainValue(this.identifier) - await this.storageService.removeValue(StorageKey.WrappedRootKey, StorageValueModes.Nonwrapped) - await this.storageService.removeValue(StorageKey.RootKeyWrapperKeyParams, StorageValueModes.Nonwrapped) - await this.storageService.removeValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped) - this.keyMode = KeyMode.RootKeyNone - this.setRootKeyInstance(undefined) - - await this.handleKeyStatusChange() - } - - private getWrappedRootKey() { - return this.storageService.getValue( - StorageKey.WrappedRootKey, - StorageValueModes.Nonwrapped, - ) - } - - public setRootKeyInstance(rootKey: RootKeyInterface | undefined): void { - this.rootKey = rootKey - } - - public getRootKey(): RootKeyInterface | undefined { - return this.rootKey - } - - private getSureRootKey(): RootKeyInterface { - return this.rootKey as RootKeyInterface - } - - private getItemsKeys() { - return this.items.getDisplayableItemsKeys() - } - - private async encryptPayloadWithKeyLookup( - payload: DecryptedPayloadInterface, - signingKeyPair?: PkcKeyPair, - ): Promise { - let key: RootKeyInterface | KeySystemRootKeyInterface | undefined - if (ContentTypeUsesKeySystemRootKeyEncryption(payload.content_type)) { - if (!payload.key_system_identifier) { - throw Error(`Key system-encrypted payload ${payload.content_type}is missing a key_system_identifier`) - } - key = this.keys.getPrimaryKeySystemRootKey(payload.key_system_identifier) - } else { - key = this.getRootKey() - } - - if (key == undefined) { - throw Error('Attempting root key encryption with no root key') - } - - return this.encryptPayload(payload, key, signingKeyPair) - } - - public async encryptPayloadsWithKeyLookup( - payloads: DecryptedPayloadInterface[], - signingKeyPair?: PkcKeyPair, - ): Promise { - return Promise.all(payloads.map((payload) => this.encryptPayloadWithKeyLookup(payload, signingKeyPair))) - } - - public async encryptPayload( - payload: DecryptedPayloadInterface, - key: RootKeyInterface | KeySystemRootKeyInterface, - signingKeyPair?: PkcKeyPair, - ): Promise { - return encryptPayload(payload, key, this.operatorManager, signingKeyPair) - } - - public async encryptPayloads( - payloads: DecryptedPayloadInterface[], - key: RootKeyInterface | KeySystemRootKeyInterface, - signingKeyPair?: PkcKeyPair, - ) { - return Promise.all(payloads.map((payload) => this.encryptPayload(payload, key, signingKeyPair))) - } - - public async decryptPayloadWithKeyLookup( - payload: EncryptedPayloadInterface, - ): Promise | ErrorDecryptingParameters> { - let key: RootKeyInterface | KeySystemRootKeyInterface | undefined - if (ContentTypeUsesKeySystemRootKeyEncryption(payload.content_type)) { - if (!payload.key_system_identifier) { - throw Error('Key system root key encrypted payload is missing key_system_identifier') - } - key = this.keys.getPrimaryKeySystemRootKey(payload.key_system_identifier) - } else { - key = this.getRootKey() - } - - if (key == undefined) { - return { - uuid: payload.uuid, - errorDecrypting: true, - waitingForKey: true, - } - } - - return this.decryptPayload(payload, key) - } - - public async decryptPayload( - payload: EncryptedPayloadInterface, - key: RootKeyInterface | KeySystemRootKeyInterface, - ): Promise | ErrorDecryptingParameters> { - return decryptPayload(payload, key, this.operatorManager) - } - - public async decryptPayloadsWithKeyLookup( - payloads: EncryptedPayloadInterface[], - ): Promise<(DecryptedParameters | ErrorDecryptingParameters)[]> { - return Promise.all(payloads.map((payload) => this.decryptPayloadWithKeyLookup(payload))) - } - - public async decryptPayloads( - payloads: EncryptedPayloadInterface[], - key: RootKeyInterface | KeySystemRootKeyInterface, - ): Promise<(DecryptedParameters | ErrorDecryptingParameters)[]> { - return Promise.all(payloads.map((payload) => this.decryptPayload(payload, key))) - } - - public async decryptErroredRootPayloads(): Promise { - const erroredRootPayloads = this.payloadManager.invalidPayloads.filter( - (i) => - ContentTypeUsesRootKeyEncryption(i.content_type) || ContentTypeUsesKeySystemRootKeyEncryption(i.content_type), - ) - if (erroredRootPayloads.length === 0) { - return - } - - const resultParams = await this.decryptPayloadsWithKeyLookup(erroredRootPayloads) - - const decryptedPayloads = resultParams.map((params) => { - const original = SureFindPayload(erroredRootPayloads, params.uuid) - if (isErrorDecryptingParameters(params)) { - return new EncryptedPayload({ - ...original.ejected(), - ...params, - }) - } else { - return new DecryptedPayload({ - ...original.ejected(), - ...params, - }) - } - }) - - await this.payloadManager.emitPayloads(decryptedPayloads, PayloadEmitSource.LocalChanged) - } - - /** - * When the root key changes, we must re-encrypt all relevant items with this new root key (by simply re-syncing). - */ - public async reencryptApplicableItemsAfterUserRootKeyChange(): Promise { - const items = this.items.getItems(ContentTypesUsingRootKeyEncryption()) - if (items.length > 0) { - /** - * Do not call sync after marking dirty. - * Re-encrypting items keys is called by consumers who have specific flows who - * will sync on their own timing - */ - await this.mutator.setItemsDirty(items) - } - } - - /** - * When the key system root key changes, we must re-encrypt all vault items keys - * with this new key system root key (by simply re-syncing). - */ - public async reencryptKeySystemItemsKeysForVault(keySystemIdentifier: KeySystemIdentifier): Promise { - const keySystemItemsKeys = this.keys.getKeySystemItemsKeys(keySystemIdentifier) - if (keySystemItemsKeys.length > 0) { - await this.mutator.setItemsDirty(keySystemItemsKeys) - } - } - - /** - * Creates a new random items key to use for item encryption, and adds it to model management. - * Consumer must call sync. If the protocol version <= 003, only one items key should be created, - * and its .itemsKey value should be equal to the root key masterKey value. - */ - public async createNewDefaultItemsKey(): Promise { - const rootKey = this.getSureRootKey() - const operatorVersion = rootKey ? rootKey.keyVersion : ProtocolVersionLatest - let itemTemplate: ItemsKeyInterface - - if (compareVersions(operatorVersion, ProtocolVersionLastNonrootItemsKey) <= 0) { - /** Create root key based items key */ - const payload = new DecryptedPayload({ - uuid: UuidGenerator.GenerateUuid(), - content_type: ContentType.ItemsKey, - content: FillItemContentSpecialized({ - itemsKey: rootKey.masterKey, - dataAuthenticationKey: rootKey.dataAuthenticationKey, - version: operatorVersion, - }), - ...PayloadTimestampDefaults(), - }) - itemTemplate = CreateDecryptedItemFromPayload(payload) - } else { - /** Create independent items key */ - itemTemplate = this.operatorManager.operatorForVersion(operatorVersion).createItemsKey() - } - - const itemsKeys = this.getItemsKeys() - const defaultKeys = itemsKeys.filter((key) => { - return key.isDefault - }) - - for (const key of defaultKeys) { - await this.mutator.changeItemsKey(key, (mutator) => { - mutator.isDefault = false - }) - } - - const itemsKey = await this.mutator.insertItem(itemTemplate) - await this.mutator.changeItemsKey(itemsKey, (mutator) => { - mutator.isDefault = true - }) - - return itemsKey - } - - public async createNewItemsKeyWithRollback(): Promise<() => Promise> { - const currentDefaultItemsKey = findDefaultItemsKey(this.getItemsKeys()) - const newDefaultItemsKey = await this.createNewDefaultItemsKey() - - const rollback = async () => { - await this.mutator.setItemToBeDeleted(newDefaultItemsKey) - - if (currentDefaultItemsKey) { - await this.mutator.changeItem(currentDefaultItemsKey, (mutator) => { - mutator.isDefault = true - }) - } - } - - return rollback - } -} diff --git a/packages/services/src/Domain/Encryption/UseCase/ItemsKey/CreateNewDefaultItemsKey.ts b/packages/services/src/Domain/Encryption/UseCase/ItemsKey/CreateNewDefaultItemsKey.ts new file mode 100644 index 000000000..ba7361ae0 --- /dev/null +++ b/packages/services/src/Domain/Encryption/UseCase/ItemsKey/CreateNewDefaultItemsKey.ts @@ -0,0 +1,76 @@ +import { OperatorManager } from '@standardnotes/encryption' +import { + ContentType, + ProtocolVersionLastNonrootItemsKey, + ProtocolVersionLatest, + compareVersions, +} from '@standardnotes/common' +import { + CreateDecryptedItemFromPayload, + DecryptedPayload, + FillItemContentSpecialized, + ItemsKeyContent, + ItemsKeyContentSpecialized, + ItemsKeyInterface, + PayloadTimestampDefaults, +} from '@standardnotes/models' +import { UuidGenerator } from '@standardnotes/utils' +import { MutatorClientInterface } from '../../../Mutator/MutatorClientInterface' +import { ItemManagerInterface } from '../../../Item/ItemManagerInterface' +import { RootKeyManager } from '../../RootKey/RootKeyManager' + +/** + * Creates a new random items key to use for item encryption, and adds it to model management. + * Consumer must call sync. If the protocol version <= 003, only one items key should be created, + * and its .itemsKey value should be equal to the root key masterKey value. + */ +export class CreateNewDefaultItemsKeyUseCase { + constructor( + private mutator: MutatorClientInterface, + private items: ItemManagerInterface, + private operatorManager: OperatorManager, + private rootKeyManager: RootKeyManager, + ) {} + + async execute(): Promise { + const rootKey = this.rootKeyManager.getSureRootKey() + const operatorVersion = rootKey ? rootKey.keyVersion : ProtocolVersionLatest + let itemTemplate: ItemsKeyInterface + + if (compareVersions(operatorVersion, ProtocolVersionLastNonrootItemsKey) <= 0) { + /** Create root key based items key */ + const payload = new DecryptedPayload({ + uuid: UuidGenerator.GenerateUuid(), + content_type: ContentType.ItemsKey, + content: FillItemContentSpecialized({ + itemsKey: rootKey.masterKey, + dataAuthenticationKey: rootKey.dataAuthenticationKey, + version: operatorVersion, + }), + ...PayloadTimestampDefaults(), + }) + itemTemplate = CreateDecryptedItemFromPayload(payload) + } else { + /** Create independent items key */ + itemTemplate = this.operatorManager.operatorForVersion(operatorVersion).createItemsKey() + } + + const itemsKeys = this.items.getDisplayableItemsKeys() + const defaultKeys = itemsKeys.filter((key) => { + return key.isDefault + }) + + for (const key of defaultKeys) { + await this.mutator.changeItemsKey(key, (mutator) => { + mutator.isDefault = false + }) + } + + const itemsKey = await this.mutator.insertItem(itemTemplate) + await this.mutator.changeItemsKey(itemsKey, (mutator) => { + mutator.isDefault = true + }) + + return itemsKey + } +} diff --git a/packages/services/src/Domain/Encryption/UseCase/ItemsKey/CreateNewItemsKeyWithRollback.ts b/packages/services/src/Domain/Encryption/UseCase/ItemsKey/CreateNewItemsKeyWithRollback.ts new file mode 100644 index 000000000..b8141cfb3 --- /dev/null +++ b/packages/services/src/Domain/Encryption/UseCase/ItemsKey/CreateNewItemsKeyWithRollback.ts @@ -0,0 +1,38 @@ +import { ItemsKeyMutator, OperatorManager, findDefaultItemsKey } from '@standardnotes/encryption' +import { MutatorClientInterface } from '../../../Mutator/MutatorClientInterface' +import { ItemManagerInterface } from '../../../Item/ItemManagerInterface' +import { RootKeyManager } from '../../RootKey/RootKeyManager' +import { CreateNewDefaultItemsKeyUseCase } from './CreateNewDefaultItemsKey' + +export class CreateNewItemsKeyWithRollbackUseCase { + private createDefaultItemsKeyUseCase = new CreateNewDefaultItemsKeyUseCase( + this.mutator, + this.items, + this.operatorManager, + this.rootKeyManager, + ) + + constructor( + private mutator: MutatorClientInterface, + private items: ItemManagerInterface, + private operatorManager: OperatorManager, + private rootKeyManager: RootKeyManager, + ) {} + + async execute(): Promise<() => Promise> { + const currentDefaultItemsKey = findDefaultItemsKey(this.items.getDisplayableItemsKeys()) + const newDefaultItemsKey = await this.createDefaultItemsKeyUseCase.execute() + + const rollback = async () => { + await this.mutator.setItemToBeDeleted(newDefaultItemsKey) + + if (currentDefaultItemsKey) { + await this.mutator.changeItem(currentDefaultItemsKey, (mutator) => { + mutator.isDefault = true + }) + } + } + + return rollback + } +} diff --git a/packages/services/src/Domain/Encryption/UseCase/RootEncryption/DecryptErroredPayloads.ts b/packages/services/src/Domain/Encryption/UseCase/RootEncryption/DecryptErroredPayloads.ts new file mode 100644 index 000000000..ebfa9c93e --- /dev/null +++ b/packages/services/src/Domain/Encryption/UseCase/RootEncryption/DecryptErroredPayloads.ts @@ -0,0 +1,55 @@ +import { + ContentTypeUsesKeySystemRootKeyEncryption, + ContentTypeUsesRootKeyEncryption, + DecryptedPayload, + EncryptedPayload, + PayloadEmitSource, + SureFindPayload, +} from '@standardnotes/models' +import { PayloadManagerInterface } from './../../../Payloads/PayloadManagerInterface' +import { KeySystemKeyManagerInterface, OperatorManager, isErrorDecryptingParameters } from '@standardnotes/encryption' +import { RootKeyDecryptPayloadWithKeyLookupUseCase } from './DecryptPayloadWithKeyLookup' +import { RootKeyManager } from '../../RootKey/RootKeyManager' + +export class DecryptErroredRootPayloadsUseCase { + constructor( + private payloads: PayloadManagerInterface, + private operatorManager: OperatorManager, + private keySystemKeyManager: KeySystemKeyManagerInterface, + private rootKeyManager: RootKeyManager, + ) {} + + async execute(): Promise { + const erroredRootPayloads = this.payloads.invalidPayloads.filter( + (i) => + ContentTypeUsesRootKeyEncryption(i.content_type) || ContentTypeUsesKeySystemRootKeyEncryption(i.content_type), + ) + if (erroredRootPayloads.length === 0) { + return + } + + const usecase = new RootKeyDecryptPayloadWithKeyLookupUseCase( + this.operatorManager, + this.keySystemKeyManager, + this.rootKeyManager, + ) + const resultParams = await usecase.executeMany(erroredRootPayloads) + + const decryptedPayloads = resultParams.map((params) => { + const original = SureFindPayload(erroredRootPayloads, params.uuid) + if (isErrorDecryptingParameters(params)) { + return new EncryptedPayload({ + ...original.ejected(), + ...params, + }) + } else { + return new DecryptedPayload({ + ...original.ejected(), + ...params, + }) + } + }) + + await this.payloads.emitPayloads(decryptedPayloads, PayloadEmitSource.LocalChanged) + } +} diff --git a/packages/services/src/Domain/Encryption/UseCase/RootEncryption/DecryptPayload.ts b/packages/services/src/Domain/Encryption/UseCase/RootEncryption/DecryptPayload.ts new file mode 100644 index 000000000..18d84bac4 --- /dev/null +++ b/packages/services/src/Domain/Encryption/UseCase/RootEncryption/DecryptPayload.ts @@ -0,0 +1,30 @@ +import { + DecryptedParameters, + ErrorDecryptingParameters, + OperatorManager, + decryptPayload, +} from '@standardnotes/encryption' +import { + EncryptedPayloadInterface, + ItemContent, + KeySystemRootKeyInterface, + RootKeyInterface, +} from '@standardnotes/models' + +export class RootKeyDecryptPayloadUseCase { + constructor(private operatorManager: OperatorManager) {} + + async executeOne( + payload: EncryptedPayloadInterface, + key: RootKeyInterface | KeySystemRootKeyInterface, + ): Promise | ErrorDecryptingParameters> { + return decryptPayload(payload, key, this.operatorManager) + } + + async executeMany( + payloads: EncryptedPayloadInterface[], + key: RootKeyInterface | KeySystemRootKeyInterface, + ): Promise<(DecryptedParameters | ErrorDecryptingParameters)[]> { + return Promise.all(payloads.map((payload) => this.executeOne(payload, key))) + } +} diff --git a/packages/services/src/Domain/Encryption/UseCase/RootEncryption/DecryptPayloadWithKeyLookup.ts b/packages/services/src/Domain/Encryption/UseCase/RootEncryption/DecryptPayloadWithKeyLookup.ts new file mode 100644 index 000000000..1611756cd --- /dev/null +++ b/packages/services/src/Domain/Encryption/UseCase/RootEncryption/DecryptPayloadWithKeyLookup.ts @@ -0,0 +1,56 @@ +import { + DecryptedParameters, + ErrorDecryptingParameters, + KeySystemKeyManagerInterface, + OperatorManager, +} from '@standardnotes/encryption' +import { + ContentTypeUsesKeySystemRootKeyEncryption, + EncryptedPayloadInterface, + ItemContent, + KeySystemRootKeyInterface, + RootKeyInterface, +} from '@standardnotes/models' + +import { RootKeyDecryptPayloadUseCase } from './DecryptPayload' +import { RootKeyManager } from '../../RootKey/RootKeyManager' + +export class RootKeyDecryptPayloadWithKeyLookupUseCase { + constructor( + private operatorManager: OperatorManager, + private keySystemKeyManager: KeySystemKeyManagerInterface, + private rootKeyManager: RootKeyManager, + ) {} + + async executeOne( + payload: EncryptedPayloadInterface, + ): Promise | ErrorDecryptingParameters> { + let key: RootKeyInterface | KeySystemRootKeyInterface | undefined + if (ContentTypeUsesKeySystemRootKeyEncryption(payload.content_type)) { + if (!payload.key_system_identifier) { + throw Error('Key system root key encrypted payload is missing key_system_identifier') + } + key = this.keySystemKeyManager.getPrimaryKeySystemRootKey(payload.key_system_identifier) + } else { + key = this.rootKeyManager.getRootKey() + } + + if (key == undefined) { + return { + uuid: payload.uuid, + errorDecrypting: true, + waitingForKey: true, + } + } + + const usecase = new RootKeyDecryptPayloadUseCase(this.operatorManager) + + return usecase.executeOne(payload, key) + } + + async executeMany( + payloads: EncryptedPayloadInterface[], + ): Promise<(DecryptedParameters | ErrorDecryptingParameters)[]> { + return Promise.all(payloads.map((payload) => this.executeOne(payload))) + } +} diff --git a/packages/services/src/Domain/Encryption/UseCase/RootEncryption/EncryptPayload.ts b/packages/services/src/Domain/Encryption/UseCase/RootEncryption/EncryptPayload.ts new file mode 100644 index 000000000..09a3453c9 --- /dev/null +++ b/packages/services/src/Domain/Encryption/UseCase/RootEncryption/EncryptPayload.ts @@ -0,0 +1,23 @@ +import { EncryptedOutputParameters, OperatorManager, encryptPayload } from '@standardnotes/encryption' +import { DecryptedPayloadInterface, KeySystemRootKeyInterface, RootKeyInterface } from '@standardnotes/models' +import { PkcKeyPair } from '@standardnotes/sncrypto-common' + +export class RootKeyEncryptPayloadUseCase { + constructor(private operatorManager: OperatorManager) {} + + async executeOne( + payload: DecryptedPayloadInterface, + key: RootKeyInterface | KeySystemRootKeyInterface, + signingKeyPair?: PkcKeyPair, + ): Promise { + return encryptPayload(payload, key, this.operatorManager, signingKeyPair) + } + + async executeMany( + payloads: DecryptedPayloadInterface[], + key: RootKeyInterface | KeySystemRootKeyInterface, + signingKeyPair?: PkcKeyPair, + ): Promise { + return Promise.all(payloads.map((payload) => this.executeOne(payload, key, signingKeyPair))) + } +} diff --git a/packages/services/src/Domain/Encryption/UseCase/RootEncryption/EncryptPayloadWithKeyLookup.ts b/packages/services/src/Domain/Encryption/UseCase/RootEncryption/EncryptPayloadWithKeyLookup.ts new file mode 100644 index 000000000..1b693610e --- /dev/null +++ b/packages/services/src/Domain/Encryption/UseCase/RootEncryption/EncryptPayloadWithKeyLookup.ts @@ -0,0 +1,48 @@ +import { EncryptedOutputParameters, KeySystemKeyManagerInterface, OperatorManager } from '@standardnotes/encryption' +import { + ContentTypeUsesKeySystemRootKeyEncryption, + DecryptedPayloadInterface, + KeySystemRootKeyInterface, + RootKeyInterface, +} from '@standardnotes/models' +import { PkcKeyPair } from '@standardnotes/sncrypto-common' + +import { RootKeyEncryptPayloadUseCase } from './EncryptPayload' +import { RootKeyManager } from '../../RootKey/RootKeyManager' + +export class RootKeyEncryptPayloadWithKeyLookupUseCase { + constructor( + private operatorManager: OperatorManager, + private keySystemKeyManager: KeySystemKeyManagerInterface, + private rootKeyManager: RootKeyManager, + ) {} + + async executeOne( + payload: DecryptedPayloadInterface, + signingKeyPair?: PkcKeyPair, + ): Promise { + let key: RootKeyInterface | KeySystemRootKeyInterface | undefined + if (ContentTypeUsesKeySystemRootKeyEncryption(payload.content_type)) { + if (!payload.key_system_identifier) { + throw Error(`Key system-encrypted payload ${payload.content_type}is missing a key_system_identifier`) + } + key = this.keySystemKeyManager.getPrimaryKeySystemRootKey(payload.key_system_identifier) + } else { + key = this.rootKeyManager.getRootKey() + } + + if (key == undefined) { + throw Error('Attempting root key encryption with no root key') + } + + const usecase = new RootKeyEncryptPayloadUseCase(this.operatorManager) + return usecase.executeOne(payload, key, signingKeyPair) + } + + async executeMany( + payloads: DecryptedPayloadInterface[], + signingKeyPair?: PkcKeyPair, + ): Promise { + return Promise.all(payloads.map((payload) => this.executeOne(payload, signingKeyPair))) + } +} diff --git a/packages/services/src/Domain/User/UserService.spec.ts b/packages/services/src/Domain/User/UserService.spec.ts index e46322180..83ba96761 100644 --- a/packages/services/src/Domain/User/UserService.spec.ts +++ b/packages/services/src/Domain/User/UserService.spec.ts @@ -20,7 +20,7 @@ describe('UserService', () => { let syncService: SyncServiceInterface let storageService: StorageServiceInterface let itemManager: ItemManagerInterface - let protocolService: EncryptionProviderInterface + let encryptionService: EncryptionProviderInterface let alertService: AlertService let challengeService: ChallengeServiceInterface let protectionService: ProtectionsClientInterface @@ -33,7 +33,7 @@ describe('UserService', () => { syncService, storageService, itemManager, - protocolService, + encryptionService, alertService, challengeService, protectionService, @@ -51,7 +51,7 @@ describe('UserService', () => { itemManager = {} as jest.Mocked - protocolService = {} as jest.Mocked + encryptionService = {} as jest.Mocked alertService = {} as jest.Mocked diff --git a/packages/services/src/Domain/User/UserService.ts b/packages/services/src/Domain/User/UserService.ts index c700b66eb..c155a252f 100644 --- a/packages/services/src/Domain/User/UserService.ts +++ b/packages/services/src/Domain/User/UserService.ts @@ -49,7 +49,7 @@ export class UserService private syncService: SyncServiceInterface, private storageService: StorageServiceInterface, private itemManager: ItemManagerInterface, - private protocolService: EncryptionProviderInterface, + private encryptionService: EncryptionProviderInterface, private alertService: AlertService, private challengeService: ChallengeServiceInterface, private protectionService: ProtectionsClientInterface, @@ -84,14 +84,14 @@ export class UserService }) .then(() => { if (!payload.awaitSync) { - void this.protocolService.decryptErroredPayloads() + void this.encryptionService.decryptErroredPayloads() } }) if (payload.awaitSync) { await syncPromise - await this.protocolService.decryptErroredPayloads() + await this.encryptionService.decryptErroredPayloads() } } } @@ -102,7 +102,7 @@ export class UserService ;(this.syncService as unknown) = undefined ;(this.storageService as unknown) = undefined ;(this.itemManager as unknown) = undefined - ;(this.protocolService as unknown) = undefined + ;(this.encryptionService as unknown) = undefined ;(this.alertService as unknown) = undefined ;(this.challengeService as unknown) = undefined ;(this.protectionService as unknown) = undefined @@ -123,7 +123,7 @@ export class UserService ephemeral = false, mergeLocal = true, ): Promise { - if (this.protocolService.hasAccount()) { + if (this.encryptionService.hasAccount()) { throw Error('Tried to register when an account already exists.') } @@ -169,7 +169,7 @@ export class UserService mergeLocal = true, awaitSync = false, ): Promise> { - if (this.protocolService.hasAccount()) { + if (this.encryptionService.hasAccount()) { throw Error('Tried to sign in when an account already exists.') } @@ -313,7 +313,7 @@ export class UserService public async signOut(force = false, source = DeinitSource.SignOut): Promise { const performSignOut = async () => { await this.sessionManager.signOut() - await this.protocolService.deleteWorkspaceSpecificKeyStateFromDevice() + await this.encryptionService.deleteWorkspaceSpecificKeyStateFromDevice() await this.storageService.clearAllData() await this.notifyEvent(AccountEvent.SignedOut, { payload: { source } }) } @@ -359,8 +359,8 @@ export class UserService canceled?: true error?: { message: string } }> { - const hasPasscode = this.protocolService.hasPasscode() - const hasAccount = this.protocolService.hasAccount() + const hasPasscode = this.encryptionService.hasPasscode() + const hasAccount = this.encryptionService.hasAccount() const prompts = [] if (hasPasscode) { prompts.push( @@ -504,14 +504,14 @@ export class UserService private async setPasscodeWithoutWarning(passcode: string, origination: KeyParamsOrigination) { const identifier = UuidGenerator.GenerateUuid() - const key = await this.protocolService.createRootKey(identifier, passcode, origination) - await this.protocolService.setNewRootKeyWrapper(key) + const key = await this.encryptionService.createRootKey(identifier, passcode, origination) + await this.encryptionService.setNewRootKeyWrapper(key) await this.rewriteItemsKeys() await this.syncService.sync() } private async removePasscodeWithoutWarning() { - await this.protocolService.removePasscode() + await this.encryptionService.removePasscode() await this.rewriteItemsKeys() } @@ -564,7 +564,7 @@ export class UserService } } - const accountPasswordValidation = await this.protocolService.validateAccountPassword(parameters.currentPassword) + const accountPasswordValidation = await this.encryptionService.validateAccountPassword(parameters.currentPassword) if (!accountPasswordValidation.valid) { return { error: Error(Messages.INVALID_PASSWORD), @@ -597,11 +597,11 @@ export class UserService return { error: Error(response.data.error?.message) } } - const rollback = await this.protocolService.createNewItemsKeyWithRollback() - await this.protocolService.reencryptApplicableItemsAfterUserRootKeyChange() + const rollback = await this.encryptionService.createNewItemsKeyWithRollback() + await this.encryptionService.reencryptApplicableItemsAfterUserRootKeyChange() await this.syncService.sync({ awaitAll: true }) - const defaultItemsKey = this.protocolService.getSureDefaultItemsKey() + const defaultItemsKey = this.encryptionService.getSureDefaultItemsKey() const itemsKeyWasSynced = !defaultItemsKey.neverSynced if (!itemsKeyWasSynced) { @@ -610,7 +610,7 @@ export class UserService newRootKey: currentRootKey, wrappingKey, }) - await this.protocolService.reencryptApplicableItemsAfterUserRootKeyChange() + await this.encryptionService.reencryptApplicableItemsAfterUserRootKeyChange() await rollback() await this.syncService.sync({ awaitAll: true }) @@ -627,11 +627,11 @@ export class UserService newEmail?: string newPassword?: string }): Promise<{ currentRootKey: SNRootKey; newRootKey: SNRootKey }> { - const currentRootKey = await this.protocolService.computeRootKey( + const currentRootKey = await this.encryptionService.computeRootKey( parameters.currentPassword, - (await this.protocolService.getRootKeyParams()) as SNRootKeyParams, + (await this.encryptionService.getRootKeyParams()) as SNRootKeyParams, ) - const newRootKey = await this.protocolService.createRootKey( + const newRootKey = await this.encryptionService.createRootKey( parameters.newEmail ?? parameters.currentEmail, parameters.newPassword ?? parameters.currentPassword, parameters.origination, diff --git a/packages/services/src/Domain/index.ts b/packages/services/src/Domain/index.ts index d27275a21..b48fa7e2b 100644 --- a/packages/services/src/Domain/index.ts +++ b/packages/services/src/Domain/index.ts @@ -61,7 +61,6 @@ export * from './Encryption/EncryptionService' export * from './Encryption/EncryptionServiceEvent' export * from './Encryption/Functions' export * from './Encryption/ItemsEncryption' -export * from './Encryption/RootKeyEncryption' export * from './Event/ApplicationEvent' export * from './Event/ApplicationEventCallback' diff --git a/packages/snjs/lib/Application/Application.ts b/packages/snjs/lib/Application/Application.ts index b957cfd38..d1cbeb0a0 100644 --- a/packages/snjs/lib/Application/Application.ts +++ b/packages/snjs/lib/Application/Application.ts @@ -139,7 +139,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli private deprecatedHttpService!: InternalServices.DeprecatedHttpService private declare httpService: HttpServiceInterface public payloadManager!: InternalServices.PayloadManager - public protocolService!: EncryptionService + public encryptionService!: EncryptionService private diskStorageService!: InternalServices.DiskStorageService private inMemoryStore!: ExternalServices.KeyValueStoreInterface /** @@ -435,7 +435,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli await this.diskStorageService.initializeFromDisk() await this.notifyEvent(ApplicationEvent.StorageReady) - await this.protocolService.initialize() + await this.encryptionService.initialize() await this.handleStage(ExternalServices.ApplicationStage.ReadyForLaunch_05) @@ -547,9 +547,9 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli let wrappingKey = response.artifacts?.wrappingKey if (!wrappingKey) { const value = response.getValueForType(ChallengeValidation.LocalPasscode) - wrappingKey = await this.protocolService.computeWrappingKey(value.value as string) + wrappingKey = await this.encryptionService.computeWrappingKey(value.value as string) } - await this.protocolService.unwrapRootKey(wrappingKey) + await this.encryptionService.unwrapRootKey(wrappingKey) } } @@ -740,22 +740,22 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli } public getUserPasswordCreationDate(): Date | undefined { - return this.protocolService.getPasswordCreatedDate() + return this.encryptionService.getPasswordCreatedDate() } public getProtocolEncryptionDisplayName(): Promise { - return this.protocolService.getEncryptionDisplayName() + return this.encryptionService.getEncryptionDisplayName() } public getUserVersion(): Common.ProtocolVersion | undefined { - return this.protocolService.getUserVersion() + return this.encryptionService.getUserVersion() } /** * Returns true if there is an upgrade available for the account or passcode */ public protocolUpgradeAvailable(): Promise { - return this.protocolService.upgradeAvailable() + return this.encryptionService.upgradeAvailable() } /** @@ -790,7 +790,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli } public hasAccount(): boolean { - return this.protocolService.hasAccount() + return this.encryptionService.hasAccount() } /** @@ -840,7 +840,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli } public async createEncryptedBackupFileForAutomatedDesktopBackups(): Promise { - return this.protocolService.createEncryptedBackupFile() + return this.encryptionService.createEncryptedBackupFile() } public async createEncryptedBackupFile(): Promise { @@ -848,7 +848,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli return } - return this.protocolService.createEncryptedBackupFile() + return this.encryptionService.createEncryptedBackupFile() } public async createDecryptedBackupFile(): Promise { @@ -856,7 +856,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli return } - return this.protocolService.createDecryptedBackupFile() + return this.encryptionService.createDecryptedBackupFile() } public isEphemeralSession(): boolean { @@ -1058,7 +1058,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this.itemManager, this.syncService, this.protectionService, - this.protocolService, + this.encryptionService, this.payloadManager, this.challengeService, this.historyManager, @@ -1083,7 +1083,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli } public async validateAccountPassword(password: string): Promise { - const { valid } = await this.protocolService.validateAccountPassword(password) + const { valid } = await this.encryptionService.validateAccountPassword(password) return valid } @@ -1096,7 +1096,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli } public hasPasscode(): boolean { - return this.protocolService.hasPasscode() + return this.encryptionService.hasPasscode() } async isLocked(): Promise { @@ -1253,7 +1253,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this.createKeySystemKeyManager() this.createProtocolService() - this.diskStorageService.provideEncryptionProvider(this.protocolService) + this.diskStorageService.provideEncryptionProvider(this.encryptionService) this.createChallengeService() this.createLegacyHttpManager() this.createHttpServiceAndApiService() @@ -1308,7 +1308,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli ;(this.deprecatedHttpService as unknown) = undefined ;(this.httpService as unknown) = undefined ;(this.payloadManager as unknown) = undefined - ;(this.protocolService as unknown) = undefined + ;(this.encryptionService as unknown) = undefined ;(this.diskStorageService as unknown) = undefined ;(this.inMemoryStore as unknown) = undefined ;(this.apiService as unknown) = undefined @@ -1392,7 +1392,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli private createAsymmetricMessageService() { this.asymmetricMessageService = new ExternalServices.AsymmetricMessageService( this.httpService, - this.protocolService, + this.encryptionService, this.contacts, this.itemManager, this.mutator, @@ -1410,7 +1410,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this.sessionManager, this.options.crypto, this.user, - this.protocolService, + this.encryptionService, this.singletonManager, this.internalEventBus, ) @@ -1424,7 +1424,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this.syncService, this.itemManager, this.mutator, - this.protocolService, + this.encryptionService, this.sessions, this.contactService, this.files, @@ -1440,7 +1440,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this.syncService, this.itemManager, this.mutator, - this.protocolService, + this.encryptionService, this.files, this.alertService, this.internalEventBus, @@ -1468,7 +1468,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this.apiService, this.mutator, this.syncService, - this.protocolService, + this.encryptionService, this.challengeService, this.httpService, this.alertService, @@ -1542,7 +1542,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli private createMigrationService() { this.migrationService = new InternalServices.SNMigrationService({ - protocolService: this.protocolService, + encryptionService: this.encryptionService, deviceInterface: this.deviceInterface, storageService: this.diskStorageService, sessionManager: this.sessionManager, @@ -1567,7 +1567,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this.syncService, this.diskStorageService, this.itemManager, - this.protocolService, + this.encryptionService, this.alertService, this.challengeService, this.protectionService, @@ -1720,7 +1720,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli } private createProtocolService() { - this.protocolService = new EncryptionService( + this.encryptionService = new EncryptionService( this.itemManager, this.mutator, this.payloadManager, @@ -1732,13 +1732,13 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this.internalEventBus, ) this.serviceObservers.push( - this.protocolService.addEventObserver(async (event) => { + this.encryptionService.addEventObserver(async (event) => { if (event === EncryptionServiceEvent.RootKeyStatusChanged) { await this.notifyEvent(ApplicationEvent.KeyStatusChanged) } }), ) - this.services.push(this.protocolService) + this.services.push(this.encryptionService) } private createKeySystemKeyManager() { @@ -1757,7 +1757,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this.itemManager, this.payloadManager, this.apiService, - this.protocolService, + this.encryptionService, this.challengeService, this.alertService, this.diskStorageService, @@ -1774,7 +1774,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this.apiService, this.userApiService, this.alertService, - this.protocolService, + this.encryptionService, this.challengeService, this.webSocketsService, this.httpService, @@ -1789,8 +1789,8 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli case ExternalServices.SessionEvent.Restored: { void (async () => { await this.sync.sync({ sourceDescription: 'Session restored pre key creation' }) - if (this.protocolService.needsNewRootKeyBasedItemsKey()) { - void this.protocolService.createNewDefaultItemsKey().then(() => { + if (this.encryptionService.needsNewRootKeyBasedItemsKey()) { + void this.encryptionService.createNewDefaultItemsKey().then(() => { void this.sync.sync({ sourceDescription: 'Session restored post key creation' }) }) } @@ -1816,7 +1816,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this.syncService = new InternalServices.SNSyncService( this.itemManager, this.sessionManager, - this.protocolService, + this.encryptionService, this.diskStorageService, this.payloadManager, this.apiService, @@ -1832,7 +1832,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli const syncEventCallback = async (eventName: ExternalServices.SyncEvent) => { const appEvent = applicationEventForSyncEvent(eventName) if (appEvent) { - await this.protocolService.onSyncEvent(eventName) + await this.encryptionService.onSyncEvent(eventName) await this.notifyEvent(appEvent) @@ -1852,7 +1852,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli private createChallengeService() { this.challengeService = new InternalServices.ChallengeService( this.diskStorageService, - this.protocolService, + this.encryptionService, this.internalEventBus, ) this.services.push(this.challengeService) @@ -1860,7 +1860,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli private createProtectionService() { this.protectionService = new InternalServices.SNProtectionService( - this.protocolService, + this.encryptionService, this.mutator, this.challengeService, this.diskStorageService, @@ -1895,7 +1895,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this.deviceInterface, this.deprecatedHttpService, this.payloadManager, - this.protocolService, + this.encryptionService, this.syncService, this.challengeService, this.listedService, @@ -1953,7 +1953,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this.filesBackupService = new FilesBackupService( this.itemManager, this.apiService, - this.protocolService, + this.encryptionService, device as FileBackupsDevice, this.statusService, this.options.crypto, @@ -2003,7 +2003,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli private createUseCases() { this._signInWithRecoveryCodes = new SignInWithRecoveryCodes( this.authManager, - this.protocolService, + this.encryptionService, this.inMemoryStore, this.options.crypto, this.sessionManager, @@ -2030,7 +2030,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli this._listRevisions = new ListRevisions(this.revisionManager) - this._getRevision = new GetRevision(this.revisionManager, this.protocolService) + this._getRevision = new GetRevision(this.revisionManager, this.encryptionService) this._deleteRevision = new DeleteRevision(this.revisionManager) } diff --git a/packages/snjs/lib/Domain/UseCase/GetRevision/GetRevision.spec.ts b/packages/snjs/lib/Domain/UseCase/GetRevision/GetRevision.spec.ts index 29b0106bf..524d0b042 100644 --- a/packages/snjs/lib/Domain/UseCase/GetRevision/GetRevision.spec.ts +++ b/packages/snjs/lib/Domain/UseCase/GetRevision/GetRevision.spec.ts @@ -17,9 +17,9 @@ import { GetRevision } from './GetRevision' describe('GetRevision', () => { let revisionManager: RevisionClientInterface - let protocolService: EncryptionProviderInterface + let encryptionService: EncryptionProviderInterface - const createUseCase = () => new GetRevision(revisionManager, protocolService) + const createUseCase = () => new GetRevision(revisionManager, encryptionService) beforeEach(() => { revisionManager = {} as jest.Mocked @@ -35,13 +35,13 @@ describe('GetRevision', () => { updated_at: '2021-01-01T00:00:00.000Z' } as jest.Mocked) - protocolService = {} as jest.Mocked - protocolService.getEmbeddedPayloadAuthenticatedData = jest.fn().mockReturnValue({ u: '00000000-0000-0000-0000-000000000000' }) + encryptionService = {} as jest.Mocked + encryptionService.getEmbeddedPayloadAuthenticatedData = jest.fn().mockReturnValue({ u: '00000000-0000-0000-0000-000000000000' }) const encryptedPayload = { content: 'foobar', } as jest.Mocked encryptedPayload.copy = jest.fn().mockReturnValue(encryptedPayload) - protocolService.decryptSplitSingle = jest.fn().mockReturnValue(encryptedPayload) + encryptionService.decryptSplitSingle = jest.fn().mockReturnValue(encryptedPayload) isRemotePayloadAllowed.mockImplementation(() => true) }) @@ -59,7 +59,7 @@ describe('GetRevision', () => { }) it('it should get a revision without uuid from embedded params', async () => { - protocolService.getEmbeddedPayloadAuthenticatedData = jest.fn().mockReturnValue({ u: undefined }) + encryptionService.getEmbeddedPayloadAuthenticatedData = jest.fn().mockReturnValue({ u: undefined }) const useCase = createUseCase() @@ -73,7 +73,7 @@ describe('GetRevision', () => { }) it('it should get a revision without embedded params', async () => { - protocolService.getEmbeddedPayloadAuthenticatedData = jest.fn().mockReturnValue(undefined) + encryptionService.getEmbeddedPayloadAuthenticatedData = jest.fn().mockReturnValue(undefined) const useCase = createUseCase() @@ -130,7 +130,7 @@ describe('GetRevision', () => { errorDecrypting: true, } as jest.Mocked encryptedPayload.copy = jest.fn().mockReturnValue(encryptedPayload) - protocolService.decryptSplitSingle = jest.fn().mockReturnValue(encryptedPayload) + encryptionService.decryptSplitSingle = jest.fn().mockReturnValue(encryptedPayload) const useCase = createUseCase() diff --git a/packages/snjs/lib/Domain/UseCase/GetRevision/GetRevision.ts b/packages/snjs/lib/Domain/UseCase/GetRevision/GetRevision.ts index 839dbe711..dcfe9985f 100644 --- a/packages/snjs/lib/Domain/UseCase/GetRevision/GetRevision.ts +++ b/packages/snjs/lib/Domain/UseCase/GetRevision/GetRevision.ts @@ -15,7 +15,10 @@ import { EncryptionProviderInterface } from '@standardnotes/encryption' import { GetRevisionDTO } from './GetRevisionDTO' export class GetRevision implements UseCaseInterface { - constructor(private revisionManager: RevisionClientInterface, private protocolService: EncryptionProviderInterface) {} + constructor( + private revisionManager: RevisionClientInterface, + private encryptionService: EncryptionProviderInterface, + ) {} async execute(dto: GetRevisionDTO): Promise> { const itemUuidOrError = Uuid.create(dto.itemUuid) @@ -63,7 +66,7 @@ export class GetRevision implements UseCaseInterface { * these olders revisions (which have not been mutated after copy) with the source item's * uuid. */ - const embeddedParams = this.protocolService.getEmbeddedPayloadAuthenticatedData(serverPayload) + const embeddedParams = this.encryptionService.getEmbeddedPayloadAuthenticatedData(serverPayload) const sourceItemUuid = embeddedParams?.u as string | undefined const payload = serverPayload.copy({ @@ -76,7 +79,7 @@ export class GetRevision implements UseCaseInterface { const encryptedPayload = new EncryptedPayload(payload) - const decryptedPayload = await this.protocolService.decryptSplitSingle({ + const decryptedPayload = await this.encryptionService.decryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [encryptedPayload] }, }) diff --git a/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.spec.ts b/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.spec.ts index 02e6e8f70..495d47ece 100644 --- a/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.spec.ts +++ b/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.spec.ts @@ -14,7 +14,7 @@ import { SignInWithRecoveryCodes } from './SignInWithRecoveryCodes' describe('SignInWithRecoveryCodes', () => { let authManager: AuthClientInterface - let protocolService: EncryptionProviderInterface + let encryptionService: EncryptionProviderInterface let inMemoryStore: KeyValueStoreInterface let crypto: PureCryptoInterface let sessionManager: SessionsClientInterface @@ -22,7 +22,7 @@ describe('SignInWithRecoveryCodes', () => { const createUseCase = () => new SignInWithRecoveryCodes( authManager, - protocolService, + encryptionService, inMemoryStore, crypto, sessionManager, @@ -50,17 +50,17 @@ describe('SignInWithRecoveryCodes', () => { }) rootKey.payload = payload - protocolService = {} as jest.Mocked - protocolService.hasAccount = jest.fn() - protocolService.computeRootKey = jest.fn().mockReturnValue(rootKey) - protocolService.platformSupportsKeyDerivation = jest.fn().mockReturnValue(true) - protocolService.supportedVersions = jest.fn().mockReturnValue([ + encryptionService = {} as jest.Mocked + encryptionService.hasAccount = jest.fn() + encryptionService.computeRootKey = jest.fn().mockReturnValue(rootKey) + encryptionService.platformSupportsKeyDerivation = jest.fn().mockReturnValue(true) + encryptionService.supportedVersions = jest.fn().mockReturnValue([ '001', '002', '003', '004', ]) - protocolService.isVersionNewerThanLibraryVersion = jest.fn() + encryptionService.isVersionNewerThanLibraryVersion = jest.fn() inMemoryStore = {} as jest.Mocked> inMemoryStore.setValue = jest.fn() @@ -79,7 +79,7 @@ describe('SignInWithRecoveryCodes', () => { }) it('should fail if an account already exists', async () => { - protocolService.hasAccount = jest.fn().mockReturnValue(true) + encryptionService.hasAccount = jest.fn().mockReturnValue(true) const useCase = createUseCase() const result = await useCase.execute({ recoveryCodes: 'recovery-codes', password: 'foobar', username: 'test@test.te' }) @@ -99,7 +99,7 @@ describe('SignInWithRecoveryCodes', () => { }) it('should fail if key params has unsupported deriviation', async () => { - protocolService.platformSupportsKeyDerivation = jest.fn().mockReturnValue(false) + encryptionService.platformSupportsKeyDerivation = jest.fn().mockReturnValue(false) const useCase = createUseCase() const result = await useCase.execute({ recoveryCodes: 'recovery-codes', password: 'foobar', username: 'test@test.te' }) @@ -109,7 +109,7 @@ describe('SignInWithRecoveryCodes', () => { }) it('should fail if key params has unsupported version', async () => { - protocolService.isVersionNewerThanLibraryVersion = jest.fn().mockReturnValue(true) + encryptionService.isVersionNewerThanLibraryVersion = jest.fn().mockReturnValue(true) authManager.recoveryKeyParams = jest.fn().mockReturnValue({ identifier: 'test@test.te', @@ -120,7 +120,7 @@ describe('SignInWithRecoveryCodes', () => { version: '006', }) - protocolService.platformSupportsKeyDerivation = jest.fn().mockReturnValue(false) + encryptionService.platformSupportsKeyDerivation = jest.fn().mockReturnValue(false) const useCase = createUseCase() const result = await useCase.execute({ recoveryCodes: 'recovery-codes', password: 'foobar', username: 'test@test.te' }) @@ -130,7 +130,7 @@ describe('SignInWithRecoveryCodes', () => { }) it('should fail if key params has expired version', async () => { - protocolService.isVersionNewerThanLibraryVersion = jest.fn().mockReturnValue(false) + encryptionService.isVersionNewerThanLibraryVersion = jest.fn().mockReturnValue(false) authManager.recoveryKeyParams = jest.fn().mockReturnValue({ identifier: 'test@test.te', @@ -141,7 +141,7 @@ describe('SignInWithRecoveryCodes', () => { version: '006', }) - protocolService.platformSupportsKeyDerivation = jest.fn().mockReturnValue(false) + encryptionService.platformSupportsKeyDerivation = jest.fn().mockReturnValue(false) const useCase = createUseCase() const result = await useCase.execute({ recoveryCodes: 'recovery-codes', password: 'foobar', username: 'test@test.te' }) diff --git a/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.ts b/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.ts index 1c8c42b4c..6bf973736 100644 --- a/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.ts +++ b/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.ts @@ -20,7 +20,7 @@ import { SignInWithRecoveryCodesDTO } from './SignInWithRecoveryCodesDTO' export class SignInWithRecoveryCodes implements UseCaseInterface { constructor( private authManager: AuthClientInterface, - private protocolService: EncryptionProviderInterface, + private encryptionService: EncryptionProviderInterface, private inMemoryStore: KeyValueStoreInterface, private crypto: PureCryptoInterface, private sessionManager: SessionsClientInterface, @@ -28,7 +28,7 @@ export class SignInWithRecoveryCodes implements UseCaseInterface { ) {} async execute(dto: SignInWithRecoveryCodesDTO): Promise> { - if (this.protocolService.hasAccount()) { + if (this.encryptionService.hasAccount()) { return Result.fail('Tried to sign in when an account already exists.') } @@ -48,19 +48,19 @@ export class SignInWithRecoveryCodes implements UseCaseInterface { const rootKeyParams = CreateAnyKeyParams(recoveryKeyParams) - if (!this.protocolService.supportedVersions().includes(rootKeyParams.version)) { - if (this.protocolService.isVersionNewerThanLibraryVersion(rootKeyParams.version)) { + if (!this.encryptionService.supportedVersions().includes(rootKeyParams.version)) { + if (this.encryptionService.isVersionNewerThanLibraryVersion(rootKeyParams.version)) { return Result.fail(UNSUPPORTED_PROTOCOL_VERSION) } return Result.fail(EXPIRED_PROTOCOL_VERSION) } - if (!this.protocolService.platformSupportsKeyDerivation(rootKeyParams)) { + if (!this.encryptionService.platformSupportsKeyDerivation(rootKeyParams)) { return Result.fail(UNSUPPORTED_KEY_DERIVATION) } - const rootKey = await this.protocolService.computeRootKey(dto.password, rootKeyParams) + const rootKey = await this.encryptionService.computeRootKey(dto.password, rootKeyParams) const signInResult = await this.authManager.signInWithRecoveryCodes({ codeVerifier, diff --git a/packages/snjs/lib/Migrations/Base.ts b/packages/snjs/lib/Migrations/Base.ts index fb3507aef..928d3d3a6 100644 --- a/packages/snjs/lib/Migrations/Base.ts +++ b/packages/snjs/lib/Migrations/Base.ts @@ -207,13 +207,13 @@ export class BaseMigration extends Migration { this.services.challengeService.addChallengeObserver(challenge, { onNonvalidatedSubmit: async (challengeResponse) => { const password = challengeResponse.values[0].value as string - const accountParams = this.services.protocolService.createKeyParams(rawAccountParams) - const rootKey = await this.services.protocolService.computeRootKey(password, accountParams) + const accountParams = this.services.encryptionService.createKeyParams(rawAccountParams) + const rootKey = await this.services.encryptionService.computeRootKey(password, accountParams) /** TS can't detect we returned early above if itemToDecrypt is null */ assert(itemToDecrypt) - const decryptedPayload = await this.services.protocolService.decryptSplitSingle({ + const decryptedPayload = await this.services.encryptionService.decryptSplitSingle({ usesRootKey: { items: [itemToDecrypt], key: rootKey, diff --git a/packages/snjs/lib/Migrations/MigrationServices.ts b/packages/snjs/lib/Migrations/MigrationServices.ts index 352ed2867..e6e7ee364 100644 --- a/packages/snjs/lib/Migrations/MigrationServices.ts +++ b/packages/snjs/lib/Migrations/MigrationServices.ts @@ -13,7 +13,7 @@ import { ChallengeService, SNSingletonManager, SNFeaturesService, DiskStorageSer import { LegacySession, MapperInterface } from '@standardnotes/domain-core' export type MigrationServices = { - protocolService: EncryptionService + encryptionService: EncryptionService deviceInterface: DeviceInterface storageService: DiskStorageService challengeService: ChallengeService diff --git a/packages/snjs/lib/Migrations/Versions/2_0_15.ts b/packages/snjs/lib/Migrations/Versions/2_0_15.ts index a36707957..4cd5f84a8 100644 --- a/packages/snjs/lib/Migrations/Versions/2_0_15.ts +++ b/packages/snjs/lib/Migrations/Versions/2_0_15.ts @@ -14,8 +14,8 @@ export class Migration2_0_15 extends Migration { } private async createNewDefaultItemsKeyIfNecessary() { - if (this.services.protocolService.needsNewRootKeyBasedItemsKey()) { - await this.services.protocolService.createNewDefaultItemsKey() + if (this.services.encryptionService.needsNewRootKeyBasedItemsKey()) { + await this.services.encryptionService.createNewDefaultItemsKey() } } } diff --git a/packages/snjs/lib/Services/Actions/ActionsService.ts b/packages/snjs/lib/Services/Actions/ActionsService.ts index 21d3bca4e..badf32fa4 100644 --- a/packages/snjs/lib/Services/Actions/ActionsService.ts +++ b/packages/snjs/lib/Services/Actions/ActionsService.ts @@ -63,7 +63,7 @@ export class SNActionsService extends AbstractService { public deviceInterface: DeviceInterface, private httpService: DeprecatedHttpService, private payloadManager: PayloadManager, - private protocolService: EncryptionService, + private encryptionService: EncryptionService, private syncService: SNSyncService, private challengeService: ChallengeService, private listedService: ListedService, @@ -81,7 +81,7 @@ export class SNActionsService extends AbstractService { ;(this.payloadManager as unknown) = undefined ;(this.listedService as unknown) = undefined ;(this.challengeService as unknown) = undefined - ;(this.protocolService as unknown) = undefined + ;(this.encryptionService as unknown) = undefined ;(this.syncService as unknown) = undefined this.payloadRequestHandlers.length = 0 this.previousPasswords.length = 0 @@ -207,7 +207,7 @@ export class SNActionsService extends AbstractService { return } - let decryptedPayload = await this.protocolService.decryptSplitSingle({ + let decryptedPayload = await this.encryptionService.decryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, @@ -218,7 +218,7 @@ export class SNActionsService extends AbstractService { } if (rootKey) { - decryptedPayload = await this.protocolService.decryptSplitSingle({ + decryptedPayload = await this.encryptionService.decryptSplitSingle({ usesRootKey: { items: [payload], key: rootKey, @@ -230,7 +230,7 @@ export class SNActionsService extends AbstractService { } for (const itemsKey of this.itemManager.getDisplayableItemsKeys()) { - const decryptedPayload = await this.protocolService.decryptSplitSingle({ + const decryptedPayload = await this.encryptionService.decryptSplitSingle({ usesItemsKey: { items: [payload], key: itemsKey, @@ -256,7 +256,7 @@ export class SNActionsService extends AbstractService { ) return undefined } - const keyParams = this.protocolService.createKeyParams(keyParamsData) + const keyParams = this.encryptionService.createKeyParams(keyParamsData) /* Try previous passwords */ for (const passwordCandidate of this.previousPasswords) { @@ -266,7 +266,7 @@ export class SNActionsService extends AbstractService { triedPasswords.push(passwordCandidate) - const key = await this.protocolService.computeRootKey(passwordCandidate, keyParams) + const key = await this.encryptionService.computeRootKey(passwordCandidate, keyParams) if (!key) { continue } @@ -341,7 +341,7 @@ export class SNActionsService extends AbstractService { return item.payload.ejected() } - const encrypted = await this.protocolService.encryptSplitSingle({ + const encrypted = await this.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [item.payload] }, }) diff --git a/packages/snjs/lib/Services/Challenge/ChallengeService.ts b/packages/snjs/lib/Services/Challenge/ChallengeService.ts index 675a39270..c932ff752 100644 --- a/packages/snjs/lib/Services/Challenge/ChallengeService.ts +++ b/packages/snjs/lib/Services/Challenge/ChallengeService.ts @@ -44,7 +44,7 @@ export class ChallengeService extends AbstractService implements ChallengeServic constructor( private storageService: DiskStorageService, - private protocolService: EncryptionService, + private encryptionService: EncryptionService, protected override internalEventBus: InternalEventBusInterface, ) { super(internalEventBus) @@ -52,7 +52,7 @@ export class ChallengeService extends AbstractService implements ChallengeServic public override deinit() { ;(this.storageService as unknown) = undefined - ;(this.protocolService as unknown) = undefined + ;(this.encryptionService as unknown) = undefined ;(this.sendChallenge as unknown) = undefined ;(this.challengeOperations as unknown) = undefined ;(this.challengeObservers as unknown) = undefined @@ -79,9 +79,9 @@ export class ChallengeService extends AbstractService implements ChallengeServic public async validateChallengeValue(value: ChallengeValue): Promise { switch (value.prompt.validation) { case ChallengeValidation.LocalPasscode: - return this.protocolService.validatePasscode(value.value as string) + return this.encryptionService.validatePasscode(value.value as string) case ChallengeValidation.AccountPassword: - return this.protocolService.validateAccountPassword(value.value as string) + return this.encryptionService.validateAccountPassword(value.value as string) case ChallengeValidation.Biometric: return { valid: value.value === true } case ChallengeValidation.Authenticator: @@ -104,7 +104,7 @@ export class ChallengeService extends AbstractService implements ChallengeServic } async promptForAccountPassword(): Promise { - if (!this.protocolService.hasAccount()) { + if (!this.encryptionService.hasAccount()) { throw Error('Requiring account password for challenge with no account') } @@ -143,7 +143,7 @@ export class ChallengeService extends AbstractService implements ChallengeServic canceled?: undefined } > { - if (!this.protocolService.hasPasscode()) { + if (!this.encryptionService.hasPasscode()) { return {} } @@ -154,12 +154,12 @@ export class ChallengeService extends AbstractService implements ChallengeServic } } - const wrappingKey = await this.protocolService.computeWrappingKey(passcode) + const wrappingKey = await this.encryptionService.computeWrappingKey(passcode) return { wrappingKey } } public isPasscodeLocked() { - return this.protocolService.isPasscodeLocked() + return this.encryptionService.isPasscodeLocked() } public addChallengeObserver(challenge: Challenge, observer: ChallengeObserver): () => void { diff --git a/packages/snjs/lib/Services/KeyRecovery/KeyRecoveryOperation.ts b/packages/snjs/lib/Services/KeyRecovery/KeyRecoveryOperation.ts index 39c8af90a..d1da868fa 100644 --- a/packages/snjs/lib/Services/KeyRecovery/KeyRecoveryOperation.ts +++ b/packages/snjs/lib/Services/KeyRecovery/KeyRecoveryOperation.ts @@ -11,7 +11,7 @@ export class KeyRecoveryOperation { constructor( private queueItem: DecryptionQueueItem, private itemManager: ItemManager, - private protocolService: EncryptionProviderInterface, + private encryptionService: EncryptionProviderInterface, private challengeService: ChallengeServiceInterface, private clientParams: SNRootKeyParams | undefined, private serverParams: SNRootKeyParams | undefined, @@ -43,7 +43,7 @@ export class KeyRecoveryOperation { const decryptionResult = await DecryptItemsKeyByPromptingUser( this.queueItem.encryptedKey, - this.protocolService, + this.encryptionService, this.challengeService, this.queueItem.keyParams, ) diff --git a/packages/snjs/lib/Services/KeyRecovery/KeyRecoveryService.ts b/packages/snjs/lib/Services/KeyRecovery/KeyRecoveryService.ts index 2cb3c5f9d..a41c21a2a 100644 --- a/packages/snjs/lib/Services/KeyRecovery/KeyRecoveryService.ts +++ b/packages/snjs/lib/Services/KeyRecovery/KeyRecoveryService.ts @@ -87,7 +87,7 @@ export class SNKeyRecoveryService extends AbstractService { @@ -279,7 +279,7 @@ export class SNKeyRecoveryService extends AbstractService { - if (!this.protocolService.hasPasscode()) { + if (!this.encryptionService.hasPasscode()) { return undefined } const { wrappingKey, canceled } = await this.challengeService.getWrappingKeyIfApplicable() @@ -312,7 +312,7 @@ export class SNKeyRecoveryService extends AbstractService { describe('protectionService', () => { let mutator: MutatorClientInterface - let protocolService: EncryptionService + let encryptionService: EncryptionService let challengeService: ChallengeService let storageService: DiskStorageService let internalEventBus: InternalEventBusInterface let protectionService: SNProtectionService const createService = () => { - return new SNProtectionService(protocolService, mutator, challengeService, storageService, internalEventBus) + return new SNProtectionService(encryptionService, mutator, challengeService, storageService, internalEventBus) } const createFile = (name: string, isProtected?: boolean) => { @@ -59,9 +59,9 @@ describe('protectionService', () => { storageService = {} as jest.Mocked storageService.getValue = jest.fn() - protocolService = {} as jest.Mocked - protocolService.hasAccount = jest.fn().mockReturnValue(true) - protocolService.hasPasscode = jest.fn().mockReturnValue(false) + encryptionService = {} as jest.Mocked + encryptionService.hasAccount = jest.fn().mockReturnValue(true) + encryptionService.hasPasscode = jest.fn().mockReturnValue(false) mutator = {} as jest.Mocked }) diff --git a/packages/snjs/lib/Services/Protection/ProtectionService.ts b/packages/snjs/lib/Services/Protection/ProtectionService.ts index 5c3e60977..f2c29f4cc 100644 --- a/packages/snjs/lib/Services/Protection/ProtectionService.ts +++ b/packages/snjs/lib/Services/Protection/ProtectionService.ts @@ -76,7 +76,7 @@ export class SNProtectionService extends AbstractService implem private mobileBiometricsTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.OnQuit constructor( - private protocolService: EncryptionService, + private encryptionService: EncryptionService, private mutator: MutatorClientInterface, private challengeService: ChallengeService, private storageService: DiskStorageService, @@ -87,7 +87,7 @@ export class SNProtectionService extends AbstractService implem public override deinit(): void { clearTimeout(this.sessionExpiryTimeout) - ;(this.protocolService as unknown) = undefined + ;(this.encryptionService as unknown) = undefined ;(this.challengeService as unknown) = undefined ;(this.storageService as unknown) = undefined super.deinit() @@ -103,7 +103,7 @@ export class SNProtectionService extends AbstractService implem } public hasProtectionSources(): boolean { - return this.protocolService.hasAccount() || this.protocolService.hasPasscode() || this.hasBiometricsEnabled() + return this.encryptionService.hasAccount() || this.encryptionService.hasPasscode() || this.hasBiometricsEnabled() } public hasUnprotectedAccessSession(): boolean { @@ -148,7 +148,7 @@ export class SNProtectionService extends AbstractService implem if (this.hasBiometricsEnabled()) { prompts.push(new ChallengePrompt(ChallengeValidation.Biometric)) } - if (this.protocolService.hasPasscode()) { + if (this.encryptionService.hasPasscode()) { prompts.push(new ChallengePrompt(ChallengeValidation.LocalPasscode)) } if (prompts.length > 0) { @@ -354,19 +354,19 @@ export class SNProtectionService extends AbstractService implem prompts.push(new ChallengePrompt(ChallengeValidation.Biometric)) } - if (this.protocolService.hasPasscode()) { + if (this.encryptionService.hasPasscode()) { prompts.push(new ChallengePrompt(ChallengeValidation.LocalPasscode)) } if (requireAccountPassword) { - if (!this.protocolService.hasAccount()) { + if (!this.encryptionService.hasAccount()) { throw Error('Requiring account password for challenge with no account') } prompts.push(new ChallengePrompt(ChallengeValidation.AccountPassword)) } if (prompts.length === 0) { - if (fallBackToAccountPassword && this.protocolService.hasAccount()) { + if (fallBackToAccountPassword && this.encryptionService.hasAccount()) { prompts.push(new ChallengePrompt(ChallengeValidation.AccountPassword)) } else { return true diff --git a/packages/snjs/lib/Services/Session/SessionManager.ts b/packages/snjs/lib/Services/Session/SessionManager.ts index e185d98e0..94bd1f09b 100644 --- a/packages/snjs/lib/Services/Session/SessionManager.ts +++ b/packages/snjs/lib/Services/Session/SessionManager.ts @@ -93,7 +93,7 @@ export class SNSessionManager private apiService: SNApiService, private userApiService: UserApiServiceInterface, private alertService: AlertService, - private protocolService: EncryptionService, + private encryptionService: EncryptionService, private challengeService: ChallengeService, private webSocketsService: SNWebSocketsService, private httpService: HttpServiceInterface, @@ -119,7 +119,7 @@ export class SNSessionManager } override deinit(): void { - ;(this.protocolService as unknown) = undefined + ;(this.encryptionService as unknown) = undefined ;(this.diskStorageService as unknown) = undefined ;(this.apiService as unknown) = undefined ;(this.alertService as unknown) = undefined @@ -205,11 +205,11 @@ export class SNSessionManager } public getPublicKey(): string { - return this.protocolService.getKeyPair().publicKey + return this.encryptionService.getKeyPair().publicKey } public getSigningPublicKey(): string { - return this.protocolService.getSigningKeyPair().publicKey + return this.encryptionService.getSigningKeyPair().publicKey } public get userUuid(): string { @@ -285,7 +285,7 @@ export class SNSessionManager onNonvalidatedSubmit: async (challengeResponse) => { const email = challengeResponse.values[0].value as string const password = challengeResponse.values[1].value as string - const currentKeyParams = this.protocolService.getAccountKeyParams() + const currentKeyParams = this.encryptionService.getAccountKeyParams() const { response } = await this.signIn( email, password, @@ -403,7 +403,7 @@ export class SNSessionManager email = cleanedEmailString(email) - const rootKey = await this.protocolService.createRootKey( + const rootKey = await this.encryptionService.createRootKey( email, password, Common.KeyParamsOrigination.Registration, @@ -525,8 +525,8 @@ export class SNSessionManager } } const keyParams = paramsResult.keyParams as SNRootKeyParams - if (!this.protocolService.supportedVersions().includes(keyParams.version)) { - if (this.protocolService.isVersionNewerThanLibraryVersion(keyParams.version)) { + if (!this.encryptionService.supportedVersions().includes(keyParams.version)) { + if (this.encryptionService.isVersionNewerThanLibraryVersion(keyParams.version)) { return { response: this.apiService.createErrorResponse(UNSUPPORTED_PROTOCOL_VERSION), } @@ -539,7 +539,7 @@ export class SNSessionManager if (Common.isProtocolVersionExpired(keyParams.version)) { /* Cost minimums only apply to now outdated versions (001 and 002) */ - const minimum = this.protocolService.costMinimumForVersion(keyParams.version) + const minimum = this.encryptionService.costMinimumForVersion(keyParams.version) if (keyParams.content002.pw_cost < minimum) { return { response: this.apiService.createErrorResponse(INVALID_PASSWORD_COST), @@ -560,14 +560,14 @@ export class SNSessionManager } } - if (!this.protocolService.platformSupportsKeyDerivation(keyParams)) { + if (!this.encryptionService.platformSupportsKeyDerivation(keyParams)) { return { response: this.apiService.createErrorResponse(UNSUPPORTED_KEY_DERIVATION), } } if (strict) { - minAllowedVersion = this.protocolService.getLatestVersion() + minAllowedVersion = this.encryptionService.getLatestVersion() } if (minAllowedVersion != undefined) { @@ -577,7 +577,7 @@ export class SNSessionManager } } } - const rootKey = await this.protocolService.computeRootKey(password, keyParams) + const rootKey = await this.encryptionService.computeRootKey(password, keyParams) const signInResponse = await this.bypassChecksAndSignInWithRootKey(email, rootKey, ephemeral) return { @@ -641,8 +641,8 @@ export class SNSessionManager let oldSigningKeyPair: PkcKeyPair | undefined try { - oldKeyPair = this.protocolService.getKeyPair() - oldSigningKeyPair = this.protocolService.getSigningKeyPair() + oldKeyPair = this.encryptionService.getKeyPair() + oldSigningKeyPair = this.encryptionService.getSigningKeyPair() } catch (error) { void error } @@ -718,7 +718,7 @@ export class SNSessionManager } private decodeDemoShareToken(token: Base64String): ShareToken { - const jsonString = this.protocolService.crypto.base64Decode(token) + const jsonString = this.encryptionService.crypto.base64Decode(token) return JSON.parse(jsonString) } @@ -735,7 +735,7 @@ export class SNSessionManager host: string, wrappingKey?: SNRootKey, ) { - await this.protocolService.setRootKey(rootKey, wrappingKey) + await this.encryptionService.setRootKey(rootKey, wrappingKey) this.memoizeUser(user) this.diskStorageService.setValue(StorageKey.User, user) diff --git a/packages/snjs/lib/Services/Sync/SyncService.ts b/packages/snjs/lib/Services/Sync/SyncService.ts index d6e3319d7..f2360175a 100644 --- a/packages/snjs/lib/Services/Sync/SyncService.ts +++ b/packages/snjs/lib/Services/Sync/SyncService.ts @@ -153,7 +153,7 @@ export class SNSyncService constructor( private itemManager: ItemManager, private sessionManager: SNSessionManager, - private protocolService: EncryptionService, + private encryptionService: EncryptionService, private storageService: DiskStorageService, private payloadManager: PayloadManager, private apiService: SNApiService, @@ -189,7 +189,7 @@ export class SNSyncService this.dealloced = true ;(this.sessionManager as unknown) = undefined ;(this.itemManager as unknown) = undefined - ;(this.protocolService as unknown) = undefined + ;(this.encryptionService as unknown) = undefined ;(this.payloadManager as unknown) = undefined ;(this.storageService as unknown) = undefined ;(this.apiService as unknown) = undefined @@ -250,7 +250,7 @@ export class SNSyncService const encryptionSplit = SplitPayloadsByEncryptionType(encryptedPayloads) const decryptionSplit = CreateDecryptionSplitWithKeyLookup(encryptionSplit) - const newlyDecryptedPayloads = await this.protocolService.decryptSplit(decryptionSplit) + const newlyDecryptedPayloads = await this.encryptionService.decryptSplit(decryptionSplit) await this.payloadManager.emitPayloads( [...alreadyDecryptedPayloads, ...newlyDecryptedPayloads], @@ -369,7 +369,7 @@ export class SNSyncService const encryptionSplit = SplitPayloadsByEncryptionType(encrypted) const decryptionSplit = CreateDecryptionSplitWithKeyLookup(encryptionSplit) - const results = await this.protocolService.decryptSplit(decryptionSplit) + const results = await this.encryptionService.decryptSplit(decryptionSplit) await this.payloadManager.emitPayloads([...nonencrypted, ...results], PayloadEmitSource.LocalDatabaseLoaded) @@ -510,7 +510,7 @@ export class SNSyncService const keyLookupSplit = CreateEncryptionSplitWithKeyLookup(encryptionSplit) - const encryptedResults = await this.protocolService.encryptSplit(keyLookupSplit) + const encryptedResults = await this.encryptionService.encryptSplit(keyLookupSplit) const contextPayloads = [ ...encryptedResults.map(CreateEncryptedServerSyncPushPayload), @@ -1138,7 +1138,7 @@ export class SNSyncService }, } - const results = await this.protocolService.decryptSplit(rootKeySplit) + const results = await this.encryptionService.decryptSplit(rootKeySplit) results.forEach((result) => { if (isDecryptedPayload(result) && result.content_type === ContentType.ItemsKey) { @@ -1168,7 +1168,7 @@ export class SNSyncService }, } - const results = await this.protocolService.decryptSplit(keySystemRootKeySplit) + const results = await this.encryptionService.decryptSplit(keySystemRootKeySplit) results.forEach((result) => { if ( @@ -1213,7 +1213,7 @@ export class SNSyncService } } - return this.protocolService.decryptSplitSingle(keyedSplit) + return this.encryptionService.decryptSplitSingle(keyedSplit) }), ) } @@ -1399,7 +1399,7 @@ export class SNSyncService const keyedSplit = CreateDecryptionSplitWithKeyLookup(encryptionSplit) - const decryptionResults = await this.protocolService.decryptSplit(keyedSplit) + const decryptionResults = await this.encryptionService.decryptSplit(keyedSplit) this.setInSync(false) diff --git a/packages/snjs/mocha/001.test.js b/packages/snjs/mocha/001.test.js index 951c2e68f..1db694631 100644 --- a/packages/snjs/mocha/001.test.js +++ b/packages/snjs/mocha/001.test.js @@ -31,7 +31,7 @@ describe('001 protocol operations', () => { }) it('cost minimum', () => { - expect(application.protocolService.costMinimumForVersion('001')).to.equal(3000) + expect(application.encryptionService.costMinimumForVersion('001')).to.equal(3000) }) it('generates valid keys for registration', async () => { @@ -46,7 +46,7 @@ describe('001 protocol operations', () => { it('generates valid keys from existing params and decrypts', async () => { const password = 'password' - const keyParams = await application.protocolService.createKeyParams({ + const keyParams = await application.encryptionService.createKeyParams({ pw_func: 'pbkdf2', pw_alg: 'sha512', pw_key_size: 512, @@ -67,7 +67,7 @@ describe('001 protocol operations', () => { 'sVuHmG0XAp1PRDE8r8XqFXijjP8Pqdwal9YFRrXK4hKLt1yyq8MwQU+1Z95Tz/b7ajYdidwFE0iDwd8Iu8281VtJsQ4yhh2tJiAzBy6newyHfhA5nH93yZ3iXRJaG87bgNQE9lsXzTV/OHAvqMuQtw/QVSWI3Qy1Pyu1Tn72q7FPKKhRRkzEEZ+Ax0BA1fHg', uuid: '54001a6f-7c22-4b34-8316-fadf9b1fc255', }) - const decrypted = await application.protocolService.decryptSplitSingle({ + const decrypted = await application.encryptionService.decryptSplitSingle({ usesRootKey: { items: [payload], key: key, diff --git a/packages/snjs/mocha/002.test.js b/packages/snjs/mocha/002.test.js index b4b616da8..dba890df0 100644 --- a/packages/snjs/mocha/002.test.js +++ b/packages/snjs/mocha/002.test.js @@ -30,7 +30,7 @@ describe('002 protocol operations', () => { }) it('cost minimum', () => { - expect(application.protocolService.costMinimumForVersion('002')).to.equal(3000) + expect(application.encryptionService.costMinimumForVersion('002')).to.equal(3000) }) it('generates valid keys for registration', async () => { @@ -46,7 +46,7 @@ describe('002 protocol operations', () => { it('generates valid keys from existing params and decrypts', async () => { const password = 'password' - const keyParams = await application.protocolService.createKeyParams({ + const keyParams = await application.encryptionService.createKeyParams({ pw_salt: '8d381ef44cdeab1489194f87066b747b46053a833ee24956e846e7b40440f5f4', pw_cost: 101000, version: '002', @@ -64,7 +64,7 @@ describe('002 protocol operations', () => { '002:24a8e8f7728bbe06605d8209d87ad338d3d15ef81154bb64d3967c77daa01333:959b042a-3892-461e-8c50-477c10c7c40a:f1d294388742dca34f6f266a01483a4e:VdlEDyjhZ35GbJDg8ruSZv3Tp6WtMME3T5LLvcBYLHIMhrMi0RlPK83lK6F0aEaZvY82pZ0ntU+XpAX7JMSEdKdPXsACML7WeFrqKb3z2qHnA7NxgnIC0yVT/Z2mRrvlY3NNrUPGwJbfRcvfS7FVyw87MemT9CSubMZRviXvXETx82t7rsgjV/AIwOOeWhFi', uuid: '959b042a-3892-461e-8c50-477c10c7c40a', }) - const decrypted = await application.protocolService.decryptSplitSingle({ + const decrypted = await application.encryptionService.decryptSplitSingle({ usesRootKey: { items: [payload], key: key, diff --git a/packages/snjs/mocha/003.test.js b/packages/snjs/mocha/003.test.js index 0def8f523..d07aca6cd 100644 --- a/packages/snjs/mocha/003.test.js +++ b/packages/snjs/mocha/003.test.js @@ -39,7 +39,7 @@ describe('003 protocol operations', () => { it('cost minimum should throw', () => { expect(() => { - sharedApplication.protocolService.costMinimumForVersion('003') + sharedApplication.encryptionService.costMinimumForVersion('003') }).to.throw('Cost minimums only apply to versions <= 002') }) @@ -59,7 +59,7 @@ describe('003 protocol operations', () => { it('computes proper keys for sign in', async () => { const identifier = 'foo@bar.com' const password = 'very_secure' - const keyParams = sharedApplication.protocolService.createKeyParams({ + const keyParams = sharedApplication.encryptionService.createKeyParams({ pw_nonce: 'baaec0131d677cf993381367eb082fe377cefe70118c1699cb9b38f0bc850e7b', identifier: identifier, version: '003', @@ -73,7 +73,7 @@ describe('003 protocol operations', () => { it('can decrypt item generated with web version 3.3.6', async () => { const identifier = 'demo@standardnotes.org' const password = 'password' - const keyParams = sharedApplication.protocolService.createKeyParams({ + const keyParams = sharedApplication.encryptionService.createKeyParams({ pw_nonce: '31107837b44d86179140b7c602a55d694243e2e9ced0c4c914ac21ad90215055', identifier: identifier, version: '003', diff --git a/packages/snjs/mocha/004.test.js b/packages/snjs/mocha/004.test.js index 2b08a9041..453dc836e 100644 --- a/packages/snjs/mocha/004.test.js +++ b/packages/snjs/mocha/004.test.js @@ -25,12 +25,12 @@ describe('004 protocol operations', function () { it('cost minimum should throw', function () { expect(function () { - application.protocolService.costMinimumForVersion('004') + application.encryptionService.costMinimumForVersion('004') }).to.throw('Cost minimums only apply to versions <= 002') }) it('generates valid keys for registration', async function () { - const key = await application.protocolService.createRootKey( + const key = await application.encryptionService.createRootKey( _identifier, _password, KeyParamsOrigination.Registration, @@ -51,7 +51,7 @@ describe('004 protocol operations', function () { it('computes proper keys for sign in', async function () { const identifier = 'foo@bar.com' const password = 'very_secure' - const keyParams = application.protocolService.createKeyParams({ + const keyParams = application.encryptionService.createKeyParams({ pw_nonce: 'baaec0131d677cf993381367eb082fe377cefe70118c1699cb9b38f0bc850e7b', identifier: identifier, version: '004', @@ -64,7 +64,7 @@ describe('004 protocol operations', function () { it('generates random key', async function () { const length = 96 - const key = await application.protocolService.crypto.generateRandomKey(length) + const key = await application.encryptionService.crypto.generateRandomKey(length) expect(key.length).to.equal(length / 4) }) @@ -78,7 +78,7 @@ describe('004 protocol operations', function () { }), }) - const operator = application.protocolService.operatorManager.operatorForVersion(ProtocolVersion.V004) + const operator = application.encryptionService.operators.operatorForVersion(ProtocolVersion.V004) const encrypted = await operator.generateEncryptedParameters(payload, rootKey) const decrypted = await operator.generateDecryptedParameters(encrypted, rootKey) @@ -97,7 +97,7 @@ describe('004 protocol operations', function () { }), }) - const operator = application.protocolService.operatorManager.operatorForVersion(ProtocolVersion.V004) + const operator = application.encryptionService.operators.operatorForVersion(ProtocolVersion.V004) const encrypted = await operator.generateEncryptedParameters(payload, rootKey) const decrypted = await operator.generateDecryptedParameters( @@ -112,7 +112,7 @@ describe('004 protocol operations', function () { }) it('generates existing keys for key params', async function () { - const key = await application.protocolService.computeRootKey(_password, rootKeyParams) + const key = await application.encryptionService.computeRootKey(_password, rootKeyParams) expect(key.compare(rootKey)).to.be.true }) diff --git a/packages/snjs/mocha/actions.test.js b/packages/snjs/mocha/actions.test.js index d28f026b3..e8c7487a1 100644 --- a/packages/snjs/mocha/actions.test.js +++ b/packages/snjs/mocha/actions.test.js @@ -25,7 +25,7 @@ describe('actions service', () => { password: this.password, }) - const rootKey = await this.application.protocolService.createRootKey( + const rootKey = await this.application.encryptionService.createRootKey( this.email, this.password, KeyParamsOrigination.Registration, @@ -117,7 +117,7 @@ describe('actions service', () => { }) const encryptedPayload = CreateEncryptedServerSyncPushPayload( - await this.application.protocolService.encryptSplitSingle({ + await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, @@ -359,14 +359,14 @@ describe('actions service', () => { const response = await this.actionsManager.runAction(this.encryptedPostAction, this.noteItem) expect(response.items[0].enc_item_key).to.satisfy((string) => { - return string.startsWith(this.application.protocolService.getLatestVersion()) + return string.startsWith(this.application.encryptionService.getLatestVersion()) }) expect(response.items[0].uuid).to.eq(this.noteItem.uuid) expect(response.items[0].auth_hash).to.not.be.ok expect(response.items[0].content_type).to.be.ok expect(response.items[0].created_at).to.be.ok expect(response.items[0].content).to.satisfy((string) => { - return string.startsWith(this.application.protocolService.getLatestVersion()) + return string.startsWith(this.application.encryptionService.getLatestVersion()) }) }) diff --git a/packages/snjs/mocha/auth.test.js b/packages/snjs/mocha/auth.test.js index 24e2a2fde..921cb2c33 100644 --- a/packages/snjs/mocha/auth.test.js +++ b/packages/snjs/mocha/auth.test.js @@ -31,7 +31,7 @@ describe('basic auth', function () { it('successfully register new account', async function () { const response = await this.application.register(this.email, this.password) expect(response).to.be.ok - expect(await this.application.protocolService.getRootKey()).to.be.ok + expect(await this.application.encryptionService.getRootKey()).to.be.ok }) it('fails register new account with short password', async function () { @@ -49,18 +49,18 @@ describe('basic auth', function () { 'For your security, please choose a longer password or, ideally, a passphrase, and try again.', ) - expect(await this.application.protocolService.getRootKey()).to.not.be.ok + expect(await this.application.encryptionService.getRootKey()).to.not.be.ok }) it('successfully signs out of account', async function () { await this.application.register(this.email, this.password) - expect(await this.application.protocolService.getRootKey()).to.be.ok + expect(await this.application.encryptionService.getRootKey()).to.be.ok this.application = await Factory.signOutApplicationAndReturnNew(this.application) - expect(await this.application.protocolService.getRootKey()).to.not.be.ok - expect(this.application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyNone) + expect(await this.application.encryptionService.getRootKey()).to.not.be.ok + expect(this.application.encryptionService.rootKeyManager.getKeyMode()).to.equal(KeyMode.RootKeyNone) }) it('successfully signs in to registered account', async function () { @@ -69,7 +69,7 @@ describe('basic auth', function () { const response = await this.application.signIn(this.email, this.password, undefined, undefined, undefined, true) expect(response).to.be.ok expect(response.data.error).to.not.be.ok - expect(await this.application.protocolService.getRootKey()).to.be.ok + expect(await this.application.encryptionService.getRootKey()).to.be.ok }).timeout(20000) it('cannot sign while already signed in', async function () { @@ -79,7 +79,7 @@ describe('basic auth', function () { const response = await this.application.signIn(this.email, this.password, undefined, undefined, undefined, true) expect(response).to.be.ok expect(response.data.error).to.not.be.ok - expect(await this.application.protocolService.getRootKey()).to.be.ok + expect(await this.application.encryptionService.getRootKey()).to.be.ok let error try { @@ -99,7 +99,7 @@ describe('basic auth', function () { error = e } expect(error).to.be.ok - expect(await this.application.protocolService.getRootKey()).to.be.ok + expect(await this.application.encryptionService.getRootKey()).to.be.ok }).timeout(20000) it('cannot perform two sign-ins at the same time', async function () { @@ -111,7 +111,7 @@ describe('basic auth', function () { const response = await this.application.signIn(this.email, this.password, undefined, undefined, undefined, true) expect(response).to.be.ok expect(response.data.error).to.not.be.ok - expect(await this.application.protocolService.getRootKey()).to.be.ok + expect(await this.application.encryptionService.getRootKey()).to.be.ok })(), (async () => { /** Make sure the first function runs first */ @@ -134,7 +134,7 @@ describe('basic auth', function () { const response = await this.application.register(this.email, this.password) expect(response).to.be.ok expect(response.error).to.not.be.ok - expect(await this.application.protocolService.getRootKey()).to.be.ok + expect(await this.application.encryptionService.getRootKey()).to.be.ok })(), (async () => { /** Make sure the first function runs first */ @@ -204,7 +204,7 @@ describe('basic auth', function () { const response = await this.application.signIn(uppercase, this.password, undefined, undefined, undefined, true) expect(response).to.be.ok expect(response.data.error).to.not.be.ok - expect(await this.application.protocolService.getRootKey()).to.be.ok + expect(await this.application.encryptionService.getRootKey()).to.be.ok }).timeout(20000) it('can sign into account regardless of whitespace', async function () { @@ -220,7 +220,7 @@ describe('basic auth', function () { const response = await this.application.signIn(withspace, this.password, undefined, undefined, undefined, true) expect(response).to.be.ok expect(response.data.error).to.not.be.ok - expect(await this.application.protocolService.getRootKey()).to.be.ok + expect(await this.application.encryptionService.getRootKey()).to.be.ok }).timeout(20000) it('fails login with wrong password', async function () { @@ -229,7 +229,7 @@ describe('basic auth', function () { const response = await this.application.signIn(this.email, 'wrongpassword', undefined, undefined, undefined, true) expect(response).to.be.ok expect(response.data.error).to.be.ok - expect(await this.application.protocolService.getRootKey()).to.not.be.ok + expect(await this.application.encryptionService.getRootKey()).to.not.be.ok }).timeout(20000) it('fails to change to short password', async function () { @@ -339,7 +339,7 @@ describe('basic auth', function () { expect(signinResponse).to.be.ok expect(signinResponse.data.error).to.not.be.ok - expect(await this.application.protocolService.getRootKey()).to.be.ok + expect(await this.application.encryptionService.getRootKey()).to.be.ok expect(this.application.itemManager.items.length).to.equal(this.expectedItemCount) expect(this.application.payloadManager.invalidPayloads.length).to.equal(0) @@ -421,7 +421,7 @@ describe('basic auth', function () { expect(signinResponse).to.be.ok expect(signinResponse.data.error).to.not.be.ok - expect(await this.application.protocolService.getRootKey()).to.be.ok + expect(await this.application.encryptionService.getRootKey()).to.be.ok } }).timeout(80000) diff --git a/packages/snjs/mocha/backups.test.js b/packages/snjs/mocha/backups.test.js index 4fbc406d7..e63531da6 100644 --- a/packages/snjs/mocha/backups.test.js +++ b/packages/snjs/mocha/backups.test.js @@ -27,10 +27,10 @@ describe('backups', function () { it('backup file should have a version number', async function () { let data = await this.application.createDecryptedBackupFile() - expect(data.version).to.equal(this.application.protocolService.getLatestVersion()) + expect(data.version).to.equal(this.application.encryptionService.getLatestVersion()) await this.application.addPasscode('passcode') data = await this.application.createEncryptedBackupFileForAutomatedDesktopBackups() - expect(data.version).to.equal(this.application.protocolService.getLatestVersion()) + expect(data.version).to.equal(this.application.encryptionService.getLatestVersion()) }) it('no passcode + no account backup file should have correct number of items', async function () { @@ -143,7 +143,7 @@ describe('backups', function () { const note = await Factory.createSyncedNote(this.application) - const encrypted = await this.application.protocolService.encryptSplitSingle({ + const encrypted = await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [note.payload], }, diff --git a/packages/snjs/mocha/device_auth.test.js b/packages/snjs/mocha/device_auth.test.js index 9b8c19d1f..e21cd7fd5 100644 --- a/packages/snjs/mocha/device_auth.test.js +++ b/packages/snjs/mocha/device_auth.test.js @@ -22,7 +22,7 @@ describe('device authentication', function () { await application.addPasscode(passcode) expect(await application.hasPasscode()).to.equal(true) expect(await application.protectionService.createLaunchChallenge()).to.be.ok - expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.WrapperOnly) + expect(application.encryptionService.rootKeyManager.getKeyMode()).to.equal(KeyMode.WrapperOnly) await Factory.safeDeinit(application) /** Recreate application and initialize */ @@ -49,10 +49,10 @@ describe('device authentication', function () { tmpApplication.submitValuesForChallenge(challenge, initialValues) } await tmpApplication.prepareForLaunch({ receiveChallenge }) - expect(await tmpApplication.protocolService.getRootKey()).to.not.be.ok + expect(await tmpApplication.encryptionService.getRootKey()).to.not.be.ok await tmpApplication.launch(true) - expect(await tmpApplication.protocolService.getRootKey()).to.be.ok - expect(tmpApplication.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.WrapperOnly) + expect(await tmpApplication.encryptionService.getRootKey()).to.be.ok + expect(tmpApplication.encryptionService.rootKeyManager.getKeyMode()).to.equal(KeyMode.WrapperOnly) await Factory.safeDeinit(tmpApplication) }).timeout(10000) @@ -65,7 +65,7 @@ describe('device authentication', function () { await application.protections.enableBiometrics() expect(await application.hasPasscode()).to.equal(true) expect((await application.protectionService.createLaunchChallenge()).prompts.length).to.equal(2) - expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.WrapperOnly) + expect(application.encryptionService.rootKeyManager.getKeyMode()).to.equal(KeyMode.WrapperOnly) await Factory.safeDeinit(application) /** Recreate application and initialize */ @@ -98,11 +98,11 @@ describe('device authentication', function () { } await tmpApplication.prepareForLaunch({ receiveChallenge }) - expect(await tmpApplication.protocolService.getRootKey()).to.not.be.ok + expect(await tmpApplication.encryptionService.getRootKey()).to.not.be.ok expect((await tmpApplication.protectionService.createLaunchChallenge()).prompts.length).to.equal(2) await tmpApplication.launch(true) - expect(await tmpApplication.protocolService.getRootKey()).to.be.ok - expect(tmpApplication.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.WrapperOnly) + expect(await tmpApplication.encryptionService.getRootKey()).to.be.ok + expect(tmpApplication.encryptionService.rootKeyManager.getKeyMode()).to.equal(KeyMode.WrapperOnly) await Factory.safeDeinit(tmpApplication) }).timeout(Factory.TwentySecondTimeout) @@ -119,11 +119,11 @@ describe('device authentication', function () { const sampleStorageKey = 'foo' const sampleStorageValue = 'bar' await application.diskStorageService.setValue(sampleStorageKey, sampleStorageValue) - expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyOnly) + expect(application.encryptionService.rootKeyManager.getKeyMode()).to.equal(KeyMode.RootKeyOnly) const passcode = 'foobar' Factory.handlePasswordChallenges(application, password) await application.addPasscode(passcode) - expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyPlusWrapper) + expect(application.encryptionService.rootKeyManager.getKeyMode()).to.equal(KeyMode.RootKeyPlusWrapper) expect(await application.hasPasscode()).to.equal(true) await Factory.safeDeinit(application) @@ -154,11 +154,11 @@ describe('device authentication', function () { await tmpApplication.prepareForLaunch({ receiveChallenge: receiveChallenge, }) - expect(await tmpApplication.protocolService.getRootKey()).to.not.be.ok + expect(await tmpApplication.encryptionService.getRootKey()).to.not.be.ok await tmpApplication.launch(true) expect(await tmpApplication.diskStorageService.getValue(sampleStorageKey)).to.equal(sampleStorageValue) - expect(await tmpApplication.protocolService.getRootKey()).to.be.ok - expect(tmpApplication.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyPlusWrapper) + expect(await tmpApplication.encryptionService.getRootKey()).to.be.ok + expect(tmpApplication.encryptionService.rootKeyManager.getKeyMode()).to.equal(KeyMode.RootKeyPlusWrapper) await Factory.safeDeinit(tmpApplication) }).timeout(Factory.TwentySecondTimeout) }) diff --git a/packages/snjs/mocha/key_recovery_service.test.js b/packages/snjs/mocha/key_recovery_service.test.js index d3db1febe..2d11ff77a 100644 --- a/packages/snjs/mocha/key_recovery_service.test.js +++ b/packages/snjs/mocha/key_recovery_service.test.js @@ -37,21 +37,21 @@ describe('key recovery service', function () { await context.register() - const randomRootKey = await application.protocolService.createRootKey( + const randomRootKey = await application.encryptionService.createRootKey( unassociatedIdentifier, unassociatedPassword, KeyParamsOrigination.Registration, ) - const randomItemsKey = await application.protocolService.operatorManager.defaultOperator().createItemsKey() + const randomItemsKey = await application.encryptionService.operators.defaultOperator().createItemsKey() - const encrypted = await application.protocolService.encryptSplitSingle({ + const encrypted = await application.encryptionService.encryptSplitSingle({ usesRootKey: { items: [randomItemsKey.payload], key: randomRootKey, }, }) - const errored = await application.protocolService.decryptSplitSingle({ + const errored = await application.encryptionService.decryptSplitSingle({ usesRootKeyWithKeyLookup: { items: [encrypted], }, @@ -89,13 +89,13 @@ describe('key recovery service', function () { }) await context.register() - const randomRootKey = await application.protocolService.createRootKey( + const randomRootKey = await application.encryptionService.createRootKey( unassociatedIdentifier, unassociatedPassword, KeyParamsOrigination.Registration, ) - const randomItemsKey = await application.protocolService.operatorManager.defaultOperator().createItemsKey() + const randomItemsKey = await application.encryptionService.operators.defaultOperator().createItemsKey() await application.payloadManager.emitPayload( randomItemsKey.payload.copy({ dirty: true, dirtyIndex: getIncrementedDirtyIndex() }), @@ -106,14 +106,14 @@ describe('key recovery service', function () { const originalSyncTime = application.payloadManager.findOne(randomItemsKey.uuid).lastSyncEnd.getTime() - const encrypted = await application.protocolService.encryptSplitSingle({ + const encrypted = await application.encryptionService.encryptSplitSingle({ usesRootKey: { items: [randomItemsKey.payload], key: randomRootKey, }, }) - const errored = await application.protocolService.decryptSplitSingle({ + const errored = await application.encryptionService.decryptSplitSingle({ usesRootKeyWithKeyLookup: { items: [encrypted], }, @@ -214,15 +214,15 @@ describe('key recovery service', function () { }) /** Create items key associated with a random root key */ - const randomRootKey = await application.protocolService.createRootKey( + const randomRootKey = await application.encryptionService.createRootKey( unassociatedIdentifier, unassociatedPassword, KeyParamsOrigination.Registration, ) - const randomItemsKey = await application.protocolService.operatorManager.defaultOperator().createItemsKey() - const randomItemsKey2 = await application.protocolService.operatorManager.defaultOperator().createItemsKey() + const randomItemsKey = await application.encryptionService.operators.defaultOperator().createItemsKey() + const randomItemsKey2 = await application.encryptionService.operators.defaultOperator().createItemsKey() - const encrypted = await application.protocolService.encryptSplit({ + const encrypted = await application.encryptionService.encryptSplit({ usesRootKey: { items: [randomItemsKey.payload, randomItemsKey2.payload], key: randomRootKey, @@ -230,7 +230,7 @@ describe('key recovery service', function () { }) /** Attempt decryption and insert into rotation in errored state */ - const decrypted = await application.protocolService.decryptSplit({ + const decrypted = await application.encryptionService.decryptSplit({ usesRootKeyWithKeyLookup: { items: encrypted, }, @@ -293,8 +293,8 @@ describe('key recovery service', function () { expect(key.errorDecrypting).to.not.be.ok } - const aKey = await contextA.application.protocolService.getRootKey() - const bKey = await contextB.application.protocolService.getRootKey() + const aKey = await contextA.application.encryptionService.getRootKey() + const bKey = await contextB.application.encryptionService.getRootKey() expect(aKey.compare(bKey)).to.equal(true) expect(contextA.application.items.findItem(note.uuid).errorDecrypting).to.not.be.ok @@ -379,7 +379,7 @@ describe('key recovery service', function () { await application.launch(true) await context.register() - const correctRootKey = await application.protocolService.getRootKey() + const correctRootKey = await application.encryptionService.getRootKey() /** * 1. Change our root key locally so that its keys params doesn't match the server's @@ -390,7 +390,7 @@ describe('key recovery service', function () { const unassociatedIdentifier = 'foorand' /** Create items key associated with a random root key */ - const randomRootKey = await application.protocolService.createRootKey( + const randomRootKey = await application.encryptionService.createRootKey( unassociatedIdentifier, unassociatedPassword, KeyParamsOrigination.Registration, @@ -398,11 +398,11 @@ describe('key recovery service', function () { const signInFunction = sinon.spy(application.keyRecoveryService, 'performServerSignIn') - await application.protocolService.setRootKey(randomRootKey) + await application.encryptionService.setRootKey(randomRootKey) - const correctItemsKey = await application.protocolService.operatorManager.defaultOperator().createItemsKey() + const correctItemsKey = await application.encryptionService.operators.defaultOperator().createItemsKey() - const encrypted = await application.protocolService.encryptSplitSingle({ + const encrypted = await application.encryptionService.encryptSplitSingle({ usesRootKey: { items: [correctItemsKey.payload], key: randomRootKey, @@ -428,7 +428,7 @@ describe('key recovery service', function () { expect(signInFunction.callCount).to.equal(1) - const clientRootKey = await application.protocolService.getRootKey() + const clientRootKey = await application.encryptionService.getRootKey() expect(clientRootKey.compare(correctRootKey)).to.equal(true) const decryptedKey = application.items.findItem(correctItemsKey.uuid) @@ -448,8 +448,8 @@ describe('key recovery service', function () { await context.register() /** Create and emit errored encrypted items key payload */ - const itemsKey = await application.protocolService.getSureDefaultItemsKey() - const encrypted = await application.protocolService.encryptSplitSingle({ + const itemsKey = await application.encryptionService.getSureDefaultItemsKey() + const encrypted = await application.encryptionService.encryptSplitSingle({ usesRootKeyWithKeyLookup: { items: [itemsKey.payload], }, @@ -485,8 +485,8 @@ describe('key recovery service', function () { await context.launch() await context.register() - const itemsKey = await application.protocolService.getSureDefaultItemsKey() - const encrypted = await application.protocolService.encryptSplitSingle({ + const itemsKey = await application.encryptionService.getSureDefaultItemsKey() + const encrypted = await application.encryptionService.encryptSplitSingle({ usesRootKeyWithKeyLookup: { items: [itemsKey.payload], }, @@ -524,8 +524,8 @@ describe('key recovery service', function () { await context.register() /** Create and emit errored encrypted items key payload */ - const itemsKey = await application.protocolService.getSureDefaultItemsKey() - const encrypted = await application.protocolService.encryptSplitSingle({ + const itemsKey = await application.encryptionService.getSureDefaultItemsKey() + const encrypted = await application.encryptionService.encryptSplitSingle({ usesRootKeyWithKeyLookup: { items: [itemsKey.payload], }, @@ -589,17 +589,17 @@ describe('key recovery service', function () { }) /** Create items key associated with a random root key */ - const randomRootKey = await application.protocolService.createRootKey( + const randomRootKey = await application.encryptionService.createRootKey( unassociatedIdentifier, unassociatedPassword, KeyParamsOrigination.Registration, ProtocolVersion.V003, ) - const randomItemsKey = await application.protocolService.operatorManager + const randomItemsKey = await application.encryptionService.operators .operatorForVersion(ProtocolVersion.V003) .createItemsKey() - const encrypted = await application.protocolService.encryptSplitSingle({ + const encrypted = await application.encryptionService.encryptSplitSingle({ usesRootKey: { items: [randomItemsKey.payload], key: randomRootKey, @@ -607,7 +607,7 @@ describe('key recovery service', function () { }) /** Attempt decryption and insert into rotation in errored state */ - const decrypted = await application.protocolService.decryptSplitSingle({ + const decrypted = await application.encryptionService.decryptSplitSingle({ usesRootKeyWithKeyLookup: { items: [encrypted], }, @@ -653,9 +653,9 @@ describe('key recovery service', function () { contextA.password = newPassword await appB.sync.sync() - const newDefaultKey = appB.protocolService.getSureDefaultItemsKey() + const newDefaultKey = appB.encryptionService.getSureDefaultItemsKey() - const encrypted = await appB.protocolService.encryptSplitSingle({ + const encrypted = await appB.encryptionService.encryptSplitSingle({ usesRootKeyWithKeyLookup: { items: [newDefaultKey.payload], }, @@ -676,13 +676,13 @@ describe('key recovery service', function () { const stored = (await appA.deviceInterface.getAllDatabaseEntries(appA.identifier)).find( (payload) => payload.uuid === newDefaultKey.uuid, ) - const storedParams = await appA.protocolService.getKeyEmbeddedKeyParamsFromItemsKey(new EncryptedPayload(stored)) + const storedParams = await appA.encryptionService.getKeyEmbeddedKeyParamsFromItemsKey(new EncryptedPayload(stored)) const correctStored = (await appB.deviceInterface.getAllDatabaseEntries(appB.identifier)).find( (payload) => payload.uuid === newDefaultKey.uuid, ) - const correctParams = await appB.protocolService.getKeyEmbeddedKeyParamsFromItemsKey( + const correctParams = await appB.encryptionService.getKeyEmbeddedKeyParamsFromItemsKey( new EncryptedPayload(correctStored), ) diff --git a/packages/snjs/mocha/keys.test.js b/packages/snjs/mocha/keys.test.js index f9ed45132..510756c1d 100644 --- a/packages/snjs/mocha/keys.test.js +++ b/packages/snjs/mocha/keys.test.js @@ -29,7 +29,7 @@ describe('keys', function () { }) it('should not have root key by default', async function () { - expect(await this.application.protocolService.getRootKey()).to.not.be.ok + expect(await this.application.encryptionService.getRootKey()).to.not.be.ok }) it('validates content types requiring root encryption', function () { @@ -43,7 +43,7 @@ describe('keys', function () { /** Items key available by default */ const payload = Factory.createNotePayload() const processedPayload = CreateEncryptedLocalStorageContextPayload( - await this.application.protocolService.encryptSplitSingle({ + await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, @@ -54,44 +54,44 @@ describe('keys', function () { it('has root key and one items key after registering user', async function () { await Factory.registerUserToApplication({ application: this.application }) - expect(this.application.protocolService.getRootKey()).to.be.ok + expect(this.application.encryptionService.getRootKey()).to.be.ok expect(this.application.itemManager.getDisplayableItemsKeys().length).to.equal(1) }) it('changing root key with passcode should re-wrap root key', async function () { const email = 'foo' const password = 'bar' - const key = await this.application.protocolService.createRootKey(email, password, KeyParamsOrigination.Registration) - await this.application.protocolService.setRootKey(key) + const key = await this.application.encryptionService.createRootKey(email, password, KeyParamsOrigination.Registration) + await this.application.encryptionService.setRootKey(key) Factory.handlePasswordChallenges(this.application, password) await this.application.addPasscode(password) /** We should be able to decrypt wrapped root key with passcode */ - const wrappingKeyParams = await this.application.protocolService.rootKeyEncryption.getRootKeyWrapperKeyParams() - const wrappingKey = await this.application.protocolService.computeRootKey(password, wrappingKeyParams) - await this.application.protocolService.unwrapRootKey(wrappingKey).catch((error) => { + const wrappingKeyParams = await this.application.encryptionService.rootKeyManager.getRootKeyWrapperKeyParams() + const wrappingKey = await this.application.encryptionService.computeRootKey(password, wrappingKeyParams) + await this.application.encryptionService.unwrapRootKey(wrappingKey).catch((error) => { expect(error).to.not.be.ok }) const newPassword = 'bar' - const newKey = await this.application.protocolService.createRootKey( + const newKey = await this.application.encryptionService.createRootKey( email, newPassword, KeyParamsOrigination.Registration, ) - await this.application.protocolService.setRootKey(newKey, wrappingKey) - await this.application.protocolService.unwrapRootKey(wrappingKey).catch((error) => { + await this.application.encryptionService.setRootKey(newKey, wrappingKey) + await this.application.encryptionService.unwrapRootKey(wrappingKey).catch((error) => { expect(error).to.not.be.ok }) }) it('items key should be encrypted with root key', async function () { await Factory.registerUserToApplication({ application: this.application }) - const itemsKey = await this.application.protocolService.getSureDefaultItemsKey() - const rootKey = await this.application.protocolService.getRootKey() + const itemsKey = await this.application.encryptionService.getSureDefaultItemsKey() + const rootKey = await this.application.encryptionService.getRootKey() /** Encrypt items key */ - const encryptedPayload = await this.application.protocolService.encryptSplitSingle({ + const encryptedPayload = await this.application.encryptionService.encryptSplitSingle({ usesRootKey: { items: [itemsKey.payloadRepresentation()], key: rootKey, @@ -102,7 +102,7 @@ describe('keys', function () { expect(encryptedPayload.items_key_id).to.not.be.ok /** Attempt to decrypt with root key. Should succeed. */ - const decryptedPayload = await this.application.protocolService.decryptSplitSingle({ + const decryptedPayload = await this.application.encryptionService.decryptSplitSingle({ usesRootKey: { items: [encryptedPayload], key: rootKey, @@ -142,35 +142,35 @@ describe('keys', function () { it('should use items key for encryption of note', async function () { const notePayload = Factory.createNotePayload() - const keyToUse = await this.application.protocolService.itemsEncryption.keyToUseForItemEncryption(notePayload) + const keyToUse = await this.application.encryptionService.itemsEncryption.keyToUseForItemEncryption(notePayload) expect(keyToUse.content_type).to.equal(ContentType.ItemsKey) }) it('encrypting an item should associate an items key to it', async function () { const note = Factory.createNotePayload() - const encryptedPayload = await this.application.protocolService.encryptSplitSingle({ + const encryptedPayload = await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [note], }, }) - const itemsKey = this.application.protocolService.itemsKeyForEncryptedPayload(encryptedPayload) + const itemsKey = this.application.encryptionService.itemsKeyForEncryptedPayload(encryptedPayload) expect(itemsKey).to.be.ok }) it('decrypt encrypted item with associated key', async function () { const note = Factory.createNotePayload() const title = note.content.title - const encryptedPayload = await this.application.protocolService.encryptSplitSingle({ + const encryptedPayload = await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [note], }, }) - const itemsKey = this.application.protocolService.itemsKeyForEncryptedPayload(encryptedPayload) + const itemsKey = this.application.encryptionService.itemsKeyForEncryptedPayload(encryptedPayload) expect(itemsKey).to.be.ok - const decryptedPayload = await this.application.protocolService.decryptSplitSingle({ + const decryptedPayload = await this.application.encryptionService.decryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [encryptedPayload], }, @@ -182,17 +182,17 @@ describe('keys', function () { it('decrypts items waiting for keys', async function () { const notePayload = Factory.createNotePayload() const title = notePayload.content.title - const encryptedPayload = await this.application.protocolService.encryptSplitSingle({ + const encryptedPayload = await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [notePayload], }, }) - const itemsKey = this.application.protocolService.itemsKeyForEncryptedPayload(encryptedPayload) + const itemsKey = this.application.encryptionService.itemsKeyForEncryptedPayload(encryptedPayload) await this.application.itemManager.removeItemLocally(itemsKey) - const erroredPayload = await this.application.protocolService.decryptSplitSingle({ + const erroredPayload = await this.application.encryptionService.decryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [encryptedPayload], }, @@ -208,7 +208,7 @@ describe('keys', function () { await this.application.mutator.emitItemsFromPayloads([keyPayload], PayloadEmitSource.LocalChanged) /** - * Sleeping is required to trigger asyncronous protocolService.decryptItemsWaitingForKeys, + * Sleeping is required to trigger asyncronous encryptionService.decryptItemsWaitingForKeys, * which occurs after keys are mapped above. */ await Factory.sleep(0.2) @@ -223,7 +223,7 @@ describe('keys', function () { it('attempting to emit errored items key for which there exists a non errored master copy should ignore it', async function () { await Factory.registerUserToApplication({ application: this.application }) - const itemsKey = await this.application.protocolService.getSureDefaultItemsKey() + const itemsKey = await this.application.encryptionService.getSureDefaultItemsKey() expect(itemsKey.errorDecrypting).to.not.be.ok @@ -250,13 +250,13 @@ describe('keys', function () { it('generating export params with logged in account should produce encrypted payload', async function () { await Factory.registerUserToApplication({ application: this.application }) const payload = Factory.createNotePayload() - const encryptedPayload = await this.application.protocolService.encryptSplitSingle({ + const encryptedPayload = await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, }) expect(typeof encryptedPayload.content).to.equal('string') - expect(encryptedPayload.content.substring(0, 3)).to.equal(this.application.protocolService.getLatestVersion()) + expect(encryptedPayload.content.substring(0, 3)).to.equal(this.application.encryptionService.getLatestVersion()) }) it('When setting passcode, should encrypt items keys', async function () { @@ -276,7 +276,7 @@ describe('keys', function () { const itemsKeyPayload = new EncryptedPayload(itemsKeyRawPayload) const authenticatedData = this.context.encryption.getEmbeddedPayloadAuthenticatedData(itemsKeyPayload) - const rootKeyParams = await this.application.protocolService.getRootKeyParams() + const rootKeyParams = await this.application.encryptionService.getRootKeyParams() expect(authenticatedData.kp).to.be.ok expect(authenticatedData.kp).to.eql(rootKeyParams.getPortableValue()) @@ -286,8 +286,8 @@ describe('keys', function () { it('correctly validates local passcode', async function () { const passcode = 'foo' await this.application.addPasscode('foo') - expect((await this.application.protocolService.validatePasscode('wrong')).valid).to.equal(false) - expect((await this.application.protocolService.validatePasscode(passcode)).valid).to.equal(true) + expect((await this.application.encryptionService.validatePasscode('wrong')).valid).to.equal(false) + expect((await this.application.encryptionService.validatePasscode(passcode)).valid).to.equal(true) }) it('signing into 003 account should delete latest offline items key and create 003 items key', async function () { @@ -296,8 +296,8 @@ describe('keys', function () { * Upon signing into an 003 account, the application should delete any neverSynced items keys, * and create a new default items key that is the default for a given protocol version. */ - const defaultItemsKey = await this.application.protocolService.getSureDefaultItemsKey() - const latestVersion = this.application.protocolService.getLatestVersion() + const defaultItemsKey = await this.application.encryptionService.getSureDefaultItemsKey() + const latestVersion = this.application.encryptionService.getLatestVersion() expect(defaultItemsKey.keyVersion).to.equal(latestVersion) /** Register with 003 version */ @@ -312,7 +312,7 @@ describe('keys', function () { expect(itemsKeys.length).to.equal(1) const newestItemsKey = itemsKeys[0] expect(newestItemsKey.keyVersion).to.equal(ProtocolVersion.V003) - const rootKey = await this.application.protocolService.getRootKey() + const rootKey = await this.application.encryptionService.getRootKey() expect(newestItemsKey.itemsKey).to.equal(rootKey.masterKey) expect(newestItemsKey.dataAuthenticationKey).to.equal(rootKey.dataAuthenticationKey) }) @@ -347,12 +347,12 @@ describe('keys', function () { expect(itemsKeys.length).to.equal(1) const originalItemsKey = itemsKeys[0] - const originalRootKey = await this.application.protocolService.getRootKey() + const originalRootKey = await this.application.encryptionService.getRootKey() /** Expect that we can decrypt raw payload with current root key */ const rawPayloads = await this.application.diskStorageService.getAllRawPayloads() const itemsKeyRawPayload = rawPayloads.find((p) => p.uuid === originalItemsKey.uuid) const itemsKeyPayload = new EncryptedPayload(itemsKeyRawPayload) - const decrypted = await this.application.protocolService.decryptSplitSingle({ + const decrypted = await this.application.encryptionService.decryptSplitSingle({ usesRootKey: { items: [itemsKeyPayload], key: originalRootKey, @@ -366,7 +366,7 @@ describe('keys', function () { Factory.handlePasswordChallenges(this.application, passcode) await this.application.changePasscode('bar') - const newRootKey = await this.application.protocolService.getRootKey() + const newRootKey = await this.application.encryptionService.getRootKey() expect(newRootKey).to.not.equal(originalRootKey) expect(newRootKey.masterKey).to.not.equal(originalRootKey.masterKey) @@ -379,7 +379,7 @@ describe('keys', function () { expect(itemsKeyRawPayload2.content).to.not.equal(itemsKeyRawPayload.content) const itemsKeyPayload2 = new EncryptedPayload(itemsKeyRawPayload2) - const decrypted2 = await this.application.protocolService.decryptSplitSingle({ + const decrypted2 = await this.application.encryptionService.decryptSplitSingle({ usesRootKey: { items: [itemsKeyPayload2], key: originalRootKey, @@ -388,7 +388,7 @@ describe('keys', function () { expect(decrypted2.errorDecrypting).to.equal(true) /** Should be able to decrypt with new root key */ - const decrypted3 = await this.application.protocolService.decryptSplitSingle({ + const decrypted3 = await this.application.encryptionService.decryptSplitSingle({ usesRootKey: { items: [itemsKeyPayload2], key: newRootKey, @@ -405,17 +405,17 @@ describe('keys', function () { }) const itemsKeys = this.application.itemManager.getDisplayableItemsKeys() expect(itemsKeys.length).to.equal(1) - const defaultItemsKey = await this.application.protocolService.getSureDefaultItemsKey() + const defaultItemsKey = await this.application.encryptionService.getSureDefaultItemsKey() const result = await this.application.changePassword(this.password, 'foobarfoo') expect(result.error).to.not.be.ok expect(this.application.itemManager.getDisplayableItemsKeys().length).to.equal(2) - const newDefaultItemsKey = await this.application.protocolService.getSureDefaultItemsKey() + const newDefaultItemsKey = await this.application.encryptionService.getSureDefaultItemsKey() expect(newDefaultItemsKey.uuid).to.not.equal(defaultItemsKey.uuid) const note = await Factory.createSyncedNote(this.application) - const payload = await this.application.protocolService.encryptSplitSingle({ + const payload = await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [note.payload], }, @@ -432,18 +432,18 @@ describe('keys', function () { }) const itemsKeys = application.itemManager.getDisplayableItemsKeys() expect(itemsKeys.length).to.equal(1) - const defaultItemsKey = application.protocolService.getSureDefaultItemsKey() + const defaultItemsKey = application.encryptionService.getSureDefaultItemsKey() const newEmail = UuidGenerator.GenerateUuid() const result = await application.changeEmail(newEmail, password) expect(result.error).to.not.be.ok expect(application.itemManager.getDisplayableItemsKeys().length).to.equal(2) - const newDefaultItemsKey = application.protocolService.getSureDefaultItemsKey() + const newDefaultItemsKey = application.encryptionService.getSureDefaultItemsKey() expect(newDefaultItemsKey.uuid).to.not.equal(defaultItemsKey.uuid) const note = await Factory.createSyncedNote(application) - const payload = await application.protocolService.encryptSplitSingle({ + const payload = await application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [note.payload], }, @@ -480,7 +480,7 @@ describe('keys', function () { it('loading the keychain root key should also load its key params', async function () { await Factory.registerUserToApplication({ application: this.application }) - const rootKey = await this.application.protocolService.rootKeyEncryption.getRootKeyFromKeychain() + const rootKey = await this.application.encryptionService.rootKeyManager.getRootKeyFromKeychain() expect(rootKey.keyParams).to.be.ok }) @@ -497,7 +497,7 @@ describe('keys', function () { it('persisted key params should exactly equal in memory rootKey.keyParams', async function () { await Factory.registerUserToApplication({ application: this.application }) - const rootKey = await this.application.protocolService.getRootKey() + const rootKey = await this.application.encryptionService.getRootKey() const rawKeyParams = await this.application.diskStorageService.getValue( StorageKey.RootKeyParams, StorageValueModes.Nonwrapped, @@ -507,7 +507,7 @@ describe('keys', function () { it('key params should have expected values', async function () { await Factory.registerUserToApplication({ application: this.application }) - const keyParamsObject = await this.application.protocolService.getRootKeyParams() + const keyParamsObject = await this.application.encryptionService.getRootKeyParams() const keyParams = keyParamsObject.content expect(keyParams.identifier).to.be.ok expect(keyParams.pw_nonce).to.be.ok @@ -533,7 +533,7 @@ describe('keys', function () { email, password, }) - const keyParamsObject = await this.application.protocolService.getRootKeyParams() + const keyParamsObject = await this.application.encryptionService.getRootKeyParams() const keyParams = keyParamsObject.content expect(keyParams.created).to.be.ok @@ -551,7 +551,7 @@ describe('keys', function () { password: this.password, version: ProtocolVersion.V003, }) - const keyParamsObject = await this.application.protocolService.getRootKeyParams() + const keyParamsObject = await this.application.encryptionService.getRootKeyParams() const keyParams = keyParamsObject.content expect(keyParams.created).to.be.ok @@ -566,12 +566,12 @@ describe('keys', function () { password: this.password, version: ProtocolVersion.V003, }) - expect(await this.application.protocolService.getEncryptionDisplayName()).to.equal('AES-256') + expect(await this.application.encryptionService.getEncryptionDisplayName()).to.equal('AES-256') this.application = await Factory.signOutApplicationAndReturnNew(this.application) /** Register with 004 account */ await this.application.register(this.email + 'new', this.password) - expect(await this.application.protocolService.getEncryptionDisplayName()).to.equal('XChaCha20-Poly1305') + expect(await this.application.encryptionService.getEncryptionDisplayName()).to.equal('XChaCha20-Poly1305') }) it('when launching app with no keychain but data, should present account recovery challenge', async function () { @@ -599,7 +599,7 @@ describe('keys', function () { await recreatedApp.prepareForLaunch({ receiveChallenge }) await recreatedApp.launch(true) - expect(recreatedApp.protocolService.getRootKey()).to.be.ok + expect(recreatedApp.encryptionService.getRootKey()).to.be.ok expect(totalChallenges).to.equal(expectedChallenges) await Factory.safeDeinit(recreatedApp) }) @@ -684,11 +684,11 @@ describe('keys', function () { /** Change password through session manager directly instead of application, * as not to create any items key (to simulate 003 client behavior) */ - const currentRootKey = await oldClient.protocolService.computeRootKey( + const currentRootKey = await oldClient.encryptionService.computeRootKey( this.password, - await oldClient.protocolService.getRootKeyParams(), + await oldClient.encryptionService.getRootKeyParams(), ) - const operator = oldClient.protocolService.operatorManager.operatorForVersion(ProtocolVersion.V003) + const operator = oldClient.encryptionService.operators.operatorForVersion(ProtocolVersion.V003) const newRootKey = await operator.createRootKey(this.email, this.password) Object.defineProperty(oldClient.apiService, 'apiVersion', { get: function () { @@ -734,11 +734,11 @@ describe('keys', function () { /** Change password through session manager directly instead of application, * as not to create any items key (to simulate 003 client behavior) */ - const currentRootKey = await this.application.protocolService.computeRootKey( + const currentRootKey = await this.application.encryptionService.computeRootKey( this.password, - await this.application.protocolService.getRootKeyParams(), + await this.application.encryptionService.getRootKeyParams(), ) - const operator = this.application.protocolService.operatorManager.operatorForVersion(ProtocolVersion.V003) + const operator = this.application.encryptionService.operators.operatorForVersion(ProtocolVersion.V003) const newRootKeyTemplate = await operator.createRootKey(this.email, this.password) const newRootKey = CreateNewRootKey({ ...newRootKeyTemplate.content, @@ -761,7 +761,7 @@ describe('keys', function () { currentServerPassword: currentRootKey.serverPassword, newRootKey, }) - await this.application.protocolService.reencryptApplicableItemsAfterUserRootKeyChange() + await this.application.encryptionService.reencryptApplicableItemsAfterUserRootKeyChange() /** Note: this may result in a deadlock if features_service syncs and results in an error */ await this.application.sync.sync({ awaitAll: true }) @@ -786,7 +786,7 @@ describe('keys', function () { * will have an updated_at value, which tell our protocol service that this key has been * synced before, which sort of "lies" to the protocol service because now it thinks it doesnt * need to create a new items key because one has already been synced with the account. - * The corrective action was to do a final check in protocolService.handleDownloadFirstSyncCompletion + * The corrective action was to do a final check in encryptionService.handleDownloadFirstSyncCompletion * to ensure there exists an items key corresponding to the user's account version. */ const promise = this.context.awaitNextSucessfulSync() @@ -794,7 +794,7 @@ describe('keys', function () { await promise await this.application.itemManager.removeAllItemsFromMemory() - expect(this.application.protocolService.getSureDefaultItemsKey()).to.not.be.ok + expect(this.application.encryptionService.getSureDefaultItemsKey()).to.not.be.ok const protocol003 = new SNProtocolOperator003(new SNWebCrypto()) const key = await protocol003.createItemsKey() @@ -810,14 +810,14 @@ describe('keys', function () { }), ) - const defaultKey = this.application.protocolService.getSureDefaultItemsKey() + const defaultKey = this.application.encryptionService.getSureDefaultItemsKey() expect(defaultKey.keyVersion).to.equal(ProtocolVersion.V003) expect(defaultKey.uuid).to.equal(key.uuid) await Factory.registerUserToApplication({ application: this.application }) const notePayload = Factory.createNotePayload() - expect(await this.application.protocolService.itemsEncryption.keyToUseForItemEncryption(notePayload)).to.be.ok + expect(await this.application.encryptionService.itemsEncryption.keyToUseForItemEncryption(notePayload)).to.be.ok }) it('having unsynced items keys should resync them upon download first sync completion', async function () { @@ -856,7 +856,7 @@ describe('keys', function () { email: this.email, password: this.password, }) - const defaultKeys = otherClient.protocolService.itemsEncryption.getItemsKeys().filter((key) => { + const defaultKeys = otherClient.encryptionService.itemsEncryption.getItemsKeys().filter((key) => { return key.isDefault }) expect(defaultKeys.length).to.equal(1) diff --git a/packages/snjs/mocha/lib/AppContext.js b/packages/snjs/mocha/lib/AppContext.js index e428f0b1e..0b9573022 100644 --- a/packages/snjs/mocha/lib/AppContext.js +++ b/packages/snjs/mocha/lib/AppContext.js @@ -70,7 +70,7 @@ export class AppContext { } get encryption() { - return this.application.protocolService + return this.application.encryptionService } get contacts() { diff --git a/packages/snjs/mocha/lib/factory.js b/packages/snjs/mocha/lib/factory.js index 45e8b8f71..87e5f9311 100644 --- a/packages/snjs/mocha/lib/factory.js +++ b/packages/snjs/mocha/lib/factory.js @@ -112,10 +112,10 @@ export function registerUserToApplication({ application, email, password, epheme } export async function setOldVersionPasscode({ application, passcode, version }) { - const identifier = await application.protocolService.crypto.generateUUID() - const operator = application.protocolService.operatorManager.operatorForVersion(version) + const identifier = await application.encryptionService.crypto.generateUUID() + const operator = application.encryptionService.operators.operatorForVersion(version) const key = await operator.createRootKey(identifier, passcode, KeyParamsOrigination.PasscodeCreate) - await application.protocolService.setNewRootKeyWrapper(key) + await application.encryptionService.setNewRootKeyWrapper(key) await application.userService.rewriteItemsKeys() await application.syncService.sync(syncOptions) } @@ -127,7 +127,7 @@ export async function setOldVersionPasscode({ application, passcode, version }) export async function registerOldUser({ application, email, password, version }) { if (!email) email = Utils.generateUuid() if (!password) password = Utils.generateUuid() - const operator = application.protocolService.operatorManager.operatorForVersion(version) + const operator = application.encryptionService.operators.operatorForVersion(version) const accountKey = await operator.createRootKey(email, password, KeyParamsOrigination.Registration) const response = await application.userApiService.register({ @@ -145,7 +145,7 @@ export async function registerOldUser({ application, email, password, version }) mode: SyncMode.DownloadFirst, ...syncOptions, }) - await application.protocolService.decryptErroredPayloads() + await application.encryptionService.decryptErroredPayloads() } export function createStorageItemPayload(contentType) { diff --git a/packages/snjs/mocha/model_tests/appmodels.test.js b/packages/snjs/mocha/model_tests/appmodels.test.js index ce9688fca..a5da2e936 100644 --- a/packages/snjs/mocha/model_tests/appmodels.test.js +++ b/packages/snjs/mocha/model_tests/appmodels.test.js @@ -282,7 +282,7 @@ describe('app models', () => { const itemsKey = this.application.itemManager.getDisplayableItemsKeys()[0] /** Encrypt item1 and emit as errored so it persists with items_key_id */ - const encrypted = await this.application.protocolService.encryptSplitSingle({ + const encrypted = await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [item1.payload], }, @@ -297,7 +297,7 @@ describe('app models', () => { expect(this.application.payloadManager.findOne(item1.uuid).errorDecrypting).to.equal(true) expect(this.application.payloadManager.findOne(item1.uuid).items_key_id).to.equal(itemsKey.uuid) - sinon.stub(this.application.protocolService.itemsEncryption, 'decryptErroredItemPayloads').callsFake(() => { + sinon.stub(this.application.encryptionService.itemsEncryption, 'decryptErroredItemPayloads').callsFake(() => { // prevent auto decryption }) diff --git a/packages/snjs/mocha/payload_encryption.test.js b/packages/snjs/mocha/payload_encryption.test.js index ad1ae81ce..fd47d3483 100644 --- a/packages/snjs/mocha/payload_encryption.test.js +++ b/packages/snjs/mocha/payload_encryption.test.js @@ -40,7 +40,7 @@ describe('payload encryption', function () { lastSyncBegan: new Date(), }) - const encryptedPayload = await this.application.protocolService.encryptSplitSingle({ + const encryptedPayload = await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [notePayload], }, @@ -114,7 +114,7 @@ describe('payload encryption', function () { it('returns valid encrypted params for syncing', async function () { const payload = Factory.createNotePayload() const encryptedPayload = CreateEncryptedServerSyncPushPayload( - await this.application.protocolService.encryptSplitSingle({ + await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, @@ -126,7 +126,7 @@ describe('payload encryption', function () { expect(encryptedPayload.content_type).to.be.ok expect(encryptedPayload.created_at).to.be.ok expect(encryptedPayload.content).to.satisfy((string) => { - return string.startsWith(this.application.protocolService.getLatestVersion()) + return string.startsWith(this.application.encryptionService.getLatestVersion()) }) }).timeout(5000) @@ -134,7 +134,7 @@ describe('payload encryption', function () { const payload = Factory.createNotePayload() const encryptedPayload = CreateEncryptedLocalStorageContextPayload( - await this.application.protocolService.encryptSplitSingle({ + await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, @@ -150,14 +150,14 @@ describe('payload encryption', function () { expect(encryptedPayload.deleted).to.not.be.ok expect(encryptedPayload.errorDecrypting).to.not.be.ok expect(encryptedPayload.content).to.satisfy((string) => { - return string.startsWith(this.application.protocolService.getLatestVersion()) + return string.startsWith(this.application.encryptionService.getLatestVersion()) }) }) it('omits deleted for export file', async function () { const payload = Factory.createNotePayload() const encryptedPayload = CreateEncryptedBackupFileContextPayload( - await this.application.protocolService.encryptSplitSingle({ + await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, @@ -170,7 +170,7 @@ describe('payload encryption', function () { expect(encryptedPayload.created_at).to.be.ok expect(encryptedPayload.deleted).to.not.be.ok expect(encryptedPayload.content).to.satisfy((string) => { - return string.startsWith(this.application.protocolService.getLatestVersion()) + return string.startsWith(this.application.encryptionService.getLatestVersion()) }) }) diff --git a/packages/snjs/mocha/protocol.test.js b/packages/snjs/mocha/protocol.test.js index 739bf2049..771e563ca 100644 --- a/packages/snjs/mocha/protocol.test.js +++ b/packages/snjs/mocha/protocol.test.js @@ -19,43 +19,43 @@ describe('protocol', function () { }) it('checks version to make sure its 004', function () { - expect(this.application.protocolService.getLatestVersion()).to.equal('004') + expect(this.application.encryptionService.getLatestVersion()).to.equal('004') }) it('checks supported versions to make sure it includes 001, 002, 003, 004', function () { - expect(this.application.protocolService.supportedVersions()).to.eql(['001', '002', '003', '004']) + expect(this.application.encryptionService.supportedVersions()).to.eql(['001', '002', '003', '004']) }) it('platform derivation support', function () { expect( - this.application.protocolService.platformSupportsKeyDerivation({ + this.application.encryptionService.platformSupportsKeyDerivation({ version: '001', }), ).to.equal(true) expect( - this.application.protocolService.platformSupportsKeyDerivation({ + this.application.encryptionService.platformSupportsKeyDerivation({ version: '002', }), ).to.equal(true) expect( - this.application.protocolService.platformSupportsKeyDerivation({ + this.application.encryptionService.platformSupportsKeyDerivation({ version: '003', }), ).to.equal(true) expect( - this.application.protocolService.platformSupportsKeyDerivation({ + this.application.encryptionService.platformSupportsKeyDerivation({ version: '004', }), ).to.equal(true) expect( - this.application.protocolService.platformSupportsKeyDerivation({ + this.application.encryptionService.platformSupportsKeyDerivation({ version: '005', }), ).to.equal(true) }) it('key params versions <= 002 should include pw_cost in portable value', function () { - const keyParams002 = this.application.protocolService.createKeyParams({ + const keyParams002 = this.application.encryptionService.createKeyParams({ version: '002', pw_cost: 5000, }) @@ -63,15 +63,15 @@ describe('protocol', function () { }) it('version comparison of 002 should be older than library version', function () { - expect(this.application.protocolService.isVersionNewerThanLibraryVersion('002')).to.equal(false) + expect(this.application.encryptionService.isVersionNewerThanLibraryVersion('002')).to.equal(false) }) it('version comparison of 005 should be newer than library version', function () { - expect(this.application.protocolService.isVersionNewerThanLibraryVersion('005')).to.equal(true) + expect(this.application.encryptionService.isVersionNewerThanLibraryVersion('005')).to.equal(true) }) it('library version should not be outdated', function () { - var currentVersion = this.application.protocolService.getLatestVersion() + var currentVersion = this.application.encryptionService.getLatestVersion() expect(isProtocolVersionExpired(currentVersion)).to.equal(false) }) @@ -91,7 +91,7 @@ describe('protocol', function () { const payload = Factory.createNotePayload() let error try { - await this.application.protocolService.decryptSplitSingle({ + await this.application.encryptionService.decryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, @@ -106,7 +106,7 @@ describe('protocol', function () { await this.application.addPasscode('123') const payload = Factory.createNotePayload() const result = CreateEncryptedServerSyncPushPayload( - await this.application.protocolService.encryptSplitSingle({ + await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, @@ -121,7 +121,7 @@ describe('protocol', function () { it('encrypted payload for server should include duplicate_of field', async function () { const payload = Factory.createNotePayload('Test') const encryptedPayload = CreateEncryptedServerSyncPushPayload( - await this.application.protocolService.encryptSplitSingle({ + await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, @@ -134,7 +134,7 @@ describe('protocol', function () { it('ejected payload for server should include duplicate_of field', async function () { const payload = Factory.createNotePayload('Test') const encryptedPayload = CreateEncryptedServerSyncPushPayload( - await this.application.protocolService.encryptSplitSingle({ + await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, @@ -147,7 +147,7 @@ describe('protocol', function () { it('encrypted payload for storage should include duplicate_of field', async function () { const payload = Factory.createNotePayload('Test') const encryptedPayload = CreateEncryptedLocalStorageContextPayload( - await this.application.protocolService.encryptSplitSingle({ + await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, @@ -160,7 +160,7 @@ describe('protocol', function () { it('ejected payload for storage should include duplicate_of field', async function () { const payload = Factory.createNotePayload('Test') const encryptedPayload = CreateEncryptedLocalStorageContextPayload( - await this.application.protocolService.encryptSplitSingle({ + await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, @@ -173,7 +173,7 @@ describe('protocol', function () { it('encrypted payload for file should include duplicate_of field', async function () { const payload = Factory.createNotePayload('Test') const encryptedPayload = CreateEncryptedBackupFileContextPayload( - await this.application.protocolService.encryptSplitSingle({ + await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, @@ -186,7 +186,7 @@ describe('protocol', function () { it('ejected payload for file should include duplicate_of field', async function () { const payload = Factory.createNotePayload('Test') const encryptedPayload = CreateEncryptedBackupFileContextPayload( - await this.application.protocolService.encryptSplitSingle({ + await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [payload], }, diff --git a/packages/snjs/mocha/recovery.test.js b/packages/snjs/mocha/recovery.test.js index a8f91dfea..1ea64dc19 100644 --- a/packages/snjs/mocha/recovery.test.js +++ b/packages/snjs/mocha/recovery.test.js @@ -44,7 +44,7 @@ describe('account recovery', function () { application = await context.signout() - expect(await application.protocolService.getRootKey()).to.not.be.ok + expect(await application.encryptionService.getRootKey()).to.not.be.ok await application.signInWithRecoveryCodes.execute({ recoveryCodes: generatedRecoveryCodes.getValue(), @@ -52,7 +52,7 @@ describe('account recovery', function () { password: context.password, }) - expect(await application.protocolService.getRootKey()).to.be.ok + expect(await application.encryptionService.getRootKey()).to.be.ok }) it('should automatically generate new recovery code after recovery sign in', async () => { @@ -96,7 +96,7 @@ describe('account recovery', function () { application = await context.signout() - expect(await application.protocolService.getRootKey()).to.not.be.ok + expect(await application.encryptionService.getRootKey()).to.not.be.ok await application.signInWithRecoveryCodes.execute({ recoveryCodes: generatedRecoveryCodes.getValue(), @@ -104,7 +104,7 @@ describe('account recovery', function () { password: 'foobar', }) - expect(await application.protocolService.getRootKey()).to.not.be.ok + expect(await application.encryptionService.getRootKey()).to.not.be.ok }) it('should not allow to sign in with invalid recovery code', async () => { @@ -112,7 +112,7 @@ describe('account recovery', function () { application = await context.signout() - expect(await application.protocolService.getRootKey()).to.not.be.ok + expect(await application.encryptionService.getRootKey()).to.not.be.ok await application.signInWithRecoveryCodes.execute({ recoveryCodes: 'invalid recovery code', @@ -120,13 +120,13 @@ describe('account recovery', function () { password: context.paswword, }) - expect(await application.protocolService.getRootKey()).to.not.be.ok + expect(await application.encryptionService.getRootKey()).to.not.be.ok }) it('should not allow to sign in with recovery code if user has none', async () => { application = await context.signout() - expect(await application.protocolService.getRootKey()).to.not.be.ok + expect(await application.encryptionService.getRootKey()).to.not.be.ok await application.signInWithRecoveryCodes.execute({ recoveryCodes: 'foo bar', @@ -134,6 +134,6 @@ describe('account recovery', function () { password: context.paswword, }) - expect(await application.protocolService.getRootKey()).to.not.be.ok + expect(await application.encryptionService.getRootKey()).to.not.be.ok }) }) diff --git a/packages/snjs/mocha/session.test.js b/packages/snjs/mocha/session.test.js index e96ffd275..df7f52c70 100644 --- a/packages/snjs/mocha/session.test.js +++ b/packages/snjs/mocha/session.test.js @@ -574,7 +574,7 @@ describe('server session', function () { password: password, }) - const oldRootKey = await appA.protocolService.getRootKey() + const oldRootKey = await appA.encryptionService.getRootKey() /** Set the session as nonsense */ appA.diskStorageService.setValue(StorageKey.Session, { @@ -597,7 +597,7 @@ describe('server session', function () { expect(appA.apiService.session.refreshToken.value).to.not.equal('bar') /** Expect that the session recovery replaces the global root key */ - const newRootKey = await appA.protocolService.getRootKey() + const newRootKey = await appA.encryptionService.getRootKey() expect(oldRootKey).to.not.equal(newRootKey) await Factory.safeDeinit(appA) diff --git a/packages/snjs/mocha/storage.test.js b/packages/snjs/mocha/storage.test.js index 1a02ea5b3..663dabdb2 100644 --- a/packages/snjs/mocha/storage.test.js +++ b/packages/snjs/mocha/storage.test.js @@ -171,7 +171,7 @@ describe('storage manager', function () { password: this.password, ephemeral: true, }) - const accountKey = await this.application.protocolService.getRootKey() + const accountKey = await this.application.encryptionService.getRootKey() expect(await this.application.diskStorageService.canDecryptWithKey(accountKey)).to.equal(true) }) @@ -201,7 +201,7 @@ describe('storage manager', function () { }) /** Should not be wrapped root key yet */ - expect(await this.application.protocolService.rootKeyEncryption.getWrappedRootKey()).to.not.be.ok + expect(await this.application.encryptionService.rootKeyManager.getWrappedRootKey()).to.not.be.ok const passcode = '123' Factory.handlePasswordChallenges(this.application, this.password) @@ -209,15 +209,15 @@ describe('storage manager', function () { await this.application.diskStorageService.setValueAndAwaitPersist('bar', 'foo') /** Root key should now be wrapped */ - expect(await this.application.protocolService.rootKeyEncryption.getWrappedRootKey()).to.be.ok + expect(await this.application.encryptionService.rootKeyManager.getWrappedRootKey()).to.be.ok - const accountKey = await this.application.protocolService.getRootKey() + const accountKey = await this.application.encryptionService.getRootKey() expect(await this.application.diskStorageService.canDecryptWithKey(accountKey)).to.equal(true) - const passcodeKey = await this.application.protocolService.computeWrappingKey(passcode) - const wrappedRootKey = await this.application.protocolService.rootKeyEncryption.getWrappedRootKey() + const passcodeKey = await this.application.encryptionService.computeWrappingKey(passcode) + const wrappedRootKey = await this.application.encryptionService.rootKeyManager.getWrappedRootKey() /** Expect that we can decrypt wrapped root key with passcode key */ const payload = new EncryptedPayload(wrappedRootKey) - const decrypted = await this.application.protocolService.decryptSplitSingle({ + const decrypted = await this.application.encryptionService.decryptSplitSingle({ usesRootKey: { items: [payload], key: passcodeKey, diff --git a/packages/snjs/mocha/sync_tests/offline.test.js b/packages/snjs/mocha/sync_tests/offline.test.js index c5ca32cea..778886cfb 100644 --- a/packages/snjs/mocha/sync_tests/offline.test.js +++ b/packages/snjs/mocha/sync_tests/offline.test.js @@ -90,7 +90,7 @@ describe('offline syncing', () => { const payload = rawPayloads2[0] expect(typeof payload.content).to.equal('string') - expect(payload.content.startsWith(this.application.protocolService.getLatestVersion())).to.equal(true) + expect(payload.content.startsWith(this.application.encryptionService.getLatestVersion())).to.equal(true) }) it('signing out while offline should succeed', async function () { diff --git a/packages/snjs/mocha/sync_tests/online.test.js b/packages/snjs/mocha/sync_tests/online.test.js index 2d902f5cf..39699132f 100644 --- a/packages/snjs/mocha/sync_tests/online.test.js +++ b/packages/snjs/mocha/sync_tests/online.test.js @@ -277,7 +277,7 @@ describe('online syncing', function () { await this.application.syncService.sync(syncOptions) const encrypted = CreateEncryptedServerSyncPushPayload( - await this.application.protocolService.encryptSplitSingle({ + await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [note.payloadRepresentation()], }, @@ -295,7 +295,7 @@ describe('online syncing', function () { expect(typeof mappedItem.content).to.equal('string') - const decryptedPayload = await this.application.protocolService.decryptSplitSingle({ + const decryptedPayload = await this.application.encryptionService.decryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [errorred], }, @@ -515,7 +515,7 @@ describe('online syncing', function () { const keyedSplit = CreateDecryptionSplitWithKeyLookup(encryptionSplit) - const decryptionResults = await this.application.protocolService.decryptSplit(keyedSplit) + const decryptionResults = await this.application.encryptionService.decryptSplit(keyedSplit) await this.application.mutator.emitItemsFromPayloads(decryptionResults, PayloadEmitSource.LocalChanged) @@ -918,7 +918,7 @@ describe('online syncing', function () { const lastSyncBegan = note.lastSyncBegan const lastSyncEnd = note.lastSyncEnd - const encrypted = await this.application.protocolService.encryptSplitSingle({ + const encrypted = await this.application.encryptionService.encryptSplitSingle({ usesItemsKeyWithKeyLookup: { items: [note.payload], }, diff --git a/packages/snjs/mocha/upgrading.test.js b/packages/snjs/mocha/upgrading.test.js index b7c6fc059..9b31f8fce 100644 --- a/packages/snjs/mocha/upgrading.test.js +++ b/packages/snjs/mocha/upgrading.test.js @@ -92,11 +92,11 @@ describe('upgrading', () => { version: oldVersion, }) - expect((await this.application.protocolService.rootKeyEncryption.getRootKeyWrapperKeyParams()).version).to.equal( + expect((await this.application.encryptionService.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal( oldVersion, ) - expect((await this.application.protocolService.getRootKeyParams()).version).to.equal(oldVersion) - expect((await this.application.protocolService.getRootKey()).keyVersion).to.equal(oldVersion) + expect((await this.application.encryptionService.getRootKeyParams()).version).to.equal(oldVersion) + expect((await this.application.encryptionService.getRootKey()).keyVersion).to.equal(oldVersion) this.application.setLaunchCallback({ receiveChallenge: this.receiveChallenge, @@ -104,15 +104,15 @@ describe('upgrading', () => { const result = await this.application.upgradeProtocolVersion() expect(result).to.deep.equal({ success: true }) - const wrappedRootKey = await this.application.protocolService.rootKeyEncryption.getWrappedRootKey() + const wrappedRootKey = await this.application.encryptionService.rootKeyManager.getWrappedRootKey() const payload = new EncryptedPayload(wrappedRootKey) expect(payload.version).to.equal(newVersion) - expect((await this.application.protocolService.rootKeyEncryption.getRootKeyWrapperKeyParams()).version).to.equal( + expect((await this.application.encryptionService.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal( newVersion, ) - expect((await this.application.protocolService.getRootKeyParams()).version).to.equal(newVersion) - expect((await this.application.protocolService.getRootKey()).keyVersion).to.equal(newVersion) + expect((await this.application.encryptionService.getRootKeyParams()).version).to.equal(newVersion) + expect((await this.application.encryptionService.getRootKey()).keyVersion).to.equal(newVersion) /** * Immediately logging out ensures we don't rely on subsequent @@ -172,7 +172,7 @@ describe('upgrading', () => { it('protocol version should be upgraded on password change', async function () { /** Delete default items key that is created on launch */ - const itemsKey = await this.application.protocolService.getSureDefaultItemsKey() + const itemsKey = await this.application.encryptionService.getSureDefaultItemsKey() await this.application.mutator.setItemToBeDeleted(itemsKey) expect(Uuids(this.application.itemManager.getDisplayableItemsKeys()).includes(itemsKey.uuid)).to.equal(false) @@ -188,8 +188,8 @@ describe('upgrading', () => { expect(this.application.itemManager.getDisplayableItemsKeys().length).to.equal(1) - expect((await this.application.protocolService.getRootKeyParams()).version).to.equal(ProtocolVersion.V003) - expect((await this.application.protocolService.getRootKey()).keyVersion).to.equal(ProtocolVersion.V003) + expect((await this.application.encryptionService.getRootKeyParams()).version).to.equal(ProtocolVersion.V003) + expect((await this.application.encryptionService.getRootKey()).keyVersion).to.equal(ProtocolVersion.V003) /** Ensure note is encrypted with 003 */ const notePayloads = await Factory.getStoragePayloadsOfType(this.application, ContentType.Note) @@ -199,11 +199,11 @@ describe('upgrading', () => { const { error } = await this.application.changePassword(this.password, 'foobarfoo') expect(error).to.not.exist - const latestVersion = this.application.protocolService.getLatestVersion() - expect((await this.application.protocolService.getRootKeyParams()).version).to.equal(latestVersion) - expect((await this.application.protocolService.getRootKey()).keyVersion).to.equal(latestVersion) + const latestVersion = this.application.encryptionService.getLatestVersion() + expect((await this.application.encryptionService.getRootKeyParams()).version).to.equal(latestVersion) + expect((await this.application.encryptionService.getRootKey()).keyVersion).to.equal(latestVersion) - const defaultItemsKey = await this.application.protocolService.getSureDefaultItemsKey() + const defaultItemsKey = await this.application.encryptionService.getSureDefaultItemsKey() expect(defaultItemsKey.keyVersion).to.equal(latestVersion) /** After change, note should now be encrypted with latest protocol version */ @@ -247,19 +247,19 @@ describe('upgrading', () => { this.application.setLaunchCallback({ receiveChallenge: this.receiveChallenge, }) - expect((await this.application.protocolService.rootKeyEncryption.getRootKeyWrapperKeyParams()).version).to.equal( + expect((await this.application.encryptionService.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal( oldVersion, ) const errors = await this.application.upgradeProtocolVersion() expect(errors).to.not.be.empty /** Ensure we're still on 003 */ - expect((await this.application.protocolService.rootKeyEncryption.getRootKeyWrapperKeyParams()).version).to.equal( + expect((await this.application.encryptionService.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal( oldVersion, ) - expect((await this.application.protocolService.getRootKeyParams()).version).to.equal(oldVersion) - expect((await this.application.protocolService.getRootKey()).keyVersion).to.equal(oldVersion) - expect((await this.application.protocolService.getSureDefaultItemsKey()).keyVersion).to.equal(oldVersion) + expect((await this.application.encryptionService.getRootKeyParams()).version).to.equal(oldVersion) + expect((await this.application.encryptionService.getRootKey()).keyVersion).to.equal(oldVersion) + expect((await this.application.encryptionService.getSureDefaultItemsKey()).keyVersion).to.equal(oldVersion) }) it('rolls back the local protocol upgrade if the server responds with an error', async function () { @@ -268,19 +268,19 @@ describe('upgrading', () => { this.application.setLaunchCallback({ receiveChallenge: this.receiveChallenge, }) - expect((await this.application.protocolService.rootKeyEncryption.getRootKeyWrapperKeyParams()).version).to.equal( + expect((await this.application.encryptionService.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal( oldVersion, ) const errors = await this.application.upgradeProtocolVersion() expect(errors).to.not.be.empty /** Ensure we're still on 003 */ - expect((await this.application.protocolService.rootKeyEncryption.getRootKeyWrapperKeyParams()).version).to.equal( + expect((await this.application.encryptionService.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal( oldVersion, ) - expect((await this.application.protocolService.getRootKeyParams()).version).to.equal(oldVersion) - expect((await this.application.protocolService.getRootKey()).keyVersion).to.equal(oldVersion) - expect((await this.application.protocolService.getSureDefaultItemsKey()).keyVersion).to.equal(oldVersion) + expect((await this.application.encryptionService.getRootKeyParams()).version).to.equal(oldVersion) + expect((await this.application.encryptionService.getRootKey()).keyVersion).to.equal(oldVersion) + expect((await this.application.encryptionService.getSureDefaultItemsKey()).keyVersion).to.equal(oldVersion) }) }) }) diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Backups/DataBackups.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Backups/DataBackups.tsx index 806825a11..171ca464d 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Backups/DataBackups.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Backups/DataBackups.tsx @@ -146,7 +146,7 @@ const DataBackups = ({ application, viewControllerManager }: Props) => { return } - if (application.protocolService.supportedVersions().includes(version)) { + if (application.encryptionService.supportedVersions().includes(version)) { await performImport(data) } else { setIsImportDataLoading(false)