585 lines
27 KiB
JavaScript
585 lines
27 KiB
JavaScript
/* eslint-disable no-unused-expressions */
|
|
/* eslint-disable no-undef */
|
|
import * as Factory from '../lib/factory.js'
|
|
import FakeWebCrypto from '../lib/fake_web_crypto.js'
|
|
chai.use(chaiAsPromised)
|
|
const expect = chai.expect
|
|
|
|
describe('2020-01-15 web migration', () => {
|
|
beforeEach(() => {
|
|
localStorage.clear()
|
|
})
|
|
|
|
afterEach(() => {
|
|
localStorage.clear()
|
|
})
|
|
|
|
/**
|
|
* This test will pass but sync afterwards will not be successful
|
|
* as we are using a random value for the legacy session token
|
|
*/
|
|
it('2020-01-15 migration with passcode and account', async function () {
|
|
const application = await Factory.createAppWithRandNamespace()
|
|
/** Create legacy migrations value so that base migration detects old app */
|
|
await application.deviceInterface.setRawStorageValue('migrations', JSON.stringify(['anything']))
|
|
const operator003 = new SNProtocolOperator003(new FakeWebCrypto())
|
|
const identifier = 'foo'
|
|
const passcode = 'bar'
|
|
/** Create old version passcode parameters */
|
|
const passcodeKey = await operator003.createRootKey(identifier, passcode)
|
|
await application.deviceInterface.setRawStorageValue(
|
|
'offlineParams',
|
|
JSON.stringify(passcodeKey.keyParams.getPortableValue()),
|
|
)
|
|
|
|
/** Create arbitrary storage values and make sure they're migrated */
|
|
const arbitraryValues = {
|
|
foo: 'bar',
|
|
zar: 'tar',
|
|
har: 'car',
|
|
}
|
|
for (const key of Object.keys(arbitraryValues)) {
|
|
await application.deviceInterface.setRawStorageValue(key, arbitraryValues[key])
|
|
}
|
|
/** Create old version account parameters */
|
|
const password = 'tar'
|
|
const accountKey = await operator003.createRootKey(identifier, password)
|
|
|
|
/** Create legacy storage and encrypt it with passcode */
|
|
const embeddedStorage = {
|
|
mk: accountKey.masterKey,
|
|
ak: accountKey.dataAuthenticationKey,
|
|
pw: accountKey.serverPassword,
|
|
jwt: 'anything',
|
|
/** Legacy versions would store json strings inside of embedded storage */
|
|
auth_params: JSON.stringify(accountKey.keyParams.getPortableValue()),
|
|
}
|
|
const storagePayload = new DecryptedPayload({
|
|
uuid: await operator003.crypto.generateUUID(),
|
|
content_type: ContentType.EncryptedStorage,
|
|
content: {
|
|
storage: embeddedStorage,
|
|
},
|
|
})
|
|
const encryptionParams = await operator003.generateEncryptedParametersAsync(storagePayload, passcodeKey)
|
|
const persistPayload = new EncryptedPayload({ ...storagePayload, ...encryptionParams })
|
|
await application.deviceInterface.setRawStorageValue('encryptedStorage', JSON.stringify(persistPayload))
|
|
|
|
/** Create encrypted item and store it in db */
|
|
const notePayload = Factory.createNotePayload()
|
|
const noteEncryptionParams = await operator003.generateEncryptedParametersAsync(notePayload, accountKey)
|
|
const noteEncryptedPayload = new EncryptedPayload({ ...notePayload, ...noteEncryptionParams })
|
|
await application.deviceInterface.saveRawDatabasePayload(noteEncryptedPayload, application.identifier)
|
|
|
|
/** Run migration */
|
|
await application.prepareForLaunch({
|
|
receiveChallenge: async (challenge) => {
|
|
application.submitValuesForChallenge(challenge, [CreateChallengeValue(challenge.prompts[0], passcode)])
|
|
},
|
|
})
|
|
|
|
await application.launch(true)
|
|
expect(application.sessionManager.online()).to.equal(true)
|
|
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyPlusWrapper)
|
|
/** Should be decrypted */
|
|
const storageMode = application.diskStorageService.domainKeyForMode(StorageValueModes.Default)
|
|
const valueStore = application.diskStorageService.values[storageMode]
|
|
expect(valueStore.content_type).to.not.be.ok
|
|
|
|
expect(await application.deviceInterface.getRawStorageValue('offlineParams')).to.not.be.ok
|
|
|
|
const keyParams = await application.diskStorageService.getValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped)
|
|
expect(typeof keyParams).to.equal('object')
|
|
|
|
/** Embedded value should match */
|
|
const migratedKeyParams = await application.diskStorageService.getValue(
|
|
StorageKey.RootKeyParams,
|
|
StorageValueModes.Nonwrapped,
|
|
)
|
|
expect(migratedKeyParams).to.eql(JSON.parse(embeddedStorage.auth_params))
|
|
const rootKey = await application.protocolService.getRootKey()
|
|
expect(rootKey.masterKey).to.equal(accountKey.masterKey)
|
|
expect(rootKey.dataAuthenticationKey).to.equal(accountKey.dataAuthenticationKey)
|
|
/** Application should not retain server password from legacy versions */
|
|
expect(rootKey.serverPassword).to.not.be.ok
|
|
expect(rootKey.keyVersion).to.equal(ProtocolVersion.V003)
|
|
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyPlusWrapper)
|
|
|
|
/** Expect note is decrypted */
|
|
expect(application.itemManager.getDisplayableNotes().length).to.equal(1)
|
|
const retrievedNote = application.itemManager.getDisplayableNotes()[0]
|
|
expect(retrievedNote.uuid).to.equal(notePayload.uuid)
|
|
expect(retrievedNote.content.text).to.equal(notePayload.content.text)
|
|
|
|
/** Ensure arbitrary values have been migrated */
|
|
for (const key of Object.keys(arbitraryValues)) {
|
|
const value = await application.diskStorageService.getValue(key)
|
|
expect(arbitraryValues[key]).to.equal(value)
|
|
}
|
|
|
|
console.warn('Expecting exception due to deiniting application while trying to renew session')
|
|
await Factory.safeDeinit(application)
|
|
}).timeout(15000)
|
|
|
|
it('2020-01-15 migration with passcode only', async function () {
|
|
const application = await Factory.createAppWithRandNamespace()
|
|
/** Create legacy migrations value so that base migration detects old app */
|
|
await application.deviceInterface.setRawStorageValue('migrations', JSON.stringify(['anything']))
|
|
const operator003 = new SNProtocolOperator003(new FakeWebCrypto())
|
|
const identifier = 'foo'
|
|
const passcode = 'bar'
|
|
/** Create old version passcode parameters */
|
|
const passcodeKey = await operator003.createRootKey(identifier, passcode)
|
|
await application.deviceInterface.setRawStorageValue(
|
|
'offlineParams',
|
|
JSON.stringify(passcodeKey.keyParams.getPortableValue()),
|
|
)
|
|
|
|
/** Create arbitrary storage values and make sure they're migrated */
|
|
const arbitraryValues = {
|
|
foo: 'bar',
|
|
zar: 'tar',
|
|
har: 'car',
|
|
}
|
|
for (const key of Object.keys(arbitraryValues)) {
|
|
await application.deviceInterface.setRawStorageValue(key, arbitraryValues[key])
|
|
}
|
|
|
|
const embeddedStorage = {
|
|
...arbitraryValues,
|
|
}
|
|
const storagePayload = new DecryptedPayload({
|
|
uuid: await operator003.crypto.generateUUID(),
|
|
content: {
|
|
storage: embeddedStorage,
|
|
},
|
|
content_type: ContentType.EncryptedStorage,
|
|
})
|
|
const encryptionParams = await operator003.generateEncryptedParametersAsync(storagePayload, passcodeKey)
|
|
const persistPayload = new EncryptedPayload({ ...storagePayload, ...encryptionParams })
|
|
await application.deviceInterface.setRawStorageValue('encryptedStorage', JSON.stringify(persistPayload))
|
|
|
|
/** Create encrypted item and store it in db */
|
|
const notePayload = Factory.createNotePayload()
|
|
const noteEncryptionParams = await operator003.generateEncryptedParametersAsync(notePayload, passcodeKey)
|
|
const noteEncryptedPayload = new EncryptedPayload({ ...notePayload, ...noteEncryptionParams })
|
|
await application.deviceInterface.saveRawDatabasePayload(noteEncryptedPayload, application.identifier)
|
|
|
|
await application.prepareForLaunch({
|
|
receiveChallenge: async (challenge) => {
|
|
application.submitValuesForChallenge(challenge, [CreateChallengeValue(challenge.prompts[0], passcode)])
|
|
},
|
|
})
|
|
await application.launch(true)
|
|
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.WrapperOnly)
|
|
/** Should be decrypted */
|
|
const storageMode = application.diskStorageService.domainKeyForMode(StorageValueModes.Default)
|
|
const valueStore = application.diskStorageService.values[storageMode]
|
|
expect(valueStore.content_type).to.not.be.ok
|
|
|
|
expect(await application.deviceInterface.getRawStorageValue('offlineParams')).to.not.be.ok
|
|
|
|
/** Embedded value should match */
|
|
const migratedKeyParams = await application.diskStorageService.getValue(
|
|
StorageKey.RootKeyParams,
|
|
StorageValueModes.Nonwrapped,
|
|
)
|
|
expect(migratedKeyParams).to.eql(embeddedStorage.auth_params)
|
|
const rootKey = await application.protocolService.getRootKey()
|
|
expect(rootKey.masterKey).to.equal(passcodeKey.masterKey)
|
|
expect(rootKey.dataAuthenticationKey).to.equal(passcodeKey.dataAuthenticationKey)
|
|
/** Root key is in memory with passcode only, so server password can be defined */
|
|
expect(rootKey.serverPassword).to.be.ok
|
|
expect(rootKey.keyVersion).to.equal(ProtocolVersion.V003)
|
|
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.WrapperOnly)
|
|
|
|
/** Expect note is decrypted */
|
|
expect(application.itemManager.getDisplayableNotes().length).to.equal(1)
|
|
const retrievedNote = application.itemManager.getDisplayableNotes()[0]
|
|
expect(retrievedNote.uuid).to.equal(notePayload.uuid)
|
|
expect(retrievedNote.content.text).to.equal(notePayload.content.text)
|
|
|
|
/** Ensure arbitrary values have been migrated */
|
|
for (const key of Object.keys(arbitraryValues)) {
|
|
const value = await application.diskStorageService.getValue(key)
|
|
expect(arbitraryValues[key]).to.equal(value)
|
|
}
|
|
await Factory.safeDeinit(application)
|
|
})
|
|
|
|
/**
|
|
* This test will pass but sync afterwards will not be successful
|
|
* as we are using a random value for the legacy session token
|
|
*/
|
|
it('2020-01-15 migration with account only', async function () {
|
|
const application = await Factory.createAppWithRandNamespace()
|
|
/** Create legacy migrations value so that base migration detects old app */
|
|
await application.deviceInterface.setRawStorageValue('migrations', JSON.stringify(['anything']))
|
|
const operator003 = new SNProtocolOperator003(new FakeWebCrypto())
|
|
const identifier = 'foo'
|
|
|
|
/** Create old version account parameters */
|
|
const password = 'tar'
|
|
const accountKey = await operator003.createRootKey(identifier, password)
|
|
|
|
/** Create arbitrary storage values and make sure they're migrated */
|
|
const storage = {
|
|
foo: 'bar',
|
|
zar: 'tar',
|
|
har: 'car',
|
|
mk: accountKey.masterKey,
|
|
ak: accountKey.dataAuthenticationKey,
|
|
pw: accountKey.serverPassword,
|
|
jwt: 'anything',
|
|
/** Legacy versions would store json strings inside of embedded storage */
|
|
auth_params: JSON.stringify(accountKey.keyParams.getPortableValue()),
|
|
}
|
|
for (const key of Object.keys(storage)) {
|
|
await application.deviceInterface.setRawStorageValue(key, storage[key])
|
|
}
|
|
/** Create encrypted item and store it in db */
|
|
const notePayload = Factory.createNotePayload()
|
|
const noteEncryptionParams = await operator003.generateEncryptedParametersAsync(notePayload, accountKey)
|
|
const noteEncryptedPayload = new EncryptedPayload({ ...notePayload, ...noteEncryptionParams })
|
|
await application.deviceInterface.saveRawDatabasePayload(noteEncryptedPayload, application.identifier)
|
|
|
|
/** Run migration */
|
|
const promptValueReply = (prompts) => {
|
|
const values = []
|
|
for (const prompt of prompts) {
|
|
if (prompt.validation === ChallengeValidation.LocalPasscode) {
|
|
values.push(CreateChallengeValue(prompt, passcode))
|
|
} else {
|
|
/** We will be prompted to reauthetnicate our session, not relevant to this test
|
|
* but pass any value to avoid exception
|
|
*/
|
|
values.push(CreateChallengeValue(prompt, 'foo'))
|
|
}
|
|
}
|
|
return values
|
|
}
|
|
const receiveChallenge = async (challenge) => {
|
|
application.addChallengeObserver(challenge, {
|
|
onInvalidValue: (value) => {
|
|
const values = promptValueReply([value.prompt])
|
|
application.submitValuesForChallenge(challenge, values)
|
|
},
|
|
})
|
|
const initialValues = promptValueReply(challenge.prompts)
|
|
application.submitValuesForChallenge(challenge, initialValues)
|
|
}
|
|
await application.prepareForLaunch({
|
|
receiveChallenge: receiveChallenge,
|
|
})
|
|
await application.launch(true)
|
|
expect(application.sessionManager.online()).to.equal(true)
|
|
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyOnly)
|
|
/** Should be decrypted */
|
|
const storageMode = application.diskStorageService.domainKeyForMode(StorageValueModes.Default)
|
|
const valueStore = application.diskStorageService.values[storageMode]
|
|
expect(valueStore.content_type).to.not.be.ok
|
|
/** Embedded value should match */
|
|
const migratedKeyParams = await application.diskStorageService.getValue(
|
|
StorageKey.RootKeyParams,
|
|
StorageValueModes.Nonwrapped,
|
|
)
|
|
expect(migratedKeyParams).to.eql(accountKey.keyParams.getPortableValue())
|
|
const rootKey = await application.protocolService.getRootKey()
|
|
expect(rootKey).to.be.ok
|
|
|
|
expect(await application.deviceInterface.getRawStorageValue('migrations')).to.not.be.ok
|
|
expect(await application.deviceInterface.getRawStorageValue('auth_params')).to.not.be.ok
|
|
expect(await application.deviceInterface.getRawStorageValue('jwt')).to.not.be.ok
|
|
|
|
const keyParams = await application.diskStorageService.getValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped)
|
|
expect(typeof keyParams).to.equal('object')
|
|
|
|
expect(rootKey.masterKey).to.equal(accountKey.masterKey)
|
|
expect(rootKey.dataAuthenticationKey).to.equal(accountKey.dataAuthenticationKey)
|
|
expect(rootKey.serverPassword).to.not.be.ok
|
|
expect(rootKey.keyVersion).to.equal(ProtocolVersion.V003)
|
|
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyOnly)
|
|
|
|
/** Expect note is decrypted */
|
|
expect(application.itemManager.getDisplayableNotes().length).to.equal(1)
|
|
const retrievedNote = application.itemManager.getDisplayableNotes()[0]
|
|
expect(retrievedNote.uuid).to.equal(notePayload.uuid)
|
|
expect(retrievedNote.content.text).to.equal(notePayload.content.text)
|
|
|
|
/** Ensure arbitrary values have been migrated */
|
|
for (const key of Object.keys(storage)) {
|
|
/** Is stringified in storage, but parsed in storageService */
|
|
if (key === 'auth_params') {
|
|
continue
|
|
}
|
|
const value = await application.diskStorageService.getValue(key)
|
|
expect(storage[key]).to.equal(value)
|
|
}
|
|
|
|
console.warn('Expecting exception due to deiniting application while trying to renew session')
|
|
await Factory.safeDeinit(application)
|
|
})
|
|
|
|
it('2020-01-15 migration with no account and no passcode', async function () {
|
|
const application = await Factory.createAppWithRandNamespace()
|
|
/** Create legacy migrations value so that base migration detects old app */
|
|
await application.deviceInterface.setRawStorageValue('migrations', JSON.stringify(['anything']))
|
|
/** Create arbitrary storage values and make sure they're migrated */
|
|
const storage = {
|
|
foo: 'bar',
|
|
zar: 'tar',
|
|
har: 'car',
|
|
}
|
|
for (const key of Object.keys(storage)) {
|
|
await application.deviceInterface.setRawStorageValue(key, storage[key])
|
|
}
|
|
|
|
/** Create item and store it in db */
|
|
const notePayload = Factory.createNotePayload()
|
|
await application.deviceInterface.saveRawDatabasePayload(notePayload.ejected(), application.identifier)
|
|
|
|
/** Run migration */
|
|
await application.prepareForLaunch({
|
|
receiveChallenge: (_challenge) => {
|
|
return null
|
|
},
|
|
})
|
|
await application.launch(true)
|
|
|
|
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyNone)
|
|
|
|
/** Should be decrypted */
|
|
const storageMode = application.diskStorageService.domainKeyForMode(StorageValueModes.Default)
|
|
const valueStore = application.diskStorageService.values[storageMode]
|
|
expect(valueStore.content_type).to.not.be.ok
|
|
const rootKey = await application.protocolService.getRootKey()
|
|
expect(rootKey).to.not.be.ok
|
|
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyNone)
|
|
|
|
expect(await application.deviceInterface.getRawStorageValue('migrations')).to.not.be.ok
|
|
|
|
/** Expect note is decrypted */
|
|
expect(application.itemManager.getDisplayableNotes().length).to.equal(1)
|
|
const retrievedNote = application.itemManager.getDisplayableNotes()[0]
|
|
expect(retrievedNote.uuid).to.equal(notePayload.uuid)
|
|
expect(retrievedNote.content.text).to.equal(notePayload.content.text)
|
|
|
|
/** Ensure arbitrary values have been migrated */
|
|
for (const key of Object.keys(storage)) {
|
|
const value = await application.diskStorageService.getValue(key)
|
|
expect(storage[key]).to.equal(value)
|
|
}
|
|
|
|
await Factory.safeDeinit(application)
|
|
})
|
|
|
|
/**
|
|
* This test will pass but sync afterwards will not be successful
|
|
* as we are using a random value for the legacy session token
|
|
*/
|
|
it('2020-01-15 migration from app v1.0.1 with account only', async function () {
|
|
const application = await Factory.createAppWithRandNamespace()
|
|
/** Create legacy migrations value so that base migration detects old app */
|
|
await application.deviceInterface.setRawStorageValue('migrations', JSON.stringify(['anything']))
|
|
const operator001 = new SNProtocolOperator001(new FakeWebCrypto())
|
|
const identifier = 'foo'
|
|
|
|
/** Create old version account parameters */
|
|
const password = 'tar'
|
|
const accountKey = await operator001.createRootKey(identifier, password)
|
|
|
|
/** Create arbitrary storage values and make sure they're migrated */
|
|
const storage = {
|
|
mk: accountKey.masterKey,
|
|
pw: accountKey.serverPassword,
|
|
jwt: 'anything',
|
|
/** Legacy versions would store json strings inside of embedded storage */
|
|
auth_params: JSON.stringify(accountKey.keyParams.getPortableValue()),
|
|
user: JSON.stringify({ uuid: 'anything', email: 'anything' }),
|
|
}
|
|
for (const key of Object.keys(storage)) {
|
|
await application.deviceInterface.setRawStorageValue(key, storage[key])
|
|
}
|
|
/** Create encrypted item and store it in db */
|
|
const notePayload = Factory.createNotePayload()
|
|
const noteEncryptionParams = await operator001.generateEncryptedParametersAsync(notePayload, accountKey)
|
|
const noteEncryptedPayload = new EncryptedPayload({ ...notePayload, ...noteEncryptionParams })
|
|
await application.deviceInterface.saveRawDatabasePayload(noteEncryptedPayload, application.identifier)
|
|
|
|
/** Run migration */
|
|
const promptValueReply = (prompts) => {
|
|
const values = []
|
|
for (const prompt of prompts) {
|
|
/** We will be prompted to reauthetnicate our session, not relevant to this test
|
|
* but pass any value to avoid exception
|
|
*/
|
|
values.push(CreateChallengeValue(prompt, 'foo'))
|
|
}
|
|
return values
|
|
}
|
|
const receiveChallenge = async (challenge) => {
|
|
application.addChallengeObserver(challenge, {
|
|
onInvalidValue: (value) => {
|
|
const values = promptValueReply([value.prompt])
|
|
application.submitValuesForChallenge(challenge, values)
|
|
},
|
|
})
|
|
const initialValues = promptValueReply(challenge.prompts)
|
|
application.submitValuesForChallenge(challenge, initialValues)
|
|
}
|
|
await application.prepareForLaunch({
|
|
receiveChallenge: receiveChallenge,
|
|
})
|
|
await application.launch(true)
|
|
expect(application.sessionManager.online()).to.equal(true)
|
|
expect(application.sessionManager.getUser()).to.be.ok
|
|
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyOnly)
|
|
/** Should be decrypted */
|
|
const storageMode = application.diskStorageService.domainKeyForMode(StorageValueModes.Default)
|
|
const valueStore = application.diskStorageService.values[storageMode]
|
|
expect(valueStore.content_type).to.not.be.ok
|
|
/** Embedded value should match */
|
|
const migratedKeyParams = await application.diskStorageService.getValue(
|
|
StorageKey.RootKeyParams,
|
|
StorageValueModes.Nonwrapped,
|
|
)
|
|
expect(migratedKeyParams).to.eql(accountKey.keyParams.getPortableValue())
|
|
const rootKey = await application.protocolService.getRootKey()
|
|
expect(rootKey).to.be.ok
|
|
|
|
expect(await application.deviceInterface.getRawStorageValue('migrations')).to.not.be.ok
|
|
expect(await application.deviceInterface.getRawStorageValue('auth_params')).to.not.be.ok
|
|
expect(await application.deviceInterface.getRawStorageValue('jwt')).to.not.be.ok
|
|
expect(await application.deviceInterface.getRawStorageValue('ak')).to.not.be.ok
|
|
expect(await application.deviceInterface.getRawStorageValue('mk')).to.not.be.ok
|
|
expect(await application.deviceInterface.getRawStorageValue('pw')).to.not.be.ok
|
|
|
|
const keyParams = await application.diskStorageService.getValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped)
|
|
expect(typeof keyParams).to.equal('object')
|
|
|
|
expect(rootKey.masterKey).to.equal(accountKey.masterKey)
|
|
expect(rootKey.dataAuthenticationKey).to.equal(accountKey.dataAuthenticationKey)
|
|
expect(rootKey.serverPassword).to.not.be.ok
|
|
expect(rootKey.keyVersion).to.equal(ProtocolVersion.V001)
|
|
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyOnly)
|
|
|
|
/** Expect note is decrypted */
|
|
expect(application.itemManager.getDisplayableNotes().length).to.equal(1)
|
|
const retrievedNote = application.itemManager.getDisplayableNotes()[0]
|
|
expect(retrievedNote.uuid).to.equal(notePayload.uuid)
|
|
expect(retrievedNote.content.text).to.equal(notePayload.content.text)
|
|
|
|
/** Ensure arbitrary values have been migrated */
|
|
for (const key of Object.keys(storage)) {
|
|
/** Is stringified in storage, but parsed in storageService */
|
|
const value = await application.diskStorageService.getValue(key)
|
|
if (key === 'auth_params') {
|
|
continue
|
|
} else if (key === 'user') {
|
|
expect(storage[key]).to.equal(JSON.stringify(value))
|
|
} else {
|
|
expect(storage[key]).to.equal(value)
|
|
}
|
|
}
|
|
await Factory.safeDeinit(application)
|
|
})
|
|
|
|
it('2020-01-15 migration from 002 app with account and passcode but missing offlineParams.version', async function () {
|
|
/**
|
|
* There was an issue where if the user had offlineParams but it was missing the version key,
|
|
* the user could not get past the passcode migration screen.
|
|
*/
|
|
const application = await Factory.createAppWithRandNamespace()
|
|
/** Create legacy migrations value so that base migration detects old app */
|
|
await application.deviceInterface.setRawStorageValue('migrations', JSON.stringify(['anything']))
|
|
const operator002 = new SNProtocolOperator002(new FakeWebCrypto())
|
|
const identifier = 'foo'
|
|
const passcode = 'bar'
|
|
/** Create old version passcode parameters */
|
|
const passcodeKey = await operator002.createRootKey(identifier, passcode)
|
|
|
|
/** The primary chaos agent */
|
|
const offlineParams = passcodeKey.keyParams.getPortableValue()
|
|
omitInPlace(offlineParams, ['version'])
|
|
|
|
await application.deviceInterface.setRawStorageValue('offlineParams', JSON.stringify(offlineParams))
|
|
|
|
/** Create old version account parameters */
|
|
const password = 'tar'
|
|
const accountKey = await operator002.createRootKey(identifier, password)
|
|
|
|
/** Create legacy storage and encrypt it with passcode */
|
|
const embeddedStorage = {
|
|
mk: accountKey.masterKey,
|
|
ak: accountKey.dataAuthenticationKey,
|
|
pw: accountKey.serverPassword,
|
|
jwt: 'anything',
|
|
/** Legacy versions would store json strings inside of embedded storage */
|
|
auth_params: JSON.stringify(accountKey.keyParams.getPortableValue()),
|
|
user: JSON.stringify({ uuid: 'anything', email: 'anything' }),
|
|
}
|
|
const storagePayload = new DecryptedPayload({
|
|
uuid: await operator002.crypto.generateUUID(),
|
|
content_type: ContentType.EncryptedStorage,
|
|
content: {
|
|
storage: embeddedStorage,
|
|
},
|
|
})
|
|
const encryptionParams = await operator002.generateEncryptedParametersAsync(storagePayload, passcodeKey)
|
|
const persistPayload = new EncryptedPayload({ ...storagePayload, ...encryptionParams })
|
|
await application.deviceInterface.setRawStorageValue('encryptedStorage', JSON.stringify(persistPayload))
|
|
|
|
/** Create encrypted item and store it in db */
|
|
const notePayload = Factory.createNotePayload()
|
|
const noteEncryptionParams = await operator002.generateEncryptedParametersAsync(notePayload, accountKey)
|
|
const noteEncryptedPayload = new EncryptedPayload({ ...notePayload, ...noteEncryptionParams })
|
|
await application.deviceInterface.saveRawDatabasePayload(noteEncryptedPayload, application.identifier)
|
|
|
|
/** Runs migration */
|
|
await application.prepareForLaunch({
|
|
receiveChallenge: async (challenge) => {
|
|
application.submitValuesForChallenge(challenge, [CreateChallengeValue(challenge.prompts[0], passcode)])
|
|
},
|
|
})
|
|
await application.launch(true)
|
|
expect(application.sessionManager.online()).to.equal(true)
|
|
expect(application.sessionManager.getUser()).to.be.ok
|
|
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyPlusWrapper)
|
|
/** Should be decrypted */
|
|
const storageMode = application.diskStorageService.domainKeyForMode(StorageValueModes.Default)
|
|
const valueStore = application.diskStorageService.values[storageMode]
|
|
expect(valueStore.content_type).to.not.be.ok
|
|
/** Embedded value should match */
|
|
const migratedKeyParams = await application.diskStorageService.getValue(
|
|
StorageKey.RootKeyParams,
|
|
StorageValueModes.Nonwrapped,
|
|
)
|
|
expect(migratedKeyParams).to.eql(accountKey.keyParams.getPortableValue())
|
|
const rootKey = await application.protocolService.getRootKey()
|
|
expect(rootKey).to.be.ok
|
|
|
|
expect(await application.deviceInterface.getRawStorageValue('migrations')).to.not.be.ok
|
|
expect(await application.deviceInterface.getRawStorageValue('auth_params')).to.not.be.ok
|
|
expect(await application.deviceInterface.getRawStorageValue('jwt')).to.not.be.ok
|
|
expect(await application.deviceInterface.getRawStorageValue('ak')).to.not.be.ok
|
|
expect(await application.deviceInterface.getRawStorageValue('mk')).to.not.be.ok
|
|
expect(await application.deviceInterface.getRawStorageValue('pw')).to.not.be.ok
|
|
|
|
const keyParams = await application.diskStorageService.getValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped)
|
|
expect(typeof keyParams).to.equal('object')
|
|
|
|
expect(rootKey.masterKey).to.equal(accountKey.masterKey)
|
|
expect(rootKey.dataAuthenticationKey).to.equal(accountKey.dataAuthenticationKey)
|
|
expect(rootKey.serverPassword).to.not.be.ok
|
|
expect(rootKey.keyVersion).to.equal(ProtocolVersion.V002)
|
|
|
|
/** Expect note is decrypted */
|
|
expect(application.itemManager.getDisplayableNotes().length).to.equal(1)
|
|
const retrievedNote = application.itemManager.getDisplayableNotes()[0]
|
|
expect(retrievedNote.uuid).to.equal(notePayload.uuid)
|
|
expect(retrievedNote.content.text).to.equal(notePayload.content.text)
|
|
|
|
await Factory.safeDeinit(application)
|
|
})
|
|
})
|