| author | Eric Roberts
<eric@devl.me> 2026-08-26 18:14:49 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-08-26 18:16:17 UTC |
| parent | b2f33f25bd0ab05f9133b15445fed2f2b23384d0 |
| borogove/Persistence.hx | +3 | -3 |
| borogove/persistence/Dummy.hx | +8 | -4 |
| borogove/persistence/IDB.js | +8 | -4 |
| borogove/persistence/Sqlite.hx | +37 | -4 |
| test/TestSqlite.hx | +40 | -0 |
| test/persistence-tests.ts | +52 | -0 |
diff --git a/borogove/Persistence.hx b/borogove/Persistence.hx index 4ea7dec..dabed42 100644 --- a/borogove/Persistence.hx +++ b/borogove/Persistence.hx @@ -370,17 +370,17 @@ interface Persistence { /** Store an OMEMO pre-key **/ - public function storeOmemoPreKey(identifier:String, keyId:Int, keyPair:PreKeyPair):Void; + public function storeOmemoPreKey(accountId:String, keyId:Int, keyPair:PreKeyPair):Promise<PreKeyPair>; /** Load an OMEMO pre-key **/ - public function getOmemoPreKey(identifier:String, keyId:Int): Promise<PreKeyPair>; + public function getOmemoPreKey(accountId:String, keyId:Int): Promise<Null<PreKeyPair>>; /** Remove an OMEMO pre-key **/ - public function removeOmemoPreKey(identifier:String, keyId:Int):Void; + public function removeOmemoPreKey(accountId:String, keyId:Int):Promise<Bool>; /** Store an OMEMO signed pre-key diff --git a/borogove/persistence/Dummy.hx b/borogove/persistence/Dummy.hx index bed3908..d2d21fa 100644 --- a/borogove/persistence/Dummy.hx +++ b/borogove/persistence/Dummy.hx @@ -214,15 +214,19 @@ class Dummy implements Persistence { } @HaxeCBridge.noemit - public function storeOmemoPreKey(identifier:String, keyId:Int, keyPair:PreKeyPair):Void { } + public function storeOmemoPreKey(accountId:String, keyId:Int, keyPair:PreKeyPair):Promise<PreKeyPair> { + return Promise.resolve(keyPair); + } @HaxeCBridge.noemit - public function getOmemoPreKey(identifier:String, keyId:Int): Promise<PreKeyPair> { - return Promise.reject("Not found"); + public function getOmemoPreKey(accountId:String, keyId:Int): Promise<Null<PreKeyPair>> { + return Promise.resolve(null); } @HaxeCBridge.noemit - public function removeOmemoPreKey(identifier:String, keyId:Int):Void { } + public function removeOmemoPreKey(accountId:String, keyId:Int):Promise<Bool> { + return Promise.resolve(true); + } @HaxeCBridge.noemit public function storeOmemoSignedPreKey(login:String, signedPreKey:SignedPreKey):Void { } diff --git a/borogove/persistence/IDB.js b/borogove/persistence/IDB.js index 90b3c62..14b3546 100644 --- a/borogove/persistence/IDB.js +++ b/borogove/persistence/IDB.js @@ -1177,21 +1177,25 @@ tx.onerror = console.error; return result || []; }, - storeOmemoPreKey(account, keyId, keyPair) { + async storeOmemoPreKey(account, keyId, keyPair) { const tx = db.transaction(["keyvaluepairs"], "readwrite"); const store = tx.objectStore("keyvaluepairs"); const storedKeyPair = { "privKey": arrayBufferToBase64(keyPair.privKey), "pubKey": arrayBufferToBase64(keyPair.pubKey), }; - store.put(storedKeyPair, "omemo:prekeys:"+account+":"+keyId.toString()); + await promisifyRequest(store.put(storedKeyPair, "omemo:prekeys:"+account+":"+keyId.toString())); + + return keyPair; }, - removeOmemoPreKey(account, keyId) { + async removeOmemoPreKey(account, keyId) { const tx = db.transaction(["keyvaluepairs"], "readwrite"); const store = tx.objectStore("keyvaluepairs"); const keyName = "omemo:prekeys:"+account+":"+keyId.toString(); - store.delete(keyName); + await promisifyRequest(store.delete(keyName)); + + return true; }, async getOmemoPreKey(account, keyId) { diff --git a/borogove/persistence/Sqlite.hx b/borogove/persistence/Sqlite.hx index 5016c94..b38cfb7 100644 --- a/borogove/persistence/Sqlite.hx +++ b/borogove/persistence/Sqlite.hx @@ -288,6 +288,18 @@ class Sqlite implements Persistence implements KeyValueStore { "PRAGMA user_version = 15"]); } return Promise.resolve(null); + }).then(_ -> { + if (version < 16) { + return exec(["CREATE TABLE omemo_prekeys ( + account_id TEXT NOT NULL, + key_id INTEGER NOT NULL, + private_key BLOB NOT NULL, + public_key BLOB NOT NULL, + PRIMARY KEY (account_id, key_id) + ) STRICT", + "PRAGMA user_version = 16"]); + } + return Promise.resolve(null); }); }); }); @@ -1461,15 +1473,36 @@ class Sqlite implements Persistence implements KeyValueStore { } @HaxeCBridge.noemit - public function storeOmemoPreKey(identifier:String, keyId:Int, keyPair:PreKeyPair):Void { } + public function storeOmemoPreKey(accountId:String, keyId:Int, keyPair:PreKeyPair):Promise<PreKeyPair> { + return db.exec( + "INSERT OR REPLACE INTO omemo_prekeys VALUES (?,?,?,?)", + [accountId, keyId, keyPair.privKey, keyPair.pubKey], + ).then(_ -> keyPair); + } @HaxeCBridge.noemit - public function getOmemoPreKey(identifier:String, keyId:Int): Promise<PreKeyPair> { - return Promise.reject("TODO"); + public function getOmemoPreKey(accountId:String, keyId:Int): Promise<Null<PreKeyPair>> { + return db.exec( + "SELECT private_key, public_key FROM omemo_prekeys WHERE account_id=? AND key_id=? LIMIT 1", + [accountId, keyId], + ).then(result -> { + for (row in result) { + return { + privKey: row.private_key, + pubKey: row.public_key, + }; + } + return null; + }); } @HaxeCBridge.noemit - public function removeOmemoPreKey(identifier:String, keyId:Int):Void { } + public function removeOmemoPreKey(accountId:String, keyId:Int):Promise<Bool> { + return db.exec( + "DELETE FROM omemo_prekeys WHERE account_id=? AND key_id=?", + [accountId, keyId], + ).then(_ -> true); + } @HaxeCBridge.noemit public function storeOmemoSignedPreKey(login:String, signedPreKey:SignedPreKey):Void { } diff --git a/test/TestSqlite.hx b/test/TestSqlite.hx index 6db1098..a78e59a 100644 --- a/test/TestSqlite.hx +++ b/test/TestSqlite.hx @@ -1404,4 +1404,44 @@ class TestSqlite extends utest.Test { }); } + public function testGetOmemoPreKeyNotFound(async: Async) { + persistence + .getOmemoPreKey("prekey-notfound@example.com", 1) + .then(result -> { + Assert.equals(null, result); + async.done(); + }) + .catchError(e -> { + Assert.fail(Std.string(e)); + async.done(); + }); + } + + public function testOmemoPreKey(async: Async) { + final login = "prekey-existing@example.com"; + final keyId = 42; + final keyPair = { + privKey: Bytes.ofHex("0001027f80ff").getData(), + pubKey: Bytes.ofHex("ff807f020100").getData(), + }; + + persistence + .storeOmemoPreKey(login, keyId, keyPair) + .then(_ -> persistence.getOmemoPreKey(login, keyId)) + .then(result -> { + Assert.equals("0001027f80ff", Bytes.ofData(result.privKey).toHex()); + Assert.equals("ff807f020100", Bytes.ofData(result.pubKey).toHex()); + return persistence.removeOmemoPreKey(login, keyId); + }) + .then(_ -> persistence.getOmemoPreKey(login, keyId)) + .then(result -> { + Assert.equals(null, result); + async.done(); + }) + .catchError(e -> { + Assert.fail(Std.string(e)); + async.done(); + }); + } + } diff --git a/test/persistence-tests.ts b/test/persistence-tests.ts index 61387af..06fa4c9 100644 --- a/test/persistence-tests.ts +++ b/test/persistence-tests.ts @@ -1596,4 +1596,56 @@ export function sharedPersistenceTests(test: PersistenceTest) { expect(result.afterReplace).toEqual(replacementDeviceIds); expect(result.afterClear).toEqual([]); }); + + test("getOmemoPreKey returns null when none is stored", async ({ + page, + persistence, + }) => { + const identifier = "omemo-prekey-not-found@example.com"; + const keyId = 1; + const result = await page.evaluate( + async ({ persistence, identifier, keyId }) => + persistence.getOmemoPreKey(identifier, keyId), + { persistence, identifier, keyId }, + ); + + expect(result).toBeNull(); + }); + + test("storeOmemoPreKey stores a removable pre-key", async ({ + page, + persistence, + }) => { + const identifier = "omemo-prekey-existing@example.com"; + const keyId = 42; + const keyPair = { + privKey: [0, 1, 2, 127, 128, 255], + pubKey: [255, 128, 127, 2, 1, 0], + }; + const result = await page.evaluate( + async ({ persistence, identifier, keyId, keyPair }) => { + await persistence.storeOmemoPreKey(identifier, keyId, { + privKey: new Uint8Array(keyPair.privKey).buffer, + pubKey: new Uint8Array(keyPair.pubKey).buffer, + }); + const loadedKeyPair = await persistence.getOmemoPreKey( + identifier, + keyId, + ); + await persistence.removeOmemoPreKey(identifier, keyId); + const afterRemove = await persistence.getOmemoPreKey(identifier, keyId); + + return { + loadedPrivKey: [...new Uint8Array(loadedKeyPair.privKey)], + loadedPubKey: [...new Uint8Array(loadedKeyPair.pubKey)], + afterRemove, + }; + }, + { persistence, identifier, keyId, keyPair }, + ); + + expect(result.loadedPrivKey).toEqual(keyPair.privKey); + expect(result.loadedPubKey).toEqual(keyPair.pubKey); + expect(result.afterRemove).toBeNull(); + }); }