@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 =
|
|
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.
|
package/dist/majik-message.d.ts
CHANGED
|
@@ -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;
|
package/dist/majik-message.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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:
|
|
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 =
|
|
140
|
-
const
|
|
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.
|
|
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.
|
|
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",
|