feat: match gsmol's v2 encrypted export format; fix export on desktop

This commit is contained in:
randogoth 2026-09-27 00:09:29 +03:00
parent f80315e7c4
commit 7d3c8b423c
3 changed files with 104 additions and 28 deletions

View file

@ -77,9 +77,12 @@ void main() {
MailRecord("bb", randomBytes(64), recipient: "bob@example.org", sentAt: 6));
final data = a.exportData();
expect(data["gsmolExport"], 1); // gsmol-compatible marker
expect(data["gsmolExport"], 2); // sealed to the identity's seed, like gsmol
expect(jsonEncode(data).contains(hex(a.seed()!)), isFalse);
// v2 is sealed to the exporting identity's seed — a restore-on-new-device
// scenario, not a transfer to someone else's identity (see the test below).
b.setIdentity(a.seed()!);
final summary = await b.importData(data);
expect(summary.mailAdded, 2);
expect(summary.pinsAdded, 1);
@ -88,7 +91,26 @@ void main() {
expect(b.contact("alice@example.org")!.history.length, 1);
expect(b.getMessage("inbox", "aa"), isNotNull);
expect(b.getMessage("sent", "bb"), isNotNull);
expect(b.seed(), isNull); // never the seed
});
test("v2 import refuses a different identity's export", () async {
final a = await freshStore("export-wrong-identity");
final c = await freshStore("round-trip-wrong-identity");
a.setIdentity(randomBytes(32));
a.pinServer("example.org", randomBytes(32));
final data = a.exportData();
c.setIdentity(randomBytes(32)); // a different seed than a's
expect(() => c.importData(data), throwsA(isA<SmolError>()));
});
test("v1 legacy export still imports without an identity", () async {
final store = await freshStore("import-legacy-v1");
final summary = await store.importData({
"gsmolExport": 1,
"servers": {"example.org": b32encode(randomBytes(32))},
});
expect(summary.pinsAdded, 1);
});
test("import never overwrites a differing trust binding", () async {