@majikah/majik-key 0.1.2 → 0.1.4

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.
package/README.md CHANGED
@@ -98,7 +98,7 @@ Every identity now generates a dual-key system derived deterministically from a
98
98
 
99
99
  ### 2. Argon2id Key Derivation
100
100
  We have transitioned to **Argon2id** (KDF v2) for encrypting private keys at rest.
101
- * **Memory-Hard:** Configured with 128 MB of memory, 4 iterations, and 4 parallelism factors.
101
+ * **Memory-Hard:** Configured with 64 MB of memory, 3 iterations, and 4 parallelism factors.
102
102
  * **Brute-Force Resistant:** Engineered to defeat GPU and ASIC-based cracking attempts that easily bypass older PBKDF2 implementations.
103
103
 
104
104
  ### 3. Seamless Auto-Migration
@@ -137,7 +137,7 @@ A Majik Key is a seed phrase account that:
137
137
 
138
138
  ### Security & Post-Quantum Readiness
139
139
  - **Post-Quantum Ready**: Implements **ML-KEM-768 (FIPS-203)** for key encapsulation, ensuring identities are secure against future quantum computing threats.
140
- - **Argon2id Key Derivation**: Uses memory-hard **Argon2id** (KDF v2) for passphrase encryption (128 MB / 4 iterations / 4 parallelism), providing industry-leading resistance to GPU/ASIC brute-force attacks.
140
+ - **Argon2id Key Derivation**: Uses memory-hard **Argon2id** (KDF v2) for passphrase encryption (64 MB / 3 iterations / 4 parallelism), providing industry-leading resistance to GPU/ASIC brute-force attacks.
141
141
  - **Seamless Auto-Migration**: Automatically detects and upgrades legacy v1 (PBKDF2) accounts to v2 (Argon2id) during import, re-deriving missing ML-KEM keys deterministically from the seed.
142
142
  - **AES-GCM Authenticated Encryption**: Industry-standard encryption for private keys at rest with unique, per-identity salts and random IVs.
143
143
  - **Locked/Unlocked States**: Private keys are only decrypted into memory when explicitly unlocked and are purged immediately upon calling `.lock()`.
