git » sdk » commit 959b452

Implement get/storeOmemoIdentityKey

author Eric Roberts
2026-08-26 18:14:47 UTC
committer Stephen Paul Weber
2026-08-26 18:16:17 UTC
parent 3c345fec4332ad385568b3693cc0793c1bd88834

Implement get/storeOmemoIdentityKey

I used a new table here to avoid base64 encoding/decoding into the
keyvaluepairs table.

Similar to get/StoreOmemoId I changed IDB.js to coerce undefined to null to
make sure we match the interface.

borogove/Persistence.hx +2 -2
borogove/persistence/Dummy.hx +5 -3
borogove/persistence/IDB.js +7 -3
borogove/persistence/Sqlite.hx +29 -3
test/TestSqlite.hx +35 -0
test/persistence-tests.ts +49 -0

diff --git a/borogove/Persistence.hx b/borogove/Persistence.hx
index c1722c2..03d7834 100644
--- a/borogove/Persistence.hx
+++ b/borogove/Persistence.hx
@@ -350,12 +350,12 @@ interface Persistence {
 	/**
 		Store the local OMEMO identity key pair for an account
 	**/
-	public function storeOmemoIdentityKey(login:String, keypair:IdentityKeyPair):Void;
+	public function storeOmemoIdentityKey(login:String, keypair:IdentityKeyPair):Promise<IdentityKeyPair>;
 
 	/**
 		Load the local OMEMO identity key pair for an account
 	**/
-	public function getOmemoIdentityKey(login:String): Promise<IdentityKeyPair>;
+	public function getOmemoIdentityKey(login:String): Promise<Null<IdentityKeyPair>>;
 
 	/**
 		Load the known OMEMO device list for a contact or account
diff --git a/borogove/persistence/Dummy.hx b/borogove/persistence/Dummy.hx
index fe93725..46c56ab 100644
--- a/borogove/persistence/Dummy.hx
+++ b/borogove/persistence/Dummy.hx
@@ -194,11 +194,13 @@ class Dummy implements Persistence {
 	}
 
 	@HaxeCBridge.noemit
-	public function storeOmemoIdentityKey(login:String, keypair:IdentityKeyPair):Void { }
+	public function storeOmemoIdentityKey(login:String, keypair:IdentityKeyPair):Promise<IdentityKeyPair> {
+		return Promise.resolve(keypair);
+	}
 
 	@HaxeCBridge.noemit
-	public function getOmemoIdentityKey(login:String): Promise<IdentityKeyPair> {
-		return Promise.reject("Not found");
+	public function getOmemoIdentityKey(login:String): Promise<Null<IdentityKeyPair>> {
+		return Promise.resolve(null);
 	}
 
 	@HaxeCBridge.noemit
diff --git a/borogove/persistence/IDB.js b/borogove/persistence/IDB.js
index 2b686ca..07ae61b 100644
--- a/borogove/persistence/IDB.js
+++ b/borogove/persistence/IDB.js
@@ -1149,10 +1149,12 @@ tx.onerror = console.error;
 			return omemoId;
 		},
 
-		storeOmemoIdentityKey(account, keypair) {
+		async storeOmemoIdentityKey(account, keypair) {
 			const tx = db.transaction(["keyvaluepairs"], "readwrite");
 			const store = tx.objectStore("keyvaluepairs");
-			store.put(keypair, "omemo:key:" + account).onerror = console.error;
+			await promisifyRequest(store.put(keypair, "omemo:key:" + account));
+
+			return keypair;
 		},
 
 		storeOmemoDeviceList(chatId, deviceIds) {
@@ -1293,7 +1295,9 @@ tx.onerror = console.error;
 		getOmemoIdentityKey(account) {
 			const tx = db.transaction(["keyvaluepairs"], "readonly");
 			const store = tx.objectStore("keyvaluepairs");
-			return promisifyRequest(store.get("omemo:key:"+account));
+			return promisifyRequest(store.get("omemo:key:"+account)).then(
+				(result) => result ?? null,
+			);
 		},
 
 		async getOmemoSignedPreKey(account, keyId) {
diff --git a/borogove/persistence/Sqlite.hx b/borogove/persistence/Sqlite.hx
index 164953d..38807c0 100644
--- a/borogove/persistence/Sqlite.hx
+++ b/borogove/persistence/Sqlite.hx
@@ -268,6 +268,16 @@ class Sqlite implements Persistence implements KeyValueStore {
 						"PRAGMA user_version = 13"]);
 					}
 					return Promise.resolve(null);
+				}).then(_ -> {
+					if (version < 14) {
+						return exec(["CREATE TABLE omemo_identity_keys (
+							account_id TEXT NOT NULL PRIMARY KEY,
+							private_key BLOB NOT NULL,
+							public_key BLOB NOT NULL
+						) STRICT",
+						"PRAGMA user_version = 14"]);
+					}
+					return Promise.resolve(null);
 				});
 			});
 		});
@@ -1386,11 +1396,27 @@ class Sqlite implements Persistence implements KeyValueStore {
 	}
 
 	@HaxeCBridge.noemit
-	public function storeOmemoIdentityKey(login:String, keypair:IdentityKeyPair):Void { }
+	public function storeOmemoIdentityKey(login:String, keypair:IdentityKeyPair):Promise<IdentityKeyPair> {
+		return db.exec(
+			"INSERT OR REPLACE INTO omemo_identity_keys VALUES (?,?,?)",
+			[login, keypair.privKey, keypair.pubKey],
+		).then(_ -> keypair);
+	}
 
 	@HaxeCBridge.noemit
-	public function getOmemoIdentityKey(login:String): Promise<IdentityKeyPair> {
-		return Promise.reject("TODO");
+	public function getOmemoIdentityKey(login:String): Promise<Null<IdentityKeyPair>> {
+		return db.exec(
+			"SELECT private_key, public_key FROM omemo_identity_keys WHERE account_id=? LIMIT 1",
+			[login],
+		).then(result -> {
+			for (row in result) {
+				return {
+					privKey: row.private_key,
+					pubKey: row.public_key,
+				};
+			}
+			return null;
+		});
 	}
 
 	@HaxeCBridge.noemit
diff --git a/test/TestSqlite.hx b/test/TestSqlite.hx
index 28f5e55..d9a9fd3 100644
--- a/test/TestSqlite.hx
+++ b/test/TestSqlite.hx
@@ -1331,4 +1331,39 @@ class TestSqlite extends utest.Test {
 				async.done();
 			});
 	}
+
+	public function testGetOmemoIdentityKeyNotFound(async: Async) {
+		persistence
+			.getOmemoIdentityKey("identity-notfound@example.com")
+			.then(result -> {
+				Assert.equals(null, result);
+				async.done();
+			})
+			.catchError(e -> {
+				Assert.fail(Std.string(e));
+				async.done();
+			});
+	}
+
+	public function testOmemoIdentityKey(async: Async) {
+		final login = "identity-existing@example.com";
+		final keyPair = {
+			privKey: Bytes.ofHex("0001027f80ff").getData(),
+			pubKey: Bytes.ofHex("ff807f020100").getData(),
+		};
+
+		persistence
+			.storeOmemoIdentityKey(login, keyPair)
+			.then(_ -> persistence.getOmemoIdentityKey(login))
+			.then(result -> {
+				Assert.equals("0001027f80ff", Bytes.ofData(result.privKey).toHex());
+				Assert.equals("ff807f020100", Bytes.ofData(result.pubKey).toHex());
+				async.done();
+			})
+			.catchError(e -> {
+				Assert.fail(Std.string(e));
+				async.done();
+			});
+	}
+
 }
diff --git a/test/persistence-tests.ts b/test/persistence-tests.ts
index 7185789..8e74c03 100644
--- a/test/persistence-tests.ts
+++ b/test/persistence-tests.ts
@@ -1493,4 +1493,53 @@ export function sharedPersistenceTests(test: PersistenceTest) {
 
 		expect(result).toBe(omemoId);
 	});
+
+	test("getOmemoIdentityKey returns null when none is stored", async ({
+		page,
+		persistence,
+	}) => {
+		const result = await page.evaluate(
+			async (persistence) =>
+				persistence.getOmemoIdentityKey(
+					"omemo-identity-not-found@example.com",
+				),
+			persistence,
+		);
+
+		expect(result).toBeNull();
+	});
+
+	test("storeOmemoIdentityKey stores the key pair", async ({
+		page,
+		persistence,
+	}) => {
+		const keyPair = {
+			privKey: [0, 1, 2, 127, 128, 255],
+			pubKey: [255, 128, 127, 2, 1, 0],
+		};
+
+		const result = await page.evaluate(
+			async ({ persistence, keyPair }) => {
+				await persistence.storeOmemoIdentityKey(
+					"omemo-identity-existing@example.com",
+					{
+						privKey: new Uint8Array(keyPair.privKey).buffer,
+						pubKey: new Uint8Array(keyPair.pubKey).buffer,
+					},
+				);
+				const loadedKeyPair = await persistence.getOmemoIdentityKey(
+					"omemo-identity-existing@example.com",
+				);
+
+				return {
+					loadedPrivKey: [...new Uint8Array(loadedKeyPair.privKey)],
+					loadedPubKey: [...new Uint8Array(loadedKeyPair.pubKey)],
+				};
+			},
+			{ persistence, keyPair },
+		);
+
+		expect(result.loadedPrivKey).toEqual([0, 1, 2, 127, 128, 255]);
+		expect(result.loadedPubKey).toEqual([255, 128, 127, 2, 1, 0]);
+	});
 }