mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-11-12 04:25:31 +08:00
209 lines
12 KiB
CoffeeScript
Executable file
209 lines
12 KiB
CoffeeScript
Executable file
{React, ReactTestUtils, DraftStore, Message} = require 'nylas-exports'
|
|
pgp = require 'kbpgp'
|
|
_ = require 'underscore'
|
|
fs = require 'fs'
|
|
|
|
Identity = require '../lib/identity'
|
|
PGPKeyStore = require '../lib/pgp-key-store'
|
|
|
|
describe "PGPKeyStore", ->
|
|
beforeEach ->
|
|
@TEST_KEY = """-----BEGIN PGP PRIVATE KEY BLOCK-----
|
|
Version: GnuPG v1
|
|
|
|
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
|
|
217s2OKjpJqtpHPf2vY=
|
|
=UY7Y
|
|
-----END PGP PRIVATE KEY BLOCK-----"""
|
|
|
|
# mock getKeyContents to get rid of all the fs.readFiles
|
|
spyOn(PGPKeyStore, "getKeyContents").andCallFake( ({key, passphrase, callback}) =>
|
|
data = @TEST_KEY
|
|
pgp.KeyManager.import_from_armored_pgp {
|
|
armored: data
|
|
}, (err, km) =>
|
|
expect(err).toEqual(null)
|
|
if km.is_pgp_locked()
|
|
expect(passphrase).toBeDefined()
|
|
km.unlock_pgp { passphrase: passphrase }, (err) =>
|
|
expect(err).toEqual(null)
|
|
key.key = km
|
|
key.setTimeout()
|
|
if callback?
|
|
callback()
|
|
)
|
|
|
|
# define an encrypted and an unencrypted message
|
|
@unencryptedMsg = new Message({clientId: 'test', subject: 'Subject', body: '<p>Body</p>'})
|
|
body = """-----BEGIN PGP MESSAGE-----
|
|
Version: Keybase OpenPGP v2.0.52 Comment: keybase.io/crypto
|
|
|
|
wcBMA5nwa6GWVDOUAQf+MjiVRIBWJyM6The6/h2MgSJTDyrN9teFFJTizOvgHNnD W4EpEmmhShNyERI67qXhC03lFczu2Zp2Qofgs8YePIEv7wwb27/cviODsE42YJvX 1zGir+jBp81s9ZiF4dex6Ir9XfiZJlypI2QV2dHjO+5pstW+XhKIc1R5vKvoFTGI 1XmZtL3EgtKfj/HkPUkq2N0G5kAoB2MTTQuurfXm+3TRkftqesyTKlek652sFjCv nSF+LQ1GYq5hI4YaUBiHnZd7wKUgDrIh2rzbuGq+AHjrHdVLMfRTbN0Xsy3OWRcC 9uWU8Nln00Ly6KbTqPXKcBDcMrOJuoxYcpmLlhRds9JoAY7MyIsj87M2mkTtAtMK hqK0PPvJKfepV+eljDhQ7y0TQ0IvNtO5/pcY2CozbFJncm/ToxxZPNJueKRcz+EH M9uBvrWNTwfHj26g405gpRDN1T8CsY5ZeiaDHduIKnBWd4za0ak0Xfw=
|
|
=1aPN
|
|
-----END PGP MESSAGE-----"""
|
|
@encryptedMsg = new Message({clientId: 'test2', subject: 'Subject', body: body})
|
|
|
|
# blow away the saved identities and set up a test pub/priv keypair
|
|
PGPKeyStore._identities = {}
|
|
pubIdent = new Identity({
|
|
addresses: ["benbitdiddle@icloud.com"]
|
|
isPriv: false
|
|
})
|
|
PGPKeyStore._identities[pubIdent.clientId] = pubIdent
|
|
privIdent = new Identity({
|
|
addresses: ["benbitdiddle@icloud.com"]
|
|
isPriv: true
|
|
})
|
|
PGPKeyStore._identities[privIdent.clientId] = privIdent
|
|
|
|
describe "when handling private keys", ->
|
|
it 'should be able to retrieve and unlock a private key', ->
|
|
expect(PGPKeyStore.privKeys().some((cv, index, array) =>
|
|
cv.hasOwnProperty("key"))).toBeFalsey
|
|
key = PGPKeyStore.privKeys(address: "benbitdiddle@icloud.com", timed: false)[0]
|
|
PGPKeyStore.getKeyContents(key: key, passphrase: "", callback: =>
|
|
expect(PGPKeyStore.privKeys({timed: false}).some((cv, index, array) =>
|
|
cv.hasOwnProperty("key"))).toBeTruthy
|
|
)
|
|
|
|
it 'should not return a private key after its timeout has passed', ->
|
|
expect(PGPKeyStore.privKeys({address: "benbitdiddle@icloud.com", timed: false}).length).toEqual(1)
|
|
PGPKeyStore.privKeys({address: "benbitdiddle@icloud.com", timed: false})[0].timeout = Date.now() - 5
|
|
expect(PGPKeyStore.privKeys(address: "benbitdiddle@icloud.com", timed: true).length).toEqual(0)
|
|
PGPKeyStore.privKeys({address: "benbitdiddle@icloud.com", timed: false})[0].setTimeout()
|
|
|
|
it 'should only return the key(s) corresponding to a supplied email address', ->
|
|
expect(PGPKeyStore.privKeys(address: "wrong@example.com", timed: true).length).toEqual(0)
|
|
|
|
it 'should return all private keys when an address is not supplied', ->
|
|
expect(PGPKeyStore.privKeys({timed: false}).length).toEqual(1)
|
|
|
|
it 'should update an existing key when it is unlocked, not add a new one', ->
|
|
timeout = PGPKeyStore.privKeys({address: "benbitdiddle@icloud.com", timed: false})[0].timeout
|
|
PGPKeyStore.getKeyContents(key: PGPKeyStore.privKeys({timed: false})[0], passphrase: "", callback: =>
|
|
# expect no new keys to have been added
|
|
expect(PGPKeyStore.privKeys({timed: false}).length).toEqual(1)
|
|
# make sure the timeout is updated
|
|
expect(timeout < PGPKeyStore.privKeys({address: "benbitdiddle@icloud.com", timed: false}).timeout)
|
|
)
|
|
|
|
describe "when decrypting messages", ->
|
|
xit 'should be able to decrypt a message', ->
|
|
# TODO for some reason, the pgp.unbox has a problem with the message body
|
|
runs( =>
|
|
spyOn(PGPKeyStore, 'trigger')
|
|
PGPKeyStore.getKeyContents(key: PGPKeyStore.privKeys({timed: false})[0], passphrase: "", callback: =>
|
|
PGPKeyStore.decrypt(@encryptedMsg)
|
|
)
|
|
)
|
|
waitsFor((=> PGPKeyStore.trigger.callCount > 0), 'message to decrypt')
|
|
runs( =>
|
|
expect(_.findWhere(PGPKeyStore._msgCache,
|
|
{clientId: @encryptedMsg.clientId})).toExist()
|
|
)
|
|
|
|
it 'should be able to handle an unencrypted message', ->
|
|
PGPKeyStore.decrypt(@unencryptedMsg)
|
|
expect(_.findWhere(PGPKeyStore._msgCache,
|
|
{clientId: @unencryptedMsg.clientId})).not.toBeDefined()
|
|
|
|
it 'should be able to tell when a message has no encrypted component', ->
|
|
expect(PGPKeyStore.hasEncryptedComponent(@unencryptedMsg)).not
|
|
expect(PGPKeyStore.hasEncryptedComponent(@encryptedMsg))
|
|
|
|
it 'should be able to handle a message with no BEGIN PGP MESSAGE block', ->
|
|
body = """Version: Keybase OpenPGP v2.0.52 Comment: keybase.io/crypto
|
|
|
|
wcBMA5nwa6GWVDOUAQf+MjiVRIBWJyM6The6/h2MgSJTDyrN9teFFJTizOvgHNnD W4EpEmmhShNyERI67qXhC03lFczu2Zp2Qofgs8YePIEv7wwb27/cviODsE42YJvX 1zGir+jBp81s9ZiF4dex6Ir9XfiZJlypI2QV2dHjO+5pstW+XhKIc1R5vKvoFTGI 1XmZtL3EgtKfj/HkPUkq2N0G5kAoB2MTTQuurfXm+3TRkftqesyTKlek652sFjCv nSF+LQ1GYq5hI4YaUBiHnZd7wKUgDrIh2rzbuGq+AHjrHdVLMfRTbN0Xsy3OWRcC 9uWU8Nln00Ly6KbTqPXKcBDcMrOJuoxYcpmLlhRds9JoAY7MyIsj87M2mkTtAtMK hqK0PPvJKfepV+eljDhQ7y0TQ0IvNtO5/pcY2CozbFJncm/ToxxZPNJueKRcz+EH M9uBvrWNTwfHj26g405gpRDN1T8CsY5ZeiaDHduIKnBWd4za0ak0Xfw=
|
|
=1aPN
|
|
-----END PGP MESSAGE-----"""
|
|
badMsg = new Message({clientId: 'test2', subject: 'Subject', body: body})
|
|
|
|
PGPKeyStore.getKeyContents(key: PGPKeyStore.privKeys({timed: false})[0], passphrase: "", callback: =>
|
|
PGPKeyStore.decrypt(badMsg)
|
|
expect(_.findWhere(PGPKeyStore._msgCache,
|
|
{clientId: badMsg.clientId})).not.toBeDefined()
|
|
)
|
|
|
|
it 'should be able to handle a message with no END PGP MESSAGE block', ->
|
|
body = """-----BEGIN PGP MESSAGE-----
|
|
Version: Keybase OpenPGP v2.0.52 Comment: keybase.io/crypto
|
|
|
|
wcBMA5nwa6GWVDOUAQf+MjiVRIBWJyM6The6/h2MgSJTDyrN9teFFJTizOvgHNnD W4EpEmmhShNyERI67qXhC03lFczu2Zp2Qofgs8YePIEv7wwb27/cviODsE42YJvX 1zGir+jBp81s9ZiF4dex6Ir9XfiZJlypI2QV2dHjO+5pstW+XhKIc1R5vKvoFTGI 1XmZtL3EgtKfj/HkPUkq2N0G5kAoB2MTTQuurfXm+3TRkftqesyTKlek652sFjCv nSF+LQ1GYq5hI4YaUBiHnZd7wKUgDrIh2rzbuGq+AHjrHdVLMfRTbN0Xsy3OWRcC 9uWU8Nln00Ly6KbTqPXKcBDcMrOJuoxYcpmLlhRds9JoAY7MyIsj87M2mkTtAtMK hqK0PPvJKfepV+eljDhQ7y0TQ0IvNtO5/pcY2CozbFJncm/ToxxZPNJueKRcz+EH M9uBvrWNTwfHj26g405gpRDN1T8CsY5ZeiaDHduIKnBWd4za0ak0Xfw=
|
|
=1aPN"""
|
|
badMsg = new Message({clientId: 'test2', subject: 'Subject', body: body})
|
|
|
|
PGPKeyStore.getKeyContents(key: PGPKeyStore.privKeys({timed: false})[0], passphrase: "", callback: =>
|
|
PGPKeyStore.decrypt(badMsg)
|
|
expect(_.findWhere(PGPKeyStore._msgCache,
|
|
{clientId: badMsg.clientId})).not.toBeDefined()
|
|
)
|
|
|
|
it 'should not return a decrypted message which has timed out', ->
|
|
PGPKeyStore._msgCache.push({clientId: "testID", body: "example body", timeout: Date.now()})
|
|
|
|
msg = new Message({clientId: "testID"})
|
|
expect(PGPKeyStore.getDecrypted(msg)).toEqual(null)
|
|
|
|
it 'should return a decrypted message', ->
|
|
timeout = Date.now() + (1000*60*60)
|
|
PGPKeyStore._msgCache.push({clientId: "testID2", body: "example body", timeout: timeout})
|
|
|
|
msg = new Message({clientId: "testID2", body: "example body"})
|
|
expect(PGPKeyStore.getDecrypted(msg)).toEqual(msg.body)
|
|
|
|
describe "when handling public keys", ->
|
|
|
|
it "should immediately return a pre-cached key", ->
|
|
expect(PGPKeyStore.pubKeys('benbitdiddle@icloud.com').length).toEqual(1)
|