@@ -833,7 +833,7 @@ async function createMessageIdentity() {
833
833
  3. **PQ Readiness**: For all new communication protocols, ensure you are utilizing the mlKemPublicKey.
834
834
 
835
835
  Security Summary
836
- - **Primary KDF**: Argon2id (128MB / 4t / 4p).
836
+ - **Primary KDF**: Argon2id (64 / 3t / 4p).
837
837
 
838
838
  - **Legacy KDF**: PBKDF2-SHA256 (250,000 iterations).
839
839
 
@@ -893,7 +893,7 @@ Secure messaging platform using Majik Keys
893
893
 
894
894
  [Read more about Majik Message here](https://majikah.solutions/products/majik-message)
895
895
 
896
- [![Majik Message Thumbnail](https://gydzizwxtftlmsdaiouw.supabase.co/storage/v1/object/public/bucket-majikah-public/main/Majikah_MajikMessage_SocialCard.webp)](https://message.majikah.solutions)
896
+ [![Majik Message Thumbnail](https://github.com/user-attachments/assets/d433c6b8-1841-4fa1-a6da-b348029d1dbe)](https://message.majikah.solutions)
897
897
 
898
898
  > Click the image to try Majik Message live.
899
899
 
@@ -41,8 +41,8 @@ export type KDF_VERSION = (typeof KDF_VERSION)[keyof typeof KDF_VERSION];
41
41
  */
42
42
  export declare const ARGON2_PARAMS: {
43
43
  readonly PASSPHRASE: {
44
- readonly m: 131072;
45
- readonly t: 4;
44
+ readonly m: 65536;
45
+ readonly t: 3;
46
46
  readonly p: 4;
47
47
  readonly dkLen: 32;
48
48
  };
@@ -37,8 +37,8 @@ export const KDF_VERSION = {
37
37
  */
38
38
  export const ARGON2_PARAMS = {
39
39
  PASSPHRASE: {
40
- m: 131072, // memory in KB (128 MB)
41
- t: 4, // time cost (passes)
40
+ m: 65536, // memory in KB (128 MB)
41
+ t: 3, // time cost (passes)
42
42
  p: 4, // parallelism (lanes)
43
43
  dkLen: 32, // output length in bytes (256-bit AES key)
44
44
  },
@@ -6,7 +6,7 @@ export interface EncryptionIdentity {
6
6
  raw: Uint8Array;
7
7
  };
8
8
  fingerprint: string;
9
- mlKemPublicKey?: Uint8Array;
9
+ mlKemPublicKey: Uint8Array;
10
10
  mlKemSecretKey?: Uint8Array;
11
11
  }
12
12
  /**
@@ -15,12 +15,6 @@ export interface EncryptionIdentity {
15
15
  * Core cryptographic engine.
16
16
  */
17
17
  export declare class EncryptionEngine {
18
- /**
19
- * Generates a random long-term identity keypair (X25519 only).
20
- * ML-KEM keys are not generated here since random identities
21
- * cannot be deterministically recovered from a mnemonic.
22
- */
23
- static generateIdentity(): Promise<EncryptionIdentity>;
24
18
  /**
25
19
  * Derive a complete identity from a BIP-39 mnemonic.
26
20
  *
@@ -11,33 +11,29 @@ export class EncryptionEngine {
11
11
  /* ================================
12
12
  * Identity
13
13
  * ================================ */
14
- /* ================================
15
- * Identity
16
- * ================================ */
17
- /**
18
- * Generates a random long-term identity keypair (X25519 only).
19
- * ML-KEM keys are not generated here since random identities
20
- * cannot be deterministically recovered from a mnemonic.
21
- */
22
- static async generateIdentity() {
23
- try {
24
- const ed = ed25519.generateKeyPair();
25
- const skCurve = ed2curve.convertSecretKey(ed.secretKey);
26
- const pkCurve = ed2curve.convertPublicKey(ed.publicKey);
27
- if (!skCurve || !pkCurve) {
28
- throw new CryptoError("Failed to convert Ed25519 keys to Curve25519");
29
- }
30
- const pkBytes = new Uint8Array(pkCurve);
31
- const skBytes = new Uint8Array(skCurve);
32
- const publicKey = { type: "public", raw: pkBytes };
33
- const privateKey = { type: "private", raw: skBytes };
34
- const fingerprint = fingerprintFromPublicRaw(pkBytes);
35
- return { publicKey, privateKey, fingerprint };
36
- }
37
- catch (err) {
38
- throw new CryptoError("Failed to generate identity", err);
39
- }
40
- }
14
+ // /**
15
+ // * Generates a random long-term identity keypair (X25519 only).
16
+ // * ML-KEM keys are not generated here since random identities
17
+ // * cannot be deterministically recovered from a mnemonic.
18
+ // */
19
+ // static async generateIdentity(): Promise<EncryptionIdentity> {
20
+ // try {
21
+ // const ed = ed25519.generateKeyPair();
22
+ // const skCurve = ed2curve.convertSecretKey(ed.secretKey);
23
+ // const pkCurve = ed2curve.convertPublicKey(ed.publicKey);
24
+ // if (!skCurve || !pkCurve) {
25
+ // throw new CryptoError("Failed to convert Ed25519 keys to Curve25519");
26
+ // }
27
+ // const pkBytes = new Uint8Array(pkCurve as Uint8Array);
28
+ // const skBytes = new Uint8Array(skCurve as Uint8Array);
29
+ // const publicKey = { type: "public", raw: pkBytes } as any;
30
+ // const privateKey = { type: "private", raw: skBytes } as any;
31
+ // const fingerprint = fingerprintFromPublicRaw(pkBytes);
32
+ // return { publicKey, privateKey, fingerprint };
33
+ // } catch (err) {
34
+ // throw new CryptoError("Failed to generate identity", err);
35
+ // }
36
+ // }
41
37
  /**
42
38
  * Derive a complete identity from a BIP-39 mnemonic.
43
39
  *
@@ -4,11 +4,11 @@ export interface MajikMessageIdentityJSON {
4
4
  id: string;
5
5
  user_id: string;
6
6
  public_key: string;
7
+ ml_key: string;
7
8
  phash: string;
8
9
  label: string;
9
10
  timestamp: string;
10
11
  restricted: boolean;
11
- kdfVersion?: number;
12
12
  }
13
13
  /**
14
14
  * MajikMessageIdentity
@@ -18,6 +18,7 @@ export declare class MajikMessageIdentity {
18
18
  private readonly _id;
19
19
  private readonly _userId;
20
20
  private readonly _publicKey;
21
+ private readonly _mlKey;
21
22
  private readonly _phash;
22
23
  private _label;
23
24
  private readonly _timestamp;
@@ -27,6 +27,7 @@ export class MajikMessageIdentity {
27
27
  _id;
28
28
  _userId;
29
29
  _publicKey;
30
+ _mlKey;
30
31
  _phash;
31
32
  _label;
32
33
  _timestamp;
@@ -38,6 +39,7 @@ export class MajikMessageIdentity {
38
39
  assertString(params.id, "id");
39
40
  assertString(params.userId, "user_id");
40
41
  assertString(params.publicKey, "public_key");
42
+ assertString(params.mlKey, "ml_key");
41
43
  assertString(params.phash, "phash");
42
44
  assertString(params.label, "label");
43
45
  assertISODate(params.timestamp, "timestamp");
@@ -45,6 +47,7 @@ export class MajikMessageIdentity {
45
47
  this._id = params.id;
46
48
  this._userId = params.userId;
47
49
  this._publicKey = params.publicKey;
50
+ this._mlKey = params.mlKey;
48
51
  this._phash = params.phash;
49
52
  this._label = params.label;
50
53
  this._timestamp = params.timestamp;
@@ -68,11 +71,12 @@ export class MajikMessageIdentity {
68
71
  assertString(label, "label");
69
72
  const timestamp = new Date().toISOString();
70
73
  const publicKey = account.publicKeyBase64;
71
- const phash = sha256(`${user.id}:${publicKey}:${account.id}`);
74
+ const phash = sha256(`${user.id}:${publicKey}:${account.id}:${account.mlKey}`);
72
75
  return new MajikMessageIdentity({
73
76
  id: account.id,
74
77
  userId: user.id,
75
78
  publicKey: publicKey,
79
+ mlKey: account.mlKey,
76
80
  phash,
77
81
  label,
78
82
  timestamp,
@@ -127,7 +131,7 @@ export class MajikMessageIdentity {
127
131
  * Detects tampering of id/public_key
128
132
  */
129
133
  validateIntegrity() {
130
- const expected = sha256(`${this._userId}:${this._publicKey}:${this._id}`);
134
+ const expected = sha256(`${this._userId}:${this._publicKey}:${this._id}:${this._mlKey}`);
131
135
  return expected === this._phash;
132
136
  }
133
137
  /**
@@ -136,7 +140,7 @@ export class MajikMessageIdentity {
136
140
  matches(userId, publicKey) {
137
141
  assertString(userId, "userId");
138
142
  assertString(publicKey, "publicKey");
139
- const hash = sha256(`${userId}:${publicKey}:${this._id}`);
143
+ const hash = sha256(`${userId}:${publicKey}:${this._id}:${this._mlKey}`);
140
144
  return hash === this._phash;
141
145
  }
142
146
  // ─────────────────────────────
@@ -147,6 +151,7 @@ export class MajikMessageIdentity {
147
151
  id: this._id,
148
152
  user_id: this._userId,
149
153
  public_key: this._publicKey,
154
+ ml_key: this._mlKey,
150
155
  phash: this._phash,
151
156
  label: this._label,
152
157
  timestamp: this._timestamp,
@@ -160,6 +165,7 @@ export class MajikMessageIdentity {
160
165
  id: obj.id,
161
166
  userId: obj.user_id,
162
167
  publicKey: obj.public_key,
168
+ mlKey: obj.ml_key,
163
169
  phash: obj.phash,
164
170
  label: obj.label,
165
171
  timestamp: obj.timestamp,
@@ -2,6 +2,7 @@ import { MajikMessageIdentityJSON } from "./database/system/identity";
2
2
  import { ISODateString } from "./types";
3
3
  export type SerializedMajikContact = Omit<MajikContactData, "publicKey"> & {
4
4
  publicKeyBase64: string;
5
+ mlKey: string;
5
6
  };
6
7
  export interface MajikContactMeta {
7
8
  label?: string;
@@ -16,6 +17,7 @@ export interface MajikContactData {
16
17
  raw: Uint8Array;
17
18
  };
18
19
  fingerprint: string;
20
+ mlKey: string;
19
21
  meta?: MajikContactMeta;
20
22
  majikah_registered?: boolean;
21
23
  }
@@ -24,6 +26,7 @@ export interface MajikContactCard {
24
26
  publicKey: string;
25
27
  fingerprint: string;
26
28
  label: string;
29
+ mlKey: string;
27
30
  }
28
31
  export declare class MajikContactError extends Error {
29
32
  cause?: unknown;
@@ -35,12 +38,13 @@ export declare class MajikContact {
35
38
  raw: Uint8Array;
36
39
  };
37
40
  readonly fingerprint: string;
41
+ readonly mlKey: string;
38
42
  meta: MajikContactMeta;
39
43
  private majikah_registered?;
40
44
  constructor(data: MajikContactData);
41
45
  static create(id: string, publicKey: CryptoKey | {
42
46
  raw: Uint8Array;
43
- }, fingerprint: string, meta?: Partial<MajikContactMeta>): MajikContact;
47
+ }, mlKey: string, fingerprint: string, meta?: Partial<MajikContactMeta>): MajikContact;
44
48
  private assertId;
45
49
  private assertPublicKey;
46
50
  private assertFingerprint;
@@ -1,6 +1,3 @@
1
- /* -------------------------------
2
- * Types
3
- * ------------------------------- */
4
1
  import { arrayBufferToBase64, base64ToArrayBuffer } from "./utils";
5
2
  /* -------------------------------
6
3
  * Errors
@@ -20,6 +17,7 @@ export class MajikContact {
20
17
  id;
21
18
  publicKey;
22
19
  fingerprint;
20
+ mlKey;
23
21
  meta;
24
22
  majikah_registered;
25
23
  constructor(data) {
@@ -29,6 +27,7 @@ export class MajikContact {
29
27
  this.id = data.id;
30
28
  this.publicKey = data.publicKey;
31
29
  this.fingerprint = data.fingerprint;
30
+ this.mlKey = data.mlKey;
32
31
  this.meta = {
33
32
  label: data.meta?.label || "",
34
33
  notes: data.meta?.notes || "",
@@ -37,12 +36,13 @@ export class MajikContact {
37
36
  updatedAt: data.meta?.updatedAt || new Date().toISOString(),
38
37
  };
39
38
  }
40
- static create(id, publicKey, fingerprint, meta) {
39
+ static create(id, publicKey, mlKey, fingerprint, meta) {
41
40
  return new MajikContact({
42
41
  id,
43
42
  publicKey,
44
43
  fingerprint,
45
44
  meta,
45
+ mlKey,
46
46
  });
47
47
  }
48
48
  assertId(id) {
@@ -145,6 +145,7 @@ export class MajikContact {
145
145
  meta: { ...this.meta },
146
146
  publicKeyBase64: await this.getPublicKeyBase64(),
147
147
  majikah_registered: this.majikah_registered,
148
+ mlKey: this.mlKey,
148
149
  };
149
150
  }
150
151
  /**
@@ -159,6 +160,7 @@ export class MajikContact {
159
160
  meta: serialized.meta,
160
161
  publicKey: { raw: publicKeyRaw },
161
162
  majikah_registered: serialized.majikah_registered,
163
+ mlKey: serialized.mlKey,
162
164
  });
163
165
  }
164
166
  catch (err) {
@@ -182,6 +184,7 @@ export class MajikContact {
182
184
  blocked: identityJSON.restricted,
183
185
  },
184
186
  majikah_registered: true,
187
+ mlKey: identityJSON.ml_key,
185
188
  };
186
189
  return new MajikContact(contactData);
187
190
  }
@@ -38,7 +38,7 @@ export interface MajikKeyIdentity {
38
38
  encryptedPrivateKey: ArrayBuffer;
39
39
  salt: string;
40
40
  kdfVersion: KDF_VERSION;
41
- mlKemPublicKey?: Uint8Array;
41
+ mlKemPublicKey: Uint8Array;
42
42
  mlKemSecretKey?: Uint8Array;
43
43
  }
44
44
  export interface SerializedIdentity {
@@ -62,7 +62,7 @@ export interface MajikKeyConstructorOptions {
62
62
  label?: string;
63
63
  timestamp?: Date;
64
64
  kdfVersion?: KDF_VERSION;
65
- mlKemPublicKey?: Uint8Array;
65
+ mlKemPublicKey: Uint8Array;
66
66
  mlKemSecretKey?: Uint8Array;
67
67
  encryptedMlKemSecretKey?: ArrayBuffer;
68
68
  encryptedMlKemSecretKeyBase64?: string;
@@ -83,7 +83,7 @@ export declare class MajikKey {
83
83
  private _salt;
84
84
  private _label;
85
85
  private _kdfVersion;
86
- private _mlKemPublicKey?;
86
+ private _mlKemPublicKey;
87
87
  private _mlKemSecretKey?;
88
88
  private _encryptedMlKemSecretKey?;
89
89
  private _encryptedMlKemSecretKeyBase64?;
package/dist/majik-key.js CHANGED
@@ -25,7 +25,7 @@ import { generateMnemonic } from "@scure/bip39";
25
25
  import { aesGcmDecrypt, aesGcmEncrypt, deriveKeyFromPassphraseArgon2, deriveKeyFromMnemonicArgon2, deriveKeyFromPassphrase, generateRandomBytes, IV_LENGTH, } from "./core/crypto/crypto-provider";
26
26
  import { EncryptionEngine } from "./core/crypto/encryption-engine";
27
27
  import { MajikContact } from "./core/majik-contact";
28
- import { arrayBufferToBase64, arrayToBase64, base64ToArrayBuffer, concatUint8Arrays, utf8ToBase64, base64ToUtf8, seedStringToArray, seedArrayToString, } from "./core/utils";
28
+ import { arrayBufferToBase64, arrayToBase64, base64ToArrayBuffer, concatUint8Arrays, utf8ToBase64, base64ToUtf8, seedStringToArray, seedArrayToString, base64ToUint8Array, } from "./core/utils";
29
29
  import { wordlist } from "@scure/bip39/wordlists/english";
30
30
  import { KDF_VERSION, KEY_ALGO, MAJIK_MNEMONIC_SALT, } from "./core/crypto/constants";
31
31
  import { MajikKeyValidator } from "./core/validator";
@@ -171,10 +171,7 @@ export class MajikKey {
171
171
  const anyParsed = parsed;
172
172
  const publicKeyBuffer = base64ToArrayBuffer(validated.publicKey);
173
173
  const encryptedPrivateKeyBuffer = base64ToArrayBuffer(validated.encryptedPrivateKey);
174
- let mlKemPublicKey;
175
- if (anyParsed.mlKemPublicKey) {
176
- mlKemPublicKey = new Uint8Array(base64ToArrayBuffer(anyParsed.mlKemPublicKey));
177
- }
174
+ const mlKemPublicKey = base64ToUint8Array(anyParsed.mlKemPublicKey);
178
175
  let encryptedMlKemSecretKey;
179
176
  let encryptedMlKemSecretKeyBase64;
180
177
  if (anyParsed.encryptedMlKemSecretKey) {
@@ -397,11 +394,13 @@ export class MajikKey {
397
394
  }
398
395
  }
399
396
  toContact() {
397
+ const mlKeyBase64 = arrayToBase64(this._mlKemPublicKey);
400
398
  return new MajikContact({
401
399
  id: this._id,
402
400
  publicKey: this._publicKey,
403
401
  fingerprint: this._fingerprint,
404
402
  meta: { label: this._label },
403
+ mlKey: mlKeyBase64,
405
404
  });
406
405
  }
407
406
  toKeyIdentity() {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@majikah/majik-key",
3
3
  "type": "module",
4
4
  "description": "A post-quantum ready seed phrase account library for the Majikah ecosystem. Manages deterministic X25519 and ML-KEM-768 identities with Argon2id protection and seamless legacy account migration.",
5
- "version": "0.1.2",
5
+ "version": "0.1.4",
6
6
  "license": "Apache-2.0",
7
7
  "author": "Zelijah",
8
8
  "main": "./dist/index.js",