Update libaxololt to a087b9e746e67995f16e077183cc0

pull/749/head
lilia 10 years ago
parent 2cb0070343
commit 8304aa903a

@ -25246,6 +25246,7 @@ axolotlInternal.curve25519 = function() {
return { return {
keyPair: function(privKey) { keyPair: function(privKey) {
return new Promise(function(resolve) {
var priv = new Uint8Array(privKey); var priv = new Uint8Array(privKey);
priv[0] &= 248; priv[0] &= 248;
priv[31] &= 127; priv[31] &= 127;
@ -25272,7 +25273,9 @@ axolotlInternal.curve25519 = function() {
Module._free(privateKey_ptr); Module._free(privateKey_ptr);
Module._free(basepoint_ptr); Module._free(basepoint_ptr);
return Promise.resolve({ pubKey: res.buffer, privKey: privKey }); resolve({ pubKey: res.buffer, privKey: privKey });
});
}, },
sharedSecret: function(pubKey, privKey) { sharedSecret: function(pubKey, privKey) {
// Where to store the result // 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(){ ;(function(){
/** /**
* CryptoJS core components. * CryptoJS core components.
@ -36816,7 +36874,57 @@ axolotlInternal.utils = function() {
'use strict'; 'use strict';
window.axolotl = window.axolotl || {}; 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 = {}; var self = {};
/****************************** /******************************
@ -36840,41 +36948,23 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
***************************/ ***************************/
var crypto_storage = {}; var crypto_storage = {};
crypto_storage.putKeyPair = function(keyName, keyPair) { function getRecord(encodedNumber) {
storage_interface.put("25519Key" + keyName, keyPair); return storage_interface.getSession(encodedNumber).then(function(serialized) {
} if (serialized === undefined) {
crypto_storage.getNewStoredKeyPair = function(keyName) {
return axolotlInternal.crypto.createKeyPair().then(function(keyPair) {
crypto_storage.putKeyPair(keyName, keyPair);
return keyPair;
});
}
crypto_storage.getStoredKeyPair = function(keyName) {
var res = storage_interface.get("25519Key" + keyName);
if (res === undefined)
return undefined; return undefined;
return { pubKey: axolotlInternal.utils.convertToArrayBuffer(res.pubKey), privKey: axolotlInternal.utils.convertToArrayBuffer(res.privKey) };
} }
return axolotlInternal.RecipientRecord.deserialize(serialized);
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) { crypto_storage.saveSession = function(encodedNumber, session, registrationId) {
var record = storage_interface.sessions.get(encodedNumber); return getRecord(encodedNumber).then(function(record) {
if (record === undefined) { if (record === undefined) {
if (registrationId === undefined) if (registrationId === undefined)
throw new Error("Tried to save a session for an existing device that didn't exist"); throw new Error("Tried to save a session for an existing device that didn't exist");
else else
record = new axolotl.sessions.RecipientRecord(session.indexInfo.remoteIdentityKey, registrationId); record = new axolotlInternal.RecipientRecord(session.indexInfo.remoteIdentityKey, registrationId);
} }
var sessions = record._sessions; var sessions = record._sessions;
if (record.identityKey === null) if (record.identityKey === null)
@ -36884,7 +36974,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var doDeleteSession = false; var doDeleteSession = false;
if (session.indexInfo.closed != -1) { 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) { if (!doDeleteSession) {
var keysLeft = false; var keysLeft = false;
@ -36916,24 +37006,27 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
else if (record.registrationId === null) else if (record.registrationId === null)
throw new Error("Had open sessions on a record that had no registrationId set"); throw new Error("Had open sessions on a record that had no registrationId set");
var identityKey = storage_interface.identityKeys.get(encodedNumber); return storage_interface.getIdentityKey(encodedNumber).then(function(identityKey) {
if (identityKey === undefined) if (identityKey !== undefined && axolotlInternal.utils.convertToString(identityKey) !== axolotlInternal.utils.convertToString(record.identityKey))
storage_interface.identityKeys.put(encodedNumber, record.identityKey);
else if (axolotlInternal.utils.convertToString(identityKey) !== axolotlInternal.utils.convertToString(record.identityKey))
throw new Error("Tried to change identity key at save time"); 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 getSessions = function(encodedNumber) {
var record = storage_interface.sessions.get(encodedNumber); return getRecord(encodedNumber).then(function(record) {
if (record === undefined) if (record === undefined)
return undefined; return undefined;
return record._sessions; return record._sessions;
} });
};
crypto_storage.getOpenSession = function(encodedNumber) { crypto_storage.getOpenSession = function(encodedNumber) {
var sessions = getSessions(encodedNumber); return getSessions(encodedNumber).then(function(sessions) {
if (sessions === undefined) if (sessions === undefined)
return undefined; return undefined;
@ -36941,10 +37034,11 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
if (sessions[key].indexInfo.closed == -1) if (sessions[key].indexInfo.closed == -1)
return sessions[key]; return sessions[key];
return undefined; return undefined;
} });
};
crypto_storage.getSessionByRemoteEphemeralKey = function(encodedNumber, remoteEphemeralKey) { crypto_storage.getSessionByRemoteEphemeralKey = function(encodedNumber, remoteEphemeralKey) {
var sessions = getSessions(encodedNumber); return getSessions(encodedNumber).then(function(sessions) {
if (sessions === undefined) if (sessions === undefined)
return undefined; return undefined;
@ -36964,15 +37058,17 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return openSession; return openSession;
return undefined; return undefined;
});
} }
crypto_storage.getSessionOrIdentityKeyByBaseKey = function(encodedNumber, baseKey) { crypto_storage.getSessionOrIdentityKeyByBaseKey = function(encodedNumber, baseKey) {
var record = storage_interface.sessions.get(encodedNumber); return getRecord(encodedNumber).then(function(record) {
if (record === undefined) { if (record === undefined) {
var identityKey = storage_interface.identityKeys.get(encodedNumber); return storage_interface.getIdentityKey(encodedNumber).then(function(identityKey) {
if (identityKey === undefined) if (identityKey === undefined)
return undefined; return undefined;
return { indexInfo: { remoteIdentityKey: identityKey } }; return { indexInfo: { remoteIdentityKey: identityKey } };
});
} }
var sessions = record._sessions; var sessions = record._sessions;
@ -36984,6 +37080,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return { indexInfo: { remoteIdentityKey: record.identityKey } }; return { indexInfo: { remoteIdentityKey: record.identityKey } };
throw new Error("Datastore inconsistency: device was stored without identity key"); 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 initSession = function(isInitiator, ourEphemeralKey, ourSignedKey, encodedNumber, theirIdentityPubKey, theirEphemeralPubKey, theirSignedPubKey) {
var ourIdentityKey = crypto_storage.getIdentityKey(); var ourIdentityKey = storage_interface.getMyIdentityKey();
if (isInitiator) { if (isInitiator) {
if (ourSignedKey !== undefined) if (ourSignedKey !== undefined)
@ -37112,7 +37209,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var ratchet = axolotlInternal.utils.convertToString(entry.ephemeralKey); var ratchet = axolotlInternal.utils.convertToString(entry.ephemeralKey);
console.log("Checking old chain with added time " + (entry.added/1000)); 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)) 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]; delete session[ratchet];
console.log("...deleted"); console.log("...deleted");
} else } else
@ -37135,7 +37232,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
for (var i in session) { for (var i in session) {
if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) { if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) {
if (!sessionClosedByRemote) if (!sessionClosedByRemote)
session.oldRatchetList[session.oldRatchetList.length] = { added: new Date().getTime(), ephemeralKey: i }; session.oldRatchetList[session.oldRatchetList.length] = { added: Date.now(), ephemeralKey: i };
else else
delete session[i].chainKey.key; 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 current root key and our ephemeral key pair to disallow ratchet stepping
delete session.currentRatchet['rootKey']; delete session.currentRatchet['rootKey'];
delete session.currentRatchet['ephemeralKeyPair']; delete session.currentRatchet['ephemeralKeyPair'];
session.indexInfo.closed = new Date().getTime(); session.indexInfo.closed = Date.now();
removeOldChains(session); removeOldChains(session);
} }
self.closeOpenSessionForDevice = function(encodedNumber) { self.closeOpenSessionForDevice = function(encodedNumber) {
var session = crypto_storage.getOpenSession(encodedNumber); return crypto_storage.getOpenSession(encodedNumber).then(function(session) {
if (session === undefined) if (session === undefined)
return; return;
closeSession(session); closeSession(session);
crypto_storage.saveSession(encodedNumber, session); return crypto_storage.saveSession(encodedNumber, session);
});
} }
var refreshPreKeys;
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) { var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
var preKeyPair = crypto_storage.getStoredKeyPair("preKey" + message.preKeyId); return storage_interface.getPreKey(message.preKeyId).then(function(preKeyPair) {
var signedPreKeyPair = crypto_storage.getStoredKeyPair("signedKey" + message.signedPreKeyId); return storage_interface.getSignedPreKey(message.signedPreKeyId).then(function(signedPreKeyPair) {
return crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, axolotlInternal.utils.convertToArrayBuffer(message.baseKey)).then(function(session) {
//TODO: Call refreshPreKeys when it looks like all our prekeys are used up? return crypto_storage.getOpenSession(encodedNumber).then(function(open_session) {
var session = crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, axolotlInternal.utils.convertToArrayBuffer(message.baseKey));
var open_session = crypto_storage.getOpenSession(encodedNumber);
if (signedPreKeyPair === undefined) { if (signedPreKeyPair === undefined) {
// Session may or may not be the right one, but if its not, we can't do anything about it // 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 // ...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 // 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 // ... to ensure that the sender actually holds the private keys for all reported pubkeys
return [new_session, function() { return [new_session, function() {
return storage_interface.removePreKey(message.preKeyId).then(function() {
if (open_session !== undefined) if (open_session !== undefined)
crypto_storage.saveSession(encodedNumber, open_session); return crypto_storage.saveSession(encodedNumber, open_session);
crypto_storage.removeStoredKeyPair("preKey" + message.preKeyId); });
}]; }];
});; });
});
});
});
});
} }
var fillMessageKeys = function(chain, counter) { var fillMessageKeys = function(chain, counter) {
@ -37255,7 +37354,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
if (!objectContainsKeys(previousRatchet.messageKeys)) if (!objectContainsKeys(previousRatchet.messageKeys))
delete session[axolotlInternal.utils.convertToString(ratchet.lastRemoteEphemeralKey)]; delete session[axolotlInternal.utils.convertToString(ratchet.lastRemoteEphemeralKey)];
else 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); }).then(finish);
} else } else
return finish(); return finish();
@ -37271,12 +37370,18 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto, 'binary'); var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto, 'binary');
var remoteEphemeralKey = axolotlInternal.utils.convertToArrayBuffer(message.ephemeralKey); var remoteEphemeralKey = axolotlInternal.utils.convertToArrayBuffer(message.ephemeralKey);
var promise;
if (session === undefined) { if (session === undefined) {
var session = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey); promise = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey).then(function(session) {
if (session === undefined) if (session === undefined)
throw new Error("No session found to decrypt message from " + encodedNumber); 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() { return maybeStepRatchet(session, remoteEphemeralKey, message.previousCounter).then(function() {
var chain = session[axolotlInternal.utils.convertToString(message.ephemeralKey)]; 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 messageProtoArray = axolotlInternal.utils.convertToArrayBuffer(messageProto);
var macInput = new Uint8Array(messageProtoArray.byteLength + 33*2 + 1); 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(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[33*2] = (3 << 4) | 3;
macInput.set(new Uint8Array(messageProtoArray), 33*2 + 1); macInput.set(new Uint8Array(messageProtoArray), 33*2 + 1);
@ -37309,17 +37414,19 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
delete session['pendingPreKey']; delete session['pendingPreKey'];
removeOldChains(session); removeOldChains(session);
crypto_storage.saveSession(encodedNumber, session, registrationId); return crypto_storage.saveSession(encodedNumber, session, registrationId).then(function() {
return [plaintext, function() { return [plaintext, function() {
closeSession(session, true); closeSession(session, true);
removeOldChains(session); 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 initSessionFromPreKeyWhisperMessage(from, preKeyProto).then(function(sessions) {
return doDecryptWhisperMessage(from, axolotlInternal.utils.convertToString(preKeyProto.message), sessions[0], preKeyProto.registrationId).then(function(result) { return doDecryptWhisperMessage(from, axolotlInternal.utils.convertToString(preKeyProto.message), sessions[0], preKeyProto.registrationId).then(function(result) {
if (sessions[1] !== undefined) if (sessions[1] !== undefined)
sessions[1](); return sessions[1]().then(function() { return result; });
return result; return result;
}); });
}); });
@ -37346,7 +37453,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
// return Promise(encoded [PreKey]WhisperMessage) // return Promise(encoded [PreKey]WhisperMessage)
self.encryptMessageFor = function(deviceObject, pushMessageContent) { 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 hadSession = session !== undefined;
var doEncryptPushMessageContent = function() { var doEncryptPushMessageContent = function() {
@ -37371,7 +37478,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var encodedMsg = axolotlInternal.utils.convertToArrayBuffer(msg.encode()); var encodedMsg = axolotlInternal.utils.convertToArrayBuffer(msg.encode());
var macInput = new Uint8Array(encodedMsg.byteLength + 33*2 + 1); 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.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(session.indexInfo.remoteIdentityKey)), 33);
macInput[33*2] = (3 << 4) | 3; macInput[33*2] = (3 << 4) | 3;
macInput.set(new Uint8Array(encodedMsg), 33*2 + 1); macInput.set(new Uint8Array(encodedMsg), 33*2 + 1);
@ -37384,16 +37491,17 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
removeOldChains(session); 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; return result;
}); });
}); });
}); });
}); });
});
} }
var preKeyMsg = new axolotlInternal.protobuf.PreKeyWhisperMessage(); 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(); preKeyMsg.registrationId = storage_interface.getMyRegistrationId();
if (session === undefined) { if (session === undefined) {
@ -37404,9 +37512,10 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
preKeyMsg.preKeyId = deviceObject.preKeyId; preKeyMsg.preKeyId = deviceObject.preKeyId;
preKeyMsg.signedPreKeyId = deviceObject.signedKeyId; preKeyMsg.signedPreKeyId = deviceObject.signedKeyId;
preKeyMsg.baseKey = axolotlInternal.utils.convertToArrayBuffer(baseKey.pubKey); preKeyMsg.baseKey = axolotlInternal.utils.convertToArrayBuffer(baseKey.pubKey);
return initSession(true, baseKey, undefined, deviceObject.encodedNumber, return initSession(true, baseKey, undefined,
deviceIdentityKey, axolotlInternal.utils.convertToArrayBuffer(deviceObject.preKey), deviceSignedKey) deviceObject.encodedNumber, deviceIdentityKey,
.then(function(new_session) { axolotlInternal.utils.convertToArrayBuffer(deviceObject.preKey),
deviceSignedKey).then(function(new_session) {
session = new_session; session = new_session;
session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey }; session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey };
return doEncryptPushMessageContent().then(function(message) { return doEncryptPushMessageContent().then(function(message) {
@ -37430,70 +37539,9 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
} else } else
return {type: 1, body: axolotlInternal.utils.convertToString(message)}; 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() { self.createIdentityKeyRecvSocket = function() {
var socketInfo = {}; var socketInfo = {};
var keyPair; var keyPair;
@ -37514,16 +37562,16 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return verifyMAC(ivAndCiphertext, keys[1], mac).then(function() { return verifyMAC(ivAndCiphertext, keys[1], mac).then(function() {
return axolotlInternal.crypto.decrypt(keys[0], ciphertext, iv).then(function(plaintext) { 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; return self;
}; };
@ -37660,10 +37734,11 @@ axolotlInternal.protobuf = function() {
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
;(function() {
var axolotlInternal = axolotlInternal || {};
axolotlInternal.RecipientRecord = function() {
'use strict'; 'use strict';
window.axolotl = window.axolotl || {};
var RecipientRecord = function(identityKey, registrationId) { var RecipientRecord = function(identityKey, registrationId) {
this._sessions = {}; this._sessions = {};
@ -37678,26 +37753,23 @@ RecipientRecord.prototype.serialize = function() {
return axolotlInternal.utils.jsonThing({sessions: this._sessions, registrationId: this.registrationId, identityKey: this.identityKey}); 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); var data = JSON.parse(serialized);
this._sessions = data.sessions; var record = new RecipientRecord(data.identityKey, data.registrationId);
if (this._sessions === undefined || this._sessions === null || typeof this._sessions !== "object" || Array.isArray(this._sessions)) 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"); throw new Error("Error deserializing RecipientRecord");
this.identityKey = data.identityKey; if (record.identityKey === undefined || record.registrationId === undefined)
this.registrationId = data.registrationId;
if (this.identityKey === undefined || this.registrationId === undefined)
throw new Error("Error deserializing RecipientRecord"); throw new Error("Error deserializing RecipientRecord");
return record;
} }
RecipientRecord.prototype.haveOpenSession = function() { RecipientRecord.prototype.haveOpenSession = function() {
return this.registrationId !== null; return this.registrationId !== null;
} }
window.axolotl.sessions = { return RecipientRecord;
RecipientRecord: RecipientRecord, }();
};
})();
})(); })();
'use strict'; 'use strict';

@ -25245,6 +25245,7 @@ axolotlInternal.curve25519 = function() {
return { return {
keyPair: function(privKey) { keyPair: function(privKey) {
return new Promise(function(resolve) {
var priv = new Uint8Array(privKey); var priv = new Uint8Array(privKey);
priv[0] &= 248; priv[0] &= 248;
priv[31] &= 127; priv[31] &= 127;
@ -25271,7 +25272,9 @@ axolotlInternal.curve25519 = function() {
Module._free(privateKey_ptr); Module._free(privateKey_ptr);
Module._free(basepoint_ptr); Module._free(basepoint_ptr);
return Promise.resolve({ pubKey: res.buffer, privKey: privKey }); resolve({ pubKey: res.buffer, privKey: privKey });
});
}, },
sharedSecret: function(pubKey, privKey) { sharedSecret: function(pubKey, privKey) {
// Where to store the result // 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(){ ;(function(){
/** /**
* CryptoJS core components. * CryptoJS core components.
@ -36815,7 +36873,57 @@ axolotlInternal.utils = function() {
'use strict'; 'use strict';
window.axolotl = window.axolotl || {}; 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 = {}; var self = {};
/****************************** /******************************
@ -36839,41 +36947,23 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
***************************/ ***************************/
var crypto_storage = {}; var crypto_storage = {};
crypto_storage.putKeyPair = function(keyName, keyPair) { function getRecord(encodedNumber) {
storage_interface.put("25519Key" + keyName, keyPair); return storage_interface.getSession(encodedNumber).then(function(serialized) {
} if (serialized === undefined) {
crypto_storage.getNewStoredKeyPair = function(keyName) {
return axolotlInternal.crypto.createKeyPair().then(function(keyPair) {
crypto_storage.putKeyPair(keyName, keyPair);
return keyPair;
});
}
crypto_storage.getStoredKeyPair = function(keyName) {
var res = storage_interface.get("25519Key" + keyName);
if (res === undefined)
return undefined; return undefined;
return { pubKey: axolotlInternal.utils.convertToArrayBuffer(res.pubKey), privKey: axolotlInternal.utils.convertToArrayBuffer(res.privKey) };
} }
return axolotlInternal.RecipientRecord.deserialize(serialized);
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) { crypto_storage.saveSession = function(encodedNumber, session, registrationId) {
var record = storage_interface.sessions.get(encodedNumber); return getRecord(encodedNumber).then(function(record) {
if (record === undefined) { if (record === undefined) {
if (registrationId === undefined) if (registrationId === undefined)
throw new Error("Tried to save a session for an existing device that didn't exist"); throw new Error("Tried to save a session for an existing device that didn't exist");
else else
record = new axolotl.sessions.RecipientRecord(session.indexInfo.remoteIdentityKey, registrationId); record = new axolotlInternal.RecipientRecord(session.indexInfo.remoteIdentityKey, registrationId);
} }
var sessions = record._sessions; var sessions = record._sessions;
if (record.identityKey === null) if (record.identityKey === null)
@ -36883,7 +36973,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var doDeleteSession = false; var doDeleteSession = false;
if (session.indexInfo.closed != -1) { 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) { if (!doDeleteSession) {
var keysLeft = false; var keysLeft = false;
@ -36915,24 +37005,27 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
else if (record.registrationId === null) else if (record.registrationId === null)
throw new Error("Had open sessions on a record that had no registrationId set"); throw new Error("Had open sessions on a record that had no registrationId set");
var identityKey = storage_interface.identityKeys.get(encodedNumber); return storage_interface.getIdentityKey(encodedNumber).then(function(identityKey) {
if (identityKey === undefined) if (identityKey !== undefined && axolotlInternal.utils.convertToString(identityKey) !== axolotlInternal.utils.convertToString(record.identityKey))
storage_interface.identityKeys.put(encodedNumber, record.identityKey);
else if (axolotlInternal.utils.convertToString(identityKey) !== axolotlInternal.utils.convertToString(record.identityKey))
throw new Error("Tried to change identity key at save time"); 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 getSessions = function(encodedNumber) {
var record = storage_interface.sessions.get(encodedNumber); return getRecord(encodedNumber).then(function(record) {
if (record === undefined) if (record === undefined)
return undefined; return undefined;
return record._sessions; return record._sessions;
} });
};
crypto_storage.getOpenSession = function(encodedNumber) { crypto_storage.getOpenSession = function(encodedNumber) {
var sessions = getSessions(encodedNumber); return getSessions(encodedNumber).then(function(sessions) {
if (sessions === undefined) if (sessions === undefined)
return undefined; return undefined;
@ -36940,10 +37033,11 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
if (sessions[key].indexInfo.closed == -1) if (sessions[key].indexInfo.closed == -1)
return sessions[key]; return sessions[key];
return undefined; return undefined;
} });
};
crypto_storage.getSessionByRemoteEphemeralKey = function(encodedNumber, remoteEphemeralKey) { crypto_storage.getSessionByRemoteEphemeralKey = function(encodedNumber, remoteEphemeralKey) {
var sessions = getSessions(encodedNumber); return getSessions(encodedNumber).then(function(sessions) {
if (sessions === undefined) if (sessions === undefined)
return undefined; return undefined;
@ -36963,15 +37057,17 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return openSession; return openSession;
return undefined; return undefined;
});
} }
crypto_storage.getSessionOrIdentityKeyByBaseKey = function(encodedNumber, baseKey) { crypto_storage.getSessionOrIdentityKeyByBaseKey = function(encodedNumber, baseKey) {
var record = storage_interface.sessions.get(encodedNumber); return getRecord(encodedNumber).then(function(record) {
if (record === undefined) { if (record === undefined) {
var identityKey = storage_interface.identityKeys.get(encodedNumber); return storage_interface.getIdentityKey(encodedNumber).then(function(identityKey) {
if (identityKey === undefined) if (identityKey === undefined)
return undefined; return undefined;
return { indexInfo: { remoteIdentityKey: identityKey } }; return { indexInfo: { remoteIdentityKey: identityKey } };
});
} }
var sessions = record._sessions; var sessions = record._sessions;
@ -36983,6 +37079,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return { indexInfo: { remoteIdentityKey: record.identityKey } }; return { indexInfo: { remoteIdentityKey: record.identityKey } };
throw new Error("Datastore inconsistency: device was stored without identity key"); 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 initSession = function(isInitiator, ourEphemeralKey, ourSignedKey, encodedNumber, theirIdentityPubKey, theirEphemeralPubKey, theirSignedPubKey) {
var ourIdentityKey = crypto_storage.getIdentityKey(); var ourIdentityKey = storage_interface.getMyIdentityKey();
if (isInitiator) { if (isInitiator) {
if (ourSignedKey !== undefined) if (ourSignedKey !== undefined)
@ -37111,7 +37208,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var ratchet = axolotlInternal.utils.convertToString(entry.ephemeralKey); var ratchet = axolotlInternal.utils.convertToString(entry.ephemeralKey);
console.log("Checking old chain with added time " + (entry.added/1000)); 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)) 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]; delete session[ratchet];
console.log("...deleted"); console.log("...deleted");
} else } else
@ -37134,7 +37231,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
for (var i in session) { for (var i in session) {
if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) { if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) {
if (!sessionClosedByRemote) if (!sessionClosedByRemote)
session.oldRatchetList[session.oldRatchetList.length] = { added: new Date().getTime(), ephemeralKey: i }; session.oldRatchetList[session.oldRatchetList.length] = { added: Date.now(), ephemeralKey: i };
else else
delete session[i].chainKey.key; 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 current root key and our ephemeral key pair to disallow ratchet stepping
delete session.currentRatchet['rootKey']; delete session.currentRatchet['rootKey'];
delete session.currentRatchet['ephemeralKeyPair']; delete session.currentRatchet['ephemeralKeyPair'];
session.indexInfo.closed = new Date().getTime(); session.indexInfo.closed = Date.now();
removeOldChains(session); removeOldChains(session);
} }
self.closeOpenSessionForDevice = function(encodedNumber) { self.closeOpenSessionForDevice = function(encodedNumber) {
var session = crypto_storage.getOpenSession(encodedNumber); return crypto_storage.getOpenSession(encodedNumber).then(function(session) {
if (session === undefined) if (session === undefined)
return; return;
closeSession(session); closeSession(session);
crypto_storage.saveSession(encodedNumber, session); return crypto_storage.saveSession(encodedNumber, session);
});
} }
var refreshPreKeys;
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) { var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
var preKeyPair = crypto_storage.getStoredKeyPair("preKey" + message.preKeyId); return storage_interface.getPreKey(message.preKeyId).then(function(preKeyPair) {
var signedPreKeyPair = crypto_storage.getStoredKeyPair("signedKey" + message.signedPreKeyId); return storage_interface.getSignedPreKey(message.signedPreKeyId).then(function(signedPreKeyPair) {
return crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, axolotlInternal.utils.convertToArrayBuffer(message.baseKey)).then(function(session) {
//TODO: Call refreshPreKeys when it looks like all our prekeys are used up? return crypto_storage.getOpenSession(encodedNumber).then(function(open_session) {
var session = crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, axolotlInternal.utils.convertToArrayBuffer(message.baseKey));
var open_session = crypto_storage.getOpenSession(encodedNumber);
if (signedPreKeyPair === undefined) { if (signedPreKeyPair === undefined) {
// Session may or may not be the right one, but if its not, we can't do anything about it // 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 // ...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 // 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 // ... to ensure that the sender actually holds the private keys for all reported pubkeys
return [new_session, function() { return [new_session, function() {
return storage_interface.removePreKey(message.preKeyId).then(function() {
if (open_session !== undefined) if (open_session !== undefined)
crypto_storage.saveSession(encodedNumber, open_session); return crypto_storage.saveSession(encodedNumber, open_session);
crypto_storage.removeStoredKeyPair("preKey" + message.preKeyId); });
}]; }];
});; });
});
});
});
});
} }
var fillMessageKeys = function(chain, counter) { var fillMessageKeys = function(chain, counter) {
@ -37254,7 +37353,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
if (!objectContainsKeys(previousRatchet.messageKeys)) if (!objectContainsKeys(previousRatchet.messageKeys))
delete session[axolotlInternal.utils.convertToString(ratchet.lastRemoteEphemeralKey)]; delete session[axolotlInternal.utils.convertToString(ratchet.lastRemoteEphemeralKey)];
else 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); }).then(finish);
} else } else
return finish(); return finish();
@ -37270,12 +37369,18 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto, 'binary'); var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto, 'binary');
var remoteEphemeralKey = axolotlInternal.utils.convertToArrayBuffer(message.ephemeralKey); var remoteEphemeralKey = axolotlInternal.utils.convertToArrayBuffer(message.ephemeralKey);
var promise;
if (session === undefined) { if (session === undefined) {
var session = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey); promise = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey).then(function(session) {
if (session === undefined) if (session === undefined)
throw new Error("No session found to decrypt message from " + encodedNumber); 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() { return maybeStepRatchet(session, remoteEphemeralKey, message.previousCounter).then(function() {
var chain = session[axolotlInternal.utils.convertToString(message.ephemeralKey)]; 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 messageProtoArray = axolotlInternal.utils.convertToArrayBuffer(messageProto);
var macInput = new Uint8Array(messageProtoArray.byteLength + 33*2 + 1); 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(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[33*2] = (3 << 4) | 3;
macInput.set(new Uint8Array(messageProtoArray), 33*2 + 1); macInput.set(new Uint8Array(messageProtoArray), 33*2 + 1);
@ -37308,17 +37413,19 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
delete session['pendingPreKey']; delete session['pendingPreKey'];
removeOldChains(session); removeOldChains(session);
crypto_storage.saveSession(encodedNumber, session, registrationId); return crypto_storage.saveSession(encodedNumber, session, registrationId).then(function() {
return [plaintext, function() { return [plaintext, function() {
closeSession(session, true); closeSession(session, true);
removeOldChains(session); 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 initSessionFromPreKeyWhisperMessage(from, preKeyProto).then(function(sessions) {
return doDecryptWhisperMessage(from, axolotlInternal.utils.convertToString(preKeyProto.message), sessions[0], preKeyProto.registrationId).then(function(result) { return doDecryptWhisperMessage(from, axolotlInternal.utils.convertToString(preKeyProto.message), sessions[0], preKeyProto.registrationId).then(function(result) {
if (sessions[1] !== undefined) if (sessions[1] !== undefined)
sessions[1](); return sessions[1]().then(function() { return result; });
return result; return result;
}); });
}); });
@ -37345,7 +37452,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
// return Promise(encoded [PreKey]WhisperMessage) // return Promise(encoded [PreKey]WhisperMessage)
self.encryptMessageFor = function(deviceObject, pushMessageContent) { 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 hadSession = session !== undefined;
var doEncryptPushMessageContent = function() { var doEncryptPushMessageContent = function() {
@ -37370,7 +37477,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var encodedMsg = axolotlInternal.utils.convertToArrayBuffer(msg.encode()); var encodedMsg = axolotlInternal.utils.convertToArrayBuffer(msg.encode());
var macInput = new Uint8Array(encodedMsg.byteLength + 33*2 + 1); 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.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(session.indexInfo.remoteIdentityKey)), 33);
macInput[33*2] = (3 << 4) | 3; macInput[33*2] = (3 << 4) | 3;
macInput.set(new Uint8Array(encodedMsg), 33*2 + 1); macInput.set(new Uint8Array(encodedMsg), 33*2 + 1);
@ -37383,16 +37490,17 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
removeOldChains(session); 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; return result;
}); });
}); });
}); });
}); });
});
} }
var preKeyMsg = new axolotlInternal.protobuf.PreKeyWhisperMessage(); 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(); preKeyMsg.registrationId = storage_interface.getMyRegistrationId();
if (session === undefined) { if (session === undefined) {
@ -37403,9 +37511,10 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
preKeyMsg.preKeyId = deviceObject.preKeyId; preKeyMsg.preKeyId = deviceObject.preKeyId;
preKeyMsg.signedPreKeyId = deviceObject.signedKeyId; preKeyMsg.signedPreKeyId = deviceObject.signedKeyId;
preKeyMsg.baseKey = axolotlInternal.utils.convertToArrayBuffer(baseKey.pubKey); preKeyMsg.baseKey = axolotlInternal.utils.convertToArrayBuffer(baseKey.pubKey);
return initSession(true, baseKey, undefined, deviceObject.encodedNumber, return initSession(true, baseKey, undefined,
deviceIdentityKey, axolotlInternal.utils.convertToArrayBuffer(deviceObject.preKey), deviceSignedKey) deviceObject.encodedNumber, deviceIdentityKey,
.then(function(new_session) { axolotlInternal.utils.convertToArrayBuffer(deviceObject.preKey),
deviceSignedKey).then(function(new_session) {
session = new_session; session = new_session;
session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey }; session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey };
return doEncryptPushMessageContent().then(function(message) { return doEncryptPushMessageContent().then(function(message) {
@ -37429,70 +37538,9 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
} else } else
return {type: 1, body: axolotlInternal.utils.convertToString(message)}; 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() { self.createIdentityKeyRecvSocket = function() {
var socketInfo = {}; var socketInfo = {};
var keyPair; var keyPair;
@ -37513,16 +37561,16 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return verifyMAC(ivAndCiphertext, keys[1], mac).then(function() { return verifyMAC(ivAndCiphertext, keys[1], mac).then(function() {
return axolotlInternal.crypto.decrypt(keys[0], ciphertext, iv).then(function(plaintext) { 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; return self;
}; };
@ -37659,10 +37733,11 @@ axolotlInternal.protobuf = function() {
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
;(function() {
var axolotlInternal = axolotlInternal || {};
axolotlInternal.RecipientRecord = function() {
'use strict'; 'use strict';
window.axolotl = window.axolotl || {};
var RecipientRecord = function(identityKey, registrationId) { var RecipientRecord = function(identityKey, registrationId) {
this._sessions = {}; this._sessions = {};
@ -37677,26 +37752,23 @@ RecipientRecord.prototype.serialize = function() {
return axolotlInternal.utils.jsonThing({sessions: this._sessions, registrationId: this.registrationId, identityKey: this.identityKey}); 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); var data = JSON.parse(serialized);
this._sessions = data.sessions; var record = new RecipientRecord(data.identityKey, data.registrationId);
if (this._sessions === undefined || this._sessions === null || typeof this._sessions !== "object" || Array.isArray(this._sessions)) 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"); throw new Error("Error deserializing RecipientRecord");
this.identityKey = data.identityKey; if (record.identityKey === undefined || record.registrationId === undefined)
this.registrationId = data.registrationId;
if (this.identityKey === undefined || this.registrationId === undefined)
throw new Error("Error deserializing RecipientRecord"); throw new Error("Error deserializing RecipientRecord");
return record;
} }
RecipientRecord.prototype.haveOpenSession = function() { RecipientRecord.prototype.haveOpenSession = function() {
return this.registrationId !== null; return this.registrationId !== null;
} }
window.axolotl.sessions = { return RecipientRecord;
RecipientRecord: RecipientRecord, }();
};
})();
})(); })();
'use strict'; 'use strict';

File diff suppressed because one or more lines are too long

@ -25168,6 +25168,7 @@ axolotlInternal.curve25519 = function() {
return { return {
keyPair: function(privKey) { keyPair: function(privKey) {
return new Promise(function(resolve) {
var priv = new Uint8Array(privKey); var priv = new Uint8Array(privKey);
priv[0] &= 248; priv[0] &= 248;
priv[31] &= 127; priv[31] &= 127;
@ -25194,7 +25195,9 @@ axolotlInternal.curve25519 = function() {
Module._free(privateKey_ptr); Module._free(privateKey_ptr);
Module._free(basepoint_ptr); Module._free(basepoint_ptr);
return Promise.resolve({ pubKey: res.buffer, privKey: privKey }); resolve({ pubKey: res.buffer, privKey: privKey });
});
}, },
sharedSecret: function(pubKey, privKey) { sharedSecret: function(pubKey, privKey) {
// Where to store the result // 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(){ ;(function(){
/** /**
* CryptoJS core components. * CryptoJS core components.
@ -36738,7 +36796,57 @@ axolotlInternal.utils = function() {
'use strict'; 'use strict';
window.axolotl = window.axolotl || {}; 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 = {}; var self = {};
/****************************** /******************************
@ -36762,41 +36870,23 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
***************************/ ***************************/
var crypto_storage = {}; var crypto_storage = {};
crypto_storage.putKeyPair = function(keyName, keyPair) { function getRecord(encodedNumber) {
storage_interface.put("25519Key" + keyName, keyPair); return storage_interface.getSession(encodedNumber).then(function(serialized) {
} if (serialized === undefined) {
crypto_storage.getNewStoredKeyPair = function(keyName) {
return axolotlInternal.crypto.createKeyPair().then(function(keyPair) {
crypto_storage.putKeyPair(keyName, keyPair);
return keyPair;
});
}
crypto_storage.getStoredKeyPair = function(keyName) {
var res = storage_interface.get("25519Key" + keyName);
if (res === undefined)
return undefined; return undefined;
return { pubKey: axolotlInternal.utils.convertToArrayBuffer(res.pubKey), privKey: axolotlInternal.utils.convertToArrayBuffer(res.privKey) };
} }
return axolotlInternal.RecipientRecord.deserialize(serialized);
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) { crypto_storage.saveSession = function(encodedNumber, session, registrationId) {
var record = storage_interface.sessions.get(encodedNumber); return getRecord(encodedNumber).then(function(record) {
if (record === undefined) { if (record === undefined) {
if (registrationId === undefined) if (registrationId === undefined)
throw new Error("Tried to save a session for an existing device that didn't exist"); throw new Error("Tried to save a session for an existing device that didn't exist");
else else
record = new axolotl.sessions.RecipientRecord(session.indexInfo.remoteIdentityKey, registrationId); record = new axolotlInternal.RecipientRecord(session.indexInfo.remoteIdentityKey, registrationId);
} }
var sessions = record._sessions; var sessions = record._sessions;
if (record.identityKey === null) if (record.identityKey === null)
@ -36806,7 +36896,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var doDeleteSession = false; var doDeleteSession = false;
if (session.indexInfo.closed != -1) { 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) { if (!doDeleteSession) {
var keysLeft = false; var keysLeft = false;
@ -36838,24 +36928,27 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
else if (record.registrationId === null) else if (record.registrationId === null)
throw new Error("Had open sessions on a record that had no registrationId set"); throw new Error("Had open sessions on a record that had no registrationId set");
var identityKey = storage_interface.identityKeys.get(encodedNumber); return storage_interface.getIdentityKey(encodedNumber).then(function(identityKey) {
if (identityKey === undefined) if (identityKey !== undefined && axolotlInternal.utils.convertToString(identityKey) !== axolotlInternal.utils.convertToString(record.identityKey))
storage_interface.identityKeys.put(encodedNumber, record.identityKey);
else if (axolotlInternal.utils.convertToString(identityKey) !== axolotlInternal.utils.convertToString(record.identityKey))
throw new Error("Tried to change identity key at save time"); 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 getSessions = function(encodedNumber) {
var record = storage_interface.sessions.get(encodedNumber); return getRecord(encodedNumber).then(function(record) {
if (record === undefined) if (record === undefined)
return undefined; return undefined;
return record._sessions; return record._sessions;
} });
};
crypto_storage.getOpenSession = function(encodedNumber) { crypto_storage.getOpenSession = function(encodedNumber) {
var sessions = getSessions(encodedNumber); return getSessions(encodedNumber).then(function(sessions) {
if (sessions === undefined) if (sessions === undefined)
return undefined; return undefined;
@ -36863,10 +36956,11 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
if (sessions[key].indexInfo.closed == -1) if (sessions[key].indexInfo.closed == -1)
return sessions[key]; return sessions[key];
return undefined; return undefined;
} });
};
crypto_storage.getSessionByRemoteEphemeralKey = function(encodedNumber, remoteEphemeralKey) { crypto_storage.getSessionByRemoteEphemeralKey = function(encodedNumber, remoteEphemeralKey) {
var sessions = getSessions(encodedNumber); return getSessions(encodedNumber).then(function(sessions) {
if (sessions === undefined) if (sessions === undefined)
return undefined; return undefined;
@ -36886,15 +36980,17 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return openSession; return openSession;
return undefined; return undefined;
});
} }
crypto_storage.getSessionOrIdentityKeyByBaseKey = function(encodedNumber, baseKey) { crypto_storage.getSessionOrIdentityKeyByBaseKey = function(encodedNumber, baseKey) {
var record = storage_interface.sessions.get(encodedNumber); return getRecord(encodedNumber).then(function(record) {
if (record === undefined) { if (record === undefined) {
var identityKey = storage_interface.identityKeys.get(encodedNumber); return storage_interface.getIdentityKey(encodedNumber).then(function(identityKey) {
if (identityKey === undefined) if (identityKey === undefined)
return undefined; return undefined;
return { indexInfo: { remoteIdentityKey: identityKey } }; return { indexInfo: { remoteIdentityKey: identityKey } };
});
} }
var sessions = record._sessions; var sessions = record._sessions;
@ -36906,6 +37002,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return { indexInfo: { remoteIdentityKey: record.identityKey } }; return { indexInfo: { remoteIdentityKey: record.identityKey } };
throw new Error("Datastore inconsistency: device was stored without identity key"); 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 initSession = function(isInitiator, ourEphemeralKey, ourSignedKey, encodedNumber, theirIdentityPubKey, theirEphemeralPubKey, theirSignedPubKey) {
var ourIdentityKey = crypto_storage.getIdentityKey(); var ourIdentityKey = storage_interface.getMyIdentityKey();
if (isInitiator) { if (isInitiator) {
if (ourSignedKey !== undefined) if (ourSignedKey !== undefined)
@ -37034,7 +37131,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var ratchet = axolotlInternal.utils.convertToString(entry.ephemeralKey); var ratchet = axolotlInternal.utils.convertToString(entry.ephemeralKey);
console.log("Checking old chain with added time " + (entry.added/1000)); 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)) 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]; delete session[ratchet];
console.log("...deleted"); console.log("...deleted");
} else } else
@ -37057,7 +37154,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
for (var i in session) { for (var i in session) {
if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) { if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) {
if (!sessionClosedByRemote) if (!sessionClosedByRemote)
session.oldRatchetList[session.oldRatchetList.length] = { added: new Date().getTime(), ephemeralKey: i }; session.oldRatchetList[session.oldRatchetList.length] = { added: Date.now(), ephemeralKey: i };
else else
delete session[i].chainKey.key; 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 current root key and our ephemeral key pair to disallow ratchet stepping
delete session.currentRatchet['rootKey']; delete session.currentRatchet['rootKey'];
delete session.currentRatchet['ephemeralKeyPair']; delete session.currentRatchet['ephemeralKeyPair'];
session.indexInfo.closed = new Date().getTime(); session.indexInfo.closed = Date.now();
removeOldChains(session); removeOldChains(session);
} }
self.closeOpenSessionForDevice = function(encodedNumber) { self.closeOpenSessionForDevice = function(encodedNumber) {
var session = crypto_storage.getOpenSession(encodedNumber); return crypto_storage.getOpenSession(encodedNumber).then(function(session) {
if (session === undefined) if (session === undefined)
return; return;
closeSession(session); closeSession(session);
crypto_storage.saveSession(encodedNumber, session); return crypto_storage.saveSession(encodedNumber, session);
});
} }
var refreshPreKeys;
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) { var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
var preKeyPair = crypto_storage.getStoredKeyPair("preKey" + message.preKeyId); return storage_interface.getPreKey(message.preKeyId).then(function(preKeyPair) {
var signedPreKeyPair = crypto_storage.getStoredKeyPair("signedKey" + message.signedPreKeyId); return storage_interface.getSignedPreKey(message.signedPreKeyId).then(function(signedPreKeyPair) {
return crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, axolotlInternal.utils.convertToArrayBuffer(message.baseKey)).then(function(session) {
//TODO: Call refreshPreKeys when it looks like all our prekeys are used up? return crypto_storage.getOpenSession(encodedNumber).then(function(open_session) {
var session = crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, axolotlInternal.utils.convertToArrayBuffer(message.baseKey));
var open_session = crypto_storage.getOpenSession(encodedNumber);
if (signedPreKeyPair === undefined) { if (signedPreKeyPair === undefined) {
// Session may or may not be the right one, but if its not, we can't do anything about it // 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 // ...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 // 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 // ... to ensure that the sender actually holds the private keys for all reported pubkeys
return [new_session, function() { return [new_session, function() {
return storage_interface.removePreKey(message.preKeyId).then(function() {
if (open_session !== undefined) if (open_session !== undefined)
crypto_storage.saveSession(encodedNumber, open_session); return crypto_storage.saveSession(encodedNumber, open_session);
crypto_storage.removeStoredKeyPair("preKey" + message.preKeyId); });
}]; }];
});; });
});
});
});
});
} }
var fillMessageKeys = function(chain, counter) { var fillMessageKeys = function(chain, counter) {
@ -37177,7 +37276,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
if (!objectContainsKeys(previousRatchet.messageKeys)) if (!objectContainsKeys(previousRatchet.messageKeys))
delete session[axolotlInternal.utils.convertToString(ratchet.lastRemoteEphemeralKey)]; delete session[axolotlInternal.utils.convertToString(ratchet.lastRemoteEphemeralKey)];
else 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); }).then(finish);
} else } else
return finish(); return finish();
@ -37193,12 +37292,18 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto, 'binary'); var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto, 'binary');
var remoteEphemeralKey = axolotlInternal.utils.convertToArrayBuffer(message.ephemeralKey); var remoteEphemeralKey = axolotlInternal.utils.convertToArrayBuffer(message.ephemeralKey);
var promise;
if (session === undefined) { if (session === undefined) {
var session = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey); promise = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey).then(function(session) {
if (session === undefined) if (session === undefined)
throw new Error("No session found to decrypt message from " + encodedNumber); 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() { return maybeStepRatchet(session, remoteEphemeralKey, message.previousCounter).then(function() {
var chain = session[axolotlInternal.utils.convertToString(message.ephemeralKey)]; 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 messageProtoArray = axolotlInternal.utils.convertToArrayBuffer(messageProto);
var macInput = new Uint8Array(messageProtoArray.byteLength + 33*2 + 1); 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(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[33*2] = (3 << 4) | 3;
macInput.set(new Uint8Array(messageProtoArray), 33*2 + 1); macInput.set(new Uint8Array(messageProtoArray), 33*2 + 1);
@ -37231,17 +37336,19 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
delete session['pendingPreKey']; delete session['pendingPreKey'];
removeOldChains(session); removeOldChains(session);
crypto_storage.saveSession(encodedNumber, session, registrationId); return crypto_storage.saveSession(encodedNumber, session, registrationId).then(function() {
return [plaintext, function() { return [plaintext, function() {
closeSession(session, true); closeSession(session, true);
removeOldChains(session); 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 initSessionFromPreKeyWhisperMessage(from, preKeyProto).then(function(sessions) {
return doDecryptWhisperMessage(from, axolotlInternal.utils.convertToString(preKeyProto.message), sessions[0], preKeyProto.registrationId).then(function(result) { return doDecryptWhisperMessage(from, axolotlInternal.utils.convertToString(preKeyProto.message), sessions[0], preKeyProto.registrationId).then(function(result) {
if (sessions[1] !== undefined) if (sessions[1] !== undefined)
sessions[1](); return sessions[1]().then(function() { return result; });
return result; return result;
}); });
}); });
@ -37268,7 +37375,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
// return Promise(encoded [PreKey]WhisperMessage) // return Promise(encoded [PreKey]WhisperMessage)
self.encryptMessageFor = function(deviceObject, pushMessageContent) { 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 hadSession = session !== undefined;
var doEncryptPushMessageContent = function() { var doEncryptPushMessageContent = function() {
@ -37293,7 +37400,7 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
var encodedMsg = axolotlInternal.utils.convertToArrayBuffer(msg.encode()); var encodedMsg = axolotlInternal.utils.convertToArrayBuffer(msg.encode());
var macInput = new Uint8Array(encodedMsg.byteLength + 33*2 + 1); 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.set(new Uint8Array(axolotlInternal.utils.convertToArrayBuffer(session.indexInfo.remoteIdentityKey)), 33);
macInput[33*2] = (3 << 4) | 3; macInput[33*2] = (3 << 4) | 3;
macInput.set(new Uint8Array(encodedMsg), 33*2 + 1); macInput.set(new Uint8Array(encodedMsg), 33*2 + 1);
@ -37306,16 +37413,17 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
removeOldChains(session); 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; return result;
}); });
}); });
}); });
}); });
});
} }
var preKeyMsg = new axolotlInternal.protobuf.PreKeyWhisperMessage(); 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(); preKeyMsg.registrationId = storage_interface.getMyRegistrationId();
if (session === undefined) { if (session === undefined) {
@ -37326,9 +37434,10 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
preKeyMsg.preKeyId = deviceObject.preKeyId; preKeyMsg.preKeyId = deviceObject.preKeyId;
preKeyMsg.signedPreKeyId = deviceObject.signedKeyId; preKeyMsg.signedPreKeyId = deviceObject.signedKeyId;
preKeyMsg.baseKey = axolotlInternal.utils.convertToArrayBuffer(baseKey.pubKey); preKeyMsg.baseKey = axolotlInternal.utils.convertToArrayBuffer(baseKey.pubKey);
return initSession(true, baseKey, undefined, deviceObject.encodedNumber, return initSession(true, baseKey, undefined,
deviceIdentityKey, axolotlInternal.utils.convertToArrayBuffer(deviceObject.preKey), deviceSignedKey) deviceObject.encodedNumber, deviceIdentityKey,
.then(function(new_session) { axolotlInternal.utils.convertToArrayBuffer(deviceObject.preKey),
deviceSignedKey).then(function(new_session) {
session = new_session; session = new_session;
session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey }; session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey };
return doEncryptPushMessageContent().then(function(message) { return doEncryptPushMessageContent().then(function(message) {
@ -37352,70 +37461,9 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
} else } else
return {type: 1, body: axolotlInternal.utils.convertToString(message)}; 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() { self.createIdentityKeyRecvSocket = function() {
var socketInfo = {}; var socketInfo = {};
var keyPair; var keyPair;
@ -37436,16 +37484,16 @@ window.axolotl.protocol = function(storage_interface, updateKeysCallback) {
return verifyMAC(ivAndCiphertext, keys[1], mac).then(function() { return verifyMAC(ivAndCiphertext, keys[1], mac).then(function() {
return axolotlInternal.crypto.decrypt(keys[0], ciphertext, iv).then(function(plaintext) { 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; return self;
}; };
@ -37582,10 +37656,11 @@ axolotlInternal.protobuf = function() {
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
;(function() {
var axolotlInternal = axolotlInternal || {};
axolotlInternal.RecipientRecord = function() {
'use strict'; 'use strict';
window.axolotl = window.axolotl || {};
var RecipientRecord = function(identityKey, registrationId) { var RecipientRecord = function(identityKey, registrationId) {
this._sessions = {}; this._sessions = {};
@ -37600,25 +37675,22 @@ RecipientRecord.prototype.serialize = function() {
return axolotlInternal.utils.jsonThing({sessions: this._sessions, registrationId: this.registrationId, identityKey: this.identityKey}); 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); var data = JSON.parse(serialized);
this._sessions = data.sessions; var record = new RecipientRecord(data.identityKey, data.registrationId);
if (this._sessions === undefined || this._sessions === null || typeof this._sessions !== "object" || Array.isArray(this._sessions)) 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"); throw new Error("Error deserializing RecipientRecord");
this.identityKey = data.identityKey; if (record.identityKey === undefined || record.registrationId === undefined)
this.registrationId = data.registrationId;
if (this.identityKey === undefined || this.registrationId === undefined)
throw new Error("Error deserializing RecipientRecord"); throw new Error("Error deserializing RecipientRecord");
return record;
} }
RecipientRecord.prototype.haveOpenSession = function() { RecipientRecord.prototype.haveOpenSession = function() {
return this.registrationId !== null; return this.registrationId !== null;
} }
window.axolotl.sessions = { return RecipientRecord;
RecipientRecord: RecipientRecord, }();
};
})();
})(); })();
Loading…
Cancel
Save