Update libaxololt to a087b9e746e67995f16e077183cc0

This commit is contained in:
lilia 2015-04-24 11:13:49 -07:00
parent 2cb0070343
commit 8304aa903a
4 changed files with 26665 additions and 1158 deletions

View file

@ -25246,6 +25246,7 @@ axolotlInternal.curve25519 = function() {
return {
keyPair: function(privKey) {
return new Promise(function(resolve) {
var priv = new Uint8Array(privKey);
priv[0] &= 248;
priv[31] &= 127;
@ -25272,7 +25273,9 @@ axolotlInternal.curve25519 = function() {
Module._free(privateKey_ptr);
Module._free(basepoint_ptr);
return Promise.resolve({ pubKey: res.buffer, privKey: privKey });
resolve({ pubKey: res.buffer, privKey: privKey });
});
},
sharedSecret: function(pubKey, privKey) {
// Where to store the result
@ -25353,6 +25356,61 @@ axolotlInternal.curve25519 = function() {
};
}();
var axolotlInternal = axolotlInternal || {};
// I am the...workee?
var origCurve25519 = axolotlInternal.curve25519;
axolotlInternal.startWorker = function(url) {
axolotlInternal.stopWorker(); // there can be only one
axolotlInternal.curve25519 = new Curve25519Worker(url);
};
axolotlInternal.stopWorker = function() {
if (axolotlInternal.curve25519 instanceof Curve25519Worker) {
var worker = axolotlInternal.curve25519.worker;
axolotlInternal.curve25519 = origCurve25519;
worker.terminate();
}
};
function Curve25519Worker(url) {
this.jobs = {};
this.jobId = 0;
this.worker = new Worker(url);
this.worker.onmessage = function(e) {
var job = this.jobs[e.data.id];
if (e.data.error && typeof job.onerror === 'function') {
job.onerror(new Error(e.data.error));
} else if (typeof job.onsuccess === 'function') {
job.onsuccess(e.data.result);
}
delete this.jobs[e.data.id];
}.bind(this);
}
Curve25519Worker.prototype = {
constructor: Curve25519Worker,
postMessage: function(methodName, args, onsuccess, onerror) {
return new Promise(function(resolve, reject) {
this.jobs[this.jobId] = { onsuccess: resolve, onerror: reject };
this.worker.postMessage({ id: this.jobId, methodName: methodName, args: args });
this.jobId++;
}.bind(this));
},
keyPair: function(privKey) {
return this.postMessage('keyPair', [privKey]);
},
sharedSecret: function(pubKey, privKey) {
return this.postMessage('sharedSecret', [pubKey, privKey]);
},
sign: function(privKey, message) {
return this.postMessage('sign', [privKey, message]);
},
verify: function(pubKey, message, sig) {
return this.postMessage('verify', [pubKey, message, sig]);
}
};
;(function(){
/**
* CryptoJS core components.
@ -36816,7 +36874,57 @@ axolotlInternal.utils = function() {
'use strict';
window.axolotl = window.axolotl || {};
window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
function isNonNegativeInteger(n) {
return (typeof n === 'number' && (n % 1) === 0 && n >= 0);
}
window.axolotl.util = {
generateIdentityKeyPair: function() {
return axolotlInternal.crypto.createKeyPair();
},
generateRegistrationId: function() {
var registrationId = new Uint16Array(axolotlInternal.crypto.getRandomBytes(2))[0];
return registrationId & 0x3fff;
},
generateSignedPreKey: function (identityKeyPair, signedKeyId) {
if (!(identityKeyPair.privKey instanceof ArrayBuffer) ||
identityKeyPair.privKey.byteLength != 32 ||
!(identityKeyPair.pubKey instanceof ArrayBuffer) ||
identityKeyPair.pubKey.byteLength != 33) {
throw new TypeError('Invalid argument for identityKeyPair');
}
if (!isNonNegativeInteger(signedKeyId)) {
throw new TypeError(
'Invalid argument for signedKeyId: ' + signedKeyId
);
}
return axolotlInternal.crypto.createKeyPair().then(function(keyPair) {
return axolotlInternal.crypto.Ed25519Sign(identityKeyPair.privKey, keyPair.pubKey).then(function(sig) {
return {
keyId : signedKeyId,
keyPair : keyPair,
signature : sig
};
});
});
},
generatePreKey: function(keyId) {
if (!isNonNegativeInteger(keyId)) {
throw new TypeError('Invalid argument for keyId: ' + keyId);
}
return axolotlInternal.crypto.createKeyPair().then(function(keyPair) {
return { keyId: keyId, keyPair: keyPair };
});
},
};
window.axolotl.protocol = function(storage_interface) {
var self = {};
/******************************
@ -36840,41 +36948,23 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
***************************/
var crypto_storage = {};
crypto_storage.putKeyPair = function(keyName, keyPair) {
storage_interface.put("25519Key" + keyName, keyPair);
function getRecord(encodedNumber) {
return storage_interface.getSession(encodedNumber).then(function(serialized) {
if (serialized === undefined) {
return undefined;
}
crypto_storage.getNewStoredKeyPair = function(keyName) {
return axolotlInternal.crypto.createKeyPair().then(function(keyPair) {
crypto_storage.putKeyPair(keyName, keyPair);
return keyPair;
return axolotlInternal.RecipientRecord.deserialize(serialized);
});
}
crypto_storage.getStoredKeyPair = function(keyName) {
var res = storage_interface.get("25519Key" + keyName);
if (res === undefined)
return undefined;
return { pubKey: axolotlInternal.utils.convertToArrayBuffer(res.pubKey), privKey: axolotlInternal.utils.convertToArrayBuffer(res.privKey) };
}
crypto_storage.removeStoredKeyPair = function(keyName) {
storage_interface.remove("25519Key" + keyName);
}
crypto_storage.getIdentityKey = function() {
return this.getStoredKeyPair("identityKey");
}
crypto_storage.saveSession = function(encodedNumber, session, registrationId) {
var record = storage_interface.sessions.get(encodedNumber);
return getRecord(encodedNumber).then(function(record) {
if (record === undefined) {
if (registrationId === undefined)
throw new Error("Tried to save a session for an existing device that didn't exist");
else
record = new axolotl.sessions.RecipientRecord(session.indexInfo.remoteIdentityKey, registrationId);
record = new axolotlInternal.RecipientRecord(session.indexInfo.remoteIdentityKey, registrationId);
}
var sessions = record._sessions;
if (record.identityKey === null)
@ -36884,7 +36974,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var doDeleteSession = false;
if (session.indexInfo.closed != -1) {
doDeleteSession = (session.indexInfo.closed < (new Date().getTime() - MESSAGE_LOST_THRESHOLD_MS));
doDeleteSession = (session.indexInfo.closed < (Date.now() - MESSAGE_LOST_THRESHOLD_MS));
if (!doDeleteSession) {
var keysLeft = false;
@ -36916,24 +37006,27 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
else if (record.registrationId === null)
throw new Error("Had open sessions on a record that had no registrationId set");
var identityKey = storage_interface.identityKeys.get(encodedNumber);
if (identityKey === undefined)
storage_interface.identityKeys.put(encodedNumber, record.identityKey);
else if (axolotlInternal.utils.convertToString(identityKey) !== axolotlInternal.utils.convertToString(record.identityKey))
return storage_interface.getIdentityKey(encodedNumber).then(function(identityKey) {
if (identityKey !== undefined && axolotlInternal.utils.convertToString(identityKey) !== axolotlInternal.utils.convertToString(record.identityKey))
throw new Error("Tried to change identity key at save time");
storage_interface.sessions.put(encodedNumber, record);
return storage_interface.putIdentityKey(encodedNumber, record.identityKey).then(function() {
return storage_interface.putSession(encodedNumber, record.serialize());
});
});
});
}
var getSessions = function(encodedNumber) {
var record = storage_interface.sessions.get(encodedNumber);
return getRecord(encodedNumber).then(function(record) {
if (record === undefined)
return undefined;
return record._sessions;
}
});
};
crypto_storage.getOpenSession = function(encodedNumber) {
var sessions = getSessions(encodedNumber);
return getSessions(encodedNumber).then(function(sessions) {
if (sessions === undefined)
return undefined;
@ -36941,10 +37034,11 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
if (sessions[key].indexInfo.closed == -1)
return sessions[key];
return undefined;
}
});
};
crypto_storage.getSessionByRemoteEphemeralKey = function(encodedNumber, remoteEphemeralKey) {
var sessions = getSessions(encodedNumber);
return getSessions(encodedNumber).then(function(sessions) {
if (sessions === undefined)
return undefined;
@ -36964,15 +37058,17 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return openSession;
return undefined;
});
}
crypto_storage.getSessionOrIdentityKeyByBaseKey = function(encodedNumber, baseKey) {
var record = storage_interface.sessions.get(encodedNumber);
return getRecord(encodedNumber).then(function(record) {
if (record === undefined) {
var identityKey = storage_interface.identityKeys.get(encodedNumber);
return storage_interface.getIdentityKey(encodedNumber).then(function(identityKey) {
if (identityKey === undefined)
return undefined;
return { indexInfo: { remoteIdentityKey: identityKey } };
});
}
var sessions = record._sessions;
@ -36984,6 +37080,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return { indexInfo: { remoteIdentityKey: record.identityKey } };
throw new Error("Datastore inconsistency: device was stored without identity key");
});
}
/*****************************
@ -37026,7 +37123,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
}
var initSession = function(isInitiator, ourEphemeralKey, ourSignedKey, encodedNumber, theirIdentityPubKey, theirEphemeralPubKey, theirSignedPubKey) {
var ourIdentityKey = crypto_storage.getIdentityKey();
var ourIdentityKey = storage_interface.getMyIdentityKey();
if (isInitiator) {
if (ourSignedKey !== undefined)
@ -37112,7 +37209,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var ratchet = axolotlInternal.utils.convertToString(entry.ephemeralKey);
console.log("Checking old chain with added time " + (entry.added/1000));
if ((!objectContainsKeys(session[ratchet].messageKeys) && (session[ratchet].chainKey === undefined || session[ratchet].chainKey.key === undefined))
|| entry.added < new Date().getTime() - MESSAGE_LOST_THRESHOLD_MS) {
|| entry.added < Date.now() - MESSAGE_LOST_THRESHOLD_MS) {
delete session[ratchet];
console.log("...deleted");
} else
@ -37135,7 +37232,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
for (var i in session) {
if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) {
if (!sessionClosedByRemote)
session.oldRatchetList[session.oldRatchetList.length] = { added: new Date().getTime(), ephemeralKey: i };
session.oldRatchetList[session.oldRatchetList.length] = { added: Date.now(), ephemeralKey: i };
else
delete session[i].chainKey.key;
}
@ -37143,28 +37240,25 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
// Delete current root key and our ephemeral key pair to disallow ratchet stepping
delete session.currentRatchet['rootKey'];
delete session.currentRatchet['ephemeralKeyPair'];
session.indexInfo.closed = new Date().getTime();
session.indexInfo.closed = Date.now();
removeOldChains(session);
}
self.closeOpenSessionForDevice = function(encodedNumber) {
var session = crypto_storage.getOpenSession(encodedNumber);
return crypto_storage.getOpenSession(encodedNumber).then(function(session) {
if (session === undefined)
return;
closeSession(session);
crypto_storage.saveSession(encodedNumber, session);
return crypto_storage.saveSession(encodedNumber, session);
});
}
var refreshPreKeys;
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
var preKeyPair = crypto_storage.getStoredKeyPair("preKey" + message.preKeyId);
var signedPreKeyPair = crypto_storage.getStoredKeyPair("signedKey" + message.signedPreKeyId);
//TODO: Call refreshPreKeys when it looks like all our prekeys are used up?
var session = crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, axolotlInternal.utils.convertToArrayBuffer(message.baseKey));
var open_session = crypto_storage.getOpenSession(encodedNumber);
return storage_interface.getPreKey(message.preKeyId).then(function(preKeyPair) {
return storage_interface.getSignedPreKey(message.signedPreKeyId).then(function(signedPreKeyPair) {
return crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, axolotlInternal.utils.convertToArrayBuffer(message.baseKey)).then(function(session) {
return crypto_storage.getOpenSession(encodedNumber).then(function(open_session) {
if (signedPreKeyPair === undefined) {
// Session may or may not be the right one, but if its not, we can't do anything about it
// ...fall through and let decryptWhisperMessage handle that case
@ -37193,11 +37287,16 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
// Note that the session is not actually saved until the very end of decryptWhisperMessage
// ... to ensure that the sender actually holds the private keys for all reported pubkeys
return [new_session, function() {
return storage_interface.removePreKey(message.preKeyId).then(function() {
if (open_session !== undefined)
crypto_storage.saveSession(encodedNumber, open_session);
crypto_storage.removeStoredKeyPair("preKey" + message.preKeyId);
return crypto_storage.saveSession(encodedNumber, open_session);
});
}];
});;
});
});
});
});
});
}
var fillMessageKeys = function(chain, counter) {
@ -37255,7 +37354,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
if (!objectContainsKeys(previousRatchet.messageKeys))
delete session[axolotlInternal.utils.convertToString(ratchet.lastRemoteEphemeralKey)];
else
session.oldRatchetList[session.oldRatchetList.length] = { added: new Date().getTime(), ephemeralKey: ratchet.lastRemoteEphemeralKey };
session.oldRatchetList[session.oldRatchetList.length] = { added: Date.now(), ephemeralKey: ratchet.lastRemoteEphemeralKey };
}).then(finish);
} else
return finish();
@ -37271,12 +37370,18 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto, 'binary');
var remoteEphemeralKey = axolotlInternal.utils.convertToArrayBuffer(message.ephemeralKey);
var promise;
if (session === undefined) {
var session = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey);
promise = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey).then(function(session) {
if (session === undefined)
throw new Error("No session found to decrypt message from " + encodedNumber);
return session;
});
} else {
promise = Promise.resolve(session);
}
return promise.then(function(session) {
return maybeStepRatchet(session, remoteEphemeralKey, message.previousCounter).then(function() {
var chain = session[axolotlInternal.utils.convertToString(message.ephemeralKey)];
@ -37287,7 +37392,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var messageProtoArray = axolotlInternal.utils.convertToArrayBuffer(messageProto);
var macInput = new Uint8Array(messageProtoArray.byteLength + 33*2 + 1);
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(session.indexInfo.remoteIdentityKey)));
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(crypto_storage.getIdentityKey().pubKey)), 33);
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(storage_interface.getMyIdentityKey().pubKey)), 33);
macInput[33*2] = (3 << 4) | 3;
macInput.set(new Uint8Array(messageProtoArray), 33*2 + 1);
@ -37309,17 +37414,19 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
delete session['pendingPreKey'];
removeOldChains(session);
crypto_storage.saveSession(encodedNumber, session, registrationId);
return crypto_storage.saveSession(encodedNumber, session, registrationId).then(function() {
return [plaintext, function() {
closeSession(session, true);
removeOldChains(session);
crypto_storage.saveSession(encodedNumber, session);
return crypto_storage.saveSession(encodedNumber, session);
}];
});
});
});
});
});
});
});
}
/*************************
@ -37338,7 +37445,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return initSessionFromPreKeyWhisperMessage(from, preKeyProto).then(function(sessions) {
return doDecryptWhisperMessage(from, axolotlInternal.utils.convertToString(preKeyProto.message), sessions[0], preKeyProto.registrationId).then(function(result) {
if (sessions[1] !== undefined)
sessions[1]();
return sessions[1]().then(function() { return result; });
return result;
});
});
@ -37346,7 +37453,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
// return Promise(encoded [PreKey]WhisperMessage)
self.encryptMessageFor = function(deviceObject, pushMessageContent) {
var session = crypto_storage.getOpenSession(deviceObject.encodedNumber);
return crypto_storage.getOpenSession(deviceObject.encodedNumber).then(function(session) {
var hadSession = session !== undefined;
var doEncryptPushMessageContent = function() {
@ -37371,7 +37478,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var encodedMsg = axolotlInternal.utils.convertToArrayBuffer(msg.encode());
var macInput = new Uint8Array(encodedMsg.byteLength + 33*2 + 1);
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(crypto_storage.getIdentityKey().pubKey)));
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(storage_interface.getMyIdentityKey().pubKey)));
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(session.indexInfo.remoteIdentityKey)), 33);
macInput[33*2] = (3 << 4) | 3;
macInput.set(new Uint8Array(encodedMsg), 33*2 + 1);
@ -37384,16 +37491,17 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
removeOldChains(session);
crypto_storage.saveSession(deviceObject.encodedNumber, session, !hadSession ? deviceObject.registrationId : undefined);
return crypto_storage.saveSession(deviceObject.encodedNumber, session, !hadSession ? deviceObject.registrationId : undefined).then(function() {
return result;
});
});
});
});
});
}
var preKeyMsg = new axolotlInternal.protobuf.PreKeyWhisperMessage();
preKeyMsg.identityKey = axolotlInternal.utils.convertToArrayBuffer(crypto_storage.getIdentityKey().pubKey);
preKeyMsg.identityKey = axolotlInternal.utils.convertToArrayBuffer(storage_interface.getMyIdentityKey().pubKey);
preKeyMsg.registrationId = storage_interface.getMyRegistrationId();
if (session === undefined) {
@ -37404,9 +37512,10 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
preKeyMsg.preKeyId = deviceObject.preKeyId;
preKeyMsg.signedPreKeyId = deviceObject.signedKeyId;
preKeyMsg.baseKey = axolotlInternal.utils.convertToArrayBuffer(baseKey.pubKey);
return initSession(true, baseKey, undefined, deviceObject.encodedNumber,
deviceIdentityKey, axolotlInternal.utils.convertToArrayBuffer(deviceObject.preKey), deviceSignedKey)
.then(function(new_session) {
return initSession(true, baseKey, undefined,
deviceObject.encodedNumber, deviceIdentityKey,
axolotlInternal.utils.convertToArrayBuffer(deviceObject.preKey),
deviceSignedKey).then(function(new_session) {
session = new_session;
session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey };
return doEncryptPushMessageContent().then(function(message) {
@ -37430,69 +37539,8 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
} else
return {type: 1, body: axolotlInternal.utils.convertToString(message)};
});
}
var GENERATE_KEYS_KEYS_GENERATED = 100;
self.generateKeys = function() {
var identityKeyPair = crypto_storage.getIdentityKey();
var identityKeyCalculated = function(identityKeyPair) {
var firstPreKeyId = storage_interface.get("maxPreKeyId", 0);
storage_interface.put("maxPreKeyId", firstPreKeyId + GENERATE_KEYS_KEYS_GENERATED);
var signedKeyId = storage_interface.get("signedKeyId", 0);
storage_interface.put("signedKeyId", signedKeyId + 1);
var keys = {};
keys.identityKey = identityKeyPair.pubKey;
keys.preKeys = [];
var generateKey = function(keyId) {
return crypto_storage.getNewStoredKeyPair("preKey" + keyId, false).then(function(keyPair) {
keys.preKeys[keyId] = {keyId: keyId, publicKey: keyPair.pubKey};
});
};
var promises = [];
for (var i = firstPreKeyId; i < firstPreKeyId + GENERATE_KEYS_KEYS_GENERATED; i++)
promises[i] = generateKey(i);
promises[firstPreKeyId + GENERATE_KEYS_KEYS_GENERATED] = crypto_storage.getNewStoredKeyPair("signedKey" + signedKeyId).then(function(keyPair) {
return axolotlInternal.crypto.Ed25519Sign(identityKeyPair.privKey, keyPair.pubKey).then(function(sig) {
keys.signedPreKey = {keyId: signedKeyId, publicKey: keyPair.pubKey, signature: sig};
});
});
//TODO: Process by date added and agressively call generateKeys when we get near maxPreKeyId in a message
crypto_storage.removeStoredKeyPair("signedKey" + (signedKeyId - 2));
return Promise.all(promises).then(function() {
storage_interface.put("lastPreKeyUpdate", Date.now());
return keys;
});
}
if (identityKeyPair === undefined)
return crypto_storage.getNewStoredKeyPair("identityKey").then(function(keyPair) { return identityKeyCalculated(keyPair); });
else
return identityKeyCalculated(identityKeyPair);
}
//TODO: Replace this stuff
refreshPreKeys = function() {
self.generateKeys().then(function(keys) {
console.log("Pre Keys updated!");
return updateKeysCallback(keys);
}).catch(function(e) {
//TODO: Notify the user somehow???
console.error(e);
});
}
if (updateKeysCallback)
window.setInterval(function() {
// Note that this will not ever run until generateKeys has been called at least once
if (storage_interface.get("lastPreKeyUpdate", Date.now()) < Date.now() - MESSAGE_LOST_THRESHOLD_MS)
refreshPreKeys();
}, 60 * 1000);
self.createIdentityKeyRecvSocket = function() {
var socketInfo = {};
@ -37514,16 +37562,16 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return verifyMAC(ivAndCiphertext, keys[1], mac).then(function() {
return axolotlInternal.crypto.decrypt(keys[0], ciphertext, iv).then(function(plaintext) {
var identityKeyMsg = axolotlInternal.protobuf.ProvisionMessage.decode(plaintext);
var provisionMessage = axolotlInternal.protobuf.ProvisionMessage.decode(plaintext);
return axolotlInternal.crypto.createKeyPair(axolotlInternal.utils.convertToArrayBuffer(identityKeyMsg.identityKeyPrivate)).then(function(identityKeyPair) {
if (crypto_storage.getStoredKeyPair("identityKey") !== undefined)
throw new Error("Tried to overwrite identity key");
crypto_storage.putKeyPair("identityKey", identityKeyPair);
identityKeyMsg.identityKeyPrivate = null;
return identityKeyMsg;
return axolotlInternal.crypto.createKeyPair(
provisionMessage.identityKeyPrivate.toArrayBuffer()
).then(function(identityKeyPair) {
return {
identityKeyPair : identityKeyPair,
number : provisionMessage.number,
provisioningCode : provisionMessage.provisioningCode
};
});
});
});
@ -37538,6 +37586,32 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
});
}
self.getRegistrationId = function(encodedNumber) {
return getRecord(encodedNumber).then(function(record) {
if (record === undefined) {
return undefined;
}
return record.registrationId;
});
};
self.hasOpenSession = function(encodedNumber) {
return getRecord(encodedNumber).then(function(record) {
if (record === undefined) {
return false;
}
return record.haveOpenSession();
});
};
self.startWorker = function(url) {
axolotlInternal.startWorker(url);
};
self.stopWorker = function() {
axolotlInternal.stopWorker();
};
return self;
};
@ -37660,10 +37734,11 @@ axolotlInternal.protobuf = function() {
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
;(function() {
var axolotlInternal = axolotlInternal || {};
axolotlInternal.RecipientRecord = function() {
'use strict';
window.axolotl = window.axolotl || {};
var RecipientRecord = function(identityKey, registrationId) {
this._sessions = {};
@ -37678,26 +37753,23 @@ RecipientRecord.prototype.serialize = function() {
return axolotlInternal.utils.jsonThing({sessions: this._sessions, registrationId: this.registrationId, identityKey: this.identityKey});
}
RecipientRecord.prototype.deserialize = function(serialized) {
RecipientRecord.deserialize = function(serialized) {
var data = JSON.parse(serialized);
this._sessions = data.sessions;
if (this._sessions === undefined || this._sessions === null || typeof this._sessions !== "object" || Array.isArray(this._sessions))
var record = new RecipientRecord(data.identityKey, data.registrationId);
record._sessions = data.sessions;
if (record._sessions === undefined || record._sessions === null || typeof record._sessions !== "object" || Array.isArray(record._sessions))
throw new Error("Error deserializing RecipientRecord");
this.identityKey = data.identityKey;
this.registrationId = data.registrationId;
if (this.identityKey === undefined || this.registrationId === undefined)
if (record.identityKey === undefined || record.registrationId === undefined)
throw new Error("Error deserializing RecipientRecord");
return record;
}
RecipientRecord.prototype.haveOpenSession = function() {
return this.registrationId !== null;
}
window.axolotl.sessions = {
RecipientRecord: RecipientRecord,
};
})();
return RecipientRecord;
}();
})();
'use strict';

View file

@ -25245,6 +25245,7 @@ axolotlInternal.curve25519 = function() {
return {
keyPair: function(privKey) {
return new Promise(function(resolve) {
var priv = new Uint8Array(privKey);
priv[0] &= 248;
priv[31] &= 127;
@ -25271,7 +25272,9 @@ axolotlInternal.curve25519 = function() {
Module._free(privateKey_ptr);
Module._free(basepoint_ptr);
return Promise.resolve({ pubKey: res.buffer, privKey: privKey });
resolve({ pubKey: res.buffer, privKey: privKey });
});
},
sharedSecret: function(pubKey, privKey) {
// Where to store the result
@ -25352,6 +25355,61 @@ axolotlInternal.curve25519 = function() {
};
}();
var axolotlInternal = axolotlInternal || {};
// I am the...workee?
var origCurve25519 = axolotlInternal.curve25519;
axolotlInternal.startWorker = function(url) {
axolotlInternal.stopWorker(); // there can be only one
axolotlInternal.curve25519 = new Curve25519Worker(url);
};
axolotlInternal.stopWorker = function() {
if (axolotlInternal.curve25519 instanceof Curve25519Worker) {
var worker = axolotlInternal.curve25519.worker;
axolotlInternal.curve25519 = origCurve25519;
worker.terminate();
}
};
function Curve25519Worker(url) {
this.jobs = {};
this.jobId = 0;
this.worker = new Worker(url);
this.worker.onmessage = function(e) {
var job = this.jobs[e.data.id];
if (e.data.error && typeof job.onerror === 'function') {
job.onerror(new Error(e.data.error));
} else if (typeof job.onsuccess === 'function') {
job.onsuccess(e.data.result);
}
delete this.jobs[e.data.id];
}.bind(this);
}
Curve25519Worker.prototype = {
constructor: Curve25519Worker,
postMessage: function(methodName, args, onsuccess, onerror) {
return new Promise(function(resolve, reject) {
this.jobs[this.jobId] = { onsuccess: resolve, onerror: reject };
this.worker.postMessage({ id: this.jobId, methodName: methodName, args: args });
this.jobId++;
}.bind(this));
},
keyPair: function(privKey) {
return this.postMessage('keyPair', [privKey]);
},
sharedSecret: function(pubKey, privKey) {
return this.postMessage('sharedSecret', [pubKey, privKey]);
},
sign: function(privKey, message) {
return this.postMessage('sign', [privKey, message]);
},
verify: function(pubKey, message, sig) {
return this.postMessage('verify', [pubKey, message, sig]);
}
};
;(function(){
/**
* CryptoJS core components.
@ -36815,7 +36873,57 @@ axolotlInternal.utils = function() {
'use strict';
window.axolotl = window.axolotl || {};
window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
function isNonNegativeInteger(n) {
return (typeof n === 'number' && (n % 1) === 0 && n >= 0);
}
window.axolotl.util = {
generateIdentityKeyPair: function() {
return axolotlInternal.crypto.createKeyPair();
},
generateRegistrationId: function() {
var registrationId = new Uint16Array(axolotlInternal.crypto.getRandomBytes(2))[0];
return registrationId & 0x3fff;
},
generateSignedPreKey: function (identityKeyPair, signedKeyId) {
if (!(identityKeyPair.privKey instanceof ArrayBuffer) ||
identityKeyPair.privKey.byteLength != 32 ||
!(identityKeyPair.pubKey instanceof ArrayBuffer) ||
identityKeyPair.pubKey.byteLength != 33) {
throw new TypeError('Invalid argument for identityKeyPair');
}
if (!isNonNegativeInteger(signedKeyId)) {
throw new TypeError(
'Invalid argument for signedKeyId: ' + signedKeyId
);
}
return axolotlInternal.crypto.createKeyPair().then(function(keyPair) {
return axolotlInternal.crypto.Ed25519Sign(identityKeyPair.privKey, keyPair.pubKey).then(function(sig) {
return {
keyId : signedKeyId,
keyPair : keyPair,
signature : sig
};
});
});
},
generatePreKey: function(keyId) {
if (!isNonNegativeInteger(keyId)) {
throw new TypeError('Invalid argument for keyId: ' + keyId);
}
return axolotlInternal.crypto.createKeyPair().then(function(keyPair) {
return { keyId: keyId, keyPair: keyPair };
});
},
};
window.axolotl.protocol = function(storage_interface) {
var self = {};
/******************************
@ -36839,41 +36947,23 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
***************************/
var crypto_storage = {};
crypto_storage.putKeyPair = function(keyName, keyPair) {
storage_interface.put("25519Key" + keyName, keyPair);
function getRecord(encodedNumber) {
return storage_interface.getSession(encodedNumber).then(function(serialized) {
if (serialized === undefined) {
return undefined;
}
crypto_storage.getNewStoredKeyPair = function(keyName) {
return axolotlInternal.crypto.createKeyPair().then(function(keyPair) {
crypto_storage.putKeyPair(keyName, keyPair);
return keyPair;
return axolotlInternal.RecipientRecord.deserialize(serialized);
});
}
crypto_storage.getStoredKeyPair = function(keyName) {
var res = storage_interface.get("25519Key" + keyName);
if (res === undefined)
return undefined;
return { pubKey: axolotlInternal.utils.convertToArrayBuffer(res.pubKey), privKey: axolotlInternal.utils.convertToArrayBuffer(res.privKey) };
}
crypto_storage.removeStoredKeyPair = function(keyName) {
storage_interface.remove("25519Key" + keyName);
}
crypto_storage.getIdentityKey = function() {
return this.getStoredKeyPair("identityKey");
}
crypto_storage.saveSession = function(encodedNumber, session, registrationId) {
var record = storage_interface.sessions.get(encodedNumber);
return getRecord(encodedNumber).then(function(record) {
if (record === undefined) {
if (registrationId === undefined)
throw new Error("Tried to save a session for an existing device that didn't exist");
else
record = new axolotl.sessions.RecipientRecord(session.indexInfo.remoteIdentityKey, registrationId);
record = new axolotlInternal.RecipientRecord(session.indexInfo.remoteIdentityKey, registrationId);
}
var sessions = record._sessions;
if (record.identityKey === null)
@ -36883,7 +36973,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var doDeleteSession = false;
if (session.indexInfo.closed != -1) {
doDeleteSession = (session.indexInfo.closed < (new Date().getTime() - MESSAGE_LOST_THRESHOLD_MS));
doDeleteSession = (session.indexInfo.closed < (Date.now() - MESSAGE_LOST_THRESHOLD_MS));
if (!doDeleteSession) {
var keysLeft = false;
@ -36915,24 +37005,27 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
else if (record.registrationId === null)
throw new Error("Had open sessions on a record that had no registrationId set");
var identityKey = storage_interface.identityKeys.get(encodedNumber);
if (identityKey === undefined)
storage_interface.identityKeys.put(encodedNumber, record.identityKey);
else if (axolotlInternal.utils.convertToString(identityKey) !== axolotlInternal.utils.convertToString(record.identityKey))
return storage_interface.getIdentityKey(encodedNumber).then(function(identityKey) {
if (identityKey !== undefined && axolotlInternal.utils.convertToString(identityKey) !== axolotlInternal.utils.convertToString(record.identityKey))
throw new Error("Tried to change identity key at save time");
storage_interface.sessions.put(encodedNumber, record);
return storage_interface.putIdentityKey(encodedNumber, record.identityKey).then(function() {
return storage_interface.putSession(encodedNumber, record.serialize());
});
});
});
}
var getSessions = function(encodedNumber) {
var record = storage_interface.sessions.get(encodedNumber);
return getRecord(encodedNumber).then(function(record) {
if (record === undefined)
return undefined;
return record._sessions;
}
});
};
crypto_storage.getOpenSession = function(encodedNumber) {
var sessions = getSessions(encodedNumber);
return getSessions(encodedNumber).then(function(sessions) {
if (sessions === undefined)
return undefined;
@ -36940,10 +37033,11 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
if (sessions[key].indexInfo.closed == -1)
return sessions[key];
return undefined;
}
});
};
crypto_storage.getSessionByRemoteEphemeralKey = function(encodedNumber, remoteEphemeralKey) {
var sessions = getSessions(encodedNumber);
return getSessions(encodedNumber).then(function(sessions) {
if (sessions === undefined)
return undefined;
@ -36963,15 +37057,17 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return openSession;
return undefined;
});
}
crypto_storage.getSessionOrIdentityKeyByBaseKey = function(encodedNumber, baseKey) {
var record = storage_interface.sessions.get(encodedNumber);
return getRecord(encodedNumber).then(function(record) {
if (record === undefined) {
var identityKey = storage_interface.identityKeys.get(encodedNumber);
return storage_interface.getIdentityKey(encodedNumber).then(function(identityKey) {
if (identityKey === undefined)
return undefined;
return { indexInfo: { remoteIdentityKey: identityKey } };
});
}
var sessions = record._sessions;
@ -36983,6 +37079,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return { indexInfo: { remoteIdentityKey: record.identityKey } };
throw new Error("Datastore inconsistency: device was stored without identity key");
});
}
/*****************************
@ -37025,7 +37122,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
}
var initSession = function(isInitiator, ourEphemeralKey, ourSignedKey, encodedNumber, theirIdentityPubKey, theirEphemeralPubKey, theirSignedPubKey) {
var ourIdentityKey = crypto_storage.getIdentityKey();
var ourIdentityKey = storage_interface.getMyIdentityKey();
if (isInitiator) {
if (ourSignedKey !== undefined)
@ -37111,7 +37208,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var ratchet = axolotlInternal.utils.convertToString(entry.ephemeralKey);
console.log("Checking old chain with added time " + (entry.added/1000));
if ((!objectContainsKeys(session[ratchet].messageKeys) && (session[ratchet].chainKey === undefined || session[ratchet].chainKey.key === undefined))
|| entry.added < new Date().getTime() - MESSAGE_LOST_THRESHOLD_MS) {
|| entry.added < Date.now() - MESSAGE_LOST_THRESHOLD_MS) {
delete session[ratchet];
console.log("...deleted");
} else
@ -37134,7 +37231,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
for (var i in session) {
if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) {
if (!sessionClosedByRemote)
session.oldRatchetList[session.oldRatchetList.length] = { added: new Date().getTime(), ephemeralKey: i };
session.oldRatchetList[session.oldRatchetList.length] = { added: Date.now(), ephemeralKey: i };
else
delete session[i].chainKey.key;
}
@ -37142,28 +37239,25 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
// Delete current root key and our ephemeral key pair to disallow ratchet stepping
delete session.currentRatchet['rootKey'];
delete session.currentRatchet['ephemeralKeyPair'];
session.indexInfo.closed = new Date().getTime();
session.indexInfo.closed = Date.now();
removeOldChains(session);
}
self.closeOpenSessionForDevice = function(encodedNumber) {
var session = crypto_storage.getOpenSession(encodedNumber);
return crypto_storage.getOpenSession(encodedNumber).then(function(session) {
if (session === undefined)
return;
closeSession(session);
crypto_storage.saveSession(encodedNumber, session);
return crypto_storage.saveSession(encodedNumber, session);
});
}
var refreshPreKeys;
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
var preKeyPair = crypto_storage.getStoredKeyPair("preKey" + message.preKeyId);
var signedPreKeyPair = crypto_storage.getStoredKeyPair("signedKey" + message.signedPreKeyId);
//TODO: Call refreshPreKeys when it looks like all our prekeys are used up?
var session = crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, axolotlInternal.utils.convertToArrayBuffer(message.baseKey));
var open_session = crypto_storage.getOpenSession(encodedNumber);
return storage_interface.getPreKey(message.preKeyId).then(function(preKeyPair) {
return storage_interface.getSignedPreKey(message.signedPreKeyId).then(function(signedPreKeyPair) {
return crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, axolotlInternal.utils.convertToArrayBuffer(message.baseKey)).then(function(session) {
return crypto_storage.getOpenSession(encodedNumber).then(function(open_session) {
if (signedPreKeyPair === undefined) {
// Session may or may not be the right one, but if its not, we can't do anything about it
// ...fall through and let decryptWhisperMessage handle that case
@ -37192,11 +37286,16 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
// Note that the session is not actually saved until the very end of decryptWhisperMessage
// ... to ensure that the sender actually holds the private keys for all reported pubkeys
return [new_session, function() {
return storage_interface.removePreKey(message.preKeyId).then(function() {
if (open_session !== undefined)
crypto_storage.saveSession(encodedNumber, open_session);
crypto_storage.removeStoredKeyPair("preKey" + message.preKeyId);
return crypto_storage.saveSession(encodedNumber, open_session);
});
}];
});;
});
});
});
});
});
}
var fillMessageKeys = function(chain, counter) {
@ -37254,7 +37353,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
if (!objectContainsKeys(previousRatchet.messageKeys))
delete session[axolotlInternal.utils.convertToString(ratchet.lastRemoteEphemeralKey)];
else
session.oldRatchetList[session.oldRatchetList.length] = { added: new Date().getTime(), ephemeralKey: ratchet.lastRemoteEphemeralKey };
session.oldRatchetList[session.oldRatchetList.length] = { added: Date.now(), ephemeralKey: ratchet.lastRemoteEphemeralKey };
}).then(finish);
} else
return finish();
@ -37270,12 +37369,18 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto, 'binary');
var remoteEphemeralKey = axolotlInternal.utils.convertToArrayBuffer(message.ephemeralKey);
var promise;
if (session === undefined) {
var session = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey);
promise = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey).then(function(session) {
if (session === undefined)
throw new Error("No session found to decrypt message from " + encodedNumber);
return session;
});
} else {
promise = Promise.resolve(session);
}
return promise.then(function(session) {
return maybeStepRatchet(session, remoteEphemeralKey, message.previousCounter).then(function() {
var chain = session[axolotlInternal.utils.convertToString(message.ephemeralKey)];
@ -37286,7 +37391,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var messageProtoArray = axolotlInternal.utils.convertToArrayBuffer(messageProto);
var macInput = new Uint8Array(messageProtoArray.byteLength + 33*2 + 1);
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(session.indexInfo.remoteIdentityKey)));
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(crypto_storage.getIdentityKey().pubKey)), 33);
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(storage_interface.getMyIdentityKey().pubKey)), 33);
macInput[33*2] = (3 << 4) | 3;
macInput.set(new Uint8Array(messageProtoArray), 33*2 + 1);
@ -37308,17 +37413,19 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
delete session['pendingPreKey'];
removeOldChains(session);
crypto_storage.saveSession(encodedNumber, session, registrationId);
return crypto_storage.saveSession(encodedNumber, session, registrationId).then(function() {
return [plaintext, function() {
closeSession(session, true);
removeOldChains(session);
crypto_storage.saveSession(encodedNumber, session);
return crypto_storage.saveSession(encodedNumber, session);
}];
});
});
});
});
});
});
});
}
/*************************
@ -37337,7 +37444,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return initSessionFromPreKeyWhisperMessage(from, preKeyProto).then(function(sessions) {
return doDecryptWhisperMessage(from, axolotlInternal.utils.convertToString(preKeyProto.message), sessions[0], preKeyProto.registrationId).then(function(result) {
if (sessions[1] !== undefined)
sessions[1]();
return sessions[1]().then(function() { return result; });
return result;
});
});
@ -37345,7 +37452,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
// return Promise(encoded [PreKey]WhisperMessage)
self.encryptMessageFor = function(deviceObject, pushMessageContent) {
var session = crypto_storage.getOpenSession(deviceObject.encodedNumber);
return crypto_storage.getOpenSession(deviceObject.encodedNumber).then(function(session) {
var hadSession = session !== undefined;
var doEncryptPushMessageContent = function() {
@ -37370,7 +37477,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var encodedMsg = axolotlInternal.utils.convertToArrayBuffer(msg.encode());
var macInput = new Uint8Array(encodedMsg.byteLength + 33*2 + 1);
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(crypto_storage.getIdentityKey().pubKey)));
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(storage_interface.getMyIdentityKey().pubKey)));
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(session.indexInfo.remoteIdentityKey)), 33);
macInput[33*2] = (3 << 4) | 3;
macInput.set(new Uint8Array(encodedMsg), 33*2 + 1);
@ -37383,16 +37490,17 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
removeOldChains(session);
crypto_storage.saveSession(deviceObject.encodedNumber, session, !hadSession ? deviceObject.registrationId : undefined);
return crypto_storage.saveSession(deviceObject.encodedNumber, session, !hadSession ? deviceObject.registrationId : undefined).then(function() {
return result;
});
});
});
});
});
}
var preKeyMsg = new axolotlInternal.protobuf.PreKeyWhisperMessage();
preKeyMsg.identityKey = axolotlInternal.utils.convertToArrayBuffer(crypto_storage.getIdentityKey().pubKey);
preKeyMsg.identityKey = axolotlInternal.utils.convertToArrayBuffer(storage_interface.getMyIdentityKey().pubKey);
preKeyMsg.registrationId = storage_interface.getMyRegistrationId();
if (session === undefined) {
@ -37403,9 +37511,10 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
preKeyMsg.preKeyId = deviceObject.preKeyId;
preKeyMsg.signedPreKeyId = deviceObject.signedKeyId;
preKeyMsg.baseKey = axolotlInternal.utils.convertToArrayBuffer(baseKey.pubKey);
return initSession(true, baseKey, undefined, deviceObject.encodedNumber,
deviceIdentityKey, axolotlInternal.utils.convertToArrayBuffer(deviceObject.preKey), deviceSignedKey)
.then(function(new_session) {
return initSession(true, baseKey, undefined,
deviceObject.encodedNumber, deviceIdentityKey,
axolotlInternal.utils.convertToArrayBuffer(deviceObject.preKey),
deviceSignedKey).then(function(new_session) {
session = new_session;
session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey };
return doEncryptPushMessageContent().then(function(message) {
@ -37429,69 +37538,8 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
} else
return {type: 1, body: axolotlInternal.utils.convertToString(message)};
});
}
var GENERATE_KEYS_KEYS_GENERATED = 100;
self.generateKeys = function() {
var identityKeyPair = crypto_storage.getIdentityKey();
var identityKeyCalculated = function(identityKeyPair) {
var firstPreKeyId = storage_interface.get("maxPreKeyId", 0);
storage_interface.put("maxPreKeyId", firstPreKeyId + GENERATE_KEYS_KEYS_GENERATED);
var signedKeyId = storage_interface.get("signedKeyId", 0);
storage_interface.put("signedKeyId", signedKeyId + 1);
var keys = {};
keys.identityKey = identityKeyPair.pubKey;
keys.preKeys = [];
var generateKey = function(keyId) {
return crypto_storage.getNewStoredKeyPair("preKey" + keyId, false).then(function(keyPair) {
keys.preKeys[keyId] = {keyId: keyId, publicKey: keyPair.pubKey};
});
};
var promises = [];
for (var i = firstPreKeyId; i < firstPreKeyId + GENERATE_KEYS_KEYS_GENERATED; i++)
promises[i] = generateKey(i);
promises[firstPreKeyId + GENERATE_KEYS_KEYS_GENERATED] = crypto_storage.getNewStoredKeyPair("signedKey" + signedKeyId).then(function(keyPair) {
return axolotlInternal.crypto.Ed25519Sign(identityKeyPair.privKey, keyPair.pubKey).then(function(sig) {
keys.signedPreKey = {keyId: signedKeyId, publicKey: keyPair.pubKey, signature: sig};
});
});
//TODO: Process by date added and agressively call generateKeys when we get near maxPreKeyId in a message
crypto_storage.removeStoredKeyPair("signedKey" + (signedKeyId - 2));
return Promise.all(promises).then(function() {
storage_interface.put("lastPreKeyUpdate", Date.now());
return keys;
});
}
if (identityKeyPair === undefined)
return crypto_storage.getNewStoredKeyPair("identityKey").then(function(keyPair) { return identityKeyCalculated(keyPair); });
else
return identityKeyCalculated(identityKeyPair);
}
//TODO: Replace this stuff
refreshPreKeys = function() {
self.generateKeys().then(function(keys) {
console.log("Pre Keys updated!");
return updateKeysCallback(keys);
}).catch(function(e) {
//TODO: Notify the user somehow???
console.error(e);
});
}
if (updateKeysCallback)
window.setInterval(function() {
// Note that this will not ever run until generateKeys has been called at least once
if (storage_interface.get("lastPreKeyUpdate", Date.now()) < Date.now() - MESSAGE_LOST_THRESHOLD_MS)
refreshPreKeys();
}, 60 * 1000);
self.createIdentityKeyRecvSocket = function() {
var socketInfo = {};
@ -37513,16 +37561,16 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return verifyMAC(ivAndCiphertext, keys[1], mac).then(function() {
return axolotlInternal.crypto.decrypt(keys[0], ciphertext, iv).then(function(plaintext) {
var identityKeyMsg = axolotlInternal.protobuf.ProvisionMessage.decode(plaintext);
var provisionMessage = axolotlInternal.protobuf.ProvisionMessage.decode(plaintext);
return axolotlInternal.crypto.createKeyPair(axolotlInternal.utils.convertToArrayBuffer(identityKeyMsg.identityKeyPrivate)).then(function(identityKeyPair) {
if (crypto_storage.getStoredKeyPair("identityKey") !== undefined)
throw new Error("Tried to overwrite identity key");
crypto_storage.putKeyPair("identityKey", identityKeyPair);
identityKeyMsg.identityKeyPrivate = null;
return identityKeyMsg;
return axolotlInternal.crypto.createKeyPair(
provisionMessage.identityKeyPrivate.toArrayBuffer()
).then(function(identityKeyPair) {
return {
identityKeyPair : identityKeyPair,
number : provisionMessage.number,
provisioningCode : provisionMessage.provisioningCode
};
});
});
});
@ -37537,6 +37585,32 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
});
}
self.getRegistrationId = function(encodedNumber) {
return getRecord(encodedNumber).then(function(record) {
if (record === undefined) {
return undefined;
}
return record.registrationId;
});
};
self.hasOpenSession = function(encodedNumber) {
return getRecord(encodedNumber).then(function(record) {
if (record === undefined) {
return false;
}
return record.haveOpenSession();
});
};
self.startWorker = function(url) {
axolotlInternal.startWorker(url);
};
self.stopWorker = function() {
axolotlInternal.stopWorker();
};
return self;
};
@ -37659,10 +37733,11 @@ axolotlInternal.protobuf = function() {
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
;(function() {
var axolotlInternal = axolotlInternal || {};
axolotlInternal.RecipientRecord = function() {
'use strict';
window.axolotl = window.axolotl || {};
var RecipientRecord = function(identityKey, registrationId) {
this._sessions = {};
@ -37677,26 +37752,23 @@ RecipientRecord.prototype.serialize = function() {
return axolotlInternal.utils.jsonThing({sessions: this._sessions, registrationId: this.registrationId, identityKey: this.identityKey});
}
RecipientRecord.prototype.deserialize = function(serialized) {
RecipientRecord.deserialize = function(serialized) {
var data = JSON.parse(serialized);
this._sessions = data.sessions;
if (this._sessions === undefined || this._sessions === null || typeof this._sessions !== "object" || Array.isArray(this._sessions))
var record = new RecipientRecord(data.identityKey, data.registrationId);
record._sessions = data.sessions;
if (record._sessions === undefined || record._sessions === null || typeof record._sessions !== "object" || Array.isArray(record._sessions))
throw new Error("Error deserializing RecipientRecord");
this.identityKey = data.identityKey;
this.registrationId = data.registrationId;
if (this.identityKey === undefined || this.registrationId === undefined)
if (record.identityKey === undefined || record.registrationId === undefined)
throw new Error("Error deserializing RecipientRecord");
return record;
}
RecipientRecord.prototype.haveOpenSession = function() {
return this.registrationId !== null;
}
window.axolotl.sessions = {
RecipientRecord: RecipientRecord,
};
})();
return RecipientRecord;
}();
})();
'use strict';

File diff suppressed because one or more lines are too long

View file

@ -25168,6 +25168,7 @@ axolotlInternal.curve25519 = function() {
return {
keyPair: function(privKey) {
return new Promise(function(resolve) {
var priv = new Uint8Array(privKey);
priv[0] &= 248;
priv[31] &= 127;
@ -25194,7 +25195,9 @@ axolotlInternal.curve25519 = function() {
Module._free(privateKey_ptr);
Module._free(basepoint_ptr);
return Promise.resolve({ pubKey: res.buffer, privKey: privKey });
resolve({ pubKey: res.buffer, privKey: privKey });
});
},
sharedSecret: function(pubKey, privKey) {
// Where to store the result
@ -25275,6 +25278,61 @@ axolotlInternal.curve25519 = function() {
};
}();
var axolotlInternal = axolotlInternal || {};
// I am the...workee?
var origCurve25519 = axolotlInternal.curve25519;
axolotlInternal.startWorker = function(url) {
axolotlInternal.stopWorker(); // there can be only one
axolotlInternal.curve25519 = new Curve25519Worker(url);
};
axolotlInternal.stopWorker = function() {
if (axolotlInternal.curve25519 instanceof Curve25519Worker) {
var worker = axolotlInternal.curve25519.worker;
axolotlInternal.curve25519 = origCurve25519;
worker.terminate();
}
};
function Curve25519Worker(url) {
this.jobs = {};
this.jobId = 0;
this.worker = new Worker(url);
this.worker.onmessage = function(e) {
var job = this.jobs[e.data.id];
if (e.data.error && typeof job.onerror === 'function') {
job.onerror(new Error(e.data.error));
} else if (typeof job.onsuccess === 'function') {
job.onsuccess(e.data.result);
}
delete this.jobs[e.data.id];
}.bind(this);
}
Curve25519Worker.prototype = {
constructor: Curve25519Worker,
postMessage: function(methodName, args, onsuccess, onerror) {
return new Promise(function(resolve, reject) {
this.jobs[this.jobId] = { onsuccess: resolve, onerror: reject };
this.worker.postMessage({ id: this.jobId, methodName: methodName, args: args });
this.jobId++;
}.bind(this));
},
keyPair: function(privKey) {
return this.postMessage('keyPair', [privKey]);
},
sharedSecret: function(pubKey, privKey) {
return this.postMessage('sharedSecret', [pubKey, privKey]);
},
sign: function(privKey, message) {
return this.postMessage('sign', [privKey, message]);
},
verify: function(pubKey, message, sig) {
return this.postMessage('verify', [pubKey, message, sig]);
}
};
;(function(){
/**
* CryptoJS core components.
@ -36738,7 +36796,57 @@ axolotlInternal.utils = function() {
'use strict';
window.axolotl = window.axolotl || {};
window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
function isNonNegativeInteger(n) {
return (typeof n === 'number' && (n % 1) === 0 && n >= 0);
}
window.axolotl.util = {
generateIdentityKeyPair: function() {
return axolotlInternal.crypto.createKeyPair();
},
generateRegistrationId: function() {
var registrationId = new Uint16Array(axolotlInternal.crypto.getRandomBytes(2))[0];
return registrationId & 0x3fff;
},
generateSignedPreKey: function (identityKeyPair, signedKeyId) {
if (!(identityKeyPair.privKey instanceof ArrayBuffer) ||
identityKeyPair.privKey.byteLength != 32 ||
!(identityKeyPair.pubKey instanceof ArrayBuffer) ||
identityKeyPair.pubKey.byteLength != 33) {
throw new TypeError('Invalid argument for identityKeyPair');
}
if (!isNonNegativeInteger(signedKeyId)) {
throw new TypeError(
'Invalid argument for signedKeyId: ' + signedKeyId
);
}
return axolotlInternal.crypto.createKeyPair().then(function(keyPair) {
return axolotlInternal.crypto.Ed25519Sign(identityKeyPair.privKey, keyPair.pubKey).then(function(sig) {
return {
keyId : signedKeyId,
keyPair : keyPair,
signature : sig
};
});
});
},
generatePreKey: function(keyId) {
if (!isNonNegativeInteger(keyId)) {
throw new TypeError('Invalid argument for keyId: ' + keyId);
}
return axolotlInternal.crypto.createKeyPair().then(function(keyPair) {
return { keyId: keyId, keyPair: keyPair };
});
},
};
window.axolotl.protocol = function(storage_interface) {
var self = {};
/******************************
@ -36762,41 +36870,23 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
***************************/
var crypto_storage = {};
crypto_storage.putKeyPair = function(keyName, keyPair) {
storage_interface.put("25519Key" + keyName, keyPair);
function getRecord(encodedNumber) {
return storage_interface.getSession(encodedNumber).then(function(serialized) {
if (serialized === undefined) {
return undefined;
}
crypto_storage.getNewStoredKeyPair = function(keyName) {
return axolotlInternal.crypto.createKeyPair().then(function(keyPair) {
crypto_storage.putKeyPair(keyName, keyPair);
return keyPair;
return axolotlInternal.RecipientRecord.deserialize(serialized);
});
}
crypto_storage.getStoredKeyPair = function(keyName) {
var res = storage_interface.get("25519Key" + keyName);
if (res === undefined)
return undefined;
return { pubKey: axolotlInternal.utils.convertToArrayBuffer(res.pubKey), privKey: axolotlInternal.utils.convertToArrayBuffer(res.privKey) };
}
crypto_storage.removeStoredKeyPair = function(keyName) {
storage_interface.remove("25519Key" + keyName);
}
crypto_storage.getIdentityKey = function() {
return this.getStoredKeyPair("identityKey");
}
crypto_storage.saveSession = function(encodedNumber, session, registrationId) {
var record = storage_interface.sessions.get(encodedNumber);
return getRecord(encodedNumber).then(function(record) {
if (record === undefined) {
if (registrationId === undefined)
throw new Error("Tried to save a session for an existing device that didn't exist");
else
record = new axolotl.sessions.RecipientRecord(session.indexInfo.remoteIdentityKey, registrationId);
record = new axolotlInternal.RecipientRecord(session.indexInfo.remoteIdentityKey, registrationId);
}
var sessions = record._sessions;
if (record.identityKey === null)
@ -36806,7 +36896,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var doDeleteSession = false;
if (session.indexInfo.closed != -1) {
doDeleteSession = (session.indexInfo.closed < (new Date().getTime() - MESSAGE_LOST_THRESHOLD_MS));
doDeleteSession = (session.indexInfo.closed < (Date.now() - MESSAGE_LOST_THRESHOLD_MS));
if (!doDeleteSession) {
var keysLeft = false;
@ -36838,24 +36928,27 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
else if (record.registrationId === null)
throw new Error("Had open sessions on a record that had no registrationId set");
var identityKey = storage_interface.identityKeys.get(encodedNumber);
if (identityKey === undefined)
storage_interface.identityKeys.put(encodedNumber, record.identityKey);
else if (axolotlInternal.utils.convertToString(identityKey) !== axolotlInternal.utils.convertToString(record.identityKey))
return storage_interface.getIdentityKey(encodedNumber).then(function(identityKey) {
if (identityKey !== undefined && axolotlInternal.utils.convertToString(identityKey) !== axolotlInternal.utils.convertToString(record.identityKey))
throw new Error("Tried to change identity key at save time");
storage_interface.sessions.put(encodedNumber, record);
return storage_interface.putIdentityKey(encodedNumber, record.identityKey).then(function() {
return storage_interface.putSession(encodedNumber, record.serialize());
});
});
});
}
var getSessions = function(encodedNumber) {
var record = storage_interface.sessions.get(encodedNumber);
return getRecord(encodedNumber).then(function(record) {
if (record === undefined)
return undefined;
return record._sessions;
}
});
};
crypto_storage.getOpenSession = function(encodedNumber) {
var sessions = getSessions(encodedNumber);
return getSessions(encodedNumber).then(function(sessions) {
if (sessions === undefined)
return undefined;
@ -36863,10 +36956,11 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
if (sessions[key].indexInfo.closed == -1)
return sessions[key];
return undefined;
}
});
};
crypto_storage.getSessionByRemoteEphemeralKey = function(encodedNumber, remoteEphemeralKey) {
var sessions = getSessions(encodedNumber);
return getSessions(encodedNumber).then(function(sessions) {
if (sessions === undefined)
return undefined;
@ -36886,15 +36980,17 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return openSession;
return undefined;
});
}
crypto_storage.getSessionOrIdentityKeyByBaseKey = function(encodedNumber, baseKey) {
var record = storage_interface.sessions.get(encodedNumber);
return getRecord(encodedNumber).then(function(record) {
if (record === undefined) {
var identityKey = storage_interface.identityKeys.get(encodedNumber);
return storage_interface.getIdentityKey(encodedNumber).then(function(identityKey) {
if (identityKey === undefined)
return undefined;
return { indexInfo: { remoteIdentityKey: identityKey } };
});
}
var sessions = record._sessions;
@ -36906,6 +37002,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return { indexInfo: { remoteIdentityKey: record.identityKey } };
throw new Error("Datastore inconsistency: device was stored without identity key");
});
}
/*****************************
@ -36948,7 +37045,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
}
var initSession = function(isInitiator, ourEphemeralKey, ourSignedKey, encodedNumber, theirIdentityPubKey, theirEphemeralPubKey, theirSignedPubKey) {
var ourIdentityKey = crypto_storage.getIdentityKey();
var ourIdentityKey = storage_interface.getMyIdentityKey();
if (isInitiator) {
if (ourSignedKey !== undefined)
@ -37034,7 +37131,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var ratchet = axolotlInternal.utils.convertToString(entry.ephemeralKey);
console.log("Checking old chain with added time " + (entry.added/1000));
if ((!objectContainsKeys(session[ratchet].messageKeys) && (session[ratchet].chainKey === undefined || session[ratchet].chainKey.key === undefined))
|| entry.added < new Date().getTime() - MESSAGE_LOST_THRESHOLD_MS) {
|| entry.added < Date.now() - MESSAGE_LOST_THRESHOLD_MS) {
delete session[ratchet];
console.log("...deleted");
} else
@ -37057,7 +37154,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
for (var i in session) {
if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) {
if (!sessionClosedByRemote)
session.oldRatchetList[session.oldRatchetList.length] = { added: new Date().getTime(), ephemeralKey: i };
session.oldRatchetList[session.oldRatchetList.length] = { added: Date.now(), ephemeralKey: i };
else
delete session[i].chainKey.key;
}
@ -37065,28 +37162,25 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
// Delete current root key and our ephemeral key pair to disallow ratchet stepping
delete session.currentRatchet['rootKey'];
delete session.currentRatchet['ephemeralKeyPair'];
session.indexInfo.closed = new Date().getTime();
session.indexInfo.closed = Date.now();
removeOldChains(session);
}
self.closeOpenSessionForDevice = function(encodedNumber) {
var session = crypto_storage.getOpenSession(encodedNumber);
return crypto_storage.getOpenSession(encodedNumber).then(function(session) {
if (session === undefined)
return;
closeSession(session);
crypto_storage.saveSession(encodedNumber, session);
return crypto_storage.saveSession(encodedNumber, session);
});
}
var refreshPreKeys;
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
var preKeyPair = crypto_storage.getStoredKeyPair("preKey" + message.preKeyId);
var signedPreKeyPair = crypto_storage.getStoredKeyPair("signedKey" + message.signedPreKeyId);
//TODO: Call refreshPreKeys when it looks like all our prekeys are used up?
var session = crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, axolotlInternal.utils.convertToArrayBuffer(message.baseKey));
var open_session = crypto_storage.getOpenSession(encodedNumber);
return storage_interface.getPreKey(message.preKeyId).then(function(preKeyPair) {
return storage_interface.getSignedPreKey(message.signedPreKeyId).then(function(signedPreKeyPair) {
return crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, axolotlInternal.utils.convertToArrayBuffer(message.baseKey)).then(function(session) {
return crypto_storage.getOpenSession(encodedNumber).then(function(open_session) {
if (signedPreKeyPair === undefined) {
// Session may or may not be the right one, but if its not, we can't do anything about it
// ...fall through and let decryptWhisperMessage handle that case
@ -37115,11 +37209,16 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
// Note that the session is not actually saved until the very end of decryptWhisperMessage
// ... to ensure that the sender actually holds the private keys for all reported pubkeys
return [new_session, function() {
return storage_interface.removePreKey(message.preKeyId).then(function() {
if (open_session !== undefined)
crypto_storage.saveSession(encodedNumber, open_session);
crypto_storage.removeStoredKeyPair("preKey" + message.preKeyId);
return crypto_storage.saveSession(encodedNumber, open_session);
});
}];
});;
});
});
});
});
});
}
var fillMessageKeys = function(chain, counter) {
@ -37177,7 +37276,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
if (!objectContainsKeys(previousRatchet.messageKeys))
delete session[axolotlInternal.utils.convertToString(ratchet.lastRemoteEphemeralKey)];
else
session.oldRatchetList[session.oldRatchetList.length] = { added: new Date().getTime(), ephemeralKey: ratchet.lastRemoteEphemeralKey };
session.oldRatchetList[session.oldRatchetList.length] = { added: Date.now(), ephemeralKey: ratchet.lastRemoteEphemeralKey };
}).then(finish);
} else
return finish();
@ -37193,12 +37292,18 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto, 'binary');
var remoteEphemeralKey = axolotlInternal.utils.convertToArrayBuffer(message.ephemeralKey);
var promise;
if (session === undefined) {
var session = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey);
promise = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey).then(function(session) {
if (session === undefined)
throw new Error("No session found to decrypt message from " + encodedNumber);
return session;
});
} else {
promise = Promise.resolve(session);
}
return promise.then(function(session) {
return maybeStepRatchet(session, remoteEphemeralKey, message.previousCounter).then(function() {
var chain = session[axolotlInternal.utils.convertToString(message.ephemeralKey)];
@ -37209,7 +37314,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var messageProtoArray = axolotlInternal.utils.convertToArrayBuffer(messageProto);
var macInput = new Uint8Array(messageProtoArray.byteLength + 33*2 + 1);
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(session.indexInfo.remoteIdentityKey)));
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(crypto_storage.getIdentityKey().pubKey)), 33);
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(storage_interface.getMyIdentityKey().pubKey)), 33);
macInput[33*2] = (3 << 4) | 3;
macInput.set(new Uint8Array(messageProtoArray), 33*2 + 1);
@ -37231,17 +37336,19 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
delete session['pendingPreKey'];
removeOldChains(session);
crypto_storage.saveSession(encodedNumber, session, registrationId);
return crypto_storage.saveSession(encodedNumber, session, registrationId).then(function() {
return [plaintext, function() {
closeSession(session, true);
removeOldChains(session);
crypto_storage.saveSession(encodedNumber, session);
return crypto_storage.saveSession(encodedNumber, session);
}];
});
});
});
});
});
});
});
}
/*************************
@ -37260,7 +37367,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return initSessionFromPreKeyWhisperMessage(from, preKeyProto).then(function(sessions) {
return doDecryptWhisperMessage(from, axolotlInternal.utils.convertToString(preKeyProto.message), sessions[0], preKeyProto.registrationId).then(function(result) {
if (sessions[1] !== undefined)
sessions[1]();
return sessions[1]().then(function() { return result; });
return result;
});
});
@ -37268,7 +37375,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
// return Promise(encoded [PreKey]WhisperMessage)
self.encryptMessageFor = function(deviceObject, pushMessageContent) {
var session = crypto_storage.getOpenSession(deviceObject.encodedNumber);
return crypto_storage.getOpenSession(deviceObject.encodedNumber).then(function(session) {
var hadSession = session !== undefined;
var doEncryptPushMessageContent = function() {
@ -37293,7 +37400,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var encodedMsg = axolotlInternal.utils.convertToArrayBuffer(msg.encode());
var macInput = new Uint8Array(encodedMsg.byteLength + 33*2 + 1);
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(crypto_storage.getIdentityKey().pubKey)));
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(storage_interface.getMyIdentityKey().pubKey)));
macInput.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(session.indexInfo.remoteIdentityKey)), 33);
macInput[33*2] = (3 << 4) | 3;
macInput.set(new Uint8Array(encodedMsg), 33*2 + 1);
@ -37306,16 +37413,17 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
removeOldChains(session);
crypto_storage.saveSession(deviceObject.encodedNumber, session, !hadSession ? deviceObject.registrationId : undefined);
return crypto_storage.saveSession(deviceObject.encodedNumber, session, !hadSession ? deviceObject.registrationId : undefined).then(function() {
return result;
});
});
});
});
});
}
var preKeyMsg = new axolotlInternal.protobuf.PreKeyWhisperMessage();
preKeyMsg.identityKey = axolotlInternal.utils.convertToArrayBuffer(crypto_storage.getIdentityKey().pubKey);
preKeyMsg.identityKey = axolotlInternal.utils.convertToArrayBuffer(storage_interface.getMyIdentityKey().pubKey);
preKeyMsg.registrationId = storage_interface.getMyRegistrationId();
if (session === undefined) {
@ -37326,9 +37434,10 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
preKeyMsg.preKeyId = deviceObject.preKeyId;
preKeyMsg.signedPreKeyId = deviceObject.signedKeyId;
preKeyMsg.baseKey = axolotlInternal.utils.convertToArrayBuffer(baseKey.pubKey);
return initSession(true, baseKey, undefined, deviceObject.encodedNumber,
deviceIdentityKey, axolotlInternal.utils.convertToArrayBuffer(deviceObject.preKey), deviceSignedKey)
.then(function(new_session) {
return initSession(true, baseKey, undefined,
deviceObject.encodedNumber, deviceIdentityKey,
axolotlInternal.utils.convertToArrayBuffer(deviceObject.preKey),
deviceSignedKey).then(function(new_session) {
session = new_session;
session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey };
return doEncryptPushMessageContent().then(function(message) {
@ -37352,69 +37461,8 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
} else
return {type: 1, body: axolotlInternal.utils.convertToString(message)};
});
}
var GENERATE_KEYS_KEYS_GENERATED = 100;
self.generateKeys = function() {
var identityKeyPair = crypto_storage.getIdentityKey();
var identityKeyCalculated = function(identityKeyPair) {
var firstPreKeyId = storage_interface.get("maxPreKeyId", 0);
storage_interface.put("maxPreKeyId", firstPreKeyId + GENERATE_KEYS_KEYS_GENERATED);
var signedKeyId = storage_interface.get("signedKeyId", 0);
storage_interface.put("signedKeyId", signedKeyId + 1);
var keys = {};
keys.identityKey = identityKeyPair.pubKey;
keys.preKeys = [];
var generateKey = function(keyId) {
return crypto_storage.getNewStoredKeyPair("preKey" + keyId, false).then(function(keyPair) {
keys.preKeys[keyId] = {keyId: keyId, publicKey: keyPair.pubKey};
});
};
var promises = [];
for (var i = firstPreKeyId; i < firstPreKeyId + GENERATE_KEYS_KEYS_GENERATED; i++)
promises[i] = generateKey(i);
promises[firstPreKeyId + GENERATE_KEYS_KEYS_GENERATED] = crypto_storage.getNewStoredKeyPair("signedKey" + signedKeyId).then(function(keyPair) {
return axolotlInternal.crypto.Ed25519Sign(identityKeyPair.privKey, keyPair.pubKey).then(function(sig) {
keys.signedPreKey = {keyId: signedKeyId, publicKey: keyPair.pubKey, signature: sig};
});
});
//TODO: Process by date added and agressively call generateKeys when we get near maxPreKeyId in a message
crypto_storage.removeStoredKeyPair("signedKey" + (signedKeyId - 2));
return Promise.all(promises).then(function() {
storage_interface.put("lastPreKeyUpdate", Date.now());
return keys;
});
}
if (identityKeyPair === undefined)
return crypto_storage.getNewStoredKeyPair("identityKey").then(function(keyPair) { return identityKeyCalculated(keyPair); });
else
return identityKeyCalculated(identityKeyPair);
}
//TODO: Replace this stuff
refreshPreKeys = function() {
self.generateKeys().then(function(keys) {
console.log("Pre Keys updated!");
return updateKeysCallback(keys);
}).catch(function(e) {
//TODO: Notify the user somehow???
console.error(e);
});
}
if (updateKeysCallback)
window.setInterval(function() {
// Note that this will not ever run until generateKeys has been called at least once
if (storage_interface.get("lastPreKeyUpdate", Date.now()) < Date.now() - MESSAGE_LOST_THRESHOLD_MS)
refreshPreKeys();
}, 60 * 1000);
self.createIdentityKeyRecvSocket = function() {
var socketInfo = {};
@ -37436,16 +37484,16 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return verifyMAC(ivAndCiphertext, keys[1], mac).then(function() {
return axolotlInternal.crypto.decrypt(keys[0], ciphertext, iv).then(function(plaintext) {
var identityKeyMsg = axolotlInternal.protobuf.ProvisionMessage.decode(plaintext);
var provisionMessage = axolotlInternal.protobuf.ProvisionMessage.decode(plaintext);
return axolotlInternal.crypto.createKeyPair(axolotlInternal.utils.convertToArrayBuffer(identityKeyMsg.identityKeyPrivate)).then(function(identityKeyPair) {
if (crypto_storage.getStoredKeyPair("identityKey") !== undefined)
throw new Error("Tried to overwrite identity key");
crypto_storage.putKeyPair("identityKey", identityKeyPair);
identityKeyMsg.identityKeyPrivate = null;
return identityKeyMsg;
return axolotlInternal.crypto.createKeyPair(
provisionMessage.identityKeyPrivate.toArrayBuffer()
).then(function(identityKeyPair) {
return {
identityKeyPair : identityKeyPair,
number : provisionMessage.number,
provisioningCode : provisionMessage.provisioningCode
};
});
});
});
@ -37460,6 +37508,32 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
});
}
self.getRegistrationId = function(encodedNumber) {
return getRecord(encodedNumber).then(function(record) {
if (record === undefined) {
return undefined;
}
return record.registrationId;
});
};
self.hasOpenSession = function(encodedNumber) {
return getRecord(encodedNumber).then(function(record) {
if (record === undefined) {
return false;
}
return record.haveOpenSession();
});
};
self.startWorker = function(url) {
axolotlInternal.startWorker(url);
};
self.stopWorker = function() {
axolotlInternal.stopWorker();
};
return self;
};
@ -37582,10 +37656,11 @@ axolotlInternal.protobuf = function() {
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
;(function() {
var axolotlInternal = axolotlInternal || {};
axolotlInternal.RecipientRecord = function() {
'use strict';
window.axolotl = window.axolotl || {};
var RecipientRecord = function(identityKey, registrationId) {
this._sessions = {};
@ -37600,25 +37675,22 @@ RecipientRecord.prototype.serialize = function() {
return axolotlInternal.utils.jsonThing({sessions: this._sessions, registrationId: this.registrationId, identityKey: this.identityKey});
}
RecipientRecord.prototype.deserialize = function(serialized) {
RecipientRecord.deserialize = function(serialized) {
var data = JSON.parse(serialized);
this._sessions = data.sessions;
if (this._sessions === undefined || this._sessions === null || typeof this._sessions !== "object" || Array.isArray(this._sessions))
var record = new RecipientRecord(data.identityKey, data.registrationId);
record._sessions = data.sessions;
if (record._sessions === undefined || record._sessions === null || typeof record._sessions !== "object" || Array.isArray(record._sessions))
throw new Error("Error deserializing RecipientRecord");
this.identityKey = data.identityKey;
this.registrationId = data.registrationId;
if (this.identityKey === undefined || this.registrationId === undefined)
if (record.identityKey === undefined || record.registrationId === undefined)
throw new Error("Error deserializing RecipientRecord");
return record;
}
RecipientRecord.prototype.haveOpenSession = function() {
return this.registrationId !== null;
}
window.axolotl.sessions = {
RecipientRecord: RecipientRecord,
};
})();
return RecipientRecord;
}();
})();