@majikah/majik-message 0.1.13 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,12 @@
1
1
  import { MajikMessageIdentityJSON } from "../database/system/identity";
2
2
  import { ISODateString } from "../types";
3
- export type SerializedMajikContact = Omit<MajikContactData, "publicKey"> & {
3
+ export type SerializedMajikContact = {
4
+ id: string;
5
+ fingerprint: string;
6
+ meta?: MajikContactMeta;
4
7
  publicKeyBase64: string;
5
8
  mlKey: string;
9
+ majikah_registered?: boolean;
6
10
  };
7
11
  export interface MajikContactMeta {
8
12
  label?: string;
@@ -46,6 +50,7 @@ export declare class MajikContact {
46
50
  raw: Uint8Array;
47
51
  }, mlKey: string, fingerprint: string, meta?: Partial<MajikContactMeta>): MajikContact;
48
52
  private assertId;
53
+ private assertMLKey;
49
54
  private assertPublicKey;
50
55
  private assertFingerprint;
51
56
  private updateTimestamp;
@@ -23,6 +23,7 @@ export class MajikContact {
23
23
  constructor(data) {
24
24
  this.assertId(data.id);
25
25
  this.assertPublicKey(data.publicKey);
26
+ this.assertMLKey(data.mlKey);
26
27
  this.assertFingerprint(data.fingerprint);
27
28
  this.id = data.id;
28
29
  this.publicKey = data.publicKey;
@@ -50,6 +51,11 @@ export class MajikContact {
50
51
  throw new MajikContactError("Contact ID must be a non-empty string");
51
52
  }
52
53
  }
54
+ assertMLKey(key) {
55
+ if (!key || typeof key !== "string") {
56
+ throw new MajikContactError("ML Key must be a non-empty string");
57
+ }
58
+ }
53
59
  assertPublicKey(key) {
54
60
  // Accept either a WebCrypto CryptoKey (with .type === 'public')
55
61
  // or a raw-key wrapper object that contains a Uint8Array `raw` field.
@@ -91,6 +91,8 @@ export declare class MajikMessage {
91
91
  exportContactAsString(contactId: string): Promise<string | null>;
92
92
  importContactFromJSON(jsonStr: string): Promise<MAJIK_API_RESPONSE>;
93
93
  importContactFromString(base64Str: string): Promise<void>;
94
+ exportContactCompressed(contact: MajikContact): Promise<string>;
95
+ importContactCompressed(base64Str: string): Promise<MajikContact>;
94
96
  addContact(contact: MajikContact): void;
95
97
  removeContact(id: string): void;
96
98
  updateContactMeta(id: string, meta: Partial<MajikContactMeta>): void;
@@ -6,13 +6,14 @@ import { MessageEnvelope } from "./core/messages/message-envelope";
6
6
  import { EnvelopeCache, } from "./core/messages/envelope-cache";
7
7
  import { MajikKeyStore } from "./core/crypto/keystore";
8
8
  import { MajikContactDirectory, } from "./core/contacts/majik-contact-directory";
9
- import { arrayBufferToBase64, arrayToBase64, base64ToArrayBuffer, base64ToUtf8, utf8ToBase64, } from "./core/utils/utilities";
9
+ import { arrayBufferToBase64, arrayToBase64, base64ToArrayBuffer, base64ToUint8Array, base64ToUtf8, } from "./core/utils/utilities";
10
10
  import { autoSaveMajikFileData, loadSavedMajikFileData, } from "./core/utils/majik-file-utils";
11
11
  import { randomBytes } from "@stablelib/random";
12
12
  import { clearAllBlobs, idbLoadBlob, idbSaveBlob, } from "./core/utils/idb-majik-system";
13
13
  import { MajikMessageChat } from "./core/database/chat/majik-message-chat";
14
14
  import { MajikKey } from "@majikah/majik-key";
15
15
  import { MajikEnvelope, } from "./core/messages/majik-envelope";
16
+ import { gzipSync, gunzipSync } from "fflate";
16
17
  // ─── MajikMessage ─────────────────────────────────────────────────────────────
17
18
  export class MajikMessage {
18
19
  userProfile = "default";
@@ -65,14 +66,15 @@ export class MajikMessage {
65
66
  const contact = this.contactDirectory.getContact(id);
66
67
  if (!contact)
67
68
  throw new Error(`No contact found for id "${id}"`);
68
- const key = await MajikKeyStore.load(id);
69
- if (!key?.mlKemPublicKey) {
69
+ // const key = await MajikKeyStore.load(id);
70
+ const mlPubKey = base64ToUint8Array(contact.mlKey);
71
+ if (!mlPubKey) {
70
72
  throw new Error(`Contact "${id}" has no ML-KEM public key. ` +
71
73
  `They may need to upgrade their account via importFromMnemonicBackup().`);
72
74
  }
73
75
  return {
74
76
  fingerprint: contact.fingerprint,
75
- mlKemPublicKey: key.mlKemPublicKey,
77
+ mlKemPublicKey: mlPubKey,
76
78
  };
77
79
  }));
78
80
  }
@@ -136,8 +138,9 @@ export class MajikMessage {
136
138
  if (this.getOwnAccountById(key.id)) {
137
139
  throw new Error("Account with the same ID already exists");
138
140
  }
139
- const keyContact = await key.toContact().toJSON();
140
- const reParsedContact = MajikContact.fromJSON(keyContact);
141
+ const keyContact = key.toContact();
142
+ const contactJSON = await keyContact.toJSON();
143
+ const reParsedContact = MajikContact.fromJSON(contactJSON);
141
144
  this.addOwnAccount(reParsedContact);
142
145
  return { id: key.id, fingerprint: key.fingerprint };
143
146
  }
@@ -269,8 +272,13 @@ export class MajikMessage {
269
272
  }, null, 2);
270
273
  }
271
274
  async exportContactAsString(contactId) {
272
- const json = await this.exportContactAsJSON(contactId);
273
- return json ? utf8ToBase64(json) : null;
275
+ // const json = await this.exportContactAsJSON(contactId);
276
+ // return json ? utf8ToBase64(json) : null;
277
+ const contact = this.contactDirectory.getContact(contactId);
278
+ if (!contact)
279
+ return null;
280
+ const compressedString = this.exportContactCompressed(contact);
281
+ return compressedString;
274
282
  }
275
283
  async importContactFromJSON(jsonStr) {
276
284
  try {
@@ -307,6 +315,47 @@ export class MajikMessage {
307
315
  if (!result.success)
308
316
  throw new Error(result.message);
309
317
  }
318
+ async exportContactCompressed(contact) {
319
+ // Prepare JSON with raw keys
320
+ let publicKeyBase64;
321
+ const anyPub = contact.publicKey;
322
+ if (anyPub?.raw instanceof Uint8Array) {
323
+ publicKeyBase64 = arrayBufferToBase64(anyPub.raw.buffer);
324
+ }
325
+ else {
326
+ const raw = await crypto.subtle.exportKey("raw", contact.publicKey);
327
+ publicKeyBase64 = arrayBufferToBase64(raw);
328
+ }
329
+ const jsonObj = {
330
+ id: contact.id,
331
+ label: contact.meta?.label || "",
332
+ publicKey: publicKeyBase64,
333
+ fingerprint: contact.fingerprint,
334
+ mlKey: contact.mlKey,
335
+ };
336
+ const jsonStr = JSON.stringify(jsonObj);
337
+ const utf8 = new TextEncoder().encode(jsonStr);
338
+ // Compress with gzip or Brotli
339
+ const compressed = gzipSync(utf8);
340
+ // Encode for string export
341
+ return arrayToBase64(compressed);
342
+ }
343
+ async importContactCompressed(base64Str) {
344
+ const compressed = base64ToArrayBuffer(base64Str);
345
+ const decompressed = gunzipSync(new Uint8Array(compressed));
346
+ const jsonStr = new TextDecoder().decode(decompressed);
347
+ const data = JSON.parse(jsonStr);
348
+ const publicKey = data.publicKey instanceof Array
349
+ ? { raw: new Uint8Array(data.publicKey) }
350
+ : await crypto.subtle.importKey("raw", base64ToArrayBuffer(data.publicKey), KEY_ALGO, true, []);
351
+ return new MajikContact({
352
+ id: data.id,
353
+ publicKey,
354
+ fingerprint: data.fingerprint,
355
+ meta: { label: data.label },
356
+ mlKey: data.mlKey,
357
+ });
358
+ }
310
359
  addContact(contact) {
311
360
  this.contactDirectory.addContact(contact);
312
361
  this.emit("new-contact", contact);
@@ -422,6 +471,7 @@ export class MajikMessage {
422
471
  return await this.composeMessage(recipientIds, plaintext, cache);
423
472
  }
424
473
  catch (err) {
474
+ console.warn("Error: ", err);
425
475
  this.emit("error", err, { context: "encryptTextForScanner" });
426
476
  return null;
427
477
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@majikah/majik-message",
3
3
  "type": "module",
4
4
  "description": "Encrypt and decrypt messages on any website or platform. Secure chats with keypairs and seed-based accounts. Open source.",
5
- "version": "0.1.13",
5
+ "version": "0.1.15",
6
6
  "license": "Apache-2.0",
7
7
  "author": "Zelijah",
8
8
  "main": "./dist/index.js",
@@ -80,7 +80,7 @@
80
80
  },
81
81
  "dependencies": {
82
82
  "@bokuweb/zstd-wasm": "^0.0.27",
83
- "@majikah/majik-key": "^0.1.4",
83
+ "@majikah/majik-key": "^0.1.8",
84
84
  "@noble/hashes": "^2.0.1",
85
85
  "@noble/post-quantum": "^0.5.4",
86
86
  "@scure/bip39": "^1.6.0",