Last active
September 9, 2015 16:22
-
-
Save daviddahl/c038f8ad0b7783afdb32 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Difference between Container Encryption and Item Encryption | |
// (Short answer: Item Encryption & decryption is identical to container encryoption and decryoption, just 1 step & simpler with no diffs, etc) | |
// Containers Encryption: | |
// https://github.com/SpiderOak/crypton/blob/a7e6a76f0c099ef2762c27f85f474bfb1c62727b/client/src/session.js#L663-L699 | |
var selfPeer = new crypton.Peer({ | |
session: this, | |
pubKey: this.account.pubKey, | |
signKeyPub: this.account.signKeyPub | |
}); | |
selfPeer.trusted = true; | |
var sessionKey = crypton.randomBytes(32); | |
var sessionKeyCiphertext = selfPeer.encryptAndSign(sessionKey); | |
if (sessionKeyCiphertext.error) { | |
return callback(sessionKeyCiphertext.error); | |
} | |
delete sessionKeyCiphertext.error; | |
var signature = 'hello'; | |
var containerNameHmac = new sjcl.misc.hmac(this.account.containerNameHmacKey); | |
containerNameHmac = sjcl.codec.hex.fromBits(containerNameHmac.encrypt(containerName)); | |
// TODO why is a session object generating container payloads? creating the | |
// initial container state should be done in container.js | |
var rawPayloadCiphertext = sjcl.encrypt(sessionKey, JSON.stringify({ | |
recordIndex: 0, | |
delta: {} | |
}), crypton.cipherOptions); | |
var payloadCiphertextHash = sjcl.hash.sha256.hash(JSON.stringify(rawPayloadCiphertext)); | |
var payloadSignature = this.account.signKeyPrivate.sign(payloadCiphertextHash, crypton.paranoia); | |
var payloadCiphertext = { | |
ciphertext: rawPayloadCiphertext, | |
signature: payloadSignature | |
}; | |
// And... | |
// https://github.com/SpiderOak/crypton/blob/master/client/src/container.js#L112-L114 | |
var rawPayloadCiphertext = sjcl.encrypt(that.sessionKey, JSON.stringify(payload), crypton.cipherOptions); | |
var payloadCiphertextHash = sjcl.hash.sha256.hash(JSON.stringify(rawPayloadCiphertext)); | |
var payloadSignature = that.session.account.signKeyPrivate.sign(payloadCiphertextHash, crypton.paranoia); | |
// Containers Decryption: | |
// https://github.com/SpiderOak/crypton/blob/master/client/src/container.js#L353-L366 | |
// Key Decryption: | |
Container.prototype.decryptKey = function (record) { | |
var peer = this.peer || this.session.account; | |
var sessionKeyRaw = this.session.account.verifyAndDecrypt(JSON.parse(record.sessionKeyCiphertext), peer); | |
if (sessionKeyRaw.error) { | |
throw new Error(sessionKeyRaw.error); | |
} | |
if (!sessionKeyRaw.verified) { | |
throw new Error('Container session key signature mismatch'); | |
} | |
this.sessionKey = JSON.parse(sessionKeyRaw.plaintext); | |
}; | |
// Container chunk decryption: | |
// https://github.com/SpiderOak/crypton/blob/master/client/src/work.js#L223-L292 | |
var record; | |
try { | |
record = JSON.parse(options.record); | |
} catch (e) {} | |
if (!record) { | |
return callback('Could not parse record'); | |
} | |
// reconstruct the peer's public signing key | |
// the key itself typically has circular references which | |
// we can't pass around with JSON to/from a worker | |
var curve = 'c' + peerSignKeyPubSerialized.curve; | |
var signPoint = sjcl.ecc.curves[curve].fromBits(peerSignKeyPubSerialized.point); | |
var peerSignKeyPub = new sjcl.ecc.ecdsa.publicKey(peerSignKeyPubSerialized.curve, signPoint.curve, signPoint); | |
var verified = false; | |
var payloadCiphertextHash = sjcl.hash.sha256.hash(JSON.stringify(record.ciphertext)); | |
try { | |
verified = peerSignKeyPub.verify(payloadCiphertextHash, record.signature); | |
} catch (e) { | |
console.error(e); | |
} | |
if (!verified) { | |
return callback('Record signature does not match expected signature'); | |
} | |
var payload; | |
try { | |
payload = JSON.parse(sjcl.decrypt(sessionKey, record.ciphertext, crypton.cipherOptions)); | |
} catch (e) {} | |
if (!payload) { | |
return callback('Could not parse record payload'); | |
} | |
// Item Encryption: | |
// https://github.com/SpiderOak/crypton/blob/master/client/src/item.js#L323-L347 | |
var sessionKey = crypton.randomBytes(32); | |
this.sessionKey = sessionKey; | |
var rawPayloadCiphertext = | |
sjcl.encrypt(this.sessionKey, itemValue, crypton.cipherOptions); | |
var payloadCiphertextHash = sjcl.hash.sha256.hash(rawPayloadCiphertext); | |
var payloadSignature = | |
this.session.account.signKeyPrivate.sign(payloadCiphertextHash, crypton.paranoia); | |
var payloadCiphertext = { | |
ciphertext: JSON.parse(rawPayloadCiphertext), // Fucking SJCL. WTF? | |
signature: payloadSignature | |
}; | |
var sessionKeyHash = sjcl.hash.sha256.hash(sessionKeyCiphertext); | |
var sessionKeySignature = | |
this.session.account.signKeyPrivate.sign(sessionKeyHash, crypton.paranoia); | |
var payload = { | |
itemNameHmac: itemNameHmac, | |
payloadCiphertext: JSON.stringify(payloadCiphertext), | |
wrappedSessionKey: JSON.stringify(sessionKeyCiphertext), | |
timelineVisible: timelineVisibleFlag | |
}; | |
return payload; | |
// Item Decryption: | |
// https://github.com/SpiderOak/crypton/blob/master/client/src/item.js#L148-L195 | |
// Check for this.sessionKey, or unwrap it | |
var sessionKeyResult; | |
var peer; | |
if (!this.sessionKey) { | |
if (this.sharedItem) { | |
peer = this.creator; | |
} else { | |
peer = this.session.createSelfPeer(); | |
} | |
sessionKeyResult = | |
this.session.account.verifyAndDecrypt(wrappedSessionKey, peer); | |
if (sessionKeyResult.error) { | |
return callback(ERRS.UNWRAP_KEY_ERROR); | |
} | |
this.sessionKey = JSON.parse(sessionKeyResult.plaintext); | |
} | |
var decrypted; | |
try { | |
decrypted = sjcl.decrypt(this.sessionKey, ct, crypton.cipherOptions); | |
} catch (ex) { | |
console.error(ex); | |
console.error(ex.stack); | |
return callback(ERRS.DECRYPT_CIPHERTEXT_ERROR); | |
} | |
var value; | |
if (decrypted) { | |
try { | |
this._value = JSON.parse(decrypted); | |
} catch (ex) { | |
console.error(ex); | |
console.error(ex.stack); | |
// XXXddahl: check to see if this is an actual JSON error (malformed string, etc) | |
this._value = decrypted; // Just a string, not an object | |
} | |
// XXXddahl: check to see if the modified_time is newer than the cached version (if any) | |
var name; | |
if (this.name) { | |
name = this.name; | |
} else { | |
name = this.getPublicName(); | |
} | |
this.session.items[name] = this; | |
this.modTime = new Date(rawData.modTime); | |
callback(null, this); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment