| author | Eric Roberts
<eric@devl.me> 2026-08-26 18:14:53 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-08-26 18:16:17 UTC |
| parent | 6ecfa5a74becff99649fa24e371c68b187ad334f |
| borogove/OMEMO.hx | +5 | -4 |
| borogove/Persistence.hx | +2 | -2 |
| borogove/SignalProtocol.hx | +1 | -1 |
| borogove/persistence/Dummy.hx | +5 | -3 |
| borogove/persistence/IDB.js | +5 | -6 |
| borogove/persistence/Sqlite.hx | +27 | -3 |
| test/TestSqlite.hx | +34 | -0 |
| test/persistence-tests.ts | +42 | -0 |
diff --git a/borogove/OMEMO.hx b/borogove/OMEMO.hx index 4fe407a..d372fd8 100644 --- a/borogove/OMEMO.hx +++ b/borogove/OMEMO.hx @@ -119,15 +119,16 @@ class OMEMOStore extends SignalProtocolStore { } // Load the identity key of a contact (partners with saveIdentity()) - public function loadIdentityKey(identifier:SignalProtocolAddress):Promise<IdentityPublicKey> { + public function loadIdentityKey(identifier:SignalProtocolAddress):Promise<Null<IdentityPublicKey>> { return persistence.getOmemoContactIdentityKey(accountId, identifier.toString()); } public function saveIdentity(identifier:SignalProtocolAddress, identityKey:IdentityPublicKey):Promise<Bool> { return persistence.getOmemoContactIdentityKey(accountId, identifier.toString()).then((prevKey) -> { - persistence.storeOmemoContactIdentityKey(accountId, identifier.toString(), identityKey); - // Return true if the key was updated, false if it matches what we already had stored - return prevKey != identityKey; + return persistence.storeOmemoContactIdentityKey(accountId, identifier.toString(), identityKey).then(_ -> { + // Return true if the key was updated, false if it matches what we already had stored + return prevKey != identityKey; + }); }); } diff --git a/borogove/Persistence.hx b/borogove/Persistence.hx index 4f6058c..94ca911 100644 --- a/borogove/Persistence.hx +++ b/borogove/Persistence.hx @@ -400,12 +400,12 @@ interface Persistence { /** Store a trusted identity key for a remote OMEMO contact **/ - public function storeOmemoContactIdentityKey(account:String, address:String, identityKey:IdentityPublicKey):Void; + public function storeOmemoContactIdentityKey(account:String, address:String, identityKey:IdentityPublicKey):Promise<IdentityPublicKey>; /** Load a stored identity key for a remote OMEMO contact **/ - public function getOmemoContactIdentityKey(account:String, address:String): Promise<IdentityPublicKey>; + public function getOmemoContactIdentityKey(account:String, address:String): Promise<Null<IdentityPublicKey>>; /** Load a stored OMEMO session for a remote device diff --git a/borogove/SignalProtocol.hx b/borogove/SignalProtocol.hx index a25da43..112bbda 100644 --- a/borogove/SignalProtocol.hx +++ b/borogove/SignalProtocol.hx @@ -129,7 +129,7 @@ abstract class SignalProtocolStore { // Return a boolean indicating whether we trust this identity abstract public function isTrustedIdentity(identifier: String, identityKey: IdentityPublicKey, _direction: Int):Promise<Bool>; - abstract public function loadIdentityKey(identifier: SignalProtocolAddress):Promise<IdentityPublicKey>; + abstract public function loadIdentityKey(identifier: SignalProtocolAddress):Promise<Null<IdentityPublicKey>>; abstract public function saveIdentity(identifier: SignalProtocolAddress, identityKey:IdentityPublicKey):Promise<Bool>; diff --git a/borogove/persistence/Dummy.hx b/borogove/persistence/Dummy.hx index edbfcd2..8f04b33 100644 --- a/borogove/persistence/Dummy.hx +++ b/borogove/persistence/Dummy.hx @@ -244,11 +244,13 @@ class Dummy implements Persistence { } @HaxeCBridge.noemit - public function storeOmemoContactIdentityKey(account:String, address:String, identityKey:IdentityPublicKey):Void { } + public function storeOmemoContactIdentityKey(account:String, address:String, identityKey:IdentityPublicKey):Promise<IdentityPublicKey> { + return Promise.resolve(identityKey); + } @HaxeCBridge.noemit - public function getOmemoContactIdentityKey(account:String, address:String): Promise<IdentityPublicKey> { - return Promise.reject("Not found"); + public function getOmemoContactIdentityKey(account:String, address:String): Promise<Null<IdentityPublicKey>> { + return Promise.resolve(null); } @HaxeCBridge.noemit diff --git a/borogove/persistence/IDB.js b/borogove/persistence/IDB.js index 709608d..98f668e 100644 --- a/borogove/persistence/IDB.js +++ b/borogove/persistence/IDB.js @@ -1436,23 +1436,22 @@ tx.onerror = console.error; const tx = db.transaction(["omemo_identities"], "readonly"); const store = tx.objectStore("omemo_identities"); const result = await promisifyRequest(store.get([account, address])); - if(!result) { + if (!result) { return null; } else { return base64ToArrayBuffer(result.pubKey); } }, - storeOmemoContactIdentityKey(account, address, identityKey) { + async storeOmemoContactIdentityKey(account, address, identityKey) { const tx = db.transaction(["omemo_identities"], "readwrite"); const store = tx.objectStore("omemo_identities"); - promisifyRequest(store.put({ + await promisifyRequest(store.put({ account: account, address: address, pubKey: arrayBufferToBase64(identityKey), - })).catch((e) => { - console.error("Failed to store contact identity key: " + e); - }); + })); + return identityKey; }, async getOmemoSession(account, address) { diff --git a/borogove/persistence/Sqlite.hx b/borogove/persistence/Sqlite.hx index 8717921..3d3886c 100644 --- a/borogove/persistence/Sqlite.hx +++ b/borogove/persistence/Sqlite.hx @@ -313,6 +313,17 @@ class Sqlite implements Persistence implements KeyValueStore { "PRAGMA user_version = 17"]); } return Promise.resolve(null); + }).then(_ -> { + if (version < 18) { + return exec(["CREATE TABLE omemo_contact_identity_keys ( + account_id TEXT NOT NULL, + address TEXT NOT NULL, + identity_key BLOB NOT NULL, + PRIMARY KEY (account_id, address) + ) STRICT", + "PRAGMA user_version = 18"]); + } + return Promise.resolve(null); }); }); }); @@ -1560,11 +1571,24 @@ class Sqlite implements Persistence implements KeyValueStore { } @HaxeCBridge.noemit - public function storeOmemoContactIdentityKey(account:String, address:String, identityKey:IdentityPublicKey):Void { } + public function storeOmemoContactIdentityKey(account:String, address:String, identityKey:IdentityPublicKey):Promise<IdentityPublicKey> { + return db.exec( + "INSERT OR REPLACE INTO omemo_contact_identity_keys VALUES (?,?,?)", + [account, address, identityKey], + ).then(_ -> identityKey); + } @HaxeCBridge.noemit - public function getOmemoContactIdentityKey(account:String, address:String): Promise<IdentityPublicKey> { - return Promise.reject("TODO"); + public function getOmemoContactIdentityKey(account:String, address:String): Promise<Null<IdentityPublicKey>> { + return db.exec( + "SELECT identity_key FROM omemo_contact_identity_keys WHERE account_id=? AND address=? LIMIT 1", + [account, address], + ).then(result -> { + for (row in result) { + return row.identity_key; + } + return null; + }); } @HaxeCBridge.noemit diff --git a/test/TestSqlite.hx b/test/TestSqlite.hx index d34f58a..58da533 100644 --- a/test/TestSqlite.hx +++ b/test/TestSqlite.hx @@ -1478,6 +1478,40 @@ class TestSqlite extends utest.Test { }); } + public function testGetOmemoContactIdentityKeyNotFound(async: Async) { + persistence + .getOmemoContactIdentityKey( + "contact-notfound@example.com", + "contact@example.com/1", + ) + .then(result -> { + Assert.equals(null, result); + async.done(); + }) + .catchError(e -> { + Assert.fail(Std.string(e)); + async.done(); + }); + } + + public function testOmemoContactIdentityKey(async: Async) { + final account = "contact-existing@example.com"; + final address = "contact@example.com/1"; + final identityKey = makeKey(); + + persistence + .storeOmemoContactIdentityKey(account, address, identityKey) + .then(_ -> persistence.getOmemoContactIdentityKey(account, address)) + .then(result -> { + assertKeyMatches(identityKey, result); + async.done(); + }) + .catchError(e -> { + Assert.fail(Std.string(e)); + async.done(); + }); + } + public function testOmemoSignedPreKey(async: Async) { final login = "signed-prekey@example.com"; final signedPreKey = { diff --git a/test/persistence-tests.ts b/test/persistence-tests.ts index 14c751e..33cf4b8 100644 --- a/test/persistence-tests.ts +++ b/test/persistence-tests.ts @@ -1747,4 +1747,46 @@ export function sharedPersistenceTests(test: PersistenceTest) { expect(result).toBeNull(); }); + + test("getOmemoContactIdentityKey returns null when none is stored", async ({ + page, + persistence, + }) => { + const result = await page.evaluate( + async ({ persistence }) => + persistence.getOmemoContactIdentityKey( + "omemo-contact-not-found@example.com", + "contact@example.com/1", + ), + { persistence }, + ); + + expect(result).toBeNull(); + }); + + test("storeOmemoContactIdentityKey and getOmemoContactIdentityKey", async ({ + page, + persistence, + }) => { + const account = "omemo-contact-existing@example.com"; + const address = "contact@example.com/1"; + const identityKey = [0, 1, 2, 127, 128, 255]; + const result = await page.evaluate( + async ({ persistence, account, address, identityKey }) => { + await persistence.storeOmemoContactIdentityKey( + account, + address, + new Uint8Array(identityKey).buffer, + ); + const loaded = await persistence.getOmemoContactIdentityKey( + account, + address, + ); + return [...new Uint8Array(loaded)]; + }, + { persistence, account, address, identityKey }, + ); + + expect(result).toEqual(identityKey); + }); }