Files
standardnotes-app-web/packages/snjs/mocha/vaults/crypto.test.js

205 lines
7.1 KiB
JavaScript

import * as Factory from '../lib/factory.js'
import * as Collaboration from '../lib/Collaboration.js'
chai.use(chaiAsPromised)
const expect = chai.expect
describe('shared vault crypto', function () {
this.timeout(Factory.TwentySecondTimeout)
let context
afterEach(async function () {
await context.deinit()
localStorage.clear()
})
beforeEach(async function () {
localStorage.clear()
context = await Factory.createAppContextWithRealCrypto()
await context.launch()
await context.register()
})
describe('root key', () => {
it('root key loaded from disk should have keypairs', async () => {
const appIdentifier = context.identifier
await context.deinit()
let recreatedContext = await Factory.createAppContextWithRealCrypto(appIdentifier)
await recreatedContext.launch()
expect(recreatedContext.encryption.getKeyPair()).to.not.be.undefined
expect(recreatedContext.encryption.getSigningKeyPair()).to.not.be.undefined
})
it('changing user password should re-encrypt all key system root keys', async () => {
console.error('TODO: implement')
})
it('changing user password should re-encrypt all trusted contacts', async () => {
console.error('TODO: implement')
})
})
describe('persistent content signature', () => {
it('storage payloads should include signatureData', async () => {
const { note, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
await contactContext.changeNoteTitleAndSync(note, 'new title')
await context.sync()
const rawPayloads = await context.application.diskStorageService.getAllRawPayloads()
const noteRawPayload = rawPayloads.find((payload) => payload.uuid === note.uuid)
expect(noteRawPayload.signatureData).to.not.be.undefined
await deinitContactContext()
})
it('changing item content should erase existing signatureData', async () => {
const { note, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
await contactContext.changeNoteTitleAndSync(note, 'new title')
await context.sync()
let updatedNote = context.items.findItem(note.uuid)
await context.changeNoteTitleAndSync(updatedNote, 'new title 2')
updatedNote = context.items.findItem(note.uuid)
expect(updatedNote.signatureData).to.be.undefined
await deinitContactContext()
})
it('encrypting an item into storage then loading it should verify authenticity of original content rather than most recent symmetric signature', async () => {
const { note, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
await contactContext.changeNoteTitleAndSync(note, 'new title')
/** Override decrypt result to return failing signature */
const objectToSpy = context.encryption
sinon.stub(objectToSpy, 'decryptSplit').callsFake(async (split) => {
objectToSpy.decryptSplit.restore()
const decryptedPayloads = await objectToSpy.decryptSplit(split)
expect(decryptedPayloads.length).to.equal(1)
const payload = decryptedPayloads[0]
const mutatedPayload = new DecryptedPayload({
...payload.ejected(),
signatureData: {
...payload.signatureData,
result: {
...payload.signatureData.result,
passes: false,
},
},
})
return [mutatedPayload]
})
await context.sync()
let updatedNote = context.items.findItem(note.uuid)
expect(updatedNote.content.title).to.equal('new title')
expect(updatedNote.signatureData.result.passes).to.equal(false)
const appIdentifier = context.identifier
await context.deinit()
let recreatedContext = await Factory.createAppContextWithRealCrypto(appIdentifier)
await recreatedContext.launch()
updatedNote = recreatedContext.items.findItem(note.uuid)
expect(updatedNote.signatureData.result.passes).to.equal(false)
/** Changing the content now should clear failing signature */
await recreatedContext.changeNoteTitleAndSync(updatedNote, 'new title 2')
updatedNote = recreatedContext.items.findItem(note.uuid)
expect(updatedNote.signatureData).to.be.undefined
await recreatedContext.deinit()
recreatedContext = await Factory.createAppContextWithRealCrypto(appIdentifier)
await recreatedContext.launch()
/** Decrypting from storage will now verify current user symmetric signature only */
updatedNote = recreatedContext.items.findItem(note.uuid)
expect(updatedNote.signatureData.result.passes).to.equal(true)
await recreatedContext.deinit()
await deinitContactContext()
})
})
describe('symmetrically encrypted items', () => {
it('created items with a payload source of remote saved should not have signature data', async () => {
const note = await context.createSyncedNote()
expect(note.payload.source).to.equal(PayloadSource.RemoteSaved)
expect(note.signatureData).to.be.undefined
})
it('retrieved items that are then remote saved should have their signature data cleared', async () => {
const { note, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
await contactContext.changeNoteTitleAndSync(contactContext.items.findItem(note.uuid), 'new title')
await context.sync()
expect(context.items.findItem(note.uuid).signatureData).to.not.be.undefined
await context.changeNoteTitleAndSync(context.items.findItem(note.uuid), 'new title')
expect(context.items.findItem(note.uuid).signatureData).to.be.undefined
await deinitContactContext()
})
it('should allow client verification of authenticity of shared item changes', async () => {
const { note, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
expect(context.contacts.isItemAuthenticallySigned(note)).to.equal('not-applicable')
const contactNote = contactContext.items.findItem(note.uuid)
expect(contactContext.contacts.isItemAuthenticallySigned(contactNote)).to.equal('yes')
await contactContext.changeNoteTitleAndSync(contactNote, 'new title')
await context.sync()
let updatedNote = context.items.findItem(note.uuid)
expect(context.contacts.isItemAuthenticallySigned(updatedNote)).to.equal('yes')
await deinitContactContext()
})
})
describe('keypair revocation', () => {
it('should be able to revoke non-current keypair', async () => {
console.error('TODO')
})
it('revoking a keypair should send a keypair revocation event to trusted contacts', async () => {
console.error('TODO')
})
it('should not be able to revoke current key pair', async () => {
console.error('TODO')
})
it('should distrust revoked keypair as contact', async () => {
console.error('TODO')
})
})
})