Skip to content

Instantly share code, notes, and snippets.

@daviddahl
Last active September 9, 2015 16:22
Show Gist options
  • Save daviddahl/c038f8ad0b7783afdb32 to your computer and use it in GitHub Desktop.
Save daviddahl/c038f8ad0b7783afdb32 to your computer and use it in GitHub Desktop.
// 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