refactor: application dependency management (#2363)

This commit is contained in:
Mo
2023-07-23 15:54:31 -05:00
committed by GitHub
parent e698b1c990
commit a77535456c
299 changed files with 7415 additions and 4890 deletions

View File

@@ -0,0 +1,121 @@
import {
AsymmetricSignatureVerificationDetachedResult,
SNRootKeyParams,
KeyedDecryptionSplit,
KeyedEncryptionSplit,
ItemAuthenticatedData,
AsymmetricallyEncryptedString,
} from '@standardnotes/encryption'
import { KeyParamsOrigination, ProtocolVersion } from '@standardnotes/common'
import {
BackupFile,
DecryptedPayloadInterface,
EncryptedPayloadInterface,
ItemContent,
ItemsKeyInterface,
RootKeyInterface,
KeySystemIdentifier,
KeySystemItemsKeyInterface,
KeySystemRootKeyInterface,
KeySystemRootKeyParamsInterface,
PortablePublicKeySet,
} from '@standardnotes/models'
import { PkcKeyPair } from '@standardnotes/sncrypto-common'
export interface EncryptionProviderInterface {
initialize(): Promise<void>
encryptSplitSingle(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface>
encryptSplit(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface[]>
decryptSplitSingle<
C extends ItemContent = ItemContent,
P extends DecryptedPayloadInterface<C> = DecryptedPayloadInterface<C>,
>(
split: KeyedDecryptionSplit,
): Promise<P | EncryptedPayloadInterface>
decryptSplit<
C extends ItemContent = ItemContent,
P extends DecryptedPayloadInterface<C> = DecryptedPayloadInterface<C>,
>(
split: KeyedDecryptionSplit,
): Promise<(P | EncryptedPayloadInterface)[]>
getEmbeddedPayloadAuthenticatedData<D extends ItemAuthenticatedData>(
payload: EncryptedPayloadInterface,
): D | undefined
getKeyEmbeddedKeyParamsFromItemsKey(key: EncryptedPayloadInterface): SNRootKeyParams | undefined
supportedVersions(): ProtocolVersion[]
isVersionNewerThanLibraryVersion(version: ProtocolVersion): boolean
platformSupportsKeyDerivation(keyParams: SNRootKeyParams): boolean
getPasswordCreatedDate(): Date | undefined
getEncryptionDisplayName(): Promise<string>
upgradeAvailable(): Promise<boolean>
createEncryptedBackupFile(): Promise<BackupFile>
createDecryptedBackupFile(): BackupFile
getUserVersion(): ProtocolVersion | undefined
hasAccount(): boolean
hasPasscode(): boolean
removePasscode(): Promise<void>
validateAccountPassword(password: string): Promise<
| {
valid: true
artifacts: {
rootKey: RootKeyInterface
}
}
| {
valid: boolean
}
>
decryptErroredPayloads(): Promise<void>
deleteWorkspaceSpecificKeyStateFromDevice(): Promise<void>
unwrapRootKey(wrappingKey: RootKeyInterface): Promise<void>
computeRootKey(password: string, keyParams: SNRootKeyParams): Promise<RootKeyInterface>
computeWrappingKey(passcode: string): Promise<RootKeyInterface>
hasRootKeyEncryptionSource(): boolean
createRootKey<K extends RootKeyInterface>(
identifier: string,
password: string,
origination: KeyParamsOrigination,
version?: ProtocolVersion,
): Promise<K>
getRootKeyParams(): SNRootKeyParams | undefined
setNewRootKeyWrapper(wrappingKey: RootKeyInterface): Promise<void>
createNewItemsKeyWithRollback(): Promise<() => Promise<void>>
reencryptApplicableItemsAfterUserRootKeyChange(): Promise<void>
getSureDefaultItemsKey(): ItemsKeyInterface
createRandomizedKeySystemRootKey(dto: { systemIdentifier: KeySystemIdentifier }): KeySystemRootKeyInterface
createUserInputtedKeySystemRootKey(dto: {
systemIdentifier: KeySystemIdentifier
userInputtedPassword: string
}): KeySystemRootKeyInterface
deriveUserInputtedKeySystemRootKey(dto: {
keyParams: KeySystemRootKeyParamsInterface
userInputtedPassword: string
}): KeySystemRootKeyInterface
createKeySystemItemsKey(
uuid: string,
keySystemIdentifier: KeySystemIdentifier,
sharedVaultUuid: string | undefined,
rootKeyToken: string,
): KeySystemItemsKeyInterface
getKeyPair(): PkcKeyPair
getSigningKeyPair(): PkcKeyPair
asymmetricSignatureVerifyDetached(
encryptedString: AsymmetricallyEncryptedString,
): AsymmetricSignatureVerificationDetachedResult
getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: string): PortablePublicKeySet
}

View File

@@ -1,3 +1,4 @@
import { FindDefaultItemsKey } from './UseCase/ItemsKey/FindDefaultItemsKey'
import { InternalEventInterface } from './../Internal/InternalEventInterface'
import { InternalEventHandlerInterface } from './../Internal/InternalEventHandlerInterface'
import { MutatorClientInterface } from './../Mutator/MutatorClientInterface'
@@ -5,27 +6,22 @@ import {
CreateAnyKeyParams,
CreateEncryptionSplitWithKeyLookup,
encryptedInputParametersFromPayload,
EncryptionProviderInterface,
ErrorDecryptingParameters,
findDefaultItemsKey,
FindPayloadInDecryptionSplit,
FindPayloadInEncryptionSplit,
isErrorDecryptingParameters,
ItemAuthenticatedData,
KeyedDecryptionSplit,
KeyedEncryptionSplit,
KeyMode,
LegacyAttachedData,
OperatorManager,
RootKeyEncryptedAuthenticatedData,
SplitPayloadsByEncryptionType,
V001Algorithm,
V002Algorithm,
PublicKeySet,
EncryptedOutputParameters,
KeySystemKeyManagerInterface,
AsymmetricSignatureVerificationDetachedResult,
AsymmetricallyEncryptedString,
EncryptionOperatorsInterface,
} from '@standardnotes/encryption'
import {
BackupFile,
@@ -42,13 +38,11 @@ import {
RootKeyInterface,
KeySystemItemsKeyInterface,
KeySystemIdentifier,
AsymmetricMessagePayload,
KeySystemRootKeyInterface,
KeySystemRootKeyParamsInterface,
TrustedContactInterface,
PortablePublicKeySet,
RootKeyParamsInterface,
} from '@standardnotes/models'
import { ClientDisplayableError } from '@standardnotes/responses'
import { PkcKeyPair, PureCryptoInterface } from '@standardnotes/sncrypto-common'
import {
extendArray,
@@ -60,7 +54,6 @@ import {
} from '@standardnotes/utils'
import {
AnyKeyParamsContent,
ApplicationIdentifier,
compareVersions,
isVersionLessThanOrEqualTo,
KeyParamsOrigination,
@@ -70,28 +63,27 @@ import {
} from '@standardnotes/common'
import { AbstractService } from '../Service/AbstractService'
import { ItemsEncryptionService } from './ItemsEncryption'
import { ItemsEncryptionService } from '../ItemsEncryption/ItemsEncryption'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { PayloadManagerInterface } from '../Payloads/PayloadManagerInterface'
import { DeviceInterface } from '../Device/DeviceInterface'
import { StorageServiceInterface } from '../Storage/StorageServiceInterface'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { SyncEvent } from '../Event/SyncEvent'
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'
import { RootKeyManager } from '../RootKeyManager/RootKeyManager'
import { RootKeyManagerEvent } from '../RootKeyManager/RootKeyManagerEvent'
import { CreateNewItemsKeyWithRollback } from './UseCase/ItemsKey/CreateNewItemsKeyWithRollback'
import { DecryptErroredTypeAPayloads } from './UseCase/TypeA/DecryptErroredPayloads'
import { CreateNewDefaultItemsKey } from './UseCase/ItemsKey/CreateNewDefaultItemsKey'
import { DecryptTypeAPayload } from './UseCase/TypeA/DecryptPayload'
import { DecryptTypeAPayloadWithKeyLookup } from './UseCase/TypeA/DecryptPayloadWithKeyLookup'
import { EncryptTypeAPayloadWithKeyLookup } from './UseCase/TypeA/EncryptPayloadWithKeyLookup'
import { EncryptTypeAPayload } from './UseCase/TypeA/EncryptPayload'
import { ValidateAccountPasswordResult } from '../RootKeyManager/ValidateAccountPasswordResult'
import { ValidatePasscodeResult } from '../RootKeyManager/ValidatePasscodeResult'
import { ContentType } from '@standardnotes/domain-core'
import { EncryptionProviderInterface } from './EncryptionProviderInterface'
import { KeyMode } from '../RootKeyManager/KeyMode'
/**
* The encryption service is responsible for the encryption and decryption of payloads, and
@@ -124,40 +116,28 @@ export class EncryptionService
extends AbstractService<EncryptionServiceEvent>
implements EncryptionProviderInterface, InternalEventHandlerInterface
{
private operators: OperatorManager
private readonly itemsEncryption: ItemsEncryptionService
private readonly rootKeyManager: RootKeyManager
constructor(
private items: ItemManagerInterface,
private mutator: MutatorClientInterface,
private payloads: PayloadManagerInterface,
public device: DeviceInterface,
private storage: StorageServiceInterface,
public readonly keys: KeySystemKeyManagerInterface,
identifier: ApplicationIdentifier,
public crypto: PureCryptoInterface,
private operators: EncryptionOperatorsInterface,
private itemsEncryption: ItemsEncryptionService,
private rootKeyManager: RootKeyManager,
private crypto: PureCryptoInterface,
private _createNewItemsKeyWithRollback: CreateNewItemsKeyWithRollback,
private _findDefaultItemsKey: FindDefaultItemsKey,
private _decryptErroredRootPayloads: DecryptErroredTypeAPayloads,
private _rootKeyEncryptPayloadWithKeyLookup: EncryptTypeAPayloadWithKeyLookup,
private _rootKeyEncryptPayload: EncryptTypeAPayload,
private _rootKeyDecryptPayload: DecryptTypeAPayload,
private _rootKeyDecryptPayloadWithKeyLookup: DecryptTypeAPayloadWithKeyLookup,
private _createDefaultItemsKey: CreateNewDefaultItemsKey,
protected override internalEventBus: InternalEventBusInterface,
) {
super(internalEventBus)
this.crypto = crypto
this.operators = new OperatorManager(crypto)
this.rootKeyManager = new RootKeyManager(
device,
storage,
items,
mutator,
this.operators,
identifier,
internalEventBus,
)
internalEventBus.addEventHandler(this, RootKeyManagerEvent.RootKeyManagerKeyStatusChanged)
this.itemsEncryption = new ItemsEncryptionService(items, payloads, storage, this.operators, keys, internalEventBus)
UuidGenerator.SetGenerator(this.crypto.generateUUID)
}
@@ -168,25 +148,21 @@ export class EncryptionService
}
}
public override async blockDeinit(): Promise<void> {
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.rootKeyManager.deinit()
;(this.rootKeyManager as unknown) = undefined
;(this.crypto as unknown) = undefined
;(this._createNewItemsKeyWithRollback as unknown) = undefined
;(this._findDefaultItemsKey as unknown) = undefined
;(this._decryptErroredRootPayloads as unknown) = undefined
;(this._rootKeyEncryptPayloadWithKeyLookup as unknown) = undefined
;(this._rootKeyEncryptPayload as unknown) = undefined
;(this._rootKeyDecryptPayload as unknown) = undefined
;(this._rootKeyDecryptPayloadWithKeyLookup as unknown) = undefined
;(this._createDefaultItemsKey as unknown) = undefined
super.deinit()
}
@@ -217,7 +193,7 @@ export class EncryptionService
return !!this.getRootKey()?.signingKeyPair
}
public async initialize() {
public async initialize(): Promise<void> {
await this.rootKeyManager.initialize()
}
@@ -250,7 +226,7 @@ export class EncryptionService
return this.rootKeyManager.getUserVersion()
}
public async upgradeAvailable() {
public async upgradeAvailable(): Promise<boolean> {
const accountUpgradeAvailable = this.accountUpgradeAvailable()
const passcodeUpgradeAvailable = await this.passcodeUpgradeAvailable()
return accountUpgradeAvailable || passcodeUpgradeAvailable
@@ -268,31 +244,12 @@ export class EncryptionService
await this.rootKeyManager.reencryptApplicableItemsAfterUserRootKeyChange()
}
/**
* 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<void> {
const keySystemItemsKeys = this.keys.getKeySystemItemsKeys(keySystemIdentifier)
if (keySystemItemsKeys.length > 0) {
await this.mutator.setItemsDirty(keySystemItemsKeys)
}
}
public async createNewItemsKeyWithRollback(): Promise<() => Promise<void>> {
const usecase = new CreateNewItemsKeyWithRollbackUseCase(
this.mutator,
this.items,
this.storage,
this.operators,
this.rootKeyManager,
)
return usecase.execute()
return this._createNewItemsKeyWithRollback.execute()
}
public async decryptErroredPayloads(): Promise<void> {
const usecase = new DecryptErroredRootPayloadsUseCase(this.payloads, this.operators, this.keys, this.rootKeyManager)
await usecase.execute()
await this._decryptErroredRootPayloads.execute()
await this.itemsEncryption.decryptErroredItemPayloads()
}
@@ -326,18 +283,10 @@ export class EncryptionService
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 rootKeyEncryptUsecase.executeMany(
const rootKeyEncrypted = await this._rootKeyEncryptPayload.executeMany(
usesRootKey.items,
usesRootKey.key,
signingKeyPair,
@@ -346,7 +295,7 @@ export class EncryptionService
}
if (usesRootKeyWithKeyLookup) {
const rootKeyEncrypted = await rootKeyEncryptWithKeyLookupUsecase.executeMany(
const rootKeyEncrypted = await this._rootKeyEncryptPayloadWithKeyLookup.executeMany(
usesRootKeyWithKeyLookup.items,
signingKeyPair,
)
@@ -354,7 +303,7 @@ export class EncryptionService
}
if (usesKeySystemRootKey) {
const keySystemRootKeyEncrypted = await rootKeyEncryptUsecase.executeMany(
const keySystemRootKeyEncrypted = await this._rootKeyEncryptPayload.executeMany(
usesKeySystemRootKey.items,
usesKeySystemRootKey.key,
signingKeyPair,
@@ -363,7 +312,7 @@ export class EncryptionService
}
if (usesKeySystemRootKeyWithKeyLookup) {
const keySystemRootKeyEncrypted = await rootKeyEncryptWithKeyLookupUsecase.executeMany(
const keySystemRootKeyEncrypted = await this._rootKeyEncryptPayloadWithKeyLookup.executeMany(
usesKeySystemRootKeyWithKeyLookup.items,
signingKeyPair,
)
@@ -423,32 +372,26 @@ export class EncryptionService
usesKeySystemRootKeyWithKeyLookup,
} = split
const rootKeyDecryptUseCase = new RootKeyDecryptPayloadUseCase(this.operators)
const rootKeyDecryptWithKeyLookupUsecase = new RootKeyDecryptPayloadWithKeyLookupUseCase(
this.operators,
this.keys,
this.rootKeyManager,
)
if (usesRootKey) {
const rootKeyDecrypted = await rootKeyDecryptUseCase.executeMany<C>(usesRootKey.items, usesRootKey.key)
const rootKeyDecrypted = await this._rootKeyDecryptPayload.executeMany<C>(usesRootKey.items, usesRootKey.key)
extendArray(resultParams, rootKeyDecrypted)
}
if (usesRootKeyWithKeyLookup) {
const rootKeyDecrypted = await rootKeyDecryptWithKeyLookupUsecase.executeMany<C>(usesRootKeyWithKeyLookup.items)
const rootKeyDecrypted = await this._rootKeyDecryptPayloadWithKeyLookup.executeMany<C>(
usesRootKeyWithKeyLookup.items,
)
extendArray(resultParams, rootKeyDecrypted)
}
if (usesKeySystemRootKey) {
const keySystemRootKeyDecrypted = await rootKeyDecryptUseCase.executeMany<C>(
const keySystemRootKeyDecrypted = await this._rootKeyDecryptPayload.executeMany<C>(
usesKeySystemRootKey.items,
usesKeySystemRootKey.key,
)
extendArray(resultParams, keySystemRootKeyDecrypted)
}
if (usesKeySystemRootKeyWithKeyLookup) {
const keySystemRootKeyDecrypted = await rootKeyDecryptWithKeyLookupUsecase.executeMany<C>(
const keySystemRootKeyDecrypted = await this._rootKeyDecryptPayloadWithKeyLookup.executeMany<C>(
usesKeySystemRootKeyWithKeyLookup.items,
)
extendArray(resultParams, keySystemRootKeyDecrypted)
@@ -640,56 +583,6 @@ export class EncryptionService
.createKeySystemItemsKey(uuid, keySystemIdentifier, sharedVaultUuid, rootKeyToken)
}
asymmetricallyEncryptMessage(dto: {
message: AsymmetricMessagePayload
senderKeyPair: PkcKeyPair
senderSigningKeyPair: PkcKeyPair
recipientPublicKey: string
}): AsymmetricallyEncryptedString {
const operator = this.operators.defaultOperator()
const encrypted = operator.asymmetricEncrypt({
stringToEncrypt: JSON.stringify(dto.message),
senderKeyPair: dto.senderKeyPair,
senderSigningKeyPair: dto.senderSigningKeyPair,
recipientPublicKey: dto.recipientPublicKey,
})
return encrypted
}
asymmetricallyDecryptMessage<M extends AsymmetricMessagePayload>(dto: {
encryptedString: AsymmetricallyEncryptedString
trustedSender: TrustedContactInterface | undefined
privateKey: string
}): M | undefined {
const defaultOperator = this.operators.defaultOperator()
const version = defaultOperator.versionForAsymmetricallyEncryptedString(dto.encryptedString)
const keyOperator = this.operators.operatorForVersion(version)
const decryptedResult = keyOperator.asymmetricDecrypt({
stringToDecrypt: dto.encryptedString,
recipientSecretKey: dto.privateKey,
})
if (!decryptedResult) {
return undefined
}
if (!decryptedResult.signatureVerified) {
return undefined
}
if (dto.trustedSender) {
if (!dto.trustedSender.isPublicKeyTrusted(decryptedResult.senderPublicKey)) {
return undefined
}
if (!dto.trustedSender.isSigningKeyTrusted(decryptedResult.signaturePublicKey)) {
return undefined
}
}
return JSON.parse(decryptedResult.plaintext)
}
asymmetricSignatureVerifyDetached(
encryptedString: AsymmetricallyEncryptedString,
): AsymmetricSignatureVerificationDetachedResult {
@@ -699,7 +592,7 @@ export class EncryptionService
return keyOperator.asymmetricSignatureVerifyDetached(encryptedString)
}
getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: AsymmetricallyEncryptedString): PublicKeySet {
getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: AsymmetricallyEncryptedString): PortablePublicKeySet {
const defaultOperator = this.operators.defaultOperator()
const version = defaultOperator.versionForAsymmetricallyEncryptedString(string)
@@ -707,15 +600,6 @@ export class EncryptionService
return keyOperator.getSenderPublicKeySetFromAsymmetricallyEncryptedString(string)
}
public async decryptBackupFile(
file: BackupFile,
password?: string,
): Promise<ClientDisplayableError | (EncryptedPayloadInterface | DecryptedPayloadInterface<ItemContent>)[]> {
const usecase = new DecryptBackupFileUseCase(this)
const result = await usecase.execute(file, password)
return result
}
/**
* Creates a key params object from a raw object
* @param keyParams - The raw key params object to create a KeyParams object from
@@ -800,7 +684,7 @@ export class EncryptionService
* If so, they must generate the unwrapping key by getting our saved wrapping key keyParams.
* After unwrapping, the root key is automatically loaded.
*/
public async unwrapRootKey(wrappingKey: RootKeyInterface) {
public async unwrapRootKey(wrappingKey: RootKeyInterface): Promise<void> {
return this.rootKeyManager.unwrapRootKey(wrappingKey)
}
/**
@@ -902,7 +786,7 @@ export class EncryptionService
* A new root key based items key is needed if our default items key content
* isnt equal to our current root key
*/
const defaultItemsKey = findDefaultItemsKey(this.itemsEncryption.getItemsKeys())
const defaultItemsKey = this._findDefaultItemsKey.execute(this.itemsEncryption.getItemsKeys()).getValue()
/** Shouldn't be undefined, but if it is, we'll take the corrective action */
if (!defaultItemsKey) {
@@ -913,8 +797,7 @@ export class EncryptionService
}
public async createNewDefaultItemsKey(): Promise<ItemsKeyInterface> {
const usecase = new CreateNewDefaultItemsKeyUseCase(this.mutator, this.items, this.operators, this.rootKeyManager)
return usecase.execute()
return this._createDefaultItemsKey.execute()
}
public getPasswordCreatedDate(): Date | undefined {
@@ -1001,7 +884,7 @@ export class EncryptionService
private async handleFullSyncCompletion() {
/** Always create a new items key after full sync, if no items key is found */
const currentItemsKey = findDefaultItemsKey(this.itemsEncryption.getItemsKeys())
const currentItemsKey = this._findDefaultItemsKey.execute(this.itemsEncryption.getItemsKeys()).getValue()
if (!currentItemsKey) {
await this.createNewDefaultItemsKey()
if (this.rootKeyManager.getKeyMode() === KeyMode.WrapperOnly) {

View File

@@ -5,11 +5,12 @@ import {
ItemsKeyContent,
RootKeyInterface,
} from '@standardnotes/models'
import { EncryptionProviderInterface, KeyRecoveryStrings, SNRootKeyParams } from '@standardnotes/encryption'
import { KeyRecoveryStrings, SNRootKeyParams } from '@standardnotes/encryption'
import { ChallengeServiceInterface } from '../Challenge/ChallengeServiceInterface'
import { ChallengePrompt } from '../Challenge/Prompt/ChallengePrompt'
import { ChallengeReason } from '../Challenge/Types/ChallengeReason'
import { ChallengeValidation } from '../Challenge/Types/ChallengeValidation'
import { EncryptionProviderInterface } from './EncryptionProviderInterface'
export async function DecryptItemsKeyWithUserFallback(
itemsKey: EncryptedPayloadInterface,

View File

@@ -1,289 +0,0 @@
import { ProtocolVersion } from '@standardnotes/common'
import {
DecryptedParameters,
ErrorDecryptingParameters,
findDefaultItemsKey,
isErrorDecryptingParameters,
OperatorManager,
StandardException,
encryptPayload,
decryptPayload,
EncryptedOutputParameters,
KeySystemKeyManagerInterface,
} from '@standardnotes/encryption'
import {
ContentTypeUsesKeySystemRootKeyEncryption,
DecryptedPayload,
DecryptedPayloadInterface,
EncryptedPayload,
EncryptedPayloadInterface,
KeySystemRootKeyInterface,
isEncryptedPayload,
ItemContent,
ItemsKeyInterface,
PayloadEmitSource,
KeySystemItemsKeyInterface,
SureFindPayload,
ContentTypeUsesRootKeyEncryption,
} from '@standardnotes/models'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { PayloadManagerInterface } from '../Payloads/PayloadManagerInterface'
import { AbstractService } from '../Service/AbstractService'
import { StorageServiceInterface } from '../Storage/StorageServiceInterface'
import { PkcKeyPair } from '@standardnotes/sncrypto-common'
import { ContentType } from '@standardnotes/domain-core'
export class ItemsEncryptionService extends AbstractService {
private removeItemsObserver!: () => void
public userVersion?: ProtocolVersion
constructor(
private itemManager: ItemManagerInterface,
private payloadManager: PayloadManagerInterface,
private storageService: StorageServiceInterface,
private operatorManager: OperatorManager,
private keys: KeySystemKeyManagerInterface,
protected override internalEventBus: InternalEventBusInterface,
) {
super(internalEventBus)
this.removeItemsObserver = this.itemManager.addObserver([ContentType.TYPES.ItemsKey], ({ changed, inserted }) => {
if (changed.concat(inserted).length > 0) {
void this.decryptErroredItemPayloads()
}
})
}
public override deinit(): void {
;(this.itemManager as unknown) = undefined
;(this.payloadManager as unknown) = undefined
;(this.storageService as unknown) = undefined
;(this.operatorManager as unknown) = undefined
;(this.keys as unknown) = undefined
this.removeItemsObserver()
;(this.removeItemsObserver as unknown) = undefined
super.deinit()
}
/**
* If encryption status changes (esp. on mobile, where local storage encryption
* can be disabled), consumers may call this function to repersist all items to
* disk using latest encryption status.
*/
async repersistAllItems(): Promise<void> {
const items = this.itemManager.items
const payloads = items.map((item) => item.payload)
return this.storageService.savePayloads(payloads)
}
public getItemsKeys(): ItemsKeyInterface[] {
return this.itemManager.getDisplayableItemsKeys()
}
public itemsKeyForEncryptedPayload(
payload: EncryptedPayloadInterface,
): ItemsKeyInterface | KeySystemItemsKeyInterface | undefined {
const itemsKeys = this.getItemsKeys()
const keySystemItemsKeys = this.itemManager.getItems<KeySystemItemsKeyInterface>(
ContentType.TYPES.KeySystemItemsKey,
)
return [...itemsKeys, ...keySystemItemsKeys].find(
(key) => key.uuid === payload.items_key_id || key.duplicateOf === payload.items_key_id,
)
}
public getDefaultItemsKey(): ItemsKeyInterface | undefined {
return findDefaultItemsKey(this.getItemsKeys())
}
private keyToUseForItemEncryption(
payload: DecryptedPayloadInterface,
): ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface | StandardException {
if (payload.key_system_identifier) {
const keySystemItemsKey = this.keys.getPrimaryKeySystemItemsKey(payload.key_system_identifier)
if (!keySystemItemsKey) {
return new StandardException('Cannot find key system items key to use for encryption')
}
return keySystemItemsKey
}
const defaultKey = this.getDefaultItemsKey()
let result: ItemsKeyInterface | undefined = undefined
if (this.userVersion && this.userVersion !== defaultKey?.keyVersion) {
/**
* The default key appears to be either newer or older than the user's account version
* We could throw an exception here, but will instead fall back to a corrective action:
* return any items key that corresponds to the user's version
*/
const itemsKeys = this.getItemsKeys()
result = itemsKeys.find((key) => key.keyVersion === this.userVersion)
} else {
result = defaultKey
}
if (!result) {
return new StandardException('Cannot find items key to use for encryption')
}
return result
}
private keyToUseForDecryptionOfPayload(
payload: EncryptedPayloadInterface,
): ItemsKeyInterface | KeySystemItemsKeyInterface | undefined {
if (payload.items_key_id) {
const itemsKey = this.itemsKeyForEncryptedPayload(payload)
return itemsKey
}
const defaultKey = this.defaultItemsKeyForItemVersion(payload.version)
return defaultKey
}
public async encryptPayloadWithKeyLookup(
payload: DecryptedPayloadInterface,
signingKeyPair?: PkcKeyPair,
): Promise<EncryptedOutputParameters> {
const key = this.keyToUseForItemEncryption(payload)
if (key instanceof StandardException) {
throw Error(key.message)
}
return this.encryptPayload(payload, key, signingKeyPair)
}
public async encryptPayload(
payload: DecryptedPayloadInterface,
key: ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface,
signingKeyPair?: PkcKeyPair,
): Promise<EncryptedOutputParameters> {
if (isEncryptedPayload(payload)) {
throw Error('Attempting to encrypt already encrypted payload.')
}
if (!payload.content) {
throw Error('Attempting to encrypt payload with no content.')
}
if (!payload.uuid) {
throw Error('Attempting to encrypt payload with no UuidGenerator.')
}
return encryptPayload(payload, key, this.operatorManager, signingKeyPair)
}
public async encryptPayloads(
payloads: DecryptedPayloadInterface[],
key: ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface,
signingKeyPair?: PkcKeyPair,
): Promise<EncryptedOutputParameters[]> {
return Promise.all(payloads.map((payload) => this.encryptPayload(payload, key, signingKeyPair)))
}
public async encryptPayloadsWithKeyLookup(
payloads: DecryptedPayloadInterface[],
signingKeyPair?: PkcKeyPair,
): Promise<EncryptedOutputParameters[]> {
return Promise.all(payloads.map((payload) => this.encryptPayloadWithKeyLookup(payload, signingKeyPair)))
}
public async decryptPayloadWithKeyLookup<C extends ItemContent = ItemContent>(
payload: EncryptedPayloadInterface,
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
const key = this.keyToUseForDecryptionOfPayload(payload)
if (key == undefined) {
return {
uuid: payload.uuid,
errorDecrypting: true,
waitingForKey: true,
}
}
return this.decryptPayload(payload, key)
}
public async decryptPayload<C extends ItemContent = ItemContent>(
payload: EncryptedPayloadInterface,
key: ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface,
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
if (!payload.content) {
return {
uuid: payload.uuid,
errorDecrypting: true,
}
}
return decryptPayload(payload, key, this.operatorManager)
}
public async decryptPayloadsWithKeyLookup<C extends ItemContent = ItemContent>(
payloads: EncryptedPayloadInterface[],
): Promise<(DecryptedParameters<C> | ErrorDecryptingParameters)[]> {
return Promise.all(payloads.map((payload) => this.decryptPayloadWithKeyLookup<C>(payload)))
}
public async decryptPayloads<C extends ItemContent = ItemContent>(
payloads: EncryptedPayloadInterface[],
key: ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface,
): Promise<(DecryptedParameters<C> | ErrorDecryptingParameters)[]> {
return Promise.all(payloads.map((payload) => this.decryptPayload<C>(payload, key)))
}
public async decryptErroredItemPayloads(): Promise<void> {
const erroredItemPayloads = this.payloadManager.invalidPayloads.filter(
(i) =>
!ContentTypeUsesRootKeyEncryption(i.content_type) && !ContentTypeUsesKeySystemRootKeyEncryption(i.content_type),
)
if (erroredItemPayloads.length === 0) {
return
}
const resultParams = await this.decryptPayloadsWithKeyLookup(erroredItemPayloads)
const decryptedPayloads = resultParams.map((params) => {
const original = SureFindPayload(erroredItemPayloads, 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 migrating from non-items key architecture, many items will not have a
* relationship with any key object. For those items, we can be sure that only 1 key
* object will correspond to that protocol version.
* @returns The items key object to decrypt items encrypted
* with previous protocol version.
*/
public defaultItemsKeyForItemVersion(
version: ProtocolVersion,
fromKeys?: ItemsKeyInterface[],
): ItemsKeyInterface | undefined {
/** Try to find one marked default first */
const searchKeys = fromKeys || this.getItemsKeys()
const priorityKey = searchKeys.find((key) => {
return key.isDefault && key.keyVersion === version
})
if (priorityKey) {
return priorityKey
}
return searchKeys.find((key) => {
return key.keyVersion === version
})
}
}

View File

@@ -1,491 +0,0 @@
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<RootKeyManagerEvent> {
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<void> {
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<boolean> {
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<boolean> {
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<K extends RootKeyInterface>(
password: string,
keyParams: RootKeyParamsInterface,
): Promise<K> {
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<void> {
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<K extends RootKeyInterface>(
identifier: string,
password: string,
origination: KeyParamsOrigination,
version?: ProtocolVersion,
): Promise<K> {
const operator = version ? this.operators.operatorForVersion(version) : this.operators.defaultOperator()
return operator.createRootKey(identifier, password, origination)
}
public async validateAccountPassword(password: string): Promise<ValidateAccountPasswordResult> {
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<ValidatePasscodeResult> {
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<ProtocolVersion> {
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<void> {
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<void> {
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<void> {
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<RootKeyContent>(payload, wrappingKey)
if (isErrorDecryptingParameters(decrypted)) {
throw Error('Unable to decrypt root key with provided wrapping key.')
} else {
const decryptedPayload = new DecryptedPayload<RootKeyContent>({
...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<void> {
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<boolean> {
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<EncryptedTransferPayload>(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<RootKeyInterface | undefined> {
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<void> {
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)
}
}
}

View File

@@ -1,3 +0,0 @@
export enum RootKeyManagerEvent {
RootKeyManagerKeyStatusChanged = 'RootKeyManagerKeyStatusChanged',
}

View File

@@ -1,13 +0,0 @@
import { RootKeyInterface } from '@standardnotes/models'
export type ValidateAccountPasswordResult =
| {
valid: true
artifacts: {
rootKey: RootKeyInterface
}
}
| {
valid: boolean
artifacts?: undefined
}

View File

@@ -1,13 +0,0 @@
import { RootKeyInterface } from '@standardnotes/models'
export type ValidatePasscodeResult =
| {
valid: true
artifacts: {
wrappingKey: RootKeyInterface
}
}
| {
valid: boolean
artifacts?: undefined
}

View File

@@ -0,0 +1,178 @@
import {
ContactPublicKeySet,
ContactPublicKeySetInterface,
PublicKeyTrustStatus,
TrustedContactInterface,
} from '@standardnotes/models'
import { DecryptMessage } from './DecryptMessage'
import { OperatorInterface, EncryptionOperatorsInterface } from '@standardnotes/encryption'
import { ProtocolVersion } from '@standardnotes/common'
function createMockPublicKeySetChain(): ContactPublicKeySetInterface {
const nMinusOne = new ContactPublicKeySet({
encryption: 'encryption-public-key-n-1',
signing: 'signing-public-key-n-1',
timestamp: new Date(-1),
previousKeySet: undefined,
})
const root = new ContactPublicKeySet({
encryption: 'encryption-public-key',
signing: 'signing-public-key',
timestamp: new Date(),
previousKeySet: nMinusOne,
})
return root
}
describe('DecryptMessage', () => {
let usecase: DecryptMessage
let operator: jest.Mocked<OperatorInterface>
beforeEach(() => {
operator = {} as jest.Mocked<OperatorInterface>
operator.versionForAsymmetricallyEncryptedString = jest.fn().mockReturnValue(ProtocolVersion.V004)
const operators = {} as jest.Mocked<EncryptionOperatorsInterface>
operators.defaultOperator = jest.fn().mockReturnValue(operator)
operators.operatorForVersion = jest.fn().mockReturnValue(operator)
usecase = new DecryptMessage(operators)
})
it('should fail if fails to decrypt', () => {
operator.asymmetricDecrypt = jest.fn().mockReturnValue(null)
const result = usecase.execute({
message: 'encrypted',
sender: undefined,
privateKey: 'private-key',
})
expect(result.isFailed()).toEqual(true)
expect(result.getError()).toEqual('Failed to decrypt message')
})
it('should fail if signature is invalid', () => {
operator.asymmetricDecrypt = jest.fn().mockReturnValue({
plaintext: 'decrypted',
signatureVerified: false,
signaturePublicKey: 'signing-public-key',
senderPublicKey: 'encryption-public-key',
})
const result = usecase.execute({
message: 'encrypted',
sender: undefined,
privateKey: 'private-key',
})
expect(result.isFailed()).toEqual(true)
expect(result.getError()).toEqual('Failed to verify signature')
})
describe('with trusted sender', () => {
it('should fail if encryption public key is not trusted', () => {
operator.asymmetricDecrypt = jest.fn().mockReturnValue({
plaintext: 'decrypted',
signatureVerified: true,
signaturePublicKey: 'signing-public-key',
senderPublicKey: 'encryption-public-key',
})
const senderContact = {
name: 'Other',
contactUuid: '456',
publicKeySet: createMockPublicKeySetChain(),
isMe: false,
} as jest.Mocked<TrustedContactInterface>
senderContact.getTrustStatusForPublicKey = jest.fn().mockReturnValue(PublicKeyTrustStatus.NotTrusted)
const result = usecase.execute({
message: 'encrypted',
sender: senderContact,
privateKey: 'private-key',
})
expect(result.isFailed()).toEqual(true)
expect(result.getError()).toEqual('Sender public key is not trusted')
})
it('should fail if signing public key is not trusted', () => {
operator.asymmetricDecrypt = jest.fn().mockReturnValue({
plaintext: 'decrypted',
signatureVerified: true,
signaturePublicKey: 'signing-public-key',
senderPublicKey: 'encryption-public-key',
})
const senderContact = {
name: 'Other',
contactUuid: '456',
publicKeySet: createMockPublicKeySetChain(),
isMe: false,
} as jest.Mocked<TrustedContactInterface>
senderContact.getTrustStatusForPublicKey = jest.fn().mockReturnValue(PublicKeyTrustStatus.Trusted)
senderContact.getTrustStatusForSigningPublicKey = jest.fn().mockReturnValue(PublicKeyTrustStatus.NotTrusted)
const result = usecase.execute({
message: 'encrypted',
sender: senderContact,
privateKey: 'private-key',
})
expect(result.isFailed()).toEqual(true)
expect(result.getError()).toEqual('Signature public key is not trusted')
})
it('should succeed with valid signature and encryption key', () => {
operator.asymmetricDecrypt = jest.fn().mockReturnValue({
plaintext: '{"foo": "bar"}',
signatureVerified: true,
signaturePublicKey: 'signing-public-key',
senderPublicKey: 'encryption-public-key',
})
const senderContact = {
name: 'Other',
contactUuid: '456',
publicKeySet: createMockPublicKeySetChain(),
isMe: false,
} as jest.Mocked<TrustedContactInterface>
senderContact.getTrustStatusForPublicKey = jest.fn().mockReturnValue(PublicKeyTrustStatus.Trusted)
senderContact.getTrustStatusForSigningPublicKey = jest.fn().mockReturnValue(PublicKeyTrustStatus.Trusted)
const result = usecase.execute({
message: 'encrypted',
sender: senderContact,
privateKey: 'private-key',
})
expect(result.isFailed()).toEqual(false)
expect(result.getValue()).toEqual({ foo: 'bar' })
})
})
describe('without trusted sender', () => {
it('should succeed with valid signature and encryption key', () => {
operator.asymmetricDecrypt = jest.fn().mockReturnValue({
plaintext: '{"foo": "bar"}',
signatureVerified: true,
signaturePublicKey: 'signing-public-key',
senderPublicKey: 'encryption-public-key',
})
const result = usecase.execute({
message: 'encrypted',
sender: undefined,
privateKey: 'private-key',
})
expect(result.isFailed()).toEqual(false)
expect(result.getValue()).toEqual({ foo: 'bar' })
})
})
})

View File

@@ -0,0 +1,44 @@
import { SyncUseCaseInterface, Result } from '@standardnotes/domain-core'
import { EncryptionOperatorsInterface } from '@standardnotes/encryption'
import { AsymmetricMessagePayload, PublicKeyTrustStatus, TrustedContactInterface } from '@standardnotes/models'
export class DecryptMessage implements SyncUseCaseInterface<AsymmetricMessagePayload> {
constructor(private operators: EncryptionOperatorsInterface) {}
execute<M extends AsymmetricMessagePayload>(dto: {
message: string
sender: TrustedContactInterface | undefined
privateKey: string
}): Result<M> {
const defaultOperator = this.operators.defaultOperator()
const version = defaultOperator.versionForAsymmetricallyEncryptedString(dto.message)
const keyOperator = this.operators.operatorForVersion(version)
const decryptedResult = keyOperator.asymmetricDecrypt({
stringToDecrypt: dto.message,
recipientSecretKey: dto.privateKey,
})
if (!decryptedResult) {
return Result.fail('Failed to decrypt message')
}
if (!decryptedResult.signatureVerified) {
return Result.fail('Failed to verify signature')
}
if (dto.sender) {
const publicKeyTrustStatus = dto.sender.getTrustStatusForPublicKey(decryptedResult.senderPublicKey)
if (publicKeyTrustStatus !== PublicKeyTrustStatus.Trusted) {
return Result.fail('Sender public key is not trusted')
}
const signingKeyTrustStatus = dto.sender.getTrustStatusForSigningPublicKey(decryptedResult.signaturePublicKey)
if (signingKeyTrustStatus !== PublicKeyTrustStatus.Trusted) {
return Result.fail('Signature public key is not trusted')
}
}
return Result.ok(JSON.parse(decryptedResult.plaintext))
}
}

View File

@@ -0,0 +1,31 @@
import { SyncUseCaseInterface, Result } from '@standardnotes/domain-core'
import { EncryptionOperatorsInterface } from '@standardnotes/encryption'
import { AsymmetricMessagePayload } from '@standardnotes/models'
export class DecryptOwnMessage<M extends AsymmetricMessagePayload> implements SyncUseCaseInterface<M> {
constructor(private operators: EncryptionOperatorsInterface) {}
execute(dto: { message: string; privateKey: string; recipientPublicKey: string }): Result<M> {
const defaultOperator = this.operators.defaultOperator()
const version = defaultOperator.versionForAsymmetricallyEncryptedString(dto.message)
const keyOperator = this.operators.operatorForVersion(version)
const result = keyOperator.asymmetricDecryptOwnMessage({
message: dto.message,
ownPrivateKey: dto.privateKey,
recipientPublicKey: dto.recipientPublicKey,
})
if (result.isFailed()) {
return Result.fail(result.getError())
}
const decryptedObject = result.getValue()
if (!decryptedObject.signatureVerified) {
return Result.fail('Failed to verify signature')
}
return Result.ok(JSON.parse(decryptedObject.plaintext))
}
}

View File

@@ -0,0 +1,28 @@
import { SyncUseCaseInterface, Result } from '@standardnotes/domain-core'
import { EncryptionOperatorsInterface } from '@standardnotes/encryption'
import { AsymmetricMessagePayload } from '@standardnotes/models'
import { PkcKeyPair } from '@standardnotes/sncrypto-common'
export class EncryptMessage implements SyncUseCaseInterface<string> {
constructor(private operators: EncryptionOperatorsInterface) {}
execute(dto: {
message: AsymmetricMessagePayload
keys: {
encryption: PkcKeyPair
signing: PkcKeyPair
}
recipientPublicKey: string
}): Result<string> {
const operator = this.operators.defaultOperator()
const encrypted = operator.asymmetricEncrypt({
stringToEncrypt: JSON.stringify(dto.message),
senderKeyPair: dto.keys.encryption,
senderSigningKeyPair: dto.keys.signing,
recipientPublicKey: dto.recipientPublicKey,
})
return Result.ok(encrypted)
}
}

View File

@@ -0,0 +1,13 @@
import { SyncUseCaseInterface, Result } from '@standardnotes/domain-core'
import { AsymmetricallyEncryptedString, EncryptionOperatorsInterface } from '@standardnotes/encryption'
import { AsymmetricItemAdditionalData } from '@standardnotes/encryption/src/Domain/Types/EncryptionAdditionalData'
export class GetMessageAdditionalData implements SyncUseCaseInterface<AsymmetricItemAdditionalData> {
constructor(private operators: EncryptionOperatorsInterface) {}
execute(dto: { message: AsymmetricallyEncryptedString }): Result<AsymmetricItemAdditionalData> {
const operator = this.operators.defaultOperator()
return operator.asymmetricStringGetAdditionalData({ encryptedString: dto.message })
}
}

View File

@@ -37,10 +37,10 @@ import {
} from '@standardnotes/models'
import { ClientDisplayableError } from '@standardnotes/responses'
import { extendArray } from '@standardnotes/utils'
import { EncryptionService } from './EncryptionService'
import { EncryptionService } from '../EncryptionService'
import { ContentType } from '@standardnotes/domain-core'
export class DecryptBackupFileUseCase {
export class DecryptBackupFile {
constructor(private encryption: EncryptionService) {}
async execute(
@@ -53,7 +53,7 @@ export class DecryptBackupFileUseCase {
} else if (isDecryptedTransferPayload(item)) {
return new DecryptedPayload(item)
} else {
throw Error('Unhandled case in decryptBackupFile')
throw Error('Unhandled case in DecryptBackupFile')
}
})

View File

@@ -1,4 +1,4 @@
import { OperatorManager } from '@standardnotes/encryption'
import { EncryptionOperatorsInterface } from '@standardnotes/encryption'
import { ProtocolVersionLastNonrootItemsKey, ProtocolVersionLatest, compareVersions } from '@standardnotes/common'
import {
CreateDecryptedItemFromPayload,
@@ -12,7 +12,7 @@ import {
import { UuidGenerator } from '@standardnotes/utils'
import { MutatorClientInterface } from '../../../Mutator/MutatorClientInterface'
import { ItemManagerInterface } from '../../../Item/ItemManagerInterface'
import { RootKeyManager } from '../../RootKey/RootKeyManager'
import { RootKeyManager } from '../../../RootKeyManager/RootKeyManager'
import { ContentType } from '@standardnotes/domain-core'
/**
@@ -20,11 +20,11 @@ import { ContentType } from '@standardnotes/domain-core'
* 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 {
export class CreateNewDefaultItemsKey {
constructor(
private mutator: MutatorClientInterface,
private items: ItemManagerInterface,
private operatorManager: OperatorManager,
private operators: EncryptionOperatorsInterface,
private rootKeyManager: RootKeyManager,
) {}
@@ -48,7 +48,7 @@ export class CreateNewDefaultItemsKeyUseCase {
itemTemplate = CreateDecryptedItemFromPayload(payload)
} else {
/** Create independent items key */
itemTemplate = this.operatorManager.operatorForVersion(operatorVersion).createItemsKey()
itemTemplate = this.operators.operatorForVersion(operatorVersion).createItemsKey()
}
const itemsKeys = this.items.getDisplayableItemsKeys()

View File

@@ -1,35 +1,25 @@
import { StorageServiceInterface } from './../../../Storage/StorageServiceInterface'
import { ItemsKeyMutator, OperatorManager, findDefaultItemsKey } from '@standardnotes/encryption'
import { ItemsKeyMutator } from '@standardnotes/encryption'
import { MutatorClientInterface } from '../../../Mutator/MutatorClientInterface'
import { ItemManagerInterface } from '../../../Item/ItemManagerInterface'
import { RootKeyManager } from '../../RootKey/RootKeyManager'
import { CreateNewDefaultItemsKeyUseCase } from './CreateNewDefaultItemsKey'
import { RemoveItemsLocallyUseCase } from '../../../UseCase/RemoveItemsLocally'
export class CreateNewItemsKeyWithRollbackUseCase {
private createDefaultItemsKeyUseCase = new CreateNewDefaultItemsKeyUseCase(
this.mutator,
this.items,
this.operatorManager,
this.rootKeyManager,
)
private removeItemsLocallyUsecase = new RemoveItemsLocallyUseCase(this.items, this.storage)
import { CreateNewDefaultItemsKey } from './CreateNewDefaultItemsKey'
import { RemoveItemsLocally } from '../../../UseCase/RemoveItemsLocally'
import { FindDefaultItemsKey } from './FindDefaultItemsKey'
export class CreateNewItemsKeyWithRollback {
constructor(
private mutator: MutatorClientInterface,
private items: ItemManagerInterface,
private storage: StorageServiceInterface,
private operatorManager: OperatorManager,
private rootKeyManager: RootKeyManager,
private createDefaultItemsKey: CreateNewDefaultItemsKey,
private removeItemsLocally: RemoveItemsLocally,
private findDefaultItemsKey: FindDefaultItemsKey,
) {}
async execute(): Promise<() => Promise<void>> {
const currentDefaultItemsKey = findDefaultItemsKey(this.items.getDisplayableItemsKeys())
const newDefaultItemsKey = await this.createDefaultItemsKeyUseCase.execute()
const currentDefaultItemsKey = this.findDefaultItemsKey.execute(this.items.getDisplayableItemsKeys()).getValue()
const newDefaultItemsKey = await this.createDefaultItemsKey.execute()
const rollback = async () => {
await this.removeItemsLocallyUsecase.execute([newDefaultItemsKey])
await this.removeItemsLocally.execute([newDefaultItemsKey])
if (currentDefaultItemsKey) {
await this.mutator.changeItem<ItemsKeyMutator>(currentDefaultItemsKey, (mutator) => {

View File

@@ -0,0 +1,33 @@
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
import { ItemsKeyInterface } from '@standardnotes/models'
export class FindDefaultItemsKey implements SyncUseCaseInterface<ItemsKeyInterface | undefined> {
execute(itemsKeys: ItemsKeyInterface[]): Result<ItemsKeyInterface | undefined> {
if (itemsKeys.length === 1) {
return Result.ok(itemsKeys[0])
}
const defaultKeys = itemsKeys.filter((key) => {
return key.isDefault
})
if (defaultKeys.length === 0) {
return Result.ok(undefined)
}
if (defaultKeys.length === 1) {
return Result.ok(defaultKeys[0])
}
/**
* Prioritize one that is synced, as neverSynced keys will likely be deleted after
* DownloadFirst sync.
*/
const syncedKeys = defaultKeys.filter((key) => !key.neverSynced)
if (syncedKeys.length > 0) {
return Result.ok(syncedKeys[0])
}
return Result.ok(undefined)
}
}

View File

@@ -6,15 +6,16 @@ import {
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'
import { PayloadManagerInterface } from '../../../Payloads/PayloadManagerInterface'
import { isErrorDecryptingParameters, EncryptionOperatorsInterface } from '@standardnotes/encryption'
import { DecryptTypeAPayloadWithKeyLookup } from './DecryptPayloadWithKeyLookup'
import { RootKeyManager } from '../../../RootKeyManager/RootKeyManager'
import { KeySystemKeyManagerInterface } from '../../../KeySystem/KeySystemKeyManagerInterface'
export class DecryptErroredRootPayloadsUseCase {
export class DecryptErroredTypeAPayloads {
constructor(
private payloads: PayloadManagerInterface,
private operatorManager: OperatorManager,
private operatorManager: EncryptionOperatorsInterface,
private keySystemKeyManager: KeySystemKeyManagerInterface,
private rootKeyManager: RootKeyManager,
) {}
@@ -28,7 +29,7 @@ export class DecryptErroredRootPayloadsUseCase {
return
}
const usecase = new RootKeyDecryptPayloadWithKeyLookupUseCase(
const usecase = new DecryptTypeAPayloadWithKeyLookup(
this.operatorManager,
this.keySystemKeyManager,
this.rootKeyManager,

View File

@@ -1,8 +1,8 @@
import {
DecryptedParameters,
ErrorDecryptingParameters,
OperatorManager,
decryptPayload,
EncryptionOperatorsInterface,
} from '@standardnotes/encryption'
import {
EncryptedPayloadInterface,
@@ -11,8 +11,8 @@ import {
RootKeyInterface,
} from '@standardnotes/models'
export class RootKeyDecryptPayloadUseCase {
constructor(private operatorManager: OperatorManager) {}
export class DecryptTypeAPayload {
constructor(private operatorManager: EncryptionOperatorsInterface) {}
async executeOne<C extends ItemContent = ItemContent>(
payload: EncryptedPayloadInterface,

View File

@@ -1,9 +1,4 @@
import {
DecryptedParameters,
ErrorDecryptingParameters,
KeySystemKeyManagerInterface,
OperatorManager,
} from '@standardnotes/encryption'
import { DecryptedParameters, ErrorDecryptingParameters, EncryptionOperatorsInterface } from '@standardnotes/encryption'
import {
ContentTypeUsesKeySystemRootKeyEncryption,
EncryptedPayloadInterface,
@@ -12,12 +7,13 @@ import {
RootKeyInterface,
} from '@standardnotes/models'
import { RootKeyDecryptPayloadUseCase } from './DecryptPayload'
import { RootKeyManager } from '../../RootKey/RootKeyManager'
import { DecryptTypeAPayload } from './DecryptPayload'
import { RootKeyManager } from '../../../RootKeyManager/RootKeyManager'
import { KeySystemKeyManagerInterface } from '../../../KeySystem/KeySystemKeyManagerInterface'
export class RootKeyDecryptPayloadWithKeyLookupUseCase {
export class DecryptTypeAPayloadWithKeyLookup {
constructor(
private operatorManager: OperatorManager,
private operators: EncryptionOperatorsInterface,
private keySystemKeyManager: KeySystemKeyManagerInterface,
private rootKeyManager: RootKeyManager,
) {}
@@ -43,7 +39,7 @@ export class RootKeyDecryptPayloadWithKeyLookupUseCase {
}
}
const usecase = new RootKeyDecryptPayloadUseCase(this.operatorManager)
const usecase = new DecryptTypeAPayload(this.operators)
return usecase.executeOne(payload, key)
}

View File

@@ -1,16 +1,16 @@
import { EncryptedOutputParameters, OperatorManager, encryptPayload } from '@standardnotes/encryption'
import { EncryptedOutputParameters, EncryptionOperatorsInterface, encryptPayload } from '@standardnotes/encryption'
import { DecryptedPayloadInterface, KeySystemRootKeyInterface, RootKeyInterface } from '@standardnotes/models'
import { PkcKeyPair } from '@standardnotes/sncrypto-common'
export class RootKeyEncryptPayloadUseCase {
constructor(private operatorManager: OperatorManager) {}
export class EncryptTypeAPayload {
constructor(private operators: EncryptionOperatorsInterface) {}
async executeOne(
payload: DecryptedPayloadInterface,
key: RootKeyInterface | KeySystemRootKeyInterface,
signingKeyPair?: PkcKeyPair,
): Promise<EncryptedOutputParameters> {
return encryptPayload(payload, key, this.operatorManager, signingKeyPair)
return encryptPayload(payload, key, this.operators, signingKeyPair)
}
async executeMany(

View File

@@ -1,4 +1,4 @@
import { EncryptedOutputParameters, KeySystemKeyManagerInterface, OperatorManager } from '@standardnotes/encryption'
import { EncryptedOutputParameters, EncryptionOperatorsInterface } from '@standardnotes/encryption'
import {
ContentTypeUsesKeySystemRootKeyEncryption,
DecryptedPayloadInterface,
@@ -7,12 +7,13 @@ import {
} from '@standardnotes/models'
import { PkcKeyPair } from '@standardnotes/sncrypto-common'
import { RootKeyEncryptPayloadUseCase } from './EncryptPayload'
import { RootKeyManager } from '../../RootKey/RootKeyManager'
import { EncryptTypeAPayload } from './EncryptPayload'
import { RootKeyManager } from '../../../RootKeyManager/RootKeyManager'
import { KeySystemKeyManagerInterface } from '../../../KeySystem/KeySystemKeyManagerInterface'
export class RootKeyEncryptPayloadWithKeyLookupUseCase {
export class EncryptTypeAPayloadWithKeyLookup {
constructor(
private operatorManager: OperatorManager,
private operators: EncryptionOperatorsInterface,
private keySystemKeyManager: KeySystemKeyManagerInterface,
private rootKeyManager: RootKeyManager,
) {}
@@ -35,7 +36,7 @@ export class RootKeyEncryptPayloadWithKeyLookupUseCase {
throw Error('Attempting root key encryption with no root key')
}
const usecase = new RootKeyEncryptPayloadUseCase(this.operatorManager)
const usecase = new EncryptTypeAPayload(this.operators)
return usecase.executeOne(payload, key, signingKeyPair)
}