chore: remove phased out storage encryption policy (#2323)
This commit is contained in:
@@ -31,7 +31,6 @@ export enum StorageKey {
|
|||||||
ProtectionExpirey = 'SessionExpiresAtKey',
|
ProtectionExpirey = 'SessionExpiresAtKey',
|
||||||
ProtectionSessionLength = 'SessionLengthKey',
|
ProtectionSessionLength = 'SessionLengthKey',
|
||||||
KeyRecoveryUndecryptableItems = 'key_recovery_undecryptable',
|
KeyRecoveryUndecryptableItems = 'key_recovery_undecryptable',
|
||||||
StorageEncryptionPolicy = 'storage_policy',
|
|
||||||
WebSocketUrl = 'webSocket_url',
|
WebSocketUrl = 'webSocket_url',
|
||||||
UserRoles = 'user_roles',
|
UserRoles = 'user_roles',
|
||||||
OfflineUserRoles = 'offline_user_roles',
|
OfflineUserRoles = 'offline_user_roles',
|
||||||
|
|||||||
@@ -7,13 +7,8 @@ export enum StoragePersistencePolicies {
|
|||||||
Ephemeral = 2,
|
Ephemeral = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum StorageEncryptionPolicy {
|
|
||||||
Default = 1,
|
|
||||||
Disabled = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum StorageValueModes {
|
export enum StorageValueModes {
|
||||||
/** Stored inside wrapped encrpyed storage object */
|
/** Stored inside wrapped encrypted storage object */
|
||||||
Default = 1,
|
Default = 1,
|
||||||
/** Stored outside storage object, unencrypted */
|
/** Stored outside storage object, unencrypted */
|
||||||
Nonwrapped = 2,
|
Nonwrapped = 2,
|
||||||
|
|||||||
@@ -1,145 +0,0 @@
|
|||||||
/**
|
|
||||||
* @jest-environment jsdom
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { SNLog } from './../Log'
|
|
||||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
|
||||||
import { AlertService, DeviceInterface, namespacedKey, RawStorageKey } from '@standardnotes/services'
|
|
||||||
import { Environment, Platform } from '@standardnotes/models'
|
|
||||||
import { SNApplication } from './Application'
|
|
||||||
|
|
||||||
describe('application', () => {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
SNLog.onLog = console.log
|
|
||||||
SNLog.onError = console.error
|
|
||||||
|
|
||||||
let application: SNApplication
|
|
||||||
let device: DeviceInterface
|
|
||||||
let crypto: PureCryptoInterface
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
const identifier = '123'
|
|
||||||
|
|
||||||
crypto = {} as jest.Mocked<PureCryptoInterface>
|
|
||||||
crypto.initialize = jest.fn()
|
|
||||||
|
|
||||||
device = {} as jest.Mocked<DeviceInterface>
|
|
||||||
device.openDatabase = jest.fn().mockResolvedValue(true)
|
|
||||||
device.getAllDatabaseEntries = jest.fn().mockReturnValue([])
|
|
||||||
device.setRawStorageValue = jest.fn()
|
|
||||||
device.getRawStorageValue = jest.fn().mockImplementation((key) => {
|
|
||||||
if (key === namespacedKey(identifier, RawStorageKey.SnjsVersion)) {
|
|
||||||
return '10.0.0'
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
application = new SNApplication({
|
|
||||||
environment: Environment.Mobile,
|
|
||||||
platform: Platform.Ios,
|
|
||||||
deviceInterface: device,
|
|
||||||
crypto: crypto,
|
|
||||||
alertService: {} as jest.Mocked<AlertService>,
|
|
||||||
identifier: identifier,
|
|
||||||
defaultHost: 'localhost',
|
|
||||||
appVersion: '1.0',
|
|
||||||
})
|
|
||||||
|
|
||||||
await application.prepareForLaunch({ receiveChallenge: jest.fn() })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('diagnostics', async () => {
|
|
||||||
const diagnostics = await application.getDiagnostics()
|
|
||||||
|
|
||||||
expect(diagnostics).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
application: expect.objectContaining({
|
|
||||||
appVersion: '1.0',
|
|
||||||
environment: 3,
|
|
||||||
platform: 1,
|
|
||||||
}),
|
|
||||||
payloads: {
|
|
||||||
integrityPayloads: [],
|
|
||||||
nonDeletedItemCount: 0,
|
|
||||||
invalidPayloadsCount: 0,
|
|
||||||
},
|
|
||||||
items: { allIds: [] },
|
|
||||||
storage: {
|
|
||||||
storagePersistable: false,
|
|
||||||
persistencePolicy: 'Default',
|
|
||||||
encryptionPolicy: 'Default',
|
|
||||||
needsPersist: false,
|
|
||||||
currentPersistPromise: false,
|
|
||||||
isStorageWrapped: false,
|
|
||||||
allRawPayloadsCount: 0,
|
|
||||||
},
|
|
||||||
encryption: expect.objectContaining({
|
|
||||||
getLatestVersion: '004',
|
|
||||||
hasAccount: false,
|
|
||||||
getUserVersion: undefined,
|
|
||||||
upgradeAvailable: false,
|
|
||||||
accountUpgradeAvailable: false,
|
|
||||||
passcodeUpgradeAvailable: false,
|
|
||||||
hasPasscode: false,
|
|
||||||
isPasscodeLocked: false,
|
|
||||||
itemsEncryption: expect.objectContaining({
|
|
||||||
itemsKeysIds: [],
|
|
||||||
}),
|
|
||||||
rootKeyEncryption: expect.objectContaining({
|
|
||||||
hasRootKey: false,
|
|
||||||
keyMode: 'RootKeyNone',
|
|
||||||
hasRootKeyWrapper: false,
|
|
||||||
hasAccount: false,
|
|
||||||
hasPasscode: false,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
api: {
|
|
||||||
hasSession: false,
|
|
||||||
user: undefined,
|
|
||||||
registering: false,
|
|
||||||
authenticating: false,
|
|
||||||
changing: false,
|
|
||||||
refreshingSession: false,
|
|
||||||
filesHost: undefined,
|
|
||||||
host: 'localhost',
|
|
||||||
},
|
|
||||||
session: {
|
|
||||||
isSessionRenewChallengePresented: false,
|
|
||||||
online: false,
|
|
||||||
offline: true,
|
|
||||||
isSignedIn: false,
|
|
||||||
isSignedIntoFirstPartyServer: false,
|
|
||||||
},
|
|
||||||
sync: {
|
|
||||||
syncToken: undefined,
|
|
||||||
cursorToken: undefined,
|
|
||||||
lastSyncDate: undefined,
|
|
||||||
outOfSync: false,
|
|
||||||
completedOnlineDownloadFirstSync: false,
|
|
||||||
clientLocked: false,
|
|
||||||
databaseLoaded: false,
|
|
||||||
syncLock: false,
|
|
||||||
dealloced: false,
|
|
||||||
itemsNeedingSync: [],
|
|
||||||
itemsNeedingSyncCount: 0,
|
|
||||||
pendingRequestCount: 0,
|
|
||||||
},
|
|
||||||
protections: expect.objectContaining({
|
|
||||||
getLastSessionLength: undefined,
|
|
||||||
hasProtectionSources: false,
|
|
||||||
hasUnprotectedAccessSession: true,
|
|
||||||
hasBiometricsEnabled: false,
|
|
||||||
}),
|
|
||||||
keyRecovery: { queueLength: 0, isProcessingQueue: false },
|
|
||||||
features: {
|
|
||||||
roles: [],
|
|
||||||
features: [],
|
|
||||||
enabledExperimentalFeatures: [],
|
|
||||||
needsInitialFeaturesUpdate: true,
|
|
||||||
completedSuccessfulFeaturesRetrieval: false,
|
|
||||||
},
|
|
||||||
migrations: { activeMigrations: [] },
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1079,15 +1079,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
|||||||
return this.userService.changePasscode(newPasscode, origination)
|
return this.userService.changePasscode(newPasscode, origination)
|
||||||
}
|
}
|
||||||
|
|
||||||
public getStorageEncryptionPolicy(): ExternalServices.StorageEncryptionPolicy {
|
|
||||||
return this.diskStorageService.getStorageEncryptionPolicy()
|
|
||||||
}
|
|
||||||
|
|
||||||
public setStorageEncryptionPolicy(encryptionPolicy: ExternalServices.StorageEncryptionPolicy): Promise<void> {
|
|
||||||
this.diskStorageService.setEncryptionPolicy(encryptionPolicy)
|
|
||||||
return this.protocolService.repersistAllItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
public enableEphemeralPersistencePolicy(): Promise<void> {
|
public enableEphemeralPersistencePolicy(): Promise<void> {
|
||||||
return this.diskStorageService.setPersistencePolicy(ExternalServices.StoragePersistencePolicies.Ephemeral)
|
return this.diskStorageService.setPersistencePolicy(ExternalServices.StoragePersistencePolicies.Ephemeral)
|
||||||
}
|
}
|
||||||
@@ -1527,7 +1518,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
|||||||
this.diskStorageService = new InternalServices.DiskStorageService(
|
this.diskStorageService = new InternalServices.DiskStorageService(
|
||||||
this.deviceInterface,
|
this.deviceInterface,
|
||||||
this.identifier,
|
this.identifier,
|
||||||
this.environment,
|
|
||||||
this.internalEventBus,
|
this.internalEventBus,
|
||||||
)
|
)
|
||||||
this.services.push(this.diskStorageService)
|
this.services.push(this.diskStorageService)
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { DiskStorageService } from './DiskStorageService'
|
import { DiskStorageService } from './DiskStorageService'
|
||||||
|
|
||||||
import { InternalEventBus, DeviceInterface, InternalEventBusInterface } from '@standardnotes/services'
|
import { InternalEventBus, DeviceInterface, InternalEventBusInterface } from '@standardnotes/services'
|
||||||
import { Environment } from '@standardnotes/models'
|
|
||||||
|
|
||||||
describe('diskStorageService', () => {
|
describe('diskStorageService', () => {
|
||||||
let storageService: DiskStorageService
|
let storageService: DiskStorageService
|
||||||
@@ -12,7 +10,7 @@ describe('diskStorageService', () => {
|
|||||||
internalEventBus = {} as jest.Mocked<InternalEventBus>
|
internalEventBus = {} as jest.Mocked<InternalEventBus>
|
||||||
device = {} as jest.Mocked<DeviceInterface>
|
device = {} as jest.Mocked<DeviceInterface>
|
||||||
|
|
||||||
storageService = new DiskStorageService(device, 'test', Environment.Desktop, internalEventBus)
|
storageService = new DiskStorageService(device, 'test', internalEventBus)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('setInitialValues should set unwrapped values as wrapped value if wrapped value is not encrypted', async () => {
|
it('setInitialValues should set unwrapped values as wrapped value if wrapped value is not encrypted', async () => {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import {
|
|||||||
DeletedPayloadInterface,
|
DeletedPayloadInterface,
|
||||||
PayloadTimestampDefaults,
|
PayloadTimestampDefaults,
|
||||||
LocalStorageEncryptedContextualPayload,
|
LocalStorageEncryptedContextualPayload,
|
||||||
Environment,
|
|
||||||
FullyFormedTransferPayload,
|
FullyFormedTransferPayload,
|
||||||
} from '@standardnotes/models'
|
} from '@standardnotes/models'
|
||||||
|
|
||||||
@@ -37,7 +36,6 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
|||||||
private encryptionProvider!: Encryption.EncryptionProviderInterface
|
private encryptionProvider!: Encryption.EncryptionProviderInterface
|
||||||
private storagePersistable = false
|
private storagePersistable = false
|
||||||
private persistencePolicy!: Services.StoragePersistencePolicies
|
private persistencePolicy!: Services.StoragePersistencePolicies
|
||||||
private encryptionPolicy!: Services.StorageEncryptionPolicy
|
|
||||||
private needsPersist = false
|
private needsPersist = false
|
||||||
private currentPersistPromise?: Promise<Services.StorageValuesObject>
|
private currentPersistPromise?: Promise<Services.StorageValuesObject>
|
||||||
|
|
||||||
@@ -46,12 +44,10 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
|||||||
constructor(
|
constructor(
|
||||||
private deviceInterface: Services.DeviceInterface,
|
private deviceInterface: Services.DeviceInterface,
|
||||||
private identifier: string,
|
private identifier: string,
|
||||||
private environment: Environment,
|
|
||||||
protected override internalEventBus: Services.InternalEventBusInterface,
|
protected override internalEventBus: Services.InternalEventBusInterface,
|
||||||
) {
|
) {
|
||||||
super(internalEventBus)
|
super(internalEventBus)
|
||||||
void this.setPersistencePolicy(Services.StoragePersistencePolicies.Default)
|
void this.setPersistencePolicy(Services.StoragePersistencePolicies.Default)
|
||||||
void this.setEncryptionPolicy(Services.StorageEncryptionPolicy.Default, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public provideEncryptionProvider(provider: Encryption.EncryptionProviderInterface): void {
|
public provideEncryptionProvider(provider: Encryption.EncryptionProviderInterface): void {
|
||||||
@@ -73,11 +69,6 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
|||||||
if (this.needsPersist) {
|
if (this.needsPersist) {
|
||||||
void this.persistValuesToDisk()
|
void this.persistValuesToDisk()
|
||||||
}
|
}
|
||||||
} else if (stage === Services.ApplicationStage.StorageDecrypted_09) {
|
|
||||||
const persistedPolicy = await this.getValue(Services.StorageKey.StorageEncryptionPolicy)
|
|
||||||
if (persistedPolicy) {
|
|
||||||
void this.setEncryptionPolicy(persistedPolicy as Services.StorageEncryptionPolicy, false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,21 +81,6 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setEncryptionPolicy(encryptionPolicy: Services.StorageEncryptionPolicy, persist = true): void {
|
|
||||||
if (
|
|
||||||
encryptionPolicy === Services.StorageEncryptionPolicy.Disabled &&
|
|
||||||
![Environment.Mobile].includes(this.environment)
|
|
||||||
) {
|
|
||||||
throw Error('Disabling storage encryption is only available on mobile.')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.encryptionPolicy = encryptionPolicy
|
|
||||||
|
|
||||||
if (persist) {
|
|
||||||
this.setValue(Services.StorageKey.StorageEncryptionPolicy, encryptionPolicy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public isEphemeralSession() {
|
public isEphemeralSession() {
|
||||||
return this.persistencePolicy === Services.StoragePersistencePolicies.Ephemeral
|
return this.persistencePolicy === Services.StoragePersistencePolicies.Ephemeral
|
||||||
}
|
}
|
||||||
@@ -329,10 +305,6 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getStorageEncryptionPolicy() {
|
|
||||||
return this.encryptionPolicy
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default persistence key. Platforms can override as needed.
|
* Default persistence key. Platforms can override as needed.
|
||||||
*/
|
*/
|
||||||
@@ -393,36 +365,29 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
|||||||
|
|
||||||
const { encrypted, decrypted, deleted, discardable } = CreatePayloadSplitWithDiscardables(payloads)
|
const { encrypted, decrypted, deleted, discardable } = CreatePayloadSplitWithDiscardables(payloads)
|
||||||
|
|
||||||
const encryptionEnabled = this.encryptionPolicy === Services.StorageEncryptionPolicy.Default
|
|
||||||
const rootKeyEncryptionAvailable = this.encryptionProvider.hasRootKeyEncryptionSource()
|
const rootKeyEncryptionAvailable = this.encryptionProvider.hasRootKeyEncryptionSource()
|
||||||
|
|
||||||
const encryptable: DecryptedPayloadInterface[] = []
|
const encryptable: DecryptedPayloadInterface[] = []
|
||||||
const unencryptable: DecryptedPayloadInterface[] = []
|
const unencryptable: DecryptedPayloadInterface[] = []
|
||||||
|
|
||||||
if (encryptionEnabled) {
|
const split = Encryption.SplitPayloadsByEncryptionType(decrypted)
|
||||||
const split = Encryption.SplitPayloadsByEncryptionType(decrypted)
|
if (split.itemsKeyEncryption) {
|
||||||
|
extendArray(encryptable, split.itemsKeyEncryption)
|
||||||
|
}
|
||||||
|
|
||||||
if (split.itemsKeyEncryption) {
|
if (split.rootKeyEncryption) {
|
||||||
extendArray(encryptable, split.itemsKeyEncryption)
|
if (!rootKeyEncryptionAvailable) {
|
||||||
|
extendArray(unencryptable, split.rootKeyEncryption)
|
||||||
|
} else {
|
||||||
|
extendArray(encryptable, split.rootKeyEncryption)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (split.rootKeyEncryption) {
|
|
||||||
if (!rootKeyEncryptionAvailable) {
|
|
||||||
extendArray(unencryptable, split.rootKeyEncryption)
|
|
||||||
} else {
|
|
||||||
extendArray(encryptable, split.rootKeyEncryption)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
extendArray(unencryptable, encryptable)
|
|
||||||
extendArray(unencryptable, decrypted)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.deletePayloads(discardable)
|
await this.deletePayloads(discardable)
|
||||||
|
|
||||||
const split = Encryption.SplitPayloadsByEncryptionType(encryptable)
|
const encryptableSplit = Encryption.SplitPayloadsByEncryptionType(encryptable)
|
||||||
|
|
||||||
const keyLookupSplit = Encryption.CreateEncryptionSplitWithKeyLookup(split)
|
const keyLookupSplit = Encryption.CreateEncryptionSplitWithKeyLookup(encryptableSplit)
|
||||||
|
|
||||||
const encryptedResults = await this.encryptionProvider.encryptSplit(keyLookupSplit)
|
const encryptedResults = await this.encryptionProvider.encryptSplit(keyLookupSplit)
|
||||||
|
|
||||||
@@ -478,7 +443,6 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
|||||||
storage: {
|
storage: {
|
||||||
storagePersistable: this.storagePersistable,
|
storagePersistable: this.storagePersistable,
|
||||||
persistencePolicy: Services.StoragePersistencePolicies[this.persistencePolicy],
|
persistencePolicy: Services.StoragePersistencePolicies[this.persistencePolicy],
|
||||||
encryptionPolicy: Services.StorageEncryptionPolicy[this.encryptionPolicy],
|
|
||||||
needsPersist: this.needsPersist,
|
needsPersist: this.needsPersist,
|
||||||
currentPersistPromise: this.currentPersistPromise != undefined,
|
currentPersistPromise: this.currentPersistPromise != undefined,
|
||||||
isStorageWrapped: this.isStorageWrapped(),
|
isStorageWrapped: this.isStorageWrapped(),
|
||||||
|
|||||||
@@ -223,28 +223,6 @@ describe('storage manager', function () {
|
|||||||
expect(decrypted.content).to.be.an.instanceof(Object)
|
expect(decrypted.content).to.be.an.instanceof(Object)
|
||||||
})
|
})
|
||||||
|
|
||||||
/** @TODO: Storage encryption disable is no longer available, remove tests and associated functionality */
|
|
||||||
it.skip('disabling storage encryption should store items without encryption', async function () {
|
|
||||||
await Factory.registerUserToApplication({
|
|
||||||
application: this.application,
|
|
||||||
email: this.email,
|
|
||||||
password: this.password,
|
|
||||||
ephemeral: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
await this.application.setStorageEncryptionPolicy(StorageEncryptionPolicy.Disabled)
|
|
||||||
|
|
||||||
const payloads = await this.application.diskStorageService.getAllRawPayloads()
|
|
||||||
const payload = payloads[0]
|
|
||||||
expect(typeof payload.content).to.not.equal('string')
|
|
||||||
expect(payload.content.references).to.be.ok
|
|
||||||
|
|
||||||
const identifier = this.application.identifier
|
|
||||||
|
|
||||||
const app = await Factory.createAndInitializeApplication(identifier, Environment.Mobile)
|
|
||||||
expect(app.diskStorageService.encryptionPolicy).to.equal(StorageEncryptionPolicy.Disabled)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('stored payloads should not contain metadata fields', async function () {
|
it('stored payloads should not contain metadata fields', async function () {
|
||||||
await this.application.addPasscode('123')
|
await this.application.addPasscode('123')
|
||||||
await Factory.createSyncedNote(this.application)
|
await Factory.createSyncedNote(this.application)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
"lint:eslint": "eslint --ext .ts lib/",
|
"lint:eslint": "eslint --ext .ts lib/",
|
||||||
"lint:fix": "eslint --fix --ext .ts lib/",
|
"lint:fix": "eslint --fix --ext .ts lib/",
|
||||||
"lint:tsc": "tsc --noEmit --emitDeclarationOnly false --project lib/tsconfig.json",
|
"lint:tsc": "tsc --noEmit --emitDeclarationOnly false --project lib/tsconfig.json",
|
||||||
"test": "jest --coverage",
|
"test": "jest",
|
||||||
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand"
|
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
Reference in New Issue
Block a user