123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- /*
- * vim: ts=4:sw=4:expandtab
- */
- ;(function() {
- 'use strict';
- var StaticByteBufferProto = new dcodeIO.ByteBuffer().__proto__;
- var StaticArrayBufferProto = new ArrayBuffer().__proto__;
- var StaticUint8ArrayProto = new Uint8Array().__proto__;
- function isStringable(thing) {
- return (thing === Object(thing) &&
- (thing.__proto__ == StaticArrayBufferProto ||
- thing.__proto__ == StaticUint8ArrayProto ||
- thing.__proto__ == StaticByteBufferProto));
- }
- function convertToArrayBuffer(thing) {
- if (thing === undefined) {
- return undefined;
- }
- if (thing === Object(thing)) {
- if (thing.__proto__ == StaticArrayBufferProto) {
- return thing;
- }
- //TODO: Several more cases here...
- }
- if (thing instanceof Array) {
- // Assuming Uint16Array from curve25519
- var res = new ArrayBuffer(thing.length * 2);
- var uint = new Uint16Array(res);
- for (var i = 0; i < thing.length; i++) {
- uint[i] = thing[i];
- }
- return res;
- }
- var str;
- if (isStringable(thing)) {
- str = stringObject(thing);
- } else if (typeof thing == "string") {
- str = thing;
- } else {
- throw new Error("Tried to convert a non-stringable thing of type " + typeof thing + " to an array buffer");
- }
- var res = new ArrayBuffer(str.length);
- var uint = new Uint8Array(res);
- for (var i = 0; i < str.length; i++) {
- uint[i] = str.charCodeAt(i);
- }
- return res;
- }
- function equalArrayBuffers(ab1, ab2) {
- if (!(ab1 instanceof ArrayBuffer && ab2 instanceof ArrayBuffer)) {
- return false;
- }
- if (ab1.byteLength !== ab2.byteLength) {
- return false;
- }
- var result = true;
- var ta1 = new Uint8Array(ab1);
- var ta2 = new Uint8Array(ab2);
- for (var i = 0; i < ab1.byteLength; ++i) {
- if (ta1[i] !== ta2[i]) { result = false; }
- }
- return result;
- }
- var Model = Backbone.Model.extend({ database: Whisper.Database });
- var PreKey = Model.extend({ storeName: 'preKeys' });
- var SignedPreKey = Model.extend({ storeName: 'signedPreKeys' });
- var Session = Model.extend({ storeName: 'sessions' });
- var SessionCollection = Backbone.Collection.extend({
- storeName: 'sessions',
- database: Whisper.Database,
- model: Session,
- fetchSessionsForNumber: function(number) {
- return this.fetch({range: [number + '.1', number + '.' + ':']});
- }
- });
- var IdentityKey = Model.extend({ storeName: 'identityKeys' });
- var Group = Model.extend({ storeName: 'groups' });
- var Item = Model.extend({ storeName: 'items' });
- function SignalProtocolStore() {}
- SignalProtocolStore.prototype = {
- constructor: SignalProtocolStore,
- getIdentityKeyPair: function() {
- var item = new Item({id: 'identityKey'});
- return new Promise(function(resolve) {
- item.fetch().then(function() {
- resolve(item.get('value'));
- });
- });
- },
- getLocalRegistrationId: function() {
- var item = new Item({id: 'registrationId'});
- return new Promise(function(resolve) {
- item.fetch().then(function() {
- resolve(item.get('value'));
- });
- });
- },
- /* Returns a prekeypair object or undefined */
- loadPreKey: function(keyId) {
- var prekey = new PreKey({id: keyId});
- return new Promise(function(resolve) {
- prekey.fetch().then(function() {
- resolve({
- pubKey: prekey.attributes.publicKey,
- privKey: prekey.attributes.privateKey
- });
- }).fail(resolve);
- });
- },
- storePreKey: function(keyId, keyPair) {
- var prekey = new PreKey({
- id : keyId,
- publicKey : keyPair.pubKey,
- privateKey : keyPair.privKey
- });
- return new Promise(function(resolve) {
- prekey.save().always(function() {
- resolve();
- });
- });
- },
- removePreKey: function(keyId) {
- var prekey = new PreKey({id: keyId});
- new Promise(function(resolve) {
- getAccountManager().refreshPreKeys().then(resolve);
- });
- return new Promise(function(resolve) {
- prekey.destroy().then(function() {
- resolve();
- });
- });
- },
- /* Returns a signed keypair object or undefined */
- loadSignedPreKey: function(keyId) {
- var prekey = new SignedPreKey({id: keyId});
- return new Promise(function(resolve) {
- prekey.fetch().then(function() {
- resolve({
- pubKey: prekey.attributes.publicKey,
- privKey: prekey.attributes.privateKey
- });
- }).fail(resolve);
- });
- },
- storeSignedPreKey: function(keyId, keyPair) {
- var prekey = new SignedPreKey({
- id : keyId,
- publicKey : keyPair.pubKey,
- privateKey : keyPair.privKey
- });
- return new Promise(function(resolve) {
- prekey.save().always(function() {
- resolve();
- });
- });
- },
- removeSignedPreKey: function(keyId) {
- var prekey = new SignedPreKey({id: keyId});
- return new Promise(function(resolve) {
- prekey.destroy().then(function() {
- resolve();
- });
- });
- },
- loadSession: function(encodedNumber) {
- if (encodedNumber === null || encodedNumber === undefined) {
- throw new Error("Tried to get session for undefined/null number");
- }
- return new Promise(function(resolve) {
- var session = new Session({id: encodedNumber});
- session.fetch().always(function() {
- resolve(session.get('record'));
- });
- });
- },
- storeSession: function(encodedNumber, record) {
- if (encodedNumber === null || encodedNumber === undefined) {
- throw new Error("Tried to put session for undefined/null number");
- }
- return new Promise(function(resolve) {
- var number = textsecure.utils.unencodeNumber(encodedNumber)[0];
- var deviceId = parseInt(textsecure.utils.unencodeNumber(encodedNumber)[1]);
- var session = new Session({id: encodedNumber});
- session.fetch().always(function() {
- session.save({
- record: record,
- deviceId: deviceId,
- number: number
- }).fail(function(e) {
- console.log('Failed to save session', encodedNumber, e);
- }).always(function() {
- resolve();
- });
- });
- });
- },
- getDeviceIds: function(number) {
- if (number === null || number === undefined) {
- throw new Error("Tried to get device ids for undefined/null number");
- }
- return new Promise(function(resolve) {
- var sessions = new SessionCollection();
- sessions.fetchSessionsForNumber(number).always(function() {
- resolve(sessions.pluck('deviceId'));
- });
- });
- },
- removeSession: function(encodedNumber) {
- return new Promise(function(resolve) {
- var session = new Session({id: encodedNumber});
- session.fetch().then(function() {
- session.destroy().then(resolve);
- });
- });
- },
- removeAllSessions: function(number) {
- if (number === null || number === undefined) {
- throw new Error("Tried to remove sessions for undefined/null number");
- }
- return new Promise(function(resolve) {
- var sessions = new SessionCollection();
- sessions.fetchSessionsForNumber(number).always(function() {
- var promises = [];
- while (sessions.length > 0) {
- promises.push(new Promise(function(res) {
- sessions.pop().destroy().then(res);
- }));
- }
- Promise.all(promises).then(resolve);
- });
- });
- },
- clearSessionStore: function() {
- return new Promise(function(resolve) {
- var sessions = new SessionCollection();
- sessions.sync('delete', sessions, {}).always(resolve);
- });
- },
- isTrustedIdentity: function(identifier, publicKey) {
- if (identifier === null || identifier === undefined) {
- throw new Error("Tried to get identity key for undefined/null key");
- }
- var number = textsecure.utils.unencodeNumber(identifier)[0];
- return new Promise(function(resolve) {
- var identityKey = new IdentityKey({id: number});
- identityKey.fetch().always(function() {
- var oldpublicKey = identityKey.get('publicKey');
- if (!oldpublicKey || equalArrayBuffers(oldpublicKey, publicKey)) {
- resolve(true);
- } else if (!storage.get('safety-numbers-approval', true)) {
- this.removeIdentityKey(identifier).then(function() {
- this.saveIdentity(identifier, publicKey).then(function() {
- console.log('Key changed for', identifier);
- this.trigger('keychange:' + identifier);
- resolve(true);
- }.bind(this));
- }.bind(this));
- } else {
- resolve(false);
- }
- }.bind(this));
- }.bind(this));
- },
- loadIdentityKey: function(identifier) {
- if (identifier === null || identifier === undefined) {
- throw new Error("Tried to get identity key for undefined/null key");
- }
- var number = textsecure.utils.unencodeNumber(identifier)[0];
- return new Promise(function(resolve) {
- var identityKey = new IdentityKey({id: number});
- identityKey.fetch().always(function() {
- resolve(identityKey.get('publicKey'));
- });
- });
- },
- saveIdentity: function(identifier, publicKey) {
- if (identifier === null || identifier === undefined) {
- throw new Error("Tried to put identity key for undefined/null key");
- }
- if (!(publicKey instanceof ArrayBuffer)) {
- publicKey = convertToArrayBuffer(publicKey);
- }
- var number = textsecure.utils.unencodeNumber(identifier)[0];
- return new Promise(function(resolve, reject) {
- var identityKey = new IdentityKey({id: number});
- identityKey.fetch().always(function() {
- var oldpublicKey = identityKey.get('publicKey');
- if (!oldpublicKey) {
- // Lookup failed, or the current key was removed, so save this one.
- identityKey.save({publicKey: publicKey}).then(resolve);
- } else {
- // Key exists, if it matches do nothing, else throw
- if (equalArrayBuffers(oldpublicKey, publicKey)) {
- resolve();
- } else {
- reject(new Error("Attempted to overwrite a different identity key"));
- }
- }
- });
- });
- },
- removeIdentityKey: function(number) {
- return new Promise(function(resolve, reject) {
- var identityKey = new IdentityKey({id: number});
- identityKey.fetch().then(function() {
- identityKey.save({publicKey: undefined});
- }).fail(function() {
- reject(new Error("Tried to remove identity for unknown number"));
- });
- resolve(textsecure.storage.protocol.removeAllSessions(number));
- });
- },
- getGroup: function(groupId) {
- if (groupId === null || groupId === undefined) {
- throw new Error("Tried to get group for undefined/null id");
- }
- return new Promise(function(resolve) {
- var group = new Group({id: groupId});
- group.fetch().always(function() {
- resolve(group.get('data'));
- });
- });
- },
- putGroup: function(groupId, group) {
- if (groupId === null || groupId === undefined) {
- throw new Error("Tried to put group key for undefined/null id");
- }
- if (group === null || group === undefined) {
- throw new Error("Tried to put undefined/null group object");
- }
- var group = new Group({id: groupId, data: group});
- return new Promise(function(resolve) {
- group.save().always(resolve);
- });
- },
- removeGroup: function(groupId) {
- if (groupId === null || groupId === undefined) {
- throw new Error("Tried to remove group key for undefined/null id");
- }
- return new Promise(function(resolve) {
- var group = new Group({id: groupId});
- group.destroy().always(resolve);
- });
- },
- };
- _.extend(SignalProtocolStore.prototype, Backbone.Events);
- window.SignalProtocolStore = SignalProtocolStore;
- })();
|