251 lines
9.3 KiB
JavaScript
251 lines
9.3 KiB
JavaScript
function ChatOmemoStorage() {
|
|
this.jid = USER_JID;
|
|
}
|
|
|
|
ChatOmemoStorage.prototype = {
|
|
Direction: {
|
|
SENDING: 1,
|
|
RECEIVING: 2,
|
|
},
|
|
|
|
// Storage is versionned to ensure a proper reset when changes are made
|
|
storageVersion: '2',
|
|
|
|
// Generic methods
|
|
put: function (key, value) {
|
|
if (key === undefined || value === undefined || key === null || value === null)
|
|
throw new Error("Tried to store undefined/null");
|
|
|
|
return localStorage.setObject(this.storageVersion + '.' + key, value);
|
|
},
|
|
get: function (key, defaultValue) {
|
|
if (key === null || key === undefined)
|
|
throw new Error("Tried to get value for undefined/null key");
|
|
if (this.storageVersion + '.' + key in localStorage) {
|
|
return localStorage.getObject(this.storageVersion + '.' + key);
|
|
} else {
|
|
return defaultValue;
|
|
}
|
|
},
|
|
remove: function (key) {
|
|
if (key === null || key === undefined)
|
|
throw new Error("Tried to remove value for undefined/null key");
|
|
|
|
localStorage.removeItem(this.storageVersion + '.' + key);
|
|
},
|
|
filter: function (search) {
|
|
return Object.keys(localStorage).filter(key => key.startsWith(this.storageVersion + '.' + this.jid + search));
|
|
},
|
|
|
|
// OMEMO specific methods
|
|
setIdentityKeyPair: function(identityKeyPair) {
|
|
return Promise.resolve(this.put(this.jid + '.identityKey', {
|
|
'privKey': MovimUtils.arrayBufferToBase64(identityKeyPair.privKey),
|
|
'pubKey': MovimUtils.arrayBufferToBase64(identityKeyPair.pubKey)
|
|
}));
|
|
},
|
|
getIdentityKeyPair: function () {
|
|
identityKeyPair = this.get(this.jid + '.identityKey');
|
|
|
|
if (!identityKeyPair) return Promise.reject();
|
|
|
|
return Promise.resolve({
|
|
'privKey': MovimUtils.base64ToArrayBuffer(identityKeyPair.privKey),
|
|
'pubKey': MovimUtils.base64ToArrayBuffer(identityKeyPair.pubKey)
|
|
});
|
|
},
|
|
|
|
setLocalRegistrationId: function (registrationId) {
|
|
return Promise.resolve(this.put(this.jid + '.registrationId', registrationId));
|
|
},
|
|
getLocalRegistrationId: function () {
|
|
return Promise.resolve(this.get(this.jid + '.registrationId'));
|
|
},
|
|
|
|
isTrustedIdentity: function (identifier, identityKey, direction) {
|
|
if (identifier === null || identifier === undefined) {
|
|
throw new Error("tried to check identity key for undefined/null key");
|
|
}
|
|
if (!(identityKey instanceof ArrayBuffer)) {
|
|
throw new Error("Expected identityKey to be an ArrayBuffer");
|
|
}
|
|
var trusted = this.get(this.jid + '.identityKey' + identifier);
|
|
if (trusted === undefined) {
|
|
return Promise.resolve(true);
|
|
}
|
|
|
|
// movim-rs: identities are stored per JID (not per device) so a contact with several
|
|
// devices would always be rejected. Blind Trust Before Verification, like Conversations:
|
|
// the fingerprints can still be checked and disabled in the contact drawer.
|
|
return Promise.resolve(true);
|
|
},
|
|
loadIdentityKey: function (identifier) {
|
|
if (identifier === null || identifier === undefined)
|
|
throw new Error("Tried to get identity key for undefined/null key");
|
|
return Promise.resolve(MovimUtils.base64ToArrayBuffer(this.get(this.jid + '.identityKey' + identifier)));
|
|
},
|
|
saveIdentity: function (identifier, identityKey) {
|
|
if (identifier === null || identifier === undefined)
|
|
throw new Error("Tried to put identity key for undefined/null key");
|
|
|
|
var address = new libsignal.SignalProtocolAddress.fromString(identifier);
|
|
|
|
var existing = this.get(this.jid + '.identityKey' + address.getName());
|
|
this.put(this.jid + '.identityKey' + address.getName(), MovimUtils.arrayBufferToBase64(identityKey))
|
|
|
|
if (existing && toString(identityKey) !== toString(existing)) {
|
|
return Promise.resolve(true);
|
|
} else {
|
|
return Promise.resolve(false);
|
|
}
|
|
|
|
},
|
|
|
|
loadPreKey: function (keyId) {
|
|
var res = this.get(this.jid + '.25519KeypreKey' + keyId);
|
|
if (res !== undefined) {
|
|
res = {
|
|
pubKey: MovimUtils.base64ToArrayBuffer(res.pubKey),
|
|
privKey: MovimUtils.base64ToArrayBuffer(res.privKey)
|
|
};
|
|
}
|
|
return Promise.resolve(res);
|
|
},
|
|
storePreKey: function (keyId, keyPair) {
|
|
keyPair.pubKey = MovimUtils.arrayBufferToBase64(keyPair.pubKey);
|
|
keyPair.privKey = MovimUtils.arrayBufferToBase64(keyPair.privKey);
|
|
return Promise.resolve(this.put(this.jid + '.25519KeypreKey' + keyId, keyPair));
|
|
},
|
|
removePreKey: function (keyId) {
|
|
return Promise.resolve(this.remove(this.jid + '.25519KeypreKey' + keyId));
|
|
},
|
|
|
|
loadCompleteSignedPreKey: function (keyId) {
|
|
var res = this.get(this.jid + '.25519KeysignedKey' + keyId);
|
|
if (res !== undefined) {
|
|
res = {
|
|
keyPair: {
|
|
pubKey: MovimUtils.base64ToArrayBuffer(res.keyPair.pubKey),
|
|
privKey: MovimUtils.base64ToArrayBuffer(res.keyPair.privKey),
|
|
},
|
|
signature: MovimUtils.base64ToArrayBuffer(res.signature),
|
|
keyId: keyId
|
|
};
|
|
}
|
|
return res;
|
|
},
|
|
loadSignedPreKey: function (keyId) {
|
|
return Promise.resolve(this.loadCompleteSignedPreKey(keyId).keyPair);
|
|
},
|
|
storeSignedPreKey: function (keyId, key) {
|
|
key.keyPair.pubKey = MovimUtils.arrayBufferToBase64(key.keyPair.pubKey);
|
|
key.keyPair.privKey = MovimUtils.arrayBufferToBase64(key.keyPair.privKey);
|
|
key.signature = MovimUtils.arrayBufferToBase64(key.signature);
|
|
return Promise.resolve(this.put(this.jid + '.25519KeysignedKey' + keyId, key));
|
|
},
|
|
removeSignedPreKey: function (keyId) {
|
|
return Promise.resolve(this.remove(this.jid + '.25519KeysignedKey' + keyId));
|
|
},
|
|
|
|
loadSession: function (identifier) {
|
|
return Promise.resolve(this.get(this.jid + '.session' + identifier));
|
|
},
|
|
storeSession: function (identifier, record) {
|
|
return Promise.resolve(this.put(this.jid + '.session' + identifier, record));
|
|
},
|
|
removeSession: function (identifier) {
|
|
return Promise.resolve(this.remove(this.jid + '.session' + identifier));
|
|
},
|
|
removeAllSessions: function () {
|
|
let filtered = this.filter('.session');
|
|
|
|
for (key in filtered) {
|
|
localStorage.removeItem(filtered[key]);
|
|
}
|
|
|
|
return Promise.resolve();
|
|
},
|
|
getOwnSessions: function () {
|
|
return this.filter('.session' + this.jid);
|
|
},
|
|
getSessions: function (identifier) {
|
|
return this.filter('.session' + identifier);
|
|
},
|
|
getOwnSessionsIds: function () {
|
|
return this.filter('.session' + this.jid).map(sessionId => sessionId.split('.').pop());
|
|
},
|
|
getSessionsIds: function (identifier) {
|
|
return this.filter('.session' + identifier).map(sessionId => sessionId.split('.').pop());
|
|
},
|
|
|
|
isJidResolved: function (identifier) {
|
|
return this.hasContactTombstone(identifier) || this.filter('.session' + identifier).length > 0;
|
|
},
|
|
|
|
removeAllSessionsOfJid: function (identifier) {
|
|
let filtered = this.filter('.session' + identifier);
|
|
|
|
for (id in filtered) {
|
|
localStorage.removeItem(filtered[id]);
|
|
}
|
|
|
|
this.remove(this.jid + '.contact' + identifier);
|
|
|
|
return Promise.resolve();
|
|
},
|
|
|
|
setContactState: function (jid, state) {
|
|
return Promise.resolve(this.put(this.jid + '.contact' + jid, state));
|
|
},
|
|
removeContactState: function (jid) {
|
|
return Promise.resolve(this.remove(this.jid + '.contact' + jid));
|
|
},
|
|
hasContactState: function(jid) {
|
|
return (this.get(this.jid + '.contact' + jid) !== undefined);
|
|
},
|
|
getContactState: function (jid) {
|
|
let state = Boolean(this.get(this.jid + '.contact' + jid));
|
|
return Promise.resolve(state);
|
|
},
|
|
|
|
/**
|
|
* Tombstone to know if a contact doesn't have OMEMO at the moment
|
|
*/
|
|
setContactTombstone: function (jid) {
|
|
return Promise.resolve(this.put(this.jid + '.contact.tombstone' + jid, true));
|
|
},
|
|
removeContactTombstone: function (jid) {
|
|
return Promise.resolve(this.remove(this.jid + '.contact.tombstone' + jid));
|
|
},
|
|
hasContactTombstone: function(jid) {
|
|
return (this.get(this.jid + '.contact.tombstone' + jid) == true);
|
|
},
|
|
|
|
getSessionState: function (identifier) {
|
|
return this.loadSession(identifier).then(json => {
|
|
if (json !== undefined) {
|
|
let session = JSON.parse(json);
|
|
return (session.state === true || session.state === undefined);
|
|
}
|
|
|
|
return false;
|
|
});
|
|
},
|
|
setSessionState: function (identifier, enabled) {
|
|
this.loadSession(identifier).then(json => {
|
|
if (json !== undefined) {
|
|
let session = JSON.parse(json);
|
|
session.state = Boolean(enabled);
|
|
this.storeSession(identifier, JSON.stringify(session));
|
|
}
|
|
});
|
|
},
|
|
|
|
toString: function(thing) {
|
|
if (typeof thing == 'string') {
|
|
return thing;
|
|
}
|
|
return new dcodeIO.ByteBuffer.wrap(thing).toString('binary');
|
|
}
|
|
};
|