feat(encryption): refactor circular dependencies on services

This commit is contained in:
Karol Sójko
2022-08-05 11:59:02 +02:00
parent 183f68c9c1
commit ffb2193924
40 changed files with 502 additions and 380 deletions

View File

@@ -39,7 +39,6 @@
"@standardnotes/common": "^1.23.1", "@standardnotes/common": "^1.23.1",
"@standardnotes/models": "workspace:*", "@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*", "@standardnotes/responses": "workspace:*",
"@standardnotes/services": "workspace:*",
"@standardnotes/sncrypto-common": "workspace:*", "@standardnotes/sncrypto-common": "workspace:*",
"@standardnotes/utils": "workspace:*", "@standardnotes/utils": "workspace:*",
"reflect-metadata": "^0.1.13" "reflect-metadata": "^0.1.13"

View File

@@ -1,4 +1,10 @@
import * as Models from '@standardnotes/models' import {
DecryptedPayloadInterface,
ItemsKeyInterface,
RootKeyInterface,
ItemContent,
EncryptedPayloadInterface,
} from '@standardnotes/models'
import { import {
DecryptedParameters, DecryptedParameters,
EncryptedParameters, EncryptedParameters,
@@ -9,8 +15,8 @@ import { isAsyncOperator } from './Functions'
import { OperatorManager } from './OperatorManager' import { OperatorManager } from './OperatorManager'
export async function encryptPayload( export async function encryptPayload(
payload: Models.DecryptedPayloadInterface, payload: DecryptedPayloadInterface,
key: Models.ItemsKeyInterface | Models.RootKeyInterface, key: ItemsKeyInterface | RootKeyInterface,
operatorManager: OperatorManager, operatorManager: OperatorManager,
): Promise<EncryptedParameters> { ): Promise<EncryptedParameters> {
const operator = operatorManager.operatorForVersion(key.keyVersion) const operator = operatorManager.operatorForVersion(key.keyVersion)
@@ -29,9 +35,9 @@ export async function encryptPayload(
return encryptionParameters return encryptionParameters
} }
export async function decryptPayload<C extends Models.ItemContent = Models.ItemContent>( export async function decryptPayload<C extends ItemContent = ItemContent>(
payload: Models.EncryptedPayloadInterface, payload: EncryptedPayloadInterface,
key: Models.ItemsKeyInterface | Models.RootKeyInterface, key: ItemsKeyInterface | RootKeyInterface,
operatorManager: OperatorManager, operatorManager: OperatorManager,
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> { ): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
const operator = operatorManager.operatorForVersion(payload.version) const operator = operatorManager.operatorForVersion(payload.version)

View File

@@ -0,0 +1,3 @@
export enum RootKeyServiceEvent {
RootKeyStatusChanged = 'RootKeyStatusChanged',
}

View File

@@ -0,0 +1,22 @@
import { ProtocolVersion } from '@standardnotes/common';
import { EncryptedPayloadInterface, ItemContent } from '@standardnotes/models';
export declare type EncryptedParameters = {
uuid: string;
content: string;
items_key_id: string | undefined;
enc_item_key: string;
version: ProtocolVersion;
/** @deprecated */
auth_hash?: string;
};
export declare type DecryptedParameters<C extends ItemContent = ItemContent> = {
uuid: string;
content: C;
};
export declare type ErrorDecryptingParameters = {
uuid: string;
errorDecrypting: true;
waitingForKey?: boolean;
};
export declare function isErrorDecryptingParameters(x: EncryptedParameters | DecryptedParameters | ErrorDecryptingParameters): x is ErrorDecryptingParameters;
export declare function encryptedParametersFromPayload(payload: EncryptedPayloadInterface): EncryptedParameters;

View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.encryptedParametersFromPayload = exports.isErrorDecryptingParameters = void 0;
function isErrorDecryptingParameters(x) {
return x.errorDecrypting;
}
exports.isErrorDecryptingParameters = isErrorDecryptingParameters;
function encryptedParametersFromPayload(payload) {
return {
uuid: payload.uuid,
content: payload.content,
items_key_id: payload.items_key_id,
enc_item_key: payload.enc_item_key,
version: payload.version,
auth_hash: payload.auth_hash,
};
}
exports.encryptedParametersFromPayload = encryptedParametersFromPayload;

View File

@@ -1,5 +1,4 @@
export * from './Algorithm' export * from './Algorithm'
export * from './Backups/BackupFileDecryptor'
export * from './Backups/BackupFileType' export * from './Backups/BackupFileType'
export * from './Keys/ItemsKey/ItemsKey' export * from './Keys/ItemsKey/ItemsKey'
export * from './Keys/ItemsKey/ItemsKeyMutator' export * from './Keys/ItemsKey/ItemsKeyMutator'
@@ -10,7 +9,6 @@ export * from './Keys/RootKey/ProtocolVersionForKeyParams'
export * from './Keys/RootKey/RootKey' export * from './Keys/RootKey/RootKey'
export * from './Keys/RootKey/RootKeyParams' export * from './Keys/RootKey/RootKeyParams'
export * from './Keys/RootKey/ValidKeyParamsKeys' export * from './Keys/RootKey/ValidKeyParamsKeys'
export * from './Keys/Utils/DecryptItemsKey'
export * from './Keys/Utils/KeyRecoveryStrings' export * from './Keys/Utils/KeyRecoveryStrings'
export * from './Operator/001/Operator001' export * from './Operator/001/Operator001'
export * from './Operator/002/Operator002' export * from './Operator/002/Operator002'
@@ -21,11 +19,9 @@ export * from './Operator/Operator'
export * from './Operator/OperatorManager' export * from './Operator/OperatorManager'
export * from './Operator/OperatorWrapper' export * from './Operator/OperatorWrapper'
export * from './Service/Encryption/EncryptionProvider' export * from './Service/Encryption/EncryptionProvider'
export * from './Service/Encryption/EncryptionService'
export * from './Service/Functions' export * from './Service/Functions'
export * from './Service/Items/ItemsEncryption'
export * from './Service/RootKey/KeyMode' export * from './Service/RootKey/KeyMode'
export * from './Service/RootKey/RootKeyEncryption' export * from './Service/RootKey/RootKeyServiceEvent'
export * from './Split/AbstractKeySplit' export * from './Split/AbstractKeySplit'
export * from './Split/EncryptionSplit' export * from './Split/EncryptionSplit'
export * from './Split/EncryptionTypeSplit' export * from './Split/EncryptionTypeSplit'

View File

@@ -5,4 +5,3 @@ export * from './Streaming/StreamingReader'
export * from './Streaming/StreamingSaver' export * from './Streaming/StreamingSaver'
export * from './Streaming/StreamingApi' export * from './Streaming/StreamingApi'
export * from './utils' export * from './utils'
export * from './Cache/FileMemoryCache'

View File

@@ -1,5 +1,4 @@
import { EncryptedBytes } from '@standardnotes/files' import { EncryptedBytes } from '../Types/EncryptedBytes'
import { FileMemoryCache } from './FileMemoryCache' import { FileMemoryCache } from './FileMemoryCache'
describe('file memory cache', () => { describe('file memory cache', () => {

View File

@@ -1,6 +1,7 @@
import { removeFromArray } from '@standardnotes/utils' import { removeFromArray } from '@standardnotes/utils'
import { Uuid } from '@standardnotes/common' import { Uuid } from '@standardnotes/common'
import { EncryptedBytes } from '@standardnotes/files'
import { EncryptedBytes } from '../Types/EncryptedBytes'
export class FileMemoryCache { export class FileMemoryCache {
private cache: Record<Uuid, EncryptedBytes> = {} private cache: Record<Uuid, EncryptedBytes> = {}

View File

@@ -5,6 +5,7 @@ export * from './Api/FileSystemApi'
export * from './Api/FileSystemNoSelection' export * from './Api/FileSystemNoSelection'
export * from './Api/FileSystemResult' export * from './Api/FileSystemResult'
export * from './Api/FilesApiInterface' export * from './Api/FilesApiInterface'
export * from './Cache/FileMemoryCache'
export * from './Chunker/ByteChunker' export * from './Chunker/ByteChunker'
export * from './Chunker/OnChunkCallback' export * from './Chunker/OnChunkCallback'
export * from './Chunker/OrderedByteChunker' export * from './Chunker/OrderedByteChunker'

View File

@@ -25,6 +25,7 @@
"dependencies": { "dependencies": {
"@standardnotes/auth": "^3.19.4", "@standardnotes/auth": "^3.19.4",
"@standardnotes/common": "^1.30.0", "@standardnotes/common": "^1.30.0",
"@standardnotes/encryption": "workspace:^",
"@standardnotes/files": "workspace:^", "@standardnotes/files": "workspace:^",
"@standardnotes/models": "workspace:^", "@standardnotes/models": "workspace:^",
"@standardnotes/responses": "workspace:*", "@standardnotes/responses": "workspace:*",

View File

@@ -2,8 +2,7 @@ import { ContentType, Uuid } from '@standardnotes/common'
import { EncryptionProvider } from '@standardnotes/encryption' import { EncryptionProvider } from '@standardnotes/encryption'
import { PayloadEmitSource, FileItem, CreateEncryptedBackupFileContextPayload } from '@standardnotes/models' import { PayloadEmitSource, FileItem, CreateEncryptedBackupFileContextPayload } from '@standardnotes/models'
import { ClientDisplayableError } from '@standardnotes/responses' import { ClientDisplayableError } from '@standardnotes/responses'
import { FileBackupMetadataFile, FileBackupsDevice, FileBackupsMapping } from '../Device/FileBackupsDevice' import { FilesApiInterface, FileBackupMetadataFile, FileBackupsDevice, FileBackupsMapping } from '@standardnotes/files'
import { FilesApiInterface } from '@standardnotes/files'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface' import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { ItemManagerInterface } from '../Item/ItemManagerInterface' import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { AbstractService } from '../Service/AbstractService' import { AbstractService } from '../Service/AbstractService'

View File

@@ -1,5 +1,5 @@
import { DecryptedTransferPayload } from '@standardnotes/models' import { DecryptedTransferPayload } from '@standardnotes/models'
import { FileBackupsDevice } from './FileBackupsDevice' import { FileBackupsDevice } from '@standardnotes/files'
export interface WebClientRequiresDesktopMethods extends FileBackupsDevice { export interface WebClientRequiresDesktopMethods extends FileBackupsDevice {
localBackupsCount(): Promise<number> localBackupsCount(): Promise<number>

View File

@@ -5,6 +5,15 @@ import {
leftVersionGreaterThanOrEqualToRight, leftVersionGreaterThanOrEqualToRight,
ProtocolVersion, ProtocolVersion,
} from '@standardnotes/common' } from '@standardnotes/common'
import {
BackupFileType,
ContentTypeUsesRootKeyEncryption,
CreateAnyKeyParams,
isItemsKey,
SNItemsKey,
SNRootKey,
SNRootKeyParams,
} from '@standardnotes/encryption'
import { import {
BackupFile, BackupFile,
CreateDecryptedItemFromPayload, CreateDecryptedItemFromPayload,
@@ -23,13 +32,7 @@ import {
} from '@standardnotes/models' } from '@standardnotes/models'
import { ClientDisplayableError } from '@standardnotes/responses' import { ClientDisplayableError } from '@standardnotes/responses'
import { extendArray } from '@standardnotes/utils' import { extendArray } from '@standardnotes/utils'
import { isItemsKey, SNItemsKey } from '../Keys/ItemsKey/ItemsKey' import { EncryptionService } from './EncryptionService'
import { ContentTypeUsesRootKeyEncryption } from '../Keys/RootKey/Functions'
import { CreateAnyKeyParams } from '../Keys/RootKey/KeyParamsFunctions'
import { SNRootKey } from '../Keys/RootKey/RootKey'
import { SNRootKeyParams } from '../Keys/RootKey/RootKeyParams'
import { EncryptionService } from '../Service/Encryption/EncryptionService'
import { BackupFileType } from './BackupFileType'
export async function DecryptBackupFile( export async function DecryptBackupFile(
file: BackupFile, file: BackupFile,

View File

@@ -1,53 +1,77 @@
import * as Common from '@standardnotes/common' import {
import * as Models from '@standardnotes/models' CreateAnyKeyParams,
CreateEncryptionSplitWithKeyLookup,
DecryptedParameters,
EncryptedParameters,
encryptedParametersFromPayload,
EncryptionProvider,
ErrorDecryptingParameters,
findDefaultItemsKey,
FindPayloadInDecryptionSplit,
FindPayloadInEncryptionSplit,
isErrorDecryptingParameters,
ItemAuthenticatedData,
KeyedDecryptionSplit,
KeyedEncryptionSplit,
KeyMode,
LegacyAttachedData,
OperatorManager,
RootKeyEncryptedAuthenticatedData,
RootKeyServiceEvent,
SNRootKey,
SNRootKeyParams,
SplitPayloadsByEncryptionType,
V001Algorithm,
V002Algorithm,
} from '@standardnotes/encryption'
import { import {
BackupFile, BackupFile,
CreateDecryptedBackupFileContextPayload, CreateDecryptedBackupFileContextPayload,
CreateEncryptedBackupFileContextPayload, CreateEncryptedBackupFileContextPayload,
DecryptedPayload,
DecryptedPayloadInterface,
EncryptedPayload, EncryptedPayload,
EncryptedPayloadInterface,
isDecryptedPayload, isDecryptedPayload,
isEncryptedPayload, isEncryptedPayload,
ItemContent,
ItemsKeyInterface,
RootKeyInterface, RootKeyInterface,
} from '@standardnotes/models' } from '@standardnotes/models'
import { ClientDisplayableError } from '@standardnotes/responses' import { ClientDisplayableError } from '@standardnotes/responses'
import * as Services from '@standardnotes/services'
import { DiagnosticInfo } from '@standardnotes/services'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common' import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import * as Utils from '@standardnotes/utils'
import { isNotUndefined } from '@standardnotes/utils'
import { V001Algorithm, V002Algorithm } from '../../Algorithm'
import { DecryptBackupFile } from '../../Backups/BackupFileDecryptor'
import { CreateAnyKeyParams } from '../../Keys/RootKey/KeyParamsFunctions'
import { SNRootKey } from '../../Keys/RootKey/RootKey'
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
import { OperatorManager } from '../../Operator/OperatorManager'
import { import {
CreateEncryptionSplitWithKeyLookup, extendArray,
FindPayloadInDecryptionSplit, isNotUndefined,
FindPayloadInEncryptionSplit, isNullOrUndefined,
} from '../../Split/EncryptionSplit' isReactNativeEnvironment,
import { SplitPayloadsByEncryptionType } from '../../Split/Functions' isWebCryptoAvailable,
import { KeyedDecryptionSplit } from '../../Split/KeyedDecryptionSplit' UuidGenerator,
import { KeyedEncryptionSplit } from '../../Split/KeyedEncryptionSplit' } from '@standardnotes/utils'
import { import {
DecryptedParameters, AnyKeyParamsContent,
EncryptedParameters, ApplicationIdentifier,
encryptedParametersFromPayload, compareVersions,
ErrorDecryptingParameters, ContentType,
isErrorDecryptingParameters, isVersionLessThanOrEqualTo,
} from '../../Types/EncryptedParameters' KeyParamsOrigination,
import { ItemAuthenticatedData } from '../../Types/ItemAuthenticatedData' ProtocolVersion,
import { LegacyAttachedData } from '../../Types/LegacyAttachedData' ProtocolVersionLastNonrootItemsKey,
import { RootKeyEncryptedAuthenticatedData } from '../../Types/RootKeyEncryptedAuthenticatedData' ProtocolVersionLatest,
import { findDefaultItemsKey } from '../Functions' } from '@standardnotes/common'
import { ItemsEncryptionService } from '../Items/ItemsEncryption'
import { KeyMode } from '../RootKey/KeyMode'
import * as RootKeyEncryption from '../RootKey/RootKeyEncryption'
import { EncryptionProvider } from './EncryptionProvider'
export enum EncryptionServiceEvent { import { AbstractService } from '../Service/AbstractService'
RootKeyStatusChanged = 'RootKeyStatusChanged', import { ItemsEncryptionService } from './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 { DiagnosticInfo } from '../Diagnostics/ServiceDiagnostics'
import { RootKeyEncryptionService } from './RootKeyEncryption'
import { DecryptBackupFile } from './BackupFileDecryptor'
import { EncryptionServiceEvent } from './EncryptionServiceEvent'
/** /**
* The encryption service is responsible for the encryption and decryption of payloads, and * The encryption service is responsible for the encryption and decryption of payloads, and
@@ -76,20 +100,20 @@ export enum EncryptionServiceEvent {
* It also exposes public methods that allows consumers to retrieve an items key * It also exposes public methods that allows consumers to retrieve an items key
* for a particular payload, and also retrieve all available items keys. * for a particular payload, and also retrieve all available items keys.
*/ */
export class EncryptionService extends Services.AbstractService<EncryptionServiceEvent> implements EncryptionProvider { export class EncryptionService extends AbstractService<EncryptionServiceEvent> implements EncryptionProvider {
private operatorManager: OperatorManager private operatorManager: OperatorManager
private readonly itemsEncryption: ItemsEncryptionService private readonly itemsEncryption: ItemsEncryptionService
private readonly rootKeyEncryption: RootKeyEncryption.RootKeyEncryptionService private readonly rootKeyEncryption: RootKeyEncryptionService
private rootKeyObserverDisposer: () => void private rootKeyObserverDisposer: () => void
constructor( constructor(
private itemManager: Services.ItemManagerInterface, private itemManager: ItemManagerInterface,
private payloadManager: Services.PayloadManagerInterface, private payloadManager: PayloadManagerInterface,
public deviceInterface: Services.DeviceInterface, public deviceInterface: DeviceInterface,
private storageService: Services.StorageServiceInterface, private storageService: StorageServiceInterface,
private identifier: Common.ApplicationIdentifier, private identifier: ApplicationIdentifier,
public crypto: PureCryptoInterface, public crypto: PureCryptoInterface,
protected override internalEventBus: Services.InternalEventBusInterface, protected override internalEventBus: InternalEventBusInterface,
) { ) {
super(internalEventBus) super(internalEventBus)
this.crypto = crypto this.crypto = crypto
@@ -104,7 +128,7 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
internalEventBus, internalEventBus,
) )
this.rootKeyEncryption = new RootKeyEncryption.RootKeyEncryptionService( this.rootKeyEncryption = new RootKeyEncryptionService(
this.itemManager, this.itemManager,
this.operatorManager, this.operatorManager,
this.deviceInterface, this.deviceInterface,
@@ -114,12 +138,12 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
) )
this.rootKeyObserverDisposer = this.rootKeyEncryption.addEventObserver((event) => { this.rootKeyObserverDisposer = this.rootKeyEncryption.addEventObserver((event) => {
this.itemsEncryption.userVersion = this.getUserVersion() this.itemsEncryption.userVersion = this.getUserVersion()
if (event === RootKeyEncryption.RootKeyServiceEvent.RootKeyStatusChanged) { if (event === RootKeyServiceEvent.RootKeyStatusChanged) {
void this.notifyEvent(EncryptionServiceEvent.RootKeyStatusChanged) void this.notifyEvent(EncryptionServiceEvent.RootKeyStatusChanged)
} }
}) })
Utils.UuidGenerator.SetGenerator(this.crypto.generateUUID) UuidGenerator.SetGenerator(this.crypto.generateUUID)
} }
public override deinit(): void { public override deinit(): void {
@@ -160,7 +184,7 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
} }
public getLatestVersion() { public getLatestVersion() {
return Common.ProtocolVersionLatest return ProtocolVersionLatest
} }
public hasAccount() { public hasAccount() {
@@ -171,7 +195,7 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
return this.rootKeyEncryption.hasRootKeyEncryptionSource() return this.rootKeyEncryption.hasRootKeyEncryptionSource()
} }
public getUserVersion(): Common.ProtocolVersion | undefined { public getUserVersion(): ProtocolVersion | undefined {
return this.rootKeyEncryption.getUserVersion() return this.rootKeyEncryption.getUserVersion()
} }
@@ -181,8 +205,8 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
return accountUpgradeAvailable || passcodeUpgradeAvailable return accountUpgradeAvailable || passcodeUpgradeAvailable
} }
public getSureDefaultItemsKey(): Models.ItemsKeyInterface { public getSureDefaultItemsKey(): ItemsKeyInterface {
return this.itemsEncryption.getDefaultItemsKey() as Models.ItemsKeyInterface return this.itemsEncryption.getDefaultItemsKey() as ItemsKeyInterface
} }
async repersistAllItems(): Promise<void> { async repersistAllItems(): Promise<void> {
@@ -201,22 +225,22 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
await this.itemsEncryption.decryptErroredPayloads() await this.itemsEncryption.decryptErroredPayloads()
} }
public itemsKeyForPayload(payload: Models.EncryptedPayloadInterface): Models.ItemsKeyInterface | undefined { public itemsKeyForPayload(payload: EncryptedPayloadInterface): ItemsKeyInterface | undefined {
return this.itemsEncryption.itemsKeyForPayload(payload) return this.itemsEncryption.itemsKeyForPayload(payload)
} }
public defaultItemsKeyForItemVersion( public defaultItemsKeyForItemVersion(
version: Common.ProtocolVersion, version: ProtocolVersion,
fromKeys?: Models.ItemsKeyInterface[], fromKeys?: ItemsKeyInterface[],
): Models.ItemsKeyInterface | undefined { ): ItemsKeyInterface | undefined {
return this.itemsEncryption.defaultItemsKeyForItemVersion(version, fromKeys) return this.itemsEncryption.defaultItemsKeyForItemVersion(version, fromKeys)
} }
public async encryptSplitSingle(split: KeyedEncryptionSplit): Promise<Models.EncryptedPayloadInterface> { public async encryptSplitSingle(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface> {
return (await this.encryptSplit(split))[0] return (await this.encryptSplit(split))[0]
} }
public async encryptSplit(split: KeyedEncryptionSplit): Promise<Models.EncryptedPayloadInterface[]> { public async encryptSplit(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface[]> {
const allEncryptedParams: EncryptedParameters[] = [] const allEncryptedParams: EncryptedParameters[] = []
if (split.usesRootKey) { if (split.usesRootKey) {
@@ -224,7 +248,7 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
split.usesRootKey.items, split.usesRootKey.items,
split.usesRootKey.key, split.usesRootKey.key,
) )
Utils.extendArray(allEncryptedParams, rootKeyEncrypted) extendArray(allEncryptedParams, rootKeyEncrypted)
} }
if (split.usesItemsKey) { if (split.usesItemsKey) {
@@ -232,21 +256,21 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
split.usesItemsKey.items, split.usesItemsKey.items,
split.usesItemsKey.key, split.usesItemsKey.key,
) )
Utils.extendArray(allEncryptedParams, itemsKeyEncrypted) extendArray(allEncryptedParams, itemsKeyEncrypted)
} }
if (split.usesRootKeyWithKeyLookup) { if (split.usesRootKeyWithKeyLookup) {
const rootKeyEncrypted = await this.rootKeyEncryption.encryptPayloadsWithKeyLookup( const rootKeyEncrypted = await this.rootKeyEncryption.encryptPayloadsWithKeyLookup(
split.usesRootKeyWithKeyLookup.items, split.usesRootKeyWithKeyLookup.items,
) )
Utils.extendArray(allEncryptedParams, rootKeyEncrypted) extendArray(allEncryptedParams, rootKeyEncrypted)
} }
if (split.usesItemsKeyWithKeyLookup) { if (split.usesItemsKeyWithKeyLookup) {
const itemsKeyEncrypted = await this.itemsEncryption.encryptPayloadsWithKeyLookup( const itemsKeyEncrypted = await this.itemsEncryption.encryptPayloadsWithKeyLookup(
split.usesItemsKeyWithKeyLookup.items, split.usesItemsKeyWithKeyLookup.items,
) )
Utils.extendArray(allEncryptedParams, itemsKeyEncrypted) extendArray(allEncryptedParams, itemsKeyEncrypted)
} }
const packagedEncrypted = allEncryptedParams.map((encryptedParams) => { const packagedEncrypted = allEncryptedParams.map((encryptedParams) => {
@@ -263,17 +287,17 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
} }
public async decryptSplitSingle< public async decryptSplitSingle<
C extends Models.ItemContent = Models.ItemContent, C extends ItemContent = ItemContent,
P extends Models.DecryptedPayloadInterface<C> = Models.DecryptedPayloadInterface<C>, P extends DecryptedPayloadInterface<C> = DecryptedPayloadInterface<C>,
>(split: KeyedDecryptionSplit): Promise<P | Models.EncryptedPayloadInterface> { >(split: KeyedDecryptionSplit): Promise<P | EncryptedPayloadInterface> {
const results = await this.decryptSplit<C, P>(split) const results = await this.decryptSplit<C, P>(split)
return results[0] return results[0]
} }
public async decryptSplit< public async decryptSplit<
C extends Models.ItemContent = Models.ItemContent, C extends ItemContent = ItemContent,
P extends Models.DecryptedPayloadInterface<C> = Models.DecryptedPayloadInterface<C>, P extends DecryptedPayloadInterface<C> = DecryptedPayloadInterface<C>,
>(split: KeyedDecryptionSplit): Promise<(P | Models.EncryptedPayloadInterface)[]> { >(split: KeyedDecryptionSplit): Promise<(P | EncryptedPayloadInterface)[]> {
const resultParams: (DecryptedParameters<C> | ErrorDecryptingParameters)[] = [] const resultParams: (DecryptedParameters<C> | ErrorDecryptingParameters)[] = []
if (split.usesRootKey) { if (split.usesRootKey) {
@@ -281,14 +305,14 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
split.usesRootKey.items, split.usesRootKey.items,
split.usesRootKey.key, split.usesRootKey.key,
) )
Utils.extendArray(resultParams, rootKeyDecrypted) extendArray(resultParams, rootKeyDecrypted)
} }
if (split.usesRootKeyWithKeyLookup) { if (split.usesRootKeyWithKeyLookup) {
const rootKeyDecrypted = await this.rootKeyEncryption.decryptPayloadsWithKeyLookup<C>( const rootKeyDecrypted = await this.rootKeyEncryption.decryptPayloadsWithKeyLookup<C>(
split.usesRootKeyWithKeyLookup.items, split.usesRootKeyWithKeyLookup.items,
) )
Utils.extendArray(resultParams, rootKeyDecrypted) extendArray(resultParams, rootKeyDecrypted)
} }
if (split.usesItemsKey) { if (split.usesItemsKey) {
@@ -296,26 +320,26 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
split.usesItemsKey.items, split.usesItemsKey.items,
split.usesItemsKey.key, split.usesItemsKey.key,
) )
Utils.extendArray(resultParams, itemsKeyDecrypted) extendArray(resultParams, itemsKeyDecrypted)
} }
if (split.usesItemsKeyWithKeyLookup) { if (split.usesItemsKeyWithKeyLookup) {
const itemsKeyDecrypted = await this.itemsEncryption.decryptPayloadsWithKeyLookup<C>( const itemsKeyDecrypted = await this.itemsEncryption.decryptPayloadsWithKeyLookup<C>(
split.usesItemsKeyWithKeyLookup.items, split.usesItemsKeyWithKeyLookup.items,
) )
Utils.extendArray(resultParams, itemsKeyDecrypted) extendArray(resultParams, itemsKeyDecrypted)
} }
const packagedResults = resultParams.map((params) => { const packagedResults = resultParams.map((params) => {
const original = FindPayloadInDecryptionSplit(params.uuid, split) const original = FindPayloadInDecryptionSplit(params.uuid, split)
if (isErrorDecryptingParameters(params)) { if (isErrorDecryptingParameters(params)) {
return new Models.EncryptedPayload({ return new EncryptedPayload({
...original.ejected(), ...original.ejected(),
...params, ...params,
}) })
} else { } else {
return new Models.DecryptedPayload<C>({ return new DecryptedPayload<C>({
...original.ejected(), ...original.ejected(),
...params, ...params,
}) as P }) as P
@@ -333,7 +357,7 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
if (!userVersion) { if (!userVersion) {
return false return false
} }
return userVersion !== Common.ProtocolVersionLatest return userVersion !== ProtocolVersionLatest
} }
/** /**
@@ -354,29 +378,24 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
* *
* Versions 004 and above are always supported. * Versions 004 and above are always supported.
*/ */
if (Common.compareVersions(keyParams.version, Common.ProtocolVersion.V004) >= 0) { if (compareVersions(keyParams.version, ProtocolVersion.V004) >= 0) {
/* keyParams.version >= 004 */ /* keyParams.version >= 004 */
return true return true
} else { } else {
return !!Utils.isWebCryptoAvailable() || Utils.isReactNativeEnvironment() return !!isWebCryptoAvailable() || isReactNativeEnvironment()
} }
} }
public supportedVersions(): Common.ProtocolVersion[] { public supportedVersions(): ProtocolVersion[] {
return [ return [ProtocolVersion.V001, ProtocolVersion.V002, ProtocolVersion.V003, ProtocolVersion.V004]
Common.ProtocolVersion.V001,
Common.ProtocolVersion.V002,
Common.ProtocolVersion.V003,
Common.ProtocolVersion.V004,
]
} }
/** /**
* Determines whether the input version is greater than the latest supported library version. * Determines whether the input version is greater than the latest supported library version.
*/ */
public isVersionNewerThanLibraryVersion(version: Common.ProtocolVersion) { public isVersionNewerThanLibraryVersion(version: ProtocolVersion) {
const libraryVersion = Common.ProtocolVersionLatest const libraryVersion = ProtocolVersionLatest
return Common.compareVersions(version, libraryVersion) === 1 return compareVersions(version, libraryVersion) === 1
} }
/** /**
@@ -384,13 +403,13 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
* This function returns the client-enforced minimum cost, to prevent the server from * This function returns the client-enforced minimum cost, to prevent the server from
* overwhelmingly under-reporting the cost. * overwhelmingly under-reporting the cost.
*/ */
public costMinimumForVersion(version: Common.ProtocolVersion) { public costMinimumForVersion(version: ProtocolVersion) {
if (Common.compareVersions(version, Common.ProtocolVersion.V003) >= 0) { if (compareVersions(version, ProtocolVersion.V003) >= 0) {
throw 'Cost minimums only apply to versions <= 002' throw 'Cost minimums only apply to versions <= 002'
} }
if (version === Common.ProtocolVersion.V001) { if (version === ProtocolVersion.V001) {
return V001Algorithm.PbkdfMinCost return V001Algorithm.PbkdfMinCost
} else if (version === Common.ProtocolVersion.V002) { } else if (version === ProtocolVersion.V002) {
return V002Algorithm.PbkdfMinCost return V002Algorithm.PbkdfMinCost
} else { } else {
throw `Invalid version for cost minimum: ${version}` throw `Invalid version for cost minimum: ${version}`
@@ -411,8 +430,8 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
public async createRootKey( public async createRootKey(
identifier: string, identifier: string,
password: string, password: string,
origination: Common.KeyParamsOrigination, origination: KeyParamsOrigination,
version?: Common.ProtocolVersion, version?: ProtocolVersion,
) { ) {
return this.rootKeyEncryption.createRootKey(identifier, password, origination, version) return this.rootKeyEncryption.createRootKey(identifier, password, origination, version)
} }
@@ -420,9 +439,7 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
public async decryptBackupFile( public async decryptBackupFile(
file: BackupFile, file: BackupFile,
password?: string, password?: string,
): Promise< ): Promise<ClientDisplayableError | (EncryptedPayloadInterface | DecryptedPayloadInterface<ItemContent>)[]> {
ClientDisplayableError | (Models.EncryptedPayloadInterface | Models.DecryptedPayloadInterface<Models.ItemContent>)[]
> {
const result = await DecryptBackupFile(file, this, password) const result = await DecryptBackupFile(file, this, password)
return result return result
} }
@@ -431,7 +448,7 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
* Creates a key params object from a raw object * Creates a key params object from a raw object
* @param keyParams - The raw key params object to create a KeyParams object from * @param keyParams - The raw key params object to create a KeyParams object from
*/ */
public createKeyParams(keyParams: Common.AnyKeyParamsContent) { public createKeyParams(keyParams: AnyKeyParamsContent) {
return CreateAnyKeyParams(keyParams) return CreateAnyKeyParams(keyParams)
} }
@@ -447,7 +464,7 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
const ejected = result.map((payload) => CreateEncryptedBackupFileContextPayload(payload)) const ejected = result.map((payload) => CreateEncryptedBackupFileContextPayload(payload))
const data: BackupFile = { const data: BackupFile = {
version: Common.ProtocolVersionLatest, version: ProtocolVersionLatest,
items: ejected, items: ejected,
} }
@@ -457,12 +474,10 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
} }
public createDecryptedBackupFile(): BackupFile { public createDecryptedBackupFile(): BackupFile {
const payloads = this.payloadManager.nonDeletedItems.filter( const payloads = this.payloadManager.nonDeletedItems.filter((item) => item.content_type !== ContentType.ItemsKey)
(item) => item.content_type !== Common.ContentType.ItemsKey,
)
const data: BackupFile = { const data: BackupFile = {
version: Common.ProtocolVersionLatest, version: ProtocolVersionLatest,
items: payloads items: payloads
.map((payload) => { .map((payload) => {
if (isDecryptedPayload(payload)) { if (isDecryptedPayload(payload)) {
@@ -557,7 +572,7 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
} }
public getEmbeddedPayloadAuthenticatedData( public getEmbeddedPayloadAuthenticatedData(
payload: Models.EncryptedPayloadInterface, payload: EncryptedPayloadInterface,
): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData | undefined { ): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData | undefined {
const version = payload.version const version = payload.version
if (!version) { if (!version) {
@@ -569,12 +584,12 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
} }
/** Returns the key params attached to this key's encrypted payload */ /** Returns the key params attached to this key's encrypted payload */
public getKeyEmbeddedKeyParams(key: Models.EncryptedPayloadInterface): SNRootKeyParams | undefined { public getKeyEmbeddedKeyParams(key: EncryptedPayloadInterface): SNRootKeyParams | undefined {
const authenticatedData = this.getEmbeddedPayloadAuthenticatedData(key) const authenticatedData = this.getEmbeddedPayloadAuthenticatedData(key)
if (!authenticatedData) { if (!authenticatedData) {
return undefined return undefined
} }
if (Common.isVersionLessThanOrEqualTo(key.version, Common.ProtocolVersion.V003)) { if (isVersionLessThanOrEqualTo(key.version, ProtocolVersion.V003)) {
const rawKeyParams = authenticatedData as LegacyAttachedData const rawKeyParams = authenticatedData as LegacyAttachedData
return this.createKeyParams(rawKeyParams) return this.createKeyParams(rawKeyParams)
} else { } else {
@@ -597,7 +612,7 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
return false return false
} }
if (Common.compareVersions(rootKey.keyVersion, Common.ProtocolVersionLastNonrootItemsKey) > 0) { if (compareVersions(rootKey.keyVersion, ProtocolVersionLastNonrootItemsKey) > 0) {
/** Is >= 004, not needed */ /** Is >= 004, not needed */
return false return false
} }
@@ -616,7 +631,7 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
return defaultItemsKey.itemsKey !== rootKey.itemsKey return defaultItemsKey.itemsKey !== rootKey.itemsKey
} }
public async createNewDefaultItemsKey(): Promise<Models.ItemsKeyInterface> { public async createNewDefaultItemsKey(): Promise<ItemsKeyInterface> {
return this.rootKeyEncryption.createNewDefaultItemsKey() return this.rootKeyEncryption.createNewDefaultItemsKey()
} }
@@ -625,11 +640,11 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
return rootKey ? rootKey.keyParams.createdDate : undefined return rootKey ? rootKey.keyParams.createdDate : undefined
} }
public async onSyncEvent(eventName: Services.SyncEvent) { public async onSyncEvent(eventName: SyncEvent) {
if (eventName === Services.SyncEvent.SyncCompletedWithAllItemsUploaded) { if (eventName === SyncEvent.SyncCompletedWithAllItemsUploaded) {
await this.handleFullSyncCompletion() await this.handleFullSyncCompletion()
} }
if (eventName === Services.SyncEvent.DownloadFirstSyncCompleted) { if (eventName === SyncEvent.DownloadFirstSyncCompleted) {
await this.handleDownloadFirstSyncCompletion() await this.handleDownloadFirstSyncCompletion()
} }
} }
@@ -665,7 +680,7 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
return key.isDefault return key.isDefault
}) })
const hasSyncedItemsKey = !Utils.isNullOrUndefined(defaultSyncedKey) const hasSyncedItemsKey = !isNullOrUndefined(defaultSyncedKey)
if (hasSyncedItemsKey) { if (hasSyncedItemsKey) {
/** Delete all never synced keys */ /** Delete all never synced keys */
await this.itemManager.setItemsToBeDeleted(neverSyncedKeys) await this.itemManager.setItemsToBeDeleted(neverSyncedKeys)
@@ -695,7 +710,7 @@ export class EncryptionService extends Services.AbstractService<EncryptionServic
/** If we do not have an items key for our current account version, create one */ /** If we do not have an items key for our current account version, create one */
const userVersion = this.getUserVersion() const userVersion = this.getUserVersion()
const accountVersionedKey = this.itemsEncryption.getItemsKeys().find((key) => key.keyVersion === userVersion) const accountVersionedKey = this.itemsEncryption.getItemsKeys().find((key) => key.keyVersion === userVersion)
if (Utils.isNullOrUndefined(accountVersionedKey)) { if (isNullOrUndefined(accountVersionedKey)) {
await this.rootKeyEncryption.createNewDefaultItemsKey() await this.rootKeyEncryption.createNewDefaultItemsKey()
} }

View File

@@ -0,0 +1,3 @@
export enum EncryptionServiceEvent {
RootKeyStatusChanged = 'RootKeyStatusChanged',
}

View File

@@ -5,15 +5,11 @@ import {
ItemsKeyContent, ItemsKeyContent,
RootKeyInterface, RootKeyInterface,
} from '@standardnotes/models' } from '@standardnotes/models'
import { import { EncryptionProvider, KeyRecoveryStrings, SNRootKeyParams } from '@standardnotes/encryption'
ChallengePrompt, import { ChallengeServiceInterface } from '../Challenge/ChallengeServiceInterface'
ChallengeReason, import { ChallengePrompt } from '../Challenge/Prompt/ChallengePrompt'
ChallengeServiceInterface, import { ChallengeReason } from '../Challenge/Types/ChallengeReason'
ChallengeValidation, import { ChallengeValidation } from '../Challenge/Types/ChallengeValidation'
} from '@standardnotes/services'
import { EncryptionProvider } from '../../Service/Encryption/EncryptionProvider'
import { SNRootKeyParams } from '../RootKey/RootKeyParams'
import { KeyRecoveryStrings } from './KeyRecoveryStrings'
export async function DecryptItemsKeyWithUserFallback( export async function DecryptItemsKeyWithUserFallback(
itemsKey: EncryptedPayloadInterface, itemsKey: EncryptedPayloadInterface,

View File

@@ -1,30 +1,45 @@
import { ContentType, ProtocolVersion } from '@standardnotes/common' import { ContentType, ProtocolVersion } from '@standardnotes/common'
import * as Models from '@standardnotes/models'
import { isEncryptedPayload } from '@standardnotes/models'
import * as Services from '@standardnotes/services'
import { DiagnosticInfo } from '@standardnotes/services'
import { Uuids } from '@standardnotes/utils'
import { OperatorManager } from '../../Operator/OperatorManager'
import * as OperatorWrapper from '../../Operator/OperatorWrapper'
import { StandardException } from '../../StandardException'
import { import {
DecryptedParameters, DecryptedParameters,
EncryptedParameters, EncryptedParameters,
ErrorDecryptingParameters, ErrorDecryptingParameters,
findDefaultItemsKey,
isErrorDecryptingParameters, isErrorDecryptingParameters,
} from '../../Types/EncryptedParameters' OperatorManager,
import { findDefaultItemsKey } from '../Functions' StandardException,
encryptPayload,
decryptPayload,
} from '@standardnotes/encryption'
import {
DecryptedPayload,
DecryptedPayloadInterface,
EncryptedPayload,
EncryptedPayloadInterface,
isEncryptedPayload,
ItemContent,
ItemsKeyInterface,
PayloadEmitSource,
SureFindPayload,
} from '@standardnotes/models'
import { Uuids } from '@standardnotes/utils'
export class ItemsEncryptionService extends Services.AbstractService { import { DiagnosticInfo } from '../Diagnostics/ServiceDiagnostics'
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'
export class ItemsEncryptionService extends AbstractService {
private removeItemsObserver!: () => void private removeItemsObserver!: () => void
public userVersion?: ProtocolVersion public userVersion?: ProtocolVersion
constructor( constructor(
private itemManager: Services.ItemManagerInterface, private itemManager: ItemManagerInterface,
private payloadManager: Services.PayloadManagerInterface, private payloadManager: PayloadManagerInterface,
private storageService: Services.StorageServiceInterface, private storageService: StorageServiceInterface,
private operatorManager: OperatorManager, private operatorManager: OperatorManager,
protected override internalEventBus: Services.InternalEventBusInterface, protected override internalEventBus: InternalEventBusInterface,
) { ) {
super(internalEventBus) super(internalEventBus)
@@ -59,19 +74,19 @@ export class ItemsEncryptionService extends Services.AbstractService {
return this.itemManager.getDisplayableItemsKeys() return this.itemManager.getDisplayableItemsKeys()
} }
public itemsKeyForPayload(payload: Models.EncryptedPayloadInterface): Models.ItemsKeyInterface | undefined { public itemsKeyForPayload(payload: EncryptedPayloadInterface): ItemsKeyInterface | undefined {
return this.getItemsKeys().find( return this.getItemsKeys().find(
(key) => key.uuid === payload.items_key_id || key.duplicateOf === payload.items_key_id, (key) => key.uuid === payload.items_key_id || key.duplicateOf === payload.items_key_id,
) )
} }
public getDefaultItemsKey(): Models.ItemsKeyInterface | undefined { public getDefaultItemsKey(): ItemsKeyInterface | undefined {
return findDefaultItemsKey(this.getItemsKeys()) return findDefaultItemsKey(this.getItemsKeys())
} }
private keyToUseForItemEncryption(): Models.ItemsKeyInterface | StandardException { private keyToUseForItemEncryption(): ItemsKeyInterface | StandardException {
const defaultKey = this.getDefaultItemsKey() const defaultKey = this.getDefaultItemsKey()
let result: Models.ItemsKeyInterface | undefined = undefined let result: ItemsKeyInterface | undefined = undefined
if (this.userVersion && this.userVersion !== defaultKey?.keyVersion) { if (this.userVersion && this.userVersion !== defaultKey?.keyVersion) {
/** /**
@@ -92,9 +107,7 @@ export class ItemsEncryptionService extends Services.AbstractService {
return result return result
} }
private keyToUseForDecryptionOfPayload( private keyToUseForDecryptionOfPayload(payload: EncryptedPayloadInterface): ItemsKeyInterface | undefined {
payload: Models.EncryptedPayloadInterface,
): Models.ItemsKeyInterface | undefined {
if (payload.items_key_id) { if (payload.items_key_id) {
const itemsKey = this.itemsKeyForPayload(payload) const itemsKey = this.itemsKeyForPayload(payload)
return itemsKey return itemsKey
@@ -104,7 +117,7 @@ export class ItemsEncryptionService extends Services.AbstractService {
return defaultKey return defaultKey
} }
public async encryptPayloadWithKeyLookup(payload: Models.DecryptedPayloadInterface): Promise<EncryptedParameters> { public async encryptPayloadWithKeyLookup(payload: DecryptedPayloadInterface): Promise<EncryptedParameters> {
const key = this.keyToUseForItemEncryption() const key = this.keyToUseForItemEncryption()
if (key instanceof StandardException) { if (key instanceof StandardException) {
@@ -115,8 +128,8 @@ export class ItemsEncryptionService extends Services.AbstractService {
} }
public async encryptPayload( public async encryptPayload(
payload: Models.DecryptedPayloadInterface, payload: DecryptedPayloadInterface,
key: Models.ItemsKeyInterface, key: ItemsKeyInterface,
): Promise<EncryptedParameters> { ): Promise<EncryptedParameters> {
if (isEncryptedPayload(payload)) { if (isEncryptedPayload(payload)) {
throw Error('Attempting to encrypt already encrypted payload.') throw Error('Attempting to encrypt already encrypted payload.')
@@ -128,24 +141,22 @@ export class ItemsEncryptionService extends Services.AbstractService {
throw Error('Attempting to encrypt payload with no UuidGenerator.') throw Error('Attempting to encrypt payload with no UuidGenerator.')
} }
return OperatorWrapper.encryptPayload(payload, key, this.operatorManager) return encryptPayload(payload, key, this.operatorManager)
} }
public async encryptPayloads( public async encryptPayloads(
payloads: Models.DecryptedPayloadInterface[], payloads: DecryptedPayloadInterface[],
key: Models.ItemsKeyInterface, key: ItemsKeyInterface,
): Promise<EncryptedParameters[]> { ): Promise<EncryptedParameters[]> {
return Promise.all(payloads.map((payload) => this.encryptPayload(payload, key))) return Promise.all(payloads.map((payload) => this.encryptPayload(payload, key)))
} }
public async encryptPayloadsWithKeyLookup( public async encryptPayloadsWithKeyLookup(payloads: DecryptedPayloadInterface[]): Promise<EncryptedParameters[]> {
payloads: Models.DecryptedPayloadInterface[],
): Promise<EncryptedParameters[]> {
return Promise.all(payloads.map((payload) => this.encryptPayloadWithKeyLookup(payload))) return Promise.all(payloads.map((payload) => this.encryptPayloadWithKeyLookup(payload)))
} }
public async decryptPayloadWithKeyLookup<C extends Models.ItemContent = Models.ItemContent>( public async decryptPayloadWithKeyLookup<C extends ItemContent = ItemContent>(
payload: Models.EncryptedPayloadInterface, payload: EncryptedPayloadInterface,
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> { ): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
const key = this.keyToUseForDecryptionOfPayload(payload) const key = this.keyToUseForDecryptionOfPayload(payload)
@@ -160,9 +171,9 @@ export class ItemsEncryptionService extends Services.AbstractService {
return this.decryptPayload(payload, key) return this.decryptPayload(payload, key)
} }
public async decryptPayload<C extends Models.ItemContent = Models.ItemContent>( public async decryptPayload<C extends ItemContent = ItemContent>(
payload: Models.EncryptedPayloadInterface, payload: EncryptedPayloadInterface,
key: Models.ItemsKeyInterface, key: ItemsKeyInterface,
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> { ): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
if (!payload.content) { if (!payload.content) {
return { return {
@@ -171,18 +182,18 @@ export class ItemsEncryptionService extends Services.AbstractService {
} }
} }
return OperatorWrapper.decryptPayload(payload, key, this.operatorManager) return decryptPayload(payload, key, this.operatorManager)
} }
public async decryptPayloadsWithKeyLookup<C extends Models.ItemContent = Models.ItemContent>( public async decryptPayloadsWithKeyLookup<C extends ItemContent = ItemContent>(
payloads: Models.EncryptedPayloadInterface[], payloads: EncryptedPayloadInterface[],
): Promise<(DecryptedParameters<C> | ErrorDecryptingParameters)[]> { ): Promise<(DecryptedParameters<C> | ErrorDecryptingParameters)[]> {
return Promise.all(payloads.map((payload) => this.decryptPayloadWithKeyLookup<C>(payload))) return Promise.all(payloads.map((payload) => this.decryptPayloadWithKeyLookup<C>(payload)))
} }
public async decryptPayloads<C extends Models.ItemContent = Models.ItemContent>( public async decryptPayloads<C extends ItemContent = ItemContent>(
payloads: Models.EncryptedPayloadInterface[], payloads: EncryptedPayloadInterface[],
key: Models.ItemsKeyInterface, key: ItemsKeyInterface,
): Promise<(DecryptedParameters<C> | ErrorDecryptingParameters)[]> { ): Promise<(DecryptedParameters<C> | ErrorDecryptingParameters)[]> {
return Promise.all(payloads.map((payload) => this.decryptPayload<C>(payload, key))) return Promise.all(payloads.map((payload) => this.decryptPayload<C>(payload, key)))
} }
@@ -196,21 +207,21 @@ export class ItemsEncryptionService extends Services.AbstractService {
const resultParams = await this.decryptPayloadsWithKeyLookup(payloads) const resultParams = await this.decryptPayloadsWithKeyLookup(payloads)
const decryptedPayloads = resultParams.map((params) => { const decryptedPayloads = resultParams.map((params) => {
const original = Models.SureFindPayload(payloads, params.uuid) const original = SureFindPayload(payloads, params.uuid)
if (isErrorDecryptingParameters(params)) { if (isErrorDecryptingParameters(params)) {
return new Models.EncryptedPayload({ return new EncryptedPayload({
...original.ejected(), ...original.ejected(),
...params, ...params,
}) })
} else { } else {
return new Models.DecryptedPayload({ return new DecryptedPayload({
...original.ejected(), ...original.ejected(),
...params, ...params,
}) })
} }
}) })
await this.payloadManager.emitPayloads(decryptedPayloads, Models.PayloadEmitSource.LocalChanged) await this.payloadManager.emitPayloads(decryptedPayloads, PayloadEmitSource.LocalChanged)
} }
/** /**
@@ -222,8 +233,8 @@ export class ItemsEncryptionService extends Services.AbstractService {
*/ */
public defaultItemsKeyForItemVersion( public defaultItemsKeyForItemVersion(
version: ProtocolVersion, version: ProtocolVersion,
fromKeys?: Models.ItemsKeyInterface[], fromKeys?: ItemsKeyInterface[],
): Models.ItemsKeyInterface | undefined { ): ItemsKeyInterface | undefined {
/** Try to find one marked default first */ /** Try to find one marked default first */
const searchKeys = fromKeys || this.getItemsKeys() const searchKeys = fromKeys || this.getItemsKeys()
const priorityKey = searchKeys.find((key) => { const priorityKey = searchKeys.find((key) => {

View File

@@ -1,49 +1,71 @@
import * as Common from '@standardnotes/common'
import * as Models from '@standardnotes/models'
import { import {
ApplicationIdentifier,
ProtocolVersionLatest,
ProtocolVersion,
AnyKeyParamsContent,
KeyParamsOrigination,
compareVersions,
ProtocolVersionLastNonrootItemsKey,
ContentType,
} from '@standardnotes/common'
import {
RootKeyServiceEvent,
KeyMode,
SNRootKeyParams,
OperatorManager,
CreateNewRootKey,
CreateAnyKeyParams,
SNRootKey,
isErrorDecryptingParameters,
EncryptedParameters,
DecryptedParameters,
ErrorDecryptingParameters,
findDefaultItemsKey,
ItemsKeyMutator,
encryptPayload,
decryptPayload,
} from '@standardnotes/encryption'
import {
CreateDecryptedItemFromPayload,
DecryptedPayload, DecryptedPayload,
DecryptedPayloadInterface,
DecryptedTransferPayload,
EncryptedPayload,
EncryptedPayloadInterface,
EncryptedTransferPayload,
FillItemContentSpecialized, FillItemContentSpecialized,
ItemContent,
ItemsKeyContent, ItemsKeyContent,
ItemsKeyContentSpecialized, ItemsKeyContentSpecialized,
ItemsKeyInterface,
NamespacedRootKeyInKeychain, NamespacedRootKeyInKeychain,
PayloadTimestampDefaults, PayloadTimestampDefaults,
RootKeyContent, RootKeyContent,
RootKeyInterface, RootKeyInterface,
} from '@standardnotes/models' } from '@standardnotes/models'
import * as Services from '@standardnotes/services'
import { UuidGenerator } from '@standardnotes/utils' import { UuidGenerator } from '@standardnotes/utils'
import { ItemsKeyMutator } from '../../Keys/ItemsKey/ItemsKeyMutator'
import { CreateNewRootKey } from '../../Keys/RootKey/Functions'
import { CreateAnyKeyParams } from '../../Keys/RootKey/KeyParamsFunctions'
import { SNRootKey } from '../../Keys/RootKey/RootKey'
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
import { OperatorManager } from '../../Operator/OperatorManager'
import * as OperatorWrapper from '../../Operator/OperatorWrapper'
import {
DecryptedParameters,
EncryptedParameters,
ErrorDecryptingParameters,
isErrorDecryptingParameters,
} from '../../Types/EncryptedParameters'
import { findDefaultItemsKey } from '../Functions'
import { KeyMode } from './KeyMode'
export enum RootKeyServiceEvent { import { DeviceInterface } from '../Device/DeviceInterface'
RootKeyStatusChanged = 'RootKeyStatusChanged', import { DiagnosticInfo } from '../Diagnostics/ServiceDiagnostics'
} 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'
export class RootKeyEncryptionService extends Services.AbstractService<RootKeyServiceEvent> { export class RootKeyEncryptionService extends AbstractService<RootKeyServiceEvent> {
private rootKey?: RootKeyInterface private rootKey?: RootKeyInterface
public keyMode = KeyMode.RootKeyNone public keyMode = KeyMode.RootKeyNone
public memoizedRootKeyParams?: SNRootKeyParams public memoizedRootKeyParams?: SNRootKeyParams
constructor( constructor(
private itemManager: Services.ItemManagerInterface, private itemManager: ItemManagerInterface,
private operatorManager: OperatorManager, private operatorManager: OperatorManager,
public deviceInterface: Services.DeviceInterface, public deviceInterface: DeviceInterface,
private storageService: Services.StorageServiceInterface, private storageService: StorageServiceInterface,
private identifier: Common.ApplicationIdentifier, private identifier: ApplicationIdentifier,
protected override internalEventBus: Services.InternalEventBusInterface, protected override internalEventBus: InternalEventBusInterface,
) { ) {
super(internalEventBus) super(internalEventBus)
} }
@@ -89,7 +111,7 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
if (!passcodeParams) { if (!passcodeParams) {
return false return false
} }
return passcodeParams.version !== Common.ProtocolVersionLatest return passcodeParams.version !== ProtocolVersionLatest
} }
public async hasRootKeyWrapper() { public async hasRootKeyWrapper() {
@@ -118,7 +140,7 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
return this.keyMode === KeyMode.WrapperOnly || this.keyMode === KeyMode.RootKeyPlusWrapper return this.keyMode === KeyMode.WrapperOnly || this.keyMode === KeyMode.RootKeyPlusWrapper
} }
public async getEncryptionSourceVersion(): Promise<Common.ProtocolVersion> { public async getEncryptionSourceVersion(): Promise<ProtocolVersion> {
if (this.hasAccount()) { if (this.hasAccount()) {
return this.getSureUserVersion() return this.getSureUserVersion()
} else if (this.hasPasscode()) { } else if (this.hasPasscode()) {
@@ -129,12 +151,12 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
throw Error('Attempting to access encryption source version without source') throw Error('Attempting to access encryption source version without source')
} }
public getUserVersion(): Common.ProtocolVersion | undefined { public getUserVersion(): ProtocolVersion | undefined {
const keyParams = this.memoizedRootKeyParams const keyParams = this.memoizedRootKeyParams
return keyParams?.version return keyParams?.version
} }
private getSureUserVersion(): Common.ProtocolVersion { private getSureUserVersion(): ProtocolVersion {
const keyParams = this.memoizedRootKeyParams as SNRootKeyParams const keyParams = this.memoizedRootKeyParams as SNRootKeyParams
return keyParams.version return keyParams.version
} }
@@ -173,15 +195,15 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
public async getRootKeyWrapperKeyParams(): Promise<SNRootKeyParams | undefined> { public async getRootKeyWrapperKeyParams(): Promise<SNRootKeyParams | undefined> {
const rawKeyParams = await this.storageService.getValue( const rawKeyParams = await this.storageService.getValue(
Services.StorageKey.RootKeyWrapperKeyParams, StorageKey.RootKeyWrapperKeyParams,
Services.StorageValueModes.Nonwrapped, StorageValueModes.Nonwrapped,
) )
if (!rawKeyParams) { if (!rawKeyParams) {
return undefined return undefined
} }
return CreateAnyKeyParams(rawKeyParams as Common.AnyKeyParamsContent) return CreateAnyKeyParams(rawKeyParams as AnyKeyParamsContent)
} }
public async getSureRootKeyWrapperKeyParams() { public async getSureRootKeyWrapperKeyParams() {
@@ -213,8 +235,8 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
public async createRootKey( public async createRootKey(
identifier: string, identifier: string,
password: string, password: string,
origination: Common.KeyParamsOrigination, origination: KeyParamsOrigination,
version?: Common.ProtocolVersion, version?: ProtocolVersion,
) { ) {
const operator = version ? this.operatorManager.operatorForVersion(version) : this.operatorManager.defaultOperator() const operator = version ? this.operatorManager.operatorForVersion(version) : this.operatorManager.defaultOperator()
return operator.createRootKey(identifier, password, origination) return operator.createRootKey(identifier, password, origination)
@@ -261,7 +283,7 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
* account keys are encrypted with wrappingKey. Here we validate * account keys are encrypted with wrappingKey. Here we validate
* by attempting to decrypt account keys. * by attempting to decrypt account keys.
*/ */
const wrappedKeyPayload = new Models.EncryptedPayload(wrappedRootKey) const wrappedKeyPayload = new EncryptedPayload(wrappedRootKey)
const decrypted = await this.decryptPayload(wrappedKeyPayload, wrappingKey) const decrypted = await this.decryptPayload(wrappedKeyPayload, wrappingKey)
return !isErrorDecryptingParameters(decrypted) return !isErrorDecryptingParameters(decrypted)
} else { } else {
@@ -270,16 +292,13 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
} }
private async recomputeAccountKeyParams(): Promise<SNRootKeyParams | undefined> { private async recomputeAccountKeyParams(): Promise<SNRootKeyParams | undefined> {
const rawKeyParams = await this.storageService.getValue( const rawKeyParams = await this.storageService.getValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped)
Services.StorageKey.RootKeyParams,
Services.StorageValueModes.Nonwrapped,
)
if (!rawKeyParams) { if (!rawKeyParams) {
return return
} }
this.memoizedRootKeyParams = CreateAnyKeyParams(rawKeyParams as Common.AnyKeyParamsContent) this.memoizedRootKeyParams = CreateAnyKeyParams(rawKeyParams as AnyKeyParamsContent)
return this.memoizedRootKeyParams return this.memoizedRootKeyParams
} }
@@ -289,25 +308,21 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
*/ */
private async wrapAndPersistRootKey(wrappingKey: SNRootKey) { private async wrapAndPersistRootKey(wrappingKey: SNRootKey) {
const rootKey = this.getSureRootKey() const rootKey = this.getSureRootKey()
const value: Models.DecryptedTransferPayload = { const value: DecryptedTransferPayload = {
...rootKey.payload.ejected(), ...rootKey.payload.ejected(),
content: FillItemContentSpecialized(rootKey.persistableValueWhenWrapping()), content: FillItemContentSpecialized(rootKey.persistableValueWhenWrapping()),
} }
const payload = new Models.DecryptedPayload(value) const payload = new DecryptedPayload(value)
const wrappedKey = await this.encryptPayload(payload, wrappingKey) const wrappedKey = await this.encryptPayload(payload, wrappingKey)
const wrappedKeyPayload = new Models.EncryptedPayload({ const wrappedKeyPayload = new EncryptedPayload({
...payload.ejected(), ...payload.ejected(),
...wrappedKey, ...wrappedKey,
errorDecrypting: false, errorDecrypting: false,
waitingForKey: false, waitingForKey: false,
}) })
this.storageService.setValue( this.storageService.setValue(StorageKey.WrappedRootKey, wrappedKeyPayload.ejected(), StorageValueModes.Nonwrapped)
Services.StorageKey.WrappedRootKey,
wrappedKeyPayload.ejected(),
Services.StorageValueModes.Nonwrapped,
)
} }
public async unwrapRootKey(wrappingKey: RootKeyInterface) { public async unwrapRootKey(wrappingKey: RootKeyInterface) {
@@ -321,8 +336,8 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
} }
const wrappedKey = this.getWrappedRootKey() const wrappedKey = this.getWrappedRootKey()
const payload = new Models.EncryptedPayload(wrappedKey) const payload = new EncryptedPayload(wrappedKey)
const decrypted = await this.decryptPayload<Models.RootKeyContent>(payload, wrappingKey) const decrypted = await this.decryptPayload<RootKeyContent>(payload, wrappingKey)
if (isErrorDecryptingParameters(decrypted)) { if (isErrorDecryptingParameters(decrypted)) {
throw Error('Unable to decrypt root key with provided wrapping key.') throw Error('Unable to decrypt root key with provided wrapping key.')
@@ -362,9 +377,9 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
} }
this.storageService.setValue( this.storageService.setValue(
Services.StorageKey.RootKeyWrapperKeyParams, StorageKey.RootKeyWrapperKeyParams,
wrappingKey.keyParams.getPortableValue(), wrappingKey.keyParams.getPortableValue(),
Services.StorageValueModes.Nonwrapped, StorageValueModes.Nonwrapped,
) )
await this.handleKeyStatusChange() await this.handleKeyStatusChange()
@@ -388,11 +403,8 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
this.keyMode = KeyMode.RootKeyOnly this.keyMode = KeyMode.RootKeyOnly
} }
await this.storageService.removeValue(Services.StorageKey.WrappedRootKey, Services.StorageValueModes.Nonwrapped) await this.storageService.removeValue(StorageKey.WrappedRootKey, StorageValueModes.Nonwrapped)
await this.storageService.removeValue( await this.storageService.removeValue(StorageKey.RootKeyWrapperKeyParams, StorageValueModes.Nonwrapped)
Services.StorageKey.RootKeyWrapperKeyParams,
Services.StorageValueModes.Nonwrapped,
)
if (this.keyMode === KeyMode.RootKeyOnly) { if (this.keyMode === KeyMode.RootKeyOnly) {
await this.saveRootKeyToKeychain() await this.saveRootKeyToKeychain()
@@ -424,9 +436,9 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
this.setRootKeyInstance(key) this.setRootKeyInstance(key)
this.storageService.setValue( this.storageService.setValue(
Services.StorageKey.RootKeyParams, StorageKey.RootKeyParams,
key.keyParams.getPortableValue(), key.keyParams.getPortableValue(),
Services.StorageValueModes.Nonwrapped, StorageValueModes.Nonwrapped,
) )
if (this.keyMode === KeyMode.RootKeyOnly) { if (this.keyMode === KeyMode.RootKeyOnly) {
@@ -446,12 +458,9 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
*/ */
public async deleteWorkspaceSpecificKeyStateFromDevice() { public async deleteWorkspaceSpecificKeyStateFromDevice() {
await this.deviceInterface.clearNamespacedKeychainValue(this.identifier) await this.deviceInterface.clearNamespacedKeychainValue(this.identifier)
await this.storageService.removeValue(Services.StorageKey.WrappedRootKey, Services.StorageValueModes.Nonwrapped) await this.storageService.removeValue(StorageKey.WrappedRootKey, StorageValueModes.Nonwrapped)
await this.storageService.removeValue( await this.storageService.removeValue(StorageKey.RootKeyWrapperKeyParams, StorageValueModes.Nonwrapped)
Services.StorageKey.RootKeyWrapperKeyParams, await this.storageService.removeValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped)
Services.StorageValueModes.Nonwrapped,
)
await this.storageService.removeValue(Services.StorageKey.RootKeyParams, Services.StorageValueModes.Nonwrapped)
this.keyMode = KeyMode.RootKeyNone this.keyMode = KeyMode.RootKeyNone
this.setRootKeyInstance(undefined) this.setRootKeyInstance(undefined)
@@ -459,9 +468,9 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
} }
private getWrappedRootKey() { private getWrappedRootKey() {
return this.storageService.getValue<Models.EncryptedTransferPayload>( return this.storageService.getValue<EncryptedTransferPayload>(
Services.StorageKey.WrappedRootKey, StorageKey.WrappedRootKey,
Services.StorageValueModes.Nonwrapped, StorageValueModes.Nonwrapped,
) )
} }
@@ -481,7 +490,7 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
return this.itemManager.getDisplayableItemsKeys() return this.itemManager.getDisplayableItemsKeys()
} }
public async encrypPayloadWithKeyLookup(payload: Models.DecryptedPayloadInterface): Promise<EncryptedParameters> { public async encrypPayloadWithKeyLookup(payload: DecryptedPayloadInterface): Promise<EncryptedParameters> {
const key = this.getRootKey() const key = this.getRootKey()
if (key == undefined) { if (key == undefined) {
@@ -491,25 +500,20 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
return this.encryptPayload(payload, key) return this.encryptPayload(payload, key)
} }
public async encryptPayloadsWithKeyLookup( public async encryptPayloadsWithKeyLookup(payloads: DecryptedPayloadInterface[]): Promise<EncryptedParameters[]> {
payloads: Models.DecryptedPayloadInterface[],
): Promise<EncryptedParameters[]> {
return Promise.all(payloads.map((payload) => this.encrypPayloadWithKeyLookup(payload))) return Promise.all(payloads.map((payload) => this.encrypPayloadWithKeyLookup(payload)))
} }
public async encryptPayload( public async encryptPayload(payload: DecryptedPayloadInterface, key: RootKeyInterface): Promise<EncryptedParameters> {
payload: Models.DecryptedPayloadInterface, return encryptPayload(payload, key, this.operatorManager)
key: RootKeyInterface,
): Promise<EncryptedParameters> {
return OperatorWrapper.encryptPayload(payload, key, this.operatorManager)
} }
public async encryptPayloads(payloads: Models.DecryptedPayloadInterface[], key: RootKeyInterface) { public async encryptPayloads(payloads: DecryptedPayloadInterface[], key: RootKeyInterface) {
return Promise.all(payloads.map((payload) => this.encryptPayload(payload, key))) return Promise.all(payloads.map((payload) => this.encryptPayload(payload, key)))
} }
public async decryptPayloadWithKeyLookup<C extends Models.ItemContent = Models.ItemContent>( public async decryptPayloadWithKeyLookup<C extends ItemContent = ItemContent>(
payload: Models.EncryptedPayloadInterface, payload: EncryptedPayloadInterface,
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> { ): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
const key = this.getRootKey() const key = this.getRootKey()
@@ -524,21 +528,21 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
return this.decryptPayload(payload, key) return this.decryptPayload(payload, key)
} }
public async decryptPayload<C extends Models.ItemContent = Models.ItemContent>( public async decryptPayload<C extends ItemContent = ItemContent>(
payload: Models.EncryptedPayloadInterface, payload: EncryptedPayloadInterface,
key: RootKeyInterface, key: RootKeyInterface,
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> { ): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
return OperatorWrapper.decryptPayload(payload, key, this.operatorManager) return decryptPayload(payload, key, this.operatorManager)
} }
public async decryptPayloadsWithKeyLookup<C extends Models.ItemContent = Models.ItemContent>( public async decryptPayloadsWithKeyLookup<C extends ItemContent = ItemContent>(
payloads: Models.EncryptedPayloadInterface[], payloads: EncryptedPayloadInterface[],
): Promise<(DecryptedParameters<C> | ErrorDecryptingParameters)[]> { ): Promise<(DecryptedParameters<C> | ErrorDecryptingParameters)[]> {
return Promise.all(payloads.map((payload) => this.decryptPayloadWithKeyLookup<C>(payload))) return Promise.all(payloads.map((payload) => this.decryptPayloadWithKeyLookup<C>(payload)))
} }
public async decryptPayloads<C extends Models.ItemContent = Models.ItemContent>( public async decryptPayloads<C extends ItemContent = ItemContent>(
payloads: Models.EncryptedPayloadInterface[], payloads: EncryptedPayloadInterface[],
key: RootKeyInterface, key: RootKeyInterface,
): Promise<(DecryptedParameters<C> | ErrorDecryptingParameters)[]> { ): Promise<(DecryptedParameters<C> | ErrorDecryptingParameters)[]> {
return Promise.all(payloads.map((payload) => this.decryptPayload<C>(payload, key))) return Promise.all(payloads.map((payload) => this.decryptPayload<C>(payload, key)))
@@ -566,24 +570,24 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
* Consumer must call sync. If the protocol version <= 003, only one items key should be created, * 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. * and its .itemsKey value should be equal to the root key masterKey value.
*/ */
public async createNewDefaultItemsKey(): Promise<Models.ItemsKeyInterface> { public async createNewDefaultItemsKey(): Promise<ItemsKeyInterface> {
const rootKey = this.getSureRootKey() const rootKey = this.getSureRootKey()
const operatorVersion = rootKey ? rootKey.keyVersion : Common.ProtocolVersionLatest const operatorVersion = rootKey ? rootKey.keyVersion : ProtocolVersionLatest
let itemTemplate: Models.ItemsKeyInterface let itemTemplate: ItemsKeyInterface
if (Common.compareVersions(operatorVersion, Common.ProtocolVersionLastNonrootItemsKey) <= 0) { if (compareVersions(operatorVersion, ProtocolVersionLastNonrootItemsKey) <= 0) {
/** Create root key based items key */ /** Create root key based items key */
const payload = new DecryptedPayload<ItemsKeyContent>({ const payload = new DecryptedPayload<ItemsKeyContent>({
uuid: UuidGenerator.GenerateUuid(), uuid: UuidGenerator.GenerateUuid(),
content_type: Common.ContentType.ItemsKey, content_type: ContentType.ItemsKey,
content: Models.FillItemContentSpecialized<ItemsKeyContentSpecialized, ItemsKeyContent>({ content: FillItemContentSpecialized<ItemsKeyContentSpecialized, ItemsKeyContent>({
itemsKey: rootKey.masterKey, itemsKey: rootKey.masterKey,
dataAuthenticationKey: rootKey.dataAuthenticationKey, dataAuthenticationKey: rootKey.dataAuthenticationKey,
version: operatorVersion, version: operatorVersion,
}), }),
...PayloadTimestampDefaults(), ...PayloadTimestampDefaults(),
}) })
itemTemplate = Models.CreateDecryptedItemFromPayload(payload) itemTemplate = CreateDecryptedItemFromPayload(payload)
} else { } else {
/** Create independent items key */ /** Create independent items key */
itemTemplate = this.operatorManager.operatorForVersion(operatorVersion).createItemsKey() itemTemplate = this.operatorManager.operatorForVersion(operatorVersion).createItemsKey()
@@ -600,7 +604,7 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
}) })
} }
const itemsKey = (await this.itemManager.insertItem(itemTemplate)) as Models.ItemsKeyInterface const itemsKey = (await this.itemManager.insertItem(itemTemplate)) as ItemsKeyInterface
await this.itemManager.changeItemsKey(itemsKey, (mutator) => { await this.itemManager.changeItemsKey(itemsKey, (mutator) => {
mutator.isDefault = true mutator.isDefault = true
@@ -626,7 +630,7 @@ export class RootKeyEncryptionService extends Services.AbstractService<RootKeySe
return rollback return rollback
} }
override async getDiagnostics(): Promise<Services.DiagnosticInfo | undefined> { override async getDiagnostics(): Promise<DiagnosticInfo | undefined> {
return { return {
rootKeyEncryption: { rootKeyEncryption: {
hasRootKey: this.rootKey != undefined, hasRootKey: this.rootKey != undefined,

View File

@@ -1,4 +1,3 @@
import { DecryptedBytes, EncryptedBytes, FileMemoryCache, OrderedByteChunker } from '@standardnotes/filepicker'
import { ClientDisplayableError } from '@standardnotes/responses' import { ClientDisplayableError } from '@standardnotes/responses'
import { ContentType } from '@standardnotes/common' import { ContentType } from '@standardnotes/common'
import { import {
@@ -13,21 +12,7 @@ import {
} from '@standardnotes/models' } from '@standardnotes/models'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common' import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { UuidGenerator } from '@standardnotes/utils' import { UuidGenerator } from '@standardnotes/utils'
import { import { EncryptionProvider, SNItemsKey } from '@standardnotes/encryption'
AbstractService,
InternalEventBusInterface,
ItemManagerInterface,
SyncServiceInterface,
AlertService,
FileSystemApi,
FilesApiInterface,
FileBackupMetadataFile,
FileHandleRead,
FileSystemNoSelection,
ChallengeServiceInterface,
FileBackupsConstantsV1,
} from '@standardnotes/services'
import { DecryptItemsKeyWithUserFallback, EncryptionProvider, SNItemsKey } from '@standardnotes/encryption'
import { import {
DownloadAndDecryptFileOperation, DownloadAndDecryptFileOperation,
EncryptAndUploadFileOperation, EncryptAndUploadFileOperation,
@@ -35,8 +20,26 @@ import {
FileDownloadProgress, FileDownloadProgress,
FilesClientInterface, FilesClientInterface,
readAndDecryptBackupFile, readAndDecryptBackupFile,
FilesApiInterface,
FileBackupsConstantsV1,
FileBackupMetadataFile,
FileSystemApi,
FileHandleRead,
FileSystemNoSelection,
EncryptedBytes,
DecryptedBytes,
OrderedByteChunker,
FileMemoryCache,
} from '@standardnotes/files' } from '@standardnotes/files'
import { AlertService } from '../Alert/AlertService'
import { ChallengeServiceInterface } from '../Challenge'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { AbstractService } from '../Service/AbstractService'
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
import { DecryptItemsKeyWithUserFallback } from '../Encryption/Functions'
const OneHundredMb = 100 * 1_000_000 const OneHundredMb = 100 * 1_000_000
export class FileService extends AbstractService implements FilesClientInterface { export class FileService extends AbstractService implements FilesClientInterface {

View File

@@ -18,11 +18,16 @@ export * from './Device/DesktopManagerInterface'
export * from './Device/DesktopWebCommunication' export * from './Device/DesktopWebCommunication'
export * from './Device/DeviceInterface' export * from './Device/DeviceInterface'
export * from './Device/Environments' export * from './Device/Environments'
export * from './Device/FileBackupsDevice'
export * from './Device/MobileDeviceInterface' export * from './Device/MobileDeviceInterface'
export * from './Device/TypeCheck' export * from './Device/TypeCheck'
export * from './Device/WebOrDesktopDeviceInterface' export * from './Device/WebOrDesktopDeviceInterface'
export * from './Diagnostics/ServiceDiagnostics' export * from './Diagnostics/ServiceDiagnostics'
export * from './Encryption/BackupFileDecryptor'
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/ApplicationEvent'
export * from './Event/ApplicationEventCallback' export * from './Event/ApplicationEventCallback'
export * from './Event/EventObserver' export * from './Event/EventObserver'
@@ -34,6 +39,7 @@ export * from './Feature/FeaturesClientInterface'
export * from './Feature/FeaturesEvent' export * from './Feature/FeaturesEvent'
export * from './Feature/OfflineSubscriptionEntitlements' export * from './Feature/OfflineSubscriptionEntitlements'
export * from './Feature/SetOfflineFeaturesFunctionResponse' export * from './Feature/SetOfflineFeaturesFunctionResponse'
export * from './Files/FileService'
export * from './Integrity/IntegrityApiInterface' export * from './Integrity/IntegrityApiInterface'
export * from './Integrity/IntegrityEvent' export * from './Integrity/IntegrityEvent'
export * from './Integrity/IntegrityEventPayload' export * from './Integrity/IntegrityEventPayload'

View File

@@ -1,4 +1,3 @@
import { SnjsVersion } from './../Version'
import { import {
HttpService, HttpService,
HttpServiceInterface, HttpServiceInterface,
@@ -10,13 +9,11 @@ import {
} from '@standardnotes/api' } from '@standardnotes/api'
import * as Common from '@standardnotes/common' import * as Common from '@standardnotes/common'
import * as ExternalServices from '@standardnotes/services' import * as ExternalServices from '@standardnotes/services'
import * as Encryption from '@standardnotes/encryption'
import * as Models from '@standardnotes/models' import * as Models from '@standardnotes/models'
import * as Responses from '@standardnotes/responses' import * as Responses from '@standardnotes/responses'
import * as InternalServices from '../Services' import * as InternalServices from '../Services'
import * as Utils from '@standardnotes/utils' import * as Utils from '@standardnotes/utils'
import * as Settings from '@standardnotes/settings' import * as Settings from '@standardnotes/settings'
import * as Files from '@standardnotes/files'
import { Subscription } from '@standardnotes/security' import { Subscription } from '@standardnotes/security'
import { UuidString, ApplicationEventPayload } from '../Types' import { UuidString, ApplicationEventPayload } from '../Types'
import { applicationEventForSyncEvent } from '@Lib/Application/Event' import { applicationEventForSyncEvent } from '@Lib/Application/Event'
@@ -36,11 +33,19 @@ import {
DeinitSource, DeinitSource,
AppGroupManagedApplication, AppGroupManagedApplication,
ApplicationInterface, ApplicationInterface,
EncryptionService,
EncryptionServiceEvent,
FilesBackupService,
FileService,
} from '@standardnotes/services' } from '@standardnotes/services'
import { SNLog } from '../Log' import { FilesClientInterface } from '@standardnotes/files'
import { ComputePrivateWorkspaceIdentifier } from '@standardnotes/encryption'
import { useBoolean } from '@standardnotes/utils' import { useBoolean } from '@standardnotes/utils'
import { BackupFile, DecryptedItemInterface, EncryptedItemInterface, ItemStream } from '@standardnotes/models' import { BackupFile, DecryptedItemInterface, EncryptedItemInterface, ItemStream } from '@standardnotes/models'
import { ClientDisplayableError } from '@standardnotes/responses' import { ClientDisplayableError } from '@standardnotes/responses'
import { SnjsVersion } from './../Version'
import { SNLog } from '../Log'
import { Challenge, ChallengeResponse } from '../Services' import { Challenge, ChallengeResponse } from '../Services'
import { ApplicationConstructorOptions, FullyResolvedApplicationOptions } from './Options/ApplicationOptions' import { ApplicationConstructorOptions, FullyResolvedApplicationOptions } from './Options/ApplicationOptions'
import { ApplicationOptionsDefaults } from './Options/Defaults' import { ApplicationOptionsDefaults } from './Options/Defaults'
@@ -78,7 +83,7 @@ export class SNApplication
private deprecatedHttpService!: InternalServices.SNHttpService private deprecatedHttpService!: InternalServices.SNHttpService
private declare httpService: HttpServiceInterface private declare httpService: HttpServiceInterface
private payloadManager!: InternalServices.PayloadManager private payloadManager!: InternalServices.PayloadManager
public protocolService!: Encryption.EncryptionService public protocolService!: EncryptionService
private diskStorageService!: InternalServices.DiskStorageService private diskStorageService!: InternalServices.DiskStorageService
private inMemoryStore!: ExternalServices.KeyValueStoreInterface<string> private inMemoryStore!: ExternalServices.KeyValueStoreInterface<string>
/** /**
@@ -104,11 +109,11 @@ export class SNApplication
private settingsService!: InternalServices.SNSettingsService private settingsService!: InternalServices.SNSettingsService
private mfaService!: InternalServices.SNMfaService private mfaService!: InternalServices.SNMfaService
private listedService!: InternalServices.ListedService private listedService!: InternalServices.ListedService
private fileService!: Files.FileService private fileService!: FileService
private mutatorService!: InternalServices.MutatorService private mutatorService!: InternalServices.MutatorService
private integrityService!: ExternalServices.IntegrityService private integrityService!: ExternalServices.IntegrityService
private statusService!: ExternalServices.StatusService private statusService!: ExternalServices.StatusService
private filesBackupService?: Files.FilesBackupService private filesBackupService?: FilesBackupService
private internalEventBus!: ExternalServices.InternalEventBusInterface private internalEventBus!: ExternalServices.InternalEventBusInterface
@@ -182,7 +187,7 @@ export class SNApplication
this.defineInternalEventHandlers() this.defineInternalEventHandlers()
} }
public get files(): Files.FilesClientInterface { public get files(): FilesClientInterface {
return this.fileService return this.fileService
} }
@@ -222,7 +227,7 @@ export class SNApplication
return this.statusService return this.statusService
} }
public get fileBackups(): Files.FilesBackupService | undefined { public get fileBackups(): FilesBackupService | undefined {
return this.filesBackupService return this.filesBackupService
} }
@@ -231,7 +236,7 @@ export class SNApplication
} }
public computePrivateWorkspaceIdentifier(userphrase: string, name: string): Promise<string | undefined> { public computePrivateWorkspaceIdentifier(userphrase: string, name: string): Promise<string | undefined> {
return Encryption.ComputePrivateWorkspaceIdentifier(this.options.crypto, userphrase, name) return ComputePrivateWorkspaceIdentifier(this.options.crypto, userphrase, name)
} }
/** /**
@@ -1117,7 +1122,7 @@ export class SNApplication
} }
private createFileService() { private createFileService() {
this.fileService = new Files.FileService( this.fileService = new FileService(
this.apiService, this.apiService,
this.itemManager, this.itemManager,
this.syncService, this.syncService,
@@ -1328,7 +1333,7 @@ export class SNApplication
} }
private createProtocolService() { private createProtocolService() {
this.protocolService = new Encryption.EncryptionService( this.protocolService = new EncryptionService(
this.itemManager, this.itemManager,
this.payloadManager, this.payloadManager,
this.deviceInterface, this.deviceInterface,
@@ -1339,7 +1344,7 @@ export class SNApplication
) )
this.serviceObservers.push( this.serviceObservers.push(
this.protocolService.addEventObserver(async (event) => { this.protocolService.addEventObserver(async (event) => {
if (event === Encryption.EncryptionServiceEvent.RootKeyStatusChanged) { if (event === EncryptionServiceEvent.RootKeyStatusChanged) {
await this.notifyEvent(ApplicationEvent.KeyStatusChanged) await this.notifyEvent(ApplicationEvent.KeyStatusChanged)
} }
}), }),
@@ -1539,7 +1544,7 @@ export class SNApplication
} }
private createFilesBackupService(device: ExternalServices.DesktopDeviceInterface): void { private createFilesBackupService(device: ExternalServices.DesktopDeviceInterface): void {
this.filesBackupService = new Files.FilesBackupService( this.filesBackupService = new FilesBackupService(
this.itemManager, this.itemManager,
this.apiService, this.apiService,
this.protocolService, this.protocolService,

View File

@@ -1,8 +1,7 @@
import { SNSessionManager } from '../Services/Session/SessionManager' import { SNSessionManager } from '../Services/Session/SessionManager'
import { ApplicationIdentifier } from '@standardnotes/common' import { ApplicationIdentifier } from '@standardnotes/common'
import { ItemManager } from '@Lib/Services/Items/ItemManager' import { ItemManager } from '@Lib/Services/Items/ItemManager'
import { EncryptionService } from '@standardnotes/encryption' import { DeviceInterface, InternalEventBusInterface, Environment, EncryptionService } from '@standardnotes/services'
import { DeviceInterface, InternalEventBusInterface, Environment } from '@standardnotes/services'
import { ChallengeService, SNSingletonManager, SNFeaturesService, DiskStorageService } from '@Lib/Services' import { ChallengeService, SNSingletonManager, SNFeaturesService, DiskStorageService } from '@Lib/Services'
export type MigrationServices = { export type MigrationServices = {

View File

@@ -1,4 +1,4 @@
import { EncryptionService, SNRootKey } from '@standardnotes/encryption' import { SNRootKey } from '@standardnotes/encryption'
import { Challenge, ChallengeService } from '../Challenge' import { Challenge, ChallengeService } from '../Challenge'
import { ListedService } from '../Listed/ListedService' import { ListedService } from '../Listed/ListedService'
import { ActionResponse, HttpResponse } from '@standardnotes/responses' import { ActionResponse, HttpResponse } from '@standardnotes/responses'
@@ -30,6 +30,7 @@ import {
ChallengeValidation, ChallengeValidation,
ChallengeReason, ChallengeReason,
ChallengePrompt, ChallengePrompt,
EncryptionService,
} from '@standardnotes/services' } from '@standardnotes/services'
/** /**

View File

@@ -12,9 +12,10 @@ import {
ApiServiceEvent, ApiServiceEvent,
MetaReceivedData, MetaReceivedData,
DiagnosticInfo, DiagnosticInfo,
FilesApiInterface,
KeyValueStoreInterface, KeyValueStoreInterface,
} from '@standardnotes/services' } from '@standardnotes/services'
import { FilesApiInterface } from '@standardnotes/files'
import { ServerSyncPushContextualPayload, SNFeatureRepo, FileContent } from '@standardnotes/models' import { ServerSyncPushContextualPayload, SNFeatureRepo, FileContent } from '@standardnotes/models'
import * as Responses from '@standardnotes/responses' import * as Responses from '@standardnotes/responses'
import { API_MESSAGE_FAILED_OFFLINE_ACTIVATION } from '@Lib/Services/Api/Messages' import { API_MESSAGE_FAILED_OFFLINE_ACTIVATION } from '@Lib/Services/Api/Messages'

View File

@@ -1,5 +1,4 @@
import { RootKeyInterface } from '@standardnotes/models' import { RootKeyInterface } from '@standardnotes/models'
import { EncryptionService } from '@standardnotes/encryption'
import { DiskStorageService } from '../Storage/DiskStorageService' import { DiskStorageService } from '../Storage/DiskStorageService'
import { removeFromArray } from '@standardnotes/utils' import { removeFromArray } from '@standardnotes/utils'
import { isValidProtectionSessionLength } from '../Protection/ProtectionService' import { isValidProtectionSessionLength } from '../Protection/ProtectionService'
@@ -14,6 +13,7 @@ import {
ChallengeInterface, ChallengeInterface,
ChallengePromptInterface, ChallengePromptInterface,
ChallengePrompt, ChallengePrompt,
EncryptionService,
} from '@standardnotes/services' } from '@standardnotes/services'
import { ChallengeResponse } from './ChallengeResponse' import { ChallengeResponse } from './ChallengeResponse'
import { ChallengeOperation } from './ChallengeOperation' import { ChallengeOperation } from './ChallengeOperation'

View File

@@ -1,5 +1,4 @@
import { ContentType, Uuid } from '@standardnotes/common' import { ContentType, Uuid } from '@standardnotes/common'
import { EncryptionService } from '@standardnotes/encryption'
import { isNullOrUndefined, removeFromArray } from '@standardnotes/utils' import { isNullOrUndefined, removeFromArray } from '@standardnotes/utils'
import { ItemManager } from '@Lib/Services/Items/ItemManager' import { ItemManager } from '@Lib/Services/Items/ItemManager'
import { SNApiService } from '@Lib/Services/Api/ApiService' import { SNApiService } from '@Lib/Services/Api/ApiService'
@@ -7,8 +6,8 @@ import { DiskStorageService } from '@Lib/Services/Storage/DiskStorageService'
import { UuidString } from '../../Types/UuidString' import { UuidString } from '../../Types/UuidString'
import * as Models from '@standardnotes/models' import * as Models from '@standardnotes/models'
import * as Responses from '@standardnotes/responses' import * as Responses from '@standardnotes/responses'
import * as Services from '@standardnotes/services'
import { isErrorDecryptingPayload, PayloadTimestampDefaults, SNNote } from '@standardnotes/models' import { isErrorDecryptingPayload, PayloadTimestampDefaults, SNNote } from '@standardnotes/models'
import { AbstractService, EncryptionService, DeviceInterface, InternalEventBusInterface } from '@standardnotes/services'
/** The amount of revisions per item above which should call for an optimization. */ /** The amount of revisions per item above which should call for an optimization. */
const DefaultItemRevisionsThreshold = 20 const DefaultItemRevisionsThreshold = 20
@@ -28,7 +27,7 @@ const LargeEntryDeltaThreshold = 25
* 2. Remote server history. Entries are automatically added by the server and must be * 2. Remote server history. Entries are automatically added by the server and must be
* retrieved per item via an API call. * retrieved per item via an API call.
*/ */
export class SNHistoryManager extends Services.AbstractService { export class SNHistoryManager extends AbstractService {
private removeChangeObserver: () => void private removeChangeObserver: () => void
/** /**
@@ -49,8 +48,8 @@ export class SNHistoryManager extends Services.AbstractService {
private storageService: DiskStorageService, private storageService: DiskStorageService,
private apiService: SNApiService, private apiService: SNApiService,
private protocolService: EncryptionService, private protocolService: EncryptionService,
public deviceInterface: Services.DeviceInterface, public deviceInterface: DeviceInterface,
protected override internalEventBus: Services.InternalEventBusInterface, protected override internalEventBus: InternalEventBusInterface,
) { ) {
super(internalEventBus) super(internalEventBus)
this.removeChangeObserver = this.itemManager.addObserver(ContentType.Note, ({ changed, inserted }) => { this.removeChangeObserver = this.itemManager.addObserver(ContentType.Note, ({ changed, inserted }) => {

View File

@@ -1,10 +1,10 @@
import { ContentType } from '@standardnotes/common' import { ContentType } from '@standardnotes/common'
import { ItemsKeyInterface } from '@standardnotes/models' import { ItemsKeyInterface } from '@standardnotes/models'
import { dateSorted } from '@standardnotes/utils' import { dateSorted } from '@standardnotes/utils'
import { SNRootKeyParams, DecryptItemsKeyByPromptingUser, EncryptionProvider } from '@standardnotes/encryption' import { SNRootKeyParams, EncryptionProvider } from '@standardnotes/encryption'
import { DecryptionQueueItem, KeyRecoveryOperationResult } from './Types' import { DecryptionQueueItem, KeyRecoveryOperationResult } from './Types'
import { serverKeyParamsAreSafe } from './Utils' import { serverKeyParamsAreSafe } from './Utils'
import { ChallengeServiceInterface } from '@standardnotes/services' import { ChallengeServiceInterface, DecryptItemsKeyByPromptingUser } from '@standardnotes/services'
import { ItemManager } from '../Items' import { ItemManager } from '../Items'
export class KeyRecoveryOperation { export class KeyRecoveryOperation {

View File

@@ -1,11 +1,5 @@
import { KeyRecoveryOperation } from './KeyRecoveryOperation' import { KeyRecoveryOperation } from './KeyRecoveryOperation'
import { import { SNRootKeyParams, SNRootKey, KeyParamsFromApiResponse, KeyRecoveryStrings } from '@standardnotes/encryption'
SNRootKeyParams,
EncryptionService,
SNRootKey,
KeyParamsFromApiResponse,
KeyRecoveryStrings,
} from '@standardnotes/encryption'
import { UserService } from '../User/UserService' import { UserService } from '../User/UserService'
import { import {
isErrorDecryptingPayload, isErrorDecryptingPayload,
@@ -37,6 +31,7 @@ import {
ChallengeValidation, ChallengeValidation,
ChallengeReason, ChallengeReason,
ChallengePrompt, ChallengePrompt,
EncryptionService,
} from '@standardnotes/services' } from '@standardnotes/services'
import { import {
UndecryptableItemsStorage, UndecryptableItemsStorage,

View File

@@ -1,8 +1,7 @@
import { SNHistoryManager } from './../History/HistoryManager' import { SNHistoryManager } from './../History/HistoryManager'
import { NoteContent, SNNote, FillItemContent, DecryptedPayload, PayloadTimestampDefaults } from '@standardnotes/models' import { NoteContent, SNNote, FillItemContent, DecryptedPayload, PayloadTimestampDefaults } from '@standardnotes/models'
import { EncryptionService } from '@standardnotes/encryption'
import { ContentType } from '@standardnotes/common' import { ContentType } from '@standardnotes/common'
import { InternalEventBusInterface } from '@standardnotes/services' import { EncryptionService, InternalEventBusInterface } from '@standardnotes/services'
import { import {
ChallengeService, ChallengeService,
MutatorService, MutatorService,

View File

@@ -1,8 +1,12 @@
import { ChallengeService } from '../Challenge' import { ChallengeService } from '../Challenge'
import { EncryptionService } from '@standardnotes/encryption'
import { DiskStorageService } from '../Storage/DiskStorageService' import { DiskStorageService } from '../Storage/DiskStorageService'
import { SNProtectionService } from './ProtectionService' import { SNProtectionService } from './ProtectionService'
import { InternalEventBus, InternalEventBusInterface, ChallengeReason } from '@standardnotes/services' import {
InternalEventBus,
InternalEventBusInterface,
ChallengeReason,
EncryptionService,
} from '@standardnotes/services'
import { UuidGenerator } from '@standardnotes/utils' import { UuidGenerator } from '@standardnotes/utils'
import { import {
DecryptedPayload, DecryptedPayload,

View File

@@ -2,7 +2,6 @@ import { Challenge } from './../Challenge/Challenge'
import { ChallengeService } from './../Challenge/ChallengeService' import { ChallengeService } from './../Challenge/ChallengeService'
import { SNLog } from '@Lib/Log' import { SNLog } from '@Lib/Log'
import { DecryptedItem } from '@standardnotes/models' import { DecryptedItem } from '@standardnotes/models'
import { EncryptionService } from '@standardnotes/encryption'
import { DiskStorageService } from '@Lib/Services/Storage/DiskStorageService' import { DiskStorageService } from '@Lib/Services/Storage/DiskStorageService'
import { isNullOrUndefined } from '@standardnotes/utils' import { isNullOrUndefined } from '@standardnotes/utils'
import { import {
@@ -15,6 +14,7 @@ import {
ChallengeReason, ChallengeReason,
ChallengePrompt, ChallengePrompt,
ChallengeValidation, ChallengeValidation,
EncryptionService,
} from '@standardnotes/services' } from '@standardnotes/services'
import { ProtectionsClientInterface } from './ClientInterface' import { ProtectionsClientInterface } from './ClientInterface'
import { ContentType } from '@standardnotes/common' import { ContentType } from '@standardnotes/common'

View File

@@ -9,19 +9,14 @@ import {
ChallengeKeyboardType, ChallengeKeyboardType,
ChallengeReason, ChallengeReason,
ChallengePromptTitle, ChallengePromptTitle,
EncryptionService,
} from '@standardnotes/services' } from '@standardnotes/services'
import { Base64String } from '@standardnotes/sncrypto-common' import { Base64String } from '@standardnotes/sncrypto-common'
import { ClientDisplayableError } from '@standardnotes/responses' import { ClientDisplayableError } from '@standardnotes/responses'
import { CopyPayloadWithContentOverride } from '@standardnotes/models' import { CopyPayloadWithContentOverride } from '@standardnotes/models'
import { isNullOrUndefined } from '@standardnotes/utils' import { isNullOrUndefined } from '@standardnotes/utils'
import { JwtSession } from './Sessions/JwtSession' import { JwtSession } from './Sessions/JwtSession'
import { import { KeyParamsFromApiResponse, SNRootKeyParams, SNRootKey, CreateNewRootKey } from '@standardnotes/encryption'
KeyParamsFromApiResponse,
SNRootKeyParams,
SNRootKey,
EncryptionService,
CreateNewRootKey,
} from '@standardnotes/encryption'
import { SessionStrings, SignInStrings } from '../Api/Messages' import { SessionStrings, SignInStrings } from '../Api/Messages'
import { RemoteSession, RawStorageValue } from './Sessions/Types' import { RemoteSession, RawStorageValue } from './Sessions/Types'
import { Session } from './Sessions/Session' import { Session } from './Sessions/Session'

View File

@@ -26,7 +26,6 @@ import { ServerSyncResponse } from '@Lib/Services/Sync/Account/Response'
import { ServerSyncResponseResolver } from '@Lib/Services/Sync/Account/ResponseResolver' import { ServerSyncResponseResolver } from '@Lib/Services/Sync/Account/ResponseResolver'
import { SyncSignal, SyncStats } from '@Lib/Services/Sync/Signals' import { SyncSignal, SyncStats } from '@Lib/Services/Sync/Signals'
import { UuidString } from '../../Types/UuidString' import { UuidString } from '../../Types/UuidString'
import * as Encryption from '@standardnotes/encryption'
import { import {
PayloadSource, PayloadSource,
CreateDecryptedItemFromPayload, CreateDecryptedItemFromPayload,
@@ -73,9 +72,15 @@ import {
SyncQueueStrategy, SyncQueueStrategy,
SyncServiceInterface, SyncServiceInterface,
DiagnosticInfo, DiagnosticInfo,
EncryptionService,
} from '@standardnotes/services' } from '@standardnotes/services'
import { OfflineSyncResponse } from './Offline/Response' import { OfflineSyncResponse } from './Offline/Response'
import { KeyedDecryptionSplit, SplitPayloadsByEncryptionType } from '@standardnotes/encryption' import {
CreateDecryptionSplitWithKeyLookup,
CreateEncryptionSplitWithKeyLookup,
KeyedDecryptionSplit,
SplitPayloadsByEncryptionType,
} from '@standardnotes/encryption'
import { CreatePayloadFromRawServerItem } from './Account/Utilities' import { CreatePayloadFromRawServerItem } from './Account/Utilities'
import { ApplicationSyncOptions } from '@Lib/Application/Options/OptionalOptions' import { ApplicationSyncOptions } from '@Lib/Application/Options/OptionalOptions'
@@ -131,7 +136,7 @@ export class SNSyncService
constructor( constructor(
private itemManager: ItemManager, private itemManager: ItemManager,
private sessionManager: SNSessionManager, private sessionManager: SNSessionManager,
private protocolService: Encryption.EncryptionService, private protocolService: EncryptionService,
private storageService: DiskStorageService, private storageService: DiskStorageService,
private payloadManager: PayloadManager, private payloadManager: PayloadManager,
private apiService: SNApiService, private apiService: SNApiService,
@@ -226,7 +231,7 @@ export class SNSyncService
isDecryptedPayload, isDecryptedPayload,
) as DecryptedPayloadInterface<ItemsKeyContent>[] ) as DecryptedPayloadInterface<ItemsKeyContent>[]
const itemsKeysSplit: Encryption.KeyedDecryptionSplit = { const itemsKeysSplit: KeyedDecryptionSplit = {
usesRootKeyWithKeyLookup: { usesRootKeyWithKeyLookup: {
items: encryptedItemsKeysPayloads, items: encryptedItemsKeysPayloads,
}, },
@@ -301,7 +306,7 @@ export class SNSyncService
} }
} }
const split: Encryption.KeyedDecryptionSplit = { const split: KeyedDecryptionSplit = {
usesItemsKeyWithKeyLookup: { usesItemsKeyWithKeyLookup: {
items: encrypted, items: encrypted,
}, },
@@ -440,9 +445,9 @@ export class SNSyncService
): Promise<ServerSyncPushContextualPayload[]> { ): Promise<ServerSyncPushContextualPayload[]> {
const payloadSplit = CreatePayloadSplit(payloads) const payloadSplit = CreatePayloadSplit(payloads)
const encryptionSplit = Encryption.SplitPayloadsByEncryptionType(payloadSplit.decrypted) const encryptionSplit = SplitPayloadsByEncryptionType(payloadSplit.decrypted)
const keyLookupSplit = Encryption.CreateEncryptionSplitWithKeyLookup(encryptionSplit) const keyLookupSplit = CreateEncryptionSplitWithKeyLookup(encryptionSplit)
const encryptedResults = await this.protocolService.encryptSplit(keyLookupSplit) const encryptedResults = await this.protocolService.encryptSplit(keyLookupSplit)
@@ -973,7 +978,7 @@ export class SNSyncService
? (CreateDecryptedItemFromPayload(previouslyProcessedItemsKey) as ItemsKeyInterface) ? (CreateDecryptedItemFromPayload(previouslyProcessedItemsKey) as ItemsKeyInterface)
: undefined : undefined
const keyedSplit: Encryption.KeyedDecryptionSplit = {} const keyedSplit: KeyedDecryptionSplit = {}
if (itemsKey) { if (itemsKey) {
keyedSplit.usesItemsKey = { keyedSplit.usesItemsKey = {
items: [encrypted], items: [encrypted],
@@ -1162,9 +1167,9 @@ export class SNSyncService
const payloadSplit = CreateNonDecryptedPayloadSplit(receivedPayloads) const payloadSplit = CreateNonDecryptedPayloadSplit(receivedPayloads)
const encryptionSplit = Encryption.SplitPayloadsByEncryptionType(payloadSplit.encrypted) const encryptionSplit = SplitPayloadsByEncryptionType(payloadSplit.encrypted)
const keyedSplit = Encryption.CreateDecryptionSplitWithKeyLookup(encryptionSplit) const keyedSplit = CreateDecryptionSplitWithKeyLookup(encryptionSplit)
const decryptionResults = await this.protocolService.decryptSplit(keyedSplit) const decryptionResults = await this.protocolService.decryptSplit(keyedSplit)

View File

@@ -1,6 +1,6 @@
import { Challenge } from '../Challenge' import { Challenge } from '../Challenge'
import { ChallengeService } from '../Challenge/ChallengeService' import { ChallengeService } from '../Challenge/ChallengeService'
import { EncryptionService, SNRootKey, SNRootKeyParams } from '@standardnotes/encryption' import { SNRootKey, SNRootKeyParams } from '@standardnotes/encryption'
import { HttpResponse, SignInResponse, User } from '@standardnotes/responses' import { HttpResponse, SignInResponse, User } from '@standardnotes/responses'
import { ItemManager } from '@Lib/Services/Items/ItemManager' import { ItemManager } from '@Lib/Services/Items/ItemManager'
import { KeyParamsOrigination } from '@standardnotes/common' import { KeyParamsOrigination } from '@standardnotes/common'
@@ -14,6 +14,7 @@ import {
InternalEventBusInterface, InternalEventBusInterface,
UserClientInterface, UserClientInterface,
StoragePersistencePolicies, StoragePersistencePolicies,
EncryptionService,
} from '@standardnotes/services' } from '@standardnotes/services'
import { SNApiService } from './../Api/ApiService' import { SNApiService } from './../Api/ApiService'
import { SNProtectionService } from '../Protection/ProtectionService' import { SNProtectionService } from '../Protection/ProtectionService'

View File

@@ -8,31 +8,40 @@ import {
SNTheme, SNTheme,
} from '@standardnotes/models' } from '@standardnotes/models'
import { removeFromArray } from '@standardnotes/utils' import { removeFromArray } from '@standardnotes/utils'
import { ApplicationEvent, ApplicationService, FeatureStatus, InternalEventBus, StorageValueModes, WebApplicationInterface } from '@standardnotes/snjs' import {
AbstractService,
WebApplicationInterface,
InternalEventBusInterface,
ApplicationEvent,
StorageValueModes,
FeatureStatus,
} from '@standardnotes/services'
const CachedThemesKey = 'cachedThemes' const CachedThemesKey = 'cachedThemes'
const TimeBeforeApplyingColorScheme = 5 const TimeBeforeApplyingColorScheme = 5
const DefaultThemeIdentifier = 'Default' const DefaultThemeIdentifier = 'Default'
export class ThemeManager extends ApplicationService { export class ThemeManager extends AbstractService {
private activeThemes: Uuid[] = [] private activeThemes: Uuid[] = []
private unregisterDesktop?: () => void private unregisterDesktop?: () => void
private unregisterStream!: () => void private unregisterStream!: () => void
private lastUseDeviceThemeSettings = false private lastUseDeviceThemeSettings = false
private unsubApp!: () => void
constructor(application: WebApplicationInterface) { constructor(
super(application, new InternalEventBus()) protected application: WebApplicationInterface,
protected override internalEventBus: InternalEventBusInterface,
) {
super(internalEventBus)
this.addAppEventObserverAfterSubclassesFinishConstructing()
this.colorSchemeEventHandler = this.colorSchemeEventHandler.bind(this) this.colorSchemeEventHandler = this.colorSchemeEventHandler.bind(this)
} }
override async onAppStart() { async onAppStart() {
super.onAppStart().catch(console.error)
this.registerObservers() this.registerObservers()
} }
override async onAppEvent(event: ApplicationEvent) { async onAppEvent(event: ApplicationEvent) {
super.onAppEvent(event).catch(console.error)
switch (event) { switch (event) {
case ApplicationEvent.SignedOut: { case ApplicationEvent.SignedOut: {
this.deactivateAllThemes() this.deactivateAllThemes()
@@ -59,6 +68,25 @@ export class ThemeManager extends ApplicationService {
} }
} }
addAppEventObserverAfterSubclassesFinishConstructing() {
setTimeout(() => {
this.addAppEventObserver()
}, 0)
}
addAppEventObserver() {
if (this.application.isStarted()) {
void this.onAppStart()
}
this.unsubApp = this.application.addEventObserver(async (event: ApplicationEvent) => {
await this.onAppEvent(event)
if (event === ApplicationEvent.Started) {
void this.onAppStart()
}
})
}
private handlePreferencesChangeEvent(): void { private handlePreferencesChangeEvent(): void {
const useDeviceThemeSettings = this.application.getPreference(PrefKey.UseSystemColorScheme, false) const useDeviceThemeSettings = this.application.getPreference(PrefKey.UseSystemColorScheme, false)
@@ -86,6 +114,10 @@ export class ThemeManager extends ApplicationService {
;(this.unregisterStream as unknown) = undefined ;(this.unregisterStream as unknown) = undefined
window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', this.colorSchemeEventHandler) window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', this.colorSchemeEventHandler)
;(this.application as unknown) = undefined
this.unsubApp()
;(this.unsubApp as unknown) = undefined
super.deinit() super.deinit()
} }

View File

@@ -4,3 +4,4 @@ export * from './Archive/ArchiveManager'
export * from './IO/IOService' export * from './IO/IOService'
export * from './Security/AutolockService' export * from './Security/AutolockService'
export * from './Storage/LocalStorage' export * from './Storage/LocalStorage'
export * from './Theme/ThemeManager'

View File

@@ -22,8 +22,7 @@ import { makeObservable, observable } from 'mobx'
import { PanelResizedData } from '@/Types/PanelResizedData' import { PanelResizedData } from '@/Types/PanelResizedData'
import { isDesktopApplication } from '@/Utils' import { isDesktopApplication } from '@/Utils'
import { DesktopManager } from './Device/DesktopManager' import { DesktopManager } from './Device/DesktopManager'
import { ArchiveManager, AutolockService, IOService, WebAlertService } from '@standardnotes/ui-services' import { ArchiveManager, AutolockService, IOService, WebAlertService, ThemeManager } from '@standardnotes/ui-services'
import { ThemeManager } from '@/Theme/ThemeManager'
type WebServices = { type WebServices = {
viewControllerManager: ViewControllerManager viewControllerManager: ViewControllerManager

View File

@@ -33,8 +33,9 @@ const createApplication = (
const viewControllerManager = new ViewControllerManager(application, device) const viewControllerManager = new ViewControllerManager(application, device)
const archiveService = new ArchiveManager(application) const archiveService = new ArchiveManager(application)
const io = new IOService(platform === Platform.MacWeb || platform === Platform.MacDesktop) const io = new IOService(platform === Platform.MacWeb || platform === Platform.MacDesktop)
const autolockService = new AutolockService(application, new InternalEventBus()) const internalEventBus = new InternalEventBus()
const themeService = new ThemeManager(application) const autolockService = new AutolockService(application, internalEventBus)
const themeService = new ThemeManager(application, internalEventBus)
application.setWebServices({ application.setWebServices({
viewControllerManager, viewControllerManager,

View File

@@ -6576,7 +6576,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@standardnotes/encryption@workspace:*, @standardnotes/encryption@workspace:packages/encryption": "@standardnotes/encryption@workspace:*, @standardnotes/encryption@workspace:^, @standardnotes/encryption@workspace:packages/encryption":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@standardnotes/encryption@workspace:packages/encryption" resolution: "@standardnotes/encryption@workspace:packages/encryption"
dependencies: dependencies:
@@ -6584,7 +6584,6 @@ __metadata:
"@standardnotes/config": 2.4.3 "@standardnotes/config": 2.4.3
"@standardnotes/models": "workspace:*" "@standardnotes/models": "workspace:*"
"@standardnotes/responses": "workspace:*" "@standardnotes/responses": "workspace:*"
"@standardnotes/services": "workspace:*"
"@standardnotes/sncrypto-common": "workspace:*" "@standardnotes/sncrypto-common": "workspace:*"
"@standardnotes/utils": "workspace:*" "@standardnotes/utils": "workspace:*"
"@types/jest": ^28.1.5 "@types/jest": ^28.1.5
@@ -7179,6 +7178,7 @@ __metadata:
dependencies: dependencies:
"@standardnotes/auth": ^3.19.4 "@standardnotes/auth": ^3.19.4
"@standardnotes/common": ^1.30.0 "@standardnotes/common": ^1.30.0
"@standardnotes/encryption": "workspace:^"
"@standardnotes/files": "workspace:^" "@standardnotes/files": "workspace:^"
"@standardnotes/models": "workspace:^" "@standardnotes/models": "workspace:^"
"@standardnotes/responses": "workspace:*" "@standardnotes/responses": "workspace:*"