ECDHE through NaCL
This commit is contained in:
parent
8db3885659
commit
e0d0df3b4a
1 changed files with 53 additions and 33 deletions
|
@ -293,9 +293,6 @@ function getRandomBytes(size) {
|
||||||
var createNewKeyPair = function(callback) {
|
var createNewKeyPair = function(callback) {
|
||||||
//TODO
|
//TODO
|
||||||
var privKey = getRandomBytes(32);
|
var privKey = getRandomBytes(32);
|
||||||
privKey[0] &= 248;
|
|
||||||
privKey[31] &= 127;
|
|
||||||
privKey[31] |= 64;
|
|
||||||
postNaclMessage({command: "bytesToPriv", priv: privKey}, function(message) {
|
postNaclMessage({command: "bytesToPriv", priv: privKey}, function(message) {
|
||||||
postNaclMessage({command: "privToPub", priv: message.res}, function(message) {
|
postNaclMessage({command: "privToPub", priv: message.res}, function(message) {
|
||||||
callback({ pubKey: message.res, privKey: privKey });
|
callback({ pubKey: message.res, privKey: privKey });
|
||||||
|
@ -346,8 +343,10 @@ function getRandomBytes(size) {
|
||||||
/*****************************
|
/*****************************
|
||||||
*** Internal Crypto stuff ***
|
*** Internal Crypto stuff ***
|
||||||
*****************************/
|
*****************************/
|
||||||
var ECDHE = function(pubKey, privKey) {
|
var ECDHE = function(pubKey, privKey, callback) {
|
||||||
return "ECDHE";//TODO
|
postNaclMessage({command: "ECDHE", priv: privKey, pub: pubKey}, function(message) {
|
||||||
|
callback(message.res);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var HKDF = function(input, salt, info) {
|
var HKDF = function(input, salt, info) {
|
||||||
|
@ -376,35 +375,53 @@ function getRandomBytes(size) {
|
||||||
/******************************
|
/******************************
|
||||||
*** Ratchet implementation ***
|
*** Ratchet implementation ***
|
||||||
******************************/
|
******************************/
|
||||||
var initSession = function(isInitiator, theirIdentityPubKey, ourEphemeralPrivKey, theirEphemeralPubKey) {
|
var initSession = function(isInitiator, theirIdentityPubKey, ourEphemeralPrivKey, theirEphemeralPubKey, callback) {
|
||||||
var ourIdentityPrivKey = crypto_storage.getIdentityPrivKey();
|
var ourIdentityPrivKey = crypto_storage.getIdentityPrivKey();
|
||||||
|
|
||||||
var sharedSecret = ECDHE(theirEphemeralPubKey, ourIdentityPrivKey);
|
var sharedSecret;
|
||||||
if (isInitiator)
|
ECDHE(theirEphemeralPubKey, ourIdentityPrivKey, function(ecRes) {
|
||||||
sharedSecret = sharedSecret + ECDHE(theirIdentityPubKey, ourEphemeralPrivKey);
|
sharedSecret = ecRes;
|
||||||
else
|
|
||||||
sharedSecret = ECDHE(theirIdentityPubKey, ourEphemeralPrivKey) + sharedSecret;
|
|
||||||
sharedSecret += ECDHE(theirEphemeralPubKey, ourEphemeralPrivKey);
|
|
||||||
|
|
||||||
var masterKey = HKDF(sharedSecret, '', "WhisperText");
|
function finishInit() {
|
||||||
return { rootKey: masterKey[0], chainKey: masterKey[1] };
|
ECDHE(theirEphemeralPubKey, ourEphemeralPrivKey, function(ecRes) {
|
||||||
|
sharedSecret += ecRes;
|
||||||
|
|
||||||
|
var masterKey = HKDF(sharedSecret, '', "WhisperText");
|
||||||
|
callback({ rootKey: masterKey[0], chainKey: masterKey[1] });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInitiator) {
|
||||||
|
ECDHE(theirIdentityPubKey, ourEphemeralPrivKey, function(ecRes) {
|
||||||
|
sharedSecret = sharedSecret + ecRes;
|
||||||
|
finishInit();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ECDHE(theirIdentityPubKey, ourEphemeralPrivKey, function(ecRes) {
|
||||||
|
sharedSecret = ecRes + sharedSecret;
|
||||||
|
finishInit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
|
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message, callback) {
|
||||||
//TODO: Check remote identity key matches known-good key
|
//TODO: Check remote identity key matches known-good key
|
||||||
|
|
||||||
var preKeyPair = crypto_storage.getAndRemovePreKeyPair(preKeyProto.preKeyId);
|
var preKeyPair = crypto_storage.getAndRemovePreKeyPair(preKeyProto.preKeyId);
|
||||||
if (preKeyPair === undefined)
|
if (preKeyPair === undefined)
|
||||||
throw "Missing preKey for PreKeyWhisperMessage";
|
throw "Missing preKey for PreKeyWhisperMessage";
|
||||||
|
|
||||||
var firstRatchet = initSession(false, message.identityKey, preKeyPair.privKey, message.baseKey);
|
initSession(false, message.identityKey, preKeyPair.privKey, message.baseKey, function(firstRatchet) {
|
||||||
|
var session = {currentRatchet: { rootKey: firstRatchet.rootKey, ephemeralKeyPair: preKeyPair,
|
||||||
|
lastRemoteEphemeralKey: message.baseKey },
|
||||||
|
oldRatchetList: []
|
||||||
|
};
|
||||||
|
session[preKeyPair.pubKey] = { messageKeys: {}, chainKey: { counter: 0, key: firstRatchet.chainKey } };
|
||||||
|
storage.saveSession(encodedNumber, session);
|
||||||
|
|
||||||
var session = {currentRatchet: { rootKey: firstRatchet.rootKey, ephemeralKeyPair: preKeyPair,
|
callback();
|
||||||
lastRemoteEphemeralKey: message.baseKey },
|
});
|
||||||
oldRatchetList: []
|
|
||||||
};
|
|
||||||
session[preKeyPair.pubKey] = { messageKeys: {}, chainKey: { counter: 0, key: firstRatchet.chainKey } };
|
|
||||||
storage.saveSession(encodedNumber, session);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var fillMessageKeys = function(chain, counter) {
|
var fillMessageKeys = function(chain, counter) {
|
||||||
|
@ -433,18 +450,20 @@ function getRandomBytes(size) {
|
||||||
|
|
||||||
delete session[ratchet.ephemeralKeyPair.pubKey];
|
delete session[ratchet.ephemeralKeyPair.pubKey];
|
||||||
|
|
||||||
var masterKey = HKDF(ECDHE(remoteKey, ratchet.ephemeralKeyPair.privKey), ratchet.rootKey, "WhisperRatchet");
|
ECDHE(remoteKey, ratchet.ephemeralKeyPair.privKey, function(sharedSecret) {
|
||||||
session[remoteKey] = { messageKeys: {}, chainKey: { counter: 0, key: masterKey.substring(32, 64) } };
|
var masterKey = HKDF(sharedSecret, ratchet.rootKey, "WhisperRatchet");
|
||||||
|
session[remoteKey] = { messageKeys: {}, chainKey: { counter: 0, key: masterKey.substring(32, 64) } };
|
||||||
|
|
||||||
createNewKeyPair(function(keyPair) {
|
createNewKeyPair(function(keyPair) {
|
||||||
ratchet.ephemeralKeyPair = keyPair;
|
ratchet.ephemeralKeyPair = keyPair;
|
||||||
|
|
||||||
masterKey = HKDF(ECDHE(remoteKey, ratchet.ephemeralKeyPair.privKey), masterKey.substring(0, 32), "WhisperRatchet");
|
masterKey = HKDF(ECDHE(remoteKey, ratchet.ephemeralKeyPair.privKey), masterKey.substring(0, 32), "WhisperRatchet");
|
||||||
ratchet.rootKey = masterKey.substring(0, 32);
|
ratchet.rootKey = masterKey.substring(0, 32);
|
||||||
session[nextRatchet.ephemeralKeyPair.pubKey] = { messageKeys: {}, chainKey: { counter: 0, key: masterKey.substring(32, 64) } };
|
session[nextRatchet.ephemeralKeyPair.pubKey] = { messageKeys: {}, chainKey: { counter: 0, key: masterKey.substring(32, 64) } };
|
||||||
|
|
||||||
ratchet.lastRemoteEphemeralKey = remoteKey;
|
ratchet.lastRemoteEphemeralKey = remoteKey;
|
||||||
callback();
|
callback();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,8 +550,9 @@ function getRandomBytes(size) {
|
||||||
break;
|
break;
|
||||||
case 3: //TYPE_MESSAGE_PREKEY_BUNDLE
|
case 3: //TYPE_MESSAGE_PREKEY_BUNDLE
|
||||||
var preKeyProto = decodePreKeyWhisperMessageProtobuf(getString(proto.message));
|
var preKeyProto = decodePreKeyWhisperMessageProtobuf(getString(proto.message));
|
||||||
initSessionFromPreKeyWhisperMessage(proto.source, preKeyProto);
|
initSessionFromPreKeyWhisperMessage(proto.source, preKeyProto, function() {
|
||||||
decryptWhisperMessage(proto.source, getString(preKeyProto.message), function(result) { callback(result); });
|
decryptWhisperMessage(proto.source, getString(preKeyProto.message), function(result) { callback(result); });
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue