@majikah/majik-key 0.2.12 → 0.2.14
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 +249 -793
- package/dist/core/types.d.ts +94 -3
- package/dist/core/web3/bitcoin/bitcoin.d.ts +71 -0
- package/dist/core/web3/bitcoin/bitcoin.js +126 -0
- package/dist/core/web3/bitcoin/constants.d.ts +2 -0
- package/dist/core/web3/bitcoin/constants.js +7 -0
- package/dist/core/web3/bitcoin/types.d.ts +23 -0
- package/dist/core/web3/bitcoin/types.js +1 -0
- package/dist/core/web3/index.d.ts +5 -0
- package/dist/core/web3/index.js +2 -0
- package/dist/core/web3/{solana.d.ts → solana/solana.d.ts} +0 -1
- package/dist/core/web3/{solana.js → solana/solana.js} +2 -28
- package/dist/core/web3/solana/types.d.ts +18 -0
- package/dist/core/web3/solana/types.js +1 -0
- package/dist/core/web3/types.d.ts +3 -22
- package/dist/core/web3/utils.d.ts +1 -0
- package/dist/core/web3/utils.js +27 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/majik-key.d.ts +232 -47
- package/dist/majik-key.js +281 -55
- package/package.json +13 -2
- /package/dist/core/web3/{constants.d.ts → solana/constants.d.ts} +0 -0
- /package/dist/core/web3/{constants.js → solana/constants.js} +0 -0
package/README.md
CHANGED
|
@@ -1,936 +1,392 @@
|
|
|
1
1
|
# Majik Key
|
|
2
2
|
|
|
3
|
-
[](https://thezelijah.world) 
|
|
4
|
-
|
|
5
|
-
**Majik Key** is a next-generation seed phrase account library for creating and managing mnemonic-based identities. It provides a post-quantum ready, high-security bridge between BIP39 mnemonics and the Majikah ecosystem.
|
|
6
|
-
|
|
7
|
-
   [](https://opensource.org/licenses/Apache-2.0) 
|
|
8
|
-
|
|
3
|
+
[](https://www.thezelijah.world) 
|
|
4
|
+
   [](https://opensource.org/licenses/Apache-2.0)
|
|
9
5
|
|
|
6
|
+
**Majik Key** turns a single BIP-39 mnemonic into a complete cryptographic identity — encryption, classical + post-quantum signing, and (experimentally) Bitcoin and Solana keys — encrypted at rest and ready to plug into the rest of the Majikah ecosystem.
|
|
10
7
|
|
|
11
8
|
---
|
|
12
|
-
- [Majik Key](#majik-key)
|
|
13
|
-
- [Next-Gen Security Architecture](#next-gen-security-architecture)
|
|
14
|
-
- [1. Post-Quantum Ready (ML-KEM)](#1-post-quantum-ready-ml-kem)
|
|
15
|
-
- [2. Argon2id Key Derivation](#2-argon2id-key-derivation)
|
|
16
|
-
- [3. Seamless Auto-Migration](#3-seamless-auto-migration)
|
|
17
|
-
- [Overview](#overview)
|
|
18
|
-
- [What is a Majik Key?](#what-is-a-majik-key)
|
|
19
|
-
- [Use Cases](#use-cases)
|
|
20
|
-
- [Features](#features)
|
|
21
|
-
- [Security \& Post-Quantum Readiness](#security--post-quantum-readiness)
|
|
22
|
-
- [BIP39 Compliance \& Key Derivation](#bip39-compliance--key-derivation)
|
|
23
|
-
- [Developer Experience](#developer-experience)
|
|
24
|
-
- [Import / Export \& Storage](#import--export--storage)
|
|
25
|
-
- [Interoperability](#interoperability)
|
|
26
|
-
- [Installation](#installation)
|
|
27
|
-
- [Quick Start](#quick-start)
|
|
28
|
-
- [API Reference](#api-reference)
|
|
29
|
-
- [Static Methods](#static-methods)
|
|
30
|
-
- [`MajikKey.create(mnemonic, passphrase, label?)`](#majikkeycreatemnemonic-passphrase-label)
|
|
31
|
-
- [`MajikKey.fromJSON(json)`](#majikkeyfromjsonjson)
|
|
32
|
-
- [`MajikKey.fromMnemonicJSON(mnemonicJson, passphrase, label?)`](#majikkeyfrommnemonicjsonmnemonicjson-passphrase-label)
|
|
33
|
-
- [`MajikKey.importFromMnemonicBackup(backup, mnemonic, passphrase, label?)`](#majikkeyimportfrommnemonicbackupbackup-mnemonic-passphrase-label)
|
|
34
|
-
- [`MajikKey.generateMnemonic(strength?)`](#majikkeygeneratemnemonicstrength)
|
|
35
|
-
- [`MajikKey.validateMnemonic(mnemonic)`](#majikkeyvalidatemnemonicmnemonic)
|
|
36
|
-
- [Instance Methods](#instance-methods)
|
|
37
|
-
- [`unlock(passphrase)`](#unlockpassphrase)
|
|
38
|
-
- [`lock()`](#lock)
|
|
39
|
-
- [`verify(passphrase)`](#verifypassphrase)
|
|
40
|
-
- [`updateLabel(newLabel)`](#updatelabelnewlabel)
|
|
41
|
-
- [`updatePassphrase(currentPassphrase, newPassphrase)`](#updatepassphrasecurrentpassphrase-newpassphrase)
|
|
42
|
-
- [`getPrivateKey()`](#getprivatekey)
|
|
43
|
-
- [`getPrivateKeyBase64()`](#getprivatekeybase64)
|
|
44
|
-
- [`toJSON()`](#tojson)
|
|
45
|
-
- [`toString(pretty?)`](#tostringpretty)
|
|
46
|
-
- [`toMnemonicJSON(mnemonic, passphrase?)`](#tomnemonicjsonmnemonic-passphrase)
|
|
47
|
-
- [`exportMnemonicBackup(mnemonic)`](#exportmnemonicbackupmnemonic)
|
|
48
|
-
- [`toContact()`](#tocontact)
|
|
49
|
-
- [`toMajikMessageIdentity(user, options?)`](#tomajikmessageidentityuser-options)
|
|
50
|
-
- [Getters](#getters)
|
|
51
|
-
- [`id: string`](#id-string)
|
|
52
|
-
- [`fingerprint: string`](#fingerprint-string)
|
|
53
|
-
- [`publicKey: CryptoKey | { raw: Uint8Array }`](#publickey-cryptokey---raw-uint8array-)
|
|
54
|
-
- [`publicKeyBase64: string`](#publickeybase64-string)
|
|
55
|
-
- [`label: string`](#label-string)
|
|
56
|
-
- [`backup: string`](#backup-string)
|
|
57
|
-
- [`timestamp: Date`](#timestamp-date)
|
|
58
|
-
- [`isLocked: boolean`](#islocked-boolean)
|
|
59
|
-
- [`isUnlocked: boolean`](#isunlocked-boolean)
|
|
60
|
-
- [`metadata: MajikKeyMetadata`](#metadata-majikkeymetadata)
|
|
61
|
-
- [Usage Examples](#usage-examples)
|
|
62
|
-
- [Example 1: Create and Manage a Key](#example-1-create-and-manage-a-key)
|
|
63
|
-
- [Example 2: Lock/Unlock Pattern](#example-2-lockunlock-pattern)
|
|
64
|
-
- [Example 3: Backup and Recovery](#example-3-backup-and-recovery)
|
|
65
|
-
- [Example 4: Update Passphrase](#example-4-update-passphrase)
|
|
66
|
-
- [Example 5: Verify Passphrase](#example-5-verify-passphrase)
|
|
67
|
-
- [Integration with Majik Message](#integration-with-majik-message)
|
|
68
|
-
- [Importing to Majik Message](#importing-to-majik-message)
|
|
69
|
-
- [Converting to Majik Message Identity](#converting-to-majik-message-identity)
|
|
70
|
-
- [Security Considerations](#security-considerations)
|
|
71
|
-
- [Best Practices](#best-practices)
|
|
72
|
-
- [What NOT to Do](#what-not-to-do)
|
|
73
|
-
- [What TO Do](#what-to-do)
|
|
74
|
-
- [Tips \& Reminders](#tips--reminders)
|
|
75
|
-
- [For Developers](#for-developers)
|
|
76
|
-
- [For Users](#for-users)
|
|
77
|
-
- [Related Projects](#related-projects)
|
|
78
|
-
- [Majik Message](#majik-message)
|
|
79
|
-
- [Contributing](#contributing)
|
|
80
|
-
- [License](#license)
|
|
81
|
-
- [Author](#author)
|
|
82
|
-
- [About the Developer](#about-the-developer)
|
|
83
|
-
- [Contact](#contact)
|
|
84
|
-
|
|
85
9
|
|
|
10
|
+
## Why Majik Key
|
|
86
11
|
|
|
12
|
+
- **One seed, one identity, five key pairs.** A 12- or 24-word mnemonic deterministically derives X25519, ML-KEM-768, Ed25519, ML-DSA-87, and (by default) a domain-separated Bitcoin key — all reproducible from the mnemonic alone.
|
|
13
|
+
- **Post-quantum from day one.** Every account gets an ML-KEM-768 (FIPS-203) encryption keypair and an ML-DSA-87 signing keypair alongside their classical counterparts (X25519, Ed25519) — no separate migration project required later.
|
|
14
|
+
- **Encrypted at rest, always.** Private key material is never persisted in plaintext. Everything is AES-256-GCM encrypted using a key derived with Argon2id.
|
|
15
|
+
- **Local-first.** Key generation and derivation run entirely offline — no network request is made in the process, verifiable directly in source.
|
|
16
|
+
- **Built for the Majikah ecosystem**, but usable standalone in any TypeScript/JavaScript project.
|
|
87
17
|
|
|
88
18
|
---
|
|
89
19
|
|
|
90
|
-
##
|
|
20
|
+
## Security Architecture
|
|
91
21
|
|
|
92
|
-
|
|
22
|
+
- **Encrypted at rest, not "hashed."** Private keys are **AES-256-GCM encrypted**, using a 256-bit key **derived via Argon2id** from your passphrase. (Argon2id is a key-derivation function, not applied to the private key directly — the private key itself is encrypted, not hashed.)
|
|
23
|
+
- **Argon2id KDF (v2), memory-hard by design.** Passphrase-based encryption uses Argon2id at **64 MB memory / 3 iterations / 4 parallel lanes**, tuned to resist GPU/ASIC brute-force attacks. A WASM implementation (`hash-wasm`) is used when available in the runtime, with an automatic, transparent fallback to a pure-JS implementation (`@noble/hashes`) — output is bit-identical either way, so switching implementations never breaks decryption.
|
|
24
|
+
- **Post-quantum ready.** ML-KEM-768 (FIPS-203) is derived from the full 64-byte BIP-39 seed for encryption/key-encapsulation, and ML-DSA-87 is derived from a domain-separated hash of that same seed for signing — both deterministic and fully recoverable from the mnemonic.
|
|
25
|
+
- **Legacy KDF read support.** Older accounts encrypted with KDF v1 (PBKDF2-SHA256, 200k–250k iterations) can still be unlocked. New accounts, and any account whose passphrase is changed via `updatePassphrase()`, always land on Argon2id (v2).
|
|
26
|
+
- **Full migration path.** `importFromMnemonicBackup()` re-derives a complete identity straight from the mnemonic — X25519, ML-KEM-768, Ed25519, ML-DSA-87, and Bitcoin — and re-encrypts everything with Argon2id in one step, so an old account becomes fully post-quantum capable automatically. A lighter `migrate()` method is also available if you only want to upgrade the KDF version without re-deriving the newer key types.
|
|
27
|
+
- **Isomorphic by design.** Uses native WebCrypto (ECDH/X25519) where the runtime supports it, and transparently falls back to a raw keypair representation where it doesn't (e.g. Node environments without X25519 in WebCrypto) — the public API is identical either way.
|
|
28
|
+
- **Multi-language mnemonics.** BIP-39 wordlists for English, French, Spanish, Italian, Japanese, Korean, Czech, Portuguese, Simplified Chinese, and Traditional Chinese are supported, lazy-loaded per language so you only pay for the ones you use.
|
|
93
29
|
|
|
94
|
-
### 1. Post-Quantum Ready (ML-KEM)
|
|
95
|
-
Every identity now generates a dual-key system derived deterministically from a 64-byte BIP39 seed:
|
|
96
|
-
* **X25519 (Curve25519):** Derived from the first 32 bytes of the seed. Used for fingerprints, contact identity, and legacy compatibility.
|
|
97
|
-
* **ML-KEM-768 (FIPS-203):** Derived from the full 64-byte seed. Provides post-quantum key encapsulation for "v3" secure envelopes.
|
|
98
|
-
|
|
99
|
-
### 2. Argon2id Key Derivation
|
|
100
|
-
We have transitioned to **Argon2id** (KDF v2) for encrypting private keys at rest.
|
|
101
|
-
* **Memory-Hard:** Configured with 64 MB of memory, 3 iterations, and 4 parallelism factors.
|
|
102
|
-
* **Brute-Force Resistant:** Engineered to defeat GPU and ASIC-based cracking attempts that easily bypass older PBKDF2 implementations.
|
|
103
|
-
|
|
104
|
-
### 3. Seamless Auto-Migration
|
|
105
|
-
The library handles "Security Debt" automatically during standard workflows:
|
|
106
|
-
* **On Import:** `importFromMnemonicBackup()` detects v1 (PBKDF2) accounts and performs a full upgrade to v2.
|
|
107
|
-
* **Deterministic Recovery:** If ML-KEM keys are missing from an old backup, they are re-derived from the mnemonic during the upgrade process.
|
|
108
|
-
* **Password Updates:** Changing a passphrase via `updatePassphrase()` automatically migrates the account to the latest Argon2id standard.
|
|
109
30
|
---
|
|
110
31
|
|
|
32
|
+
## Architecture
|
|
111
33
|
|
|
34
|
+
```mermaid
|
|
35
|
+
flowchart TD
|
|
36
|
+
A[12/24-word BIP-39 Seed Phrase] --> B[Majik Key]
|
|
112
37
|
|
|
113
|
-
|
|
38
|
+
%% Signing branch
|
|
39
|
+
B --> S[Signing]
|
|
40
|
+
S --> S1[Ed25519]
|
|
41
|
+
S --> S2[ML-DSA-87]
|
|
114
42
|
|
|
115
|
-
|
|
43
|
+
%% Encryption branch
|
|
44
|
+
B --> E[Encryption]
|
|
45
|
+
E --> E1[ML-KEM-768]
|
|
46
|
+
E --> E2[AES-256-GCM]
|
|
116
47
|
|
|
117
|
-
|
|
48
|
+
%% Identity branch
|
|
49
|
+
B --> I[Identity]
|
|
50
|
+
I --> I1[BIP-39]
|
|
51
|
+
I --> I2[X25519]
|
|
118
52
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
- Provides locked/unlocked state management for enhanced security
|
|
124
|
-
- Is fully compatible with **Majik Message** and other Majikah products
|
|
53
|
+
%% Experimental Web3 branch
|
|
54
|
+
B -.-> W[Web3 - Experimental]
|
|
55
|
+
W -.-> W1[Bitcoin - BIP-32/84]
|
|
56
|
+
W -.-> W2[Solana - Ed25519-derived]
|
|
125
57
|
|
|
126
|
-
|
|
58
|
+
%% Products (fan-in)
|
|
59
|
+
S1 --> P1[Majik Signature]
|
|
60
|
+
S2 --> P1
|
|
127
61
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
62
|
+
S1 --> P2[Majik Buwiz]
|
|
63
|
+
S2 --> P2
|
|
64
|
+
E1 --> P2
|
|
65
|
+
E2 --> P2
|
|
66
|
+
I1 --> P2
|
|
67
|
+
I2 --> P2
|
|
68
|
+
|
|
133
69
|
|
|
134
|
-
|
|
70
|
+
E1 --> P3[Majik Message]
|
|
71
|
+
E2 --> P3
|
|
135
72
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
### Security & Post-Quantum Readiness
|
|
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 (64 MB / 3 iterations / 4 parallelism), providing industry-leading resistance to GPU/ASIC brute-force attacks.
|
|
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
|
-
- **AES-GCM Authenticated Encryption**: Industry-standard encryption for private keys at rest with unique, per-identity salts and random IVs.
|
|
143
|
-
- **Locked/Unlocked States**: Private keys are only decrypted into memory when explicitly unlocked and are purged immediately upon calling `.lock()`.
|
|
144
|
-
|
|
145
|
-
### BIP39 Compliance & Key Derivation
|
|
146
|
-
- **Standard Mnemonic Generation**: Generate high-entropy 12 or 24-word seed phrases (128/256-bit strength).
|
|
147
|
-
- **Deterministic Multi-Key Derivation**:
|
|
148
|
-
- **X25519 (Curve25519)**: Derived from the first 32 bytes of the seed for legacy compatibility and fingerprints.
|
|
149
|
-
- **ML-KEM-768**: Derived from the full 64-byte seed for post-quantum security.
|
|
150
|
-
- **Built-in Validation**: Full BIP39 mnemonic validation and error handling.
|
|
151
|
-
|
|
152
|
-
### Developer Experience
|
|
153
|
-
- **First-Class TypeScript Support**: Full type definitions included for all interfaces and classes.
|
|
154
|
-
- **Fluent API**: Intuitive method chaining for common operations (e.g., `key.unlock(p).updateLabel(l)`).
|
|
155
|
-
- **Comprehensive Error Handling**: Specialized `MajikKeyError` and `CryptoError` classes for precise debugging.
|
|
156
|
-
- **Isomorphic Support**: Works across Node.js and modern browser environments.
|
|
157
|
-
|
|
158
|
-
### Import / Export & Storage
|
|
159
|
-
- **Security-Minded JSON Serialization**: Export accounts to JSON format for storage without ever exposing raw private keys or seed phrases.
|
|
160
|
-
- **MnemonicJSON Format**: A secure, portable format for seed phrase storage and recovery.
|
|
161
|
-
- **Mnemonic-Encrypted Backups**: Export and import specialized backup strings that utilize the mnemonic as a secondary encryption layer.
|
|
162
|
-
|
|
163
|
-
### Interoperability
|
|
164
|
-
- **Majik Message Integration**: Native support for exporting identities compatible with **Majik Message v3** envelopes.
|
|
165
|
-
- **Contact Portability**: Convert Majik Keys directly into contact formats for easy sharing of public identities.
|
|
166
|
-
- **Ecosystem Ready**: Designed as the core identity provider for all current and future Majikah products.
|
|
167
|
-
---
|
|
168
|
-
|
|
169
|
-
## Installation
|
|
170
|
-
|
|
171
|
-
```bash
|
|
172
|
-
# Using npm
|
|
173
|
-
npm install @majikah/majik-key
|
|
73
|
+
I1 --> P4[Majik Universal ID]
|
|
74
|
+
I2 --> P4
|
|
174
75
|
|
|
76
|
+
P4 --> P5[Majik SLink]
|
|
175
77
|
```
|
|
176
78
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
## Quick Start
|
|
180
|
-
|
|
181
|
-
```ts
|
|
182
|
-
import { MajikKey } from '@majikah/majik-key';
|
|
183
|
-
|
|
184
|
-
// Generate a new mnemonic
|
|
185
|
-
const mnemonic = MajikKey.generateMnemonic(); // 12 words
|
|
186
|
-
console.log('Save this mnemonic:', mnemonic);
|
|
187
|
-
|
|
188
|
-
// Create a new Majik Key (unlocked state)
|
|
189
|
-
const key = await MajikKey.create(mnemonic, 'secure-passphrase', 'My PQ Account');
|
|
190
|
-
|
|
191
|
-
// 2. Access your keys (requires unlock)
|
|
192
|
-
console.log('Fingerprint:', key.fingerprint);
|
|
193
|
-
console.log('PQ Ready:', key.metadata.kdfVersion); // 'argon2id'
|
|
194
|
-
console.log('Key ID:', key.id);
|
|
195
|
-
console.log('Is Unlocked:', key.isUnlocked); // true
|
|
196
|
-
|
|
197
|
-
// Lock the key (clear private keys from memory)
|
|
198
|
-
key.lock();
|
|
199
|
-
console.log('Is Locked:', key.isLocked); // true
|
|
200
|
-
|
|
201
|
-
// Unlock when needed
|
|
202
|
-
await key.unlock('my-secure-passphrase');
|
|
203
|
-
console.log('Is Unlocked:', key.isUnlocked); // true
|
|
204
|
-
|
|
205
|
-
// Access private key (only when unlocked)
|
|
206
|
-
const privateKey = key.getPrivateKey();
|
|
207
|
-
const privateKeyBase64 = key.getPrivateKeyBase64();
|
|
208
|
-
|
|
209
|
-
// Save to storage (private keys never included)
|
|
210
|
-
const json = key.toJSON();
|
|
211
|
-
localStorage.setItem('myKey', JSON.stringify(json));
|
|
212
|
-
|
|
213
|
-
// Load from storage (locked state)
|
|
214
|
-
const loadedKey = MajikKey.fromJSON(json);
|
|
215
|
-
await loadedKey.unlock('my-secure-passphrase');
|
|
216
|
-
```
|
|
79
|
+
Your Majik Key is generated entirely offline. No network request is made during key creation — verifiable in source code.
|
|
217
80
|
|
|
218
81
|
---
|
|
219
82
|
|
|
220
|
-
##
|
|
221
|
-
|
|
222
|
-
### Static Methods
|
|
83
|
+
## Powering the Majikah Ecosystem
|
|
223
84
|
|
|
224
|
-
|
|
225
|
-
Create a new Majik Key from a mnemonic phrase. Generates a new Argon2id-protected account.
|
|
85
|
+
Majik Key is the shared identity layer underneath every Majikah product. Here's what each one draws from it.
|
|
226
86
|
|
|
227
|
-
|
|
228
|
-
- `mnemonic: string` - BIP39 mnemonic phrase (12-24 words)
|
|
229
|
-
- `passphrase: string` - Passphrase to encrypt the private key at rest
|
|
230
|
-
- `label?: string` - Optional label for the key
|
|
87
|
+
### [Majik Signature](https://majikah.solutions/products/majik-signature) — Flagship
|
|
231
88
|
|
|
232
|
-
**
|
|
89
|
+
**Post-quantum cryptographic file signing and verification.**
|
|
233
90
|
|
|
234
|
-
|
|
235
|
-
```ts
|
|
236
|
-
const mnemonic = 'witch collapse practice feed shame open despair creek road again ice least';
|
|
237
|
-
const key = await MajikKey.create(mnemonic, 'my-password', 'Personal Account');
|
|
91
|
+
[](https://www.npmjs.com/package/@majikah/majik-signature) [](https://www.npmjs.com/package/@majikah/majik-signature) [](https://bundlephobia.com/package/@majikah/majik-signature) [](https://opensource.org/licenses/Apache-2.0)
|
|
238
92
|
|
|
239
|
-
|
|
93
|
+
[](https://signature.majikah.solutions)
|
|
240
94
|
|
|
241
|
-
|
|
95
|
+
Majik Signature consumes a Majik Key's **Ed25519** and **ML-DSA-87** signing keypairs to require *both* a classical and a post-quantum signature before a file verifies — hybrid security with forward secrecy against future quantum attacks on either scheme alone.
|
|
242
96
|
|
|
243
|
-
|
|
244
|
-
|
|
97
|
+
```typescript
|
|
98
|
+
import { MajikKey } from '@majikah/majik-key';
|
|
99
|
+
import { MajikSignature } from '@majikah/majik-signature';
|
|
245
100
|
|
|
246
|
-
|
|
247
|
-
|
|
101
|
+
// 1. Sign a file and embed the signature (requires an unlocked key with signing keys)
|
|
102
|
+
const { blob, signature } = await MajikSignature.signFile(myFileBlob, myUnlockedKey, {
|
|
103
|
+
// Optional: restrict future signers
|
|
104
|
+
expectedSigners: [ MajikSignature.expectedSignerFromKey(myUnlockedKey) ]
|
|
105
|
+
});
|
|
248
106
|
|
|
249
|
-
|
|
107
|
+
// 2. Verify a signed file's embedded signatures
|
|
108
|
+
const results = await MajikSignature.verifyFile(blob, myUnlockedKey);
|
|
109
|
+
results.forEach(res => {
|
|
110
|
+
console.log(`Signer ${res.signerId} valid?`, res.valid);
|
|
111
|
+
});
|
|
250
112
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const key = MajikKey.fromJSON(json);
|
|
255
|
-
await key.unlock('my-password');
|
|
113
|
+
// 3. Seal a multi-sig file to prevent further signatures
|
|
114
|
+
const { sealInfo } = await MajikSignature.seal(blob, myUnlockedKey);
|
|
115
|
+
console.log("File sealed at:", sealInfo.sealTimestamp);
|
|
256
116
|
```
|
|
257
117
|
|
|
258
|
-
|
|
118
|
+
### Majik Message
|
|
259
119
|
|
|
260
|
-
|
|
261
|
-
Create a Majik Key from MnemonicJSON format. Auto-migrates legacy PBKDF2 accounts to Argon2id + ML-KEM.
|
|
120
|
+
**Post-quantum secure messaging envelopes.**
|
|
262
121
|
|
|
263
|
-
|
|
264
|
-
- `mnemonicJson: MnemonicJSON | string` - MnemonicJSON object or string
|
|
265
|
-
- `passphrase: string` - Passphrase to encrypt the key at rest
|
|
266
|
-
- `label?: string` - Optional label for the key
|
|
122
|
+
Majik Key derives an ML-KEM-768 keypair specifically so it can be used for Majik Message's v3 secure envelopes — the ML-KEM-768 keypair handles post-quantum key encapsulation, and AES-256-GCM handles the actual payload encryption once a shared secret is established.
|
|
267
123
|
|
|
268
|
-
|
|
124
|
+
Majik Key ships a direct integration point for this: `toMajikMessageIdentity()` converts an unlocked key into a `MajikMessageIdentity`, ready to hand to Majik Message.
|
|
269
125
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const mnemonicData = {
|
|
273
|
-
id: 'backup-id',
|
|
274
|
-
seed: ['word1', 'word2', ...],
|
|
275
|
-
phrase: 'optional-encryption-phrase'
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
const key = await MajikKey.fromMnemonicJSON(mnemonicData, 'my-password');
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
---
|
|
126
|
+
```typescript
|
|
127
|
+
import { MajikKey } from '@majikah/majik-key';
|
|
282
128
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
- `mnemonic: string` - The mnemonic phrase used to encrypt the backup
|
|
289
|
-
- `passphrase: string` - Passphrase to encrypt the imported key
|
|
290
|
-
- `label?: string` - Optional label for the key
|
|
291
|
-
|
|
292
|
-
**Returns:** `Promise<MajikKey>` - A new unlocked MajikKey instance
|
|
293
|
-
|
|
294
|
-
**Example:**
|
|
295
|
-
```ts
|
|
296
|
-
const backupString = 'eT8xY2F...'; // From exportMnemonicBackup()
|
|
297
|
-
const key = await MajikKey.importFromMnemonicBackup(
|
|
298
|
-
backupString,
|
|
299
|
-
mnemonic,
|
|
300
|
-
'new-password',
|
|
301
|
-
'Restored Account'
|
|
302
|
-
);
|
|
129
|
+
// user: an existing MajikUser instance (from @thezelijah/majik-user)
|
|
130
|
+
const identity = await key.toMajikMessageIdentity(user, {
|
|
131
|
+
label: 'My Device',
|
|
132
|
+
restricted: false,
|
|
133
|
+
});
|
|
303
134
|
```
|
|
304
135
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
#### `MajikKey.generateMnemonic(strength?)`
|
|
308
|
-
Generate a new BIP39 mnemonic phrase.
|
|
309
|
-
|
|
310
|
-
**Parameters:**
|
|
311
|
-
- `strength?: 128 | 256` - Entropy strength (128 = 12 words, 256 = 24 words). Default: 128
|
|
312
|
-
|
|
313
|
-
**Returns:** `string` - A new mnemonic phrase
|
|
314
|
-
|
|
315
|
-
**Example:**
|
|
316
|
-
```ts
|
|
317
|
-
const mnemonic12 = MajikKey.generateMnemonic(); // 12 words
|
|
318
|
-
const mnemonic24 = MajikKey.generateMnemonic(256); // 24 words
|
|
319
|
-
```
|
|
136
|
+
### Majik Buwiz
|
|
320
137
|
|
|
321
|
-
|
|
138
|
+
**Multi-key custody built on the full Majik Key stack.**
|
|
322
139
|
|
|
323
|
-
|
|
324
|
-
Validate a BIP39 mnemonic phrase.
|
|
140
|
+
Majik Buwiz is built on Majik Key's complete key set: Ed25519/ML-DSA-87 for signing, ML-KEM-768/AES-256-GCM for encryption, and X25519/BIP-39 for identity — plus the experimental Bitcoin and Solana keys described below for multi-chain support. Everything a Buwiz account needs is derivable from, and recoverable with, the same mnemonic.
|
|
325
141
|
|
|
326
|
-
|
|
327
|
-
- `mnemonic: string` - Mnemonic phrase to validate
|
|
142
|
+
### Majik Universal ID & Majik SLink
|
|
328
143
|
|
|
329
|
-
**
|
|
144
|
+
**A portable identity primitive, and shareable links built on top of it.**
|
|
330
145
|
|
|
331
|
-
|
|
332
|
-
```ts
|
|
333
|
-
const isValid = MajikKey.validateMnemonic('witch collapse practice...');
|
|
334
|
-
```
|
|
146
|
+
Majik Universal ID is built on the Identity branch of Majik Key — the BIP-39-derived X25519 keypair, public key, and fingerprint, exportable via `toContact()` as a `MajikContact` for use across apps. Majik SLink extends that identity layer downstream, per the architecture above. Both are separate Majikah packages; consult [majikah.solutions](https://majikah.solutions) for the latest on their APIs.
|
|
335
147
|
|
|
336
148
|
---
|
|
337
149
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
#### `unlock(passphrase)`
|
|
341
|
-
Unlock the Majik Key by decrypting the private key. Decrypts X25519 and ML-KEM private keys into memory.
|
|
342
|
-
|
|
343
|
-
**Parameters:**
|
|
344
|
-
- `passphrase: string` - Passphrase to decrypt the private key
|
|
150
|
+
## Experimental Web3 Support
|
|
345
151
|
|
|
346
|
-
**
|
|
347
|
-
|
|
348
|
-
**Throws:** `MajikKeyError` if passphrase is incorrect or key is already unlocked
|
|
349
|
-
|
|
350
|
-
**Example:**
|
|
351
|
-
```ts
|
|
352
|
-
await key.unlock('my-password');
|
|
353
|
-
```
|
|
152
|
+
Majik Key can derive **Bitcoin** and **Solana** key material directly from the same mnemonic. This is marked experimental — the shape of the `web3` namespace may change without a major version bump.
|
|
354
153
|
|
|
355
|
-
|
|
154
|
+
### What's built in vs. what needs an extra install
|
|
356
155
|
|
|
357
|
-
|
|
358
|
-
|
|
156
|
+
- **Bitcoin key derivation is automatic.** Every account created via `create()` or `importFromMnemonicBackup()` also derives and encrypts a Bitcoin keypair (real BIP-32 HD derivation off the raw 64-byte seed, using a Majik-specific domain-separated path by default). This works out of the box — no extra install needed for the private key, public key, or WIF export.
|
|
157
|
+
- **Solana key derivation is on-demand.** Rather than storing a separate Solana keypair, Majik Key derives it deterministically from your Ed25519 signing key each time you access `key.web3.solana` (and caches it in memory for as long as the key stays unlocked). The base58 Solana address also works with no extra install.
|
|
158
|
+
- **The optional peer dependencies are only needed for chain-native address/transaction objects:**
|
|
359
159
|
|
|
360
|
-
|
|
160
|
+
| Chain | Peer dependency | Needed for |
|
|
161
|
+
| :--- | :--- | :--- |
|
|
162
|
+
| Bitcoin | `@scure/btc-signer` | Native SegWit (bech32) address encoding, PSBT construction |
|
|
163
|
+
| Solana | `@solana/kit` | Real `KeyPairSigner` instances, kit-native `Address` type |
|
|
361
164
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
165
|
+
```bash
|
|
166
|
+
npm install @scure/btc-signer # for Bitcoin addresses
|
|
167
|
+
npm install @solana/kit # for Solana signer/address objects
|
|
365
168
|
```
|
|
366
169
|
|
|
367
|
-
|
|
170
|
+
Everything else — raw key bytes, WIF export, message signing (ECDSA/Schnorr for Bitcoin, Ed25519 for Solana), and base58 Solana addresses — works with zero extra dependencies.
|
|
368
171
|
|
|
369
|
-
|
|
370
|
-
Verify that a passphrase can decrypt the private key.
|
|
172
|
+
### Two Bitcoin paths, on purpose
|
|
371
173
|
|
|
372
|
-
|
|
373
|
-
- `passphrase: string` - Passphrase to verify
|
|
174
|
+
By default, Bitcoin keys use `MAJIK_BITCOIN_DOMAIN_PATH` — a real, standard BIP-32 derivation, but not the path a generic wallet would derive by default, so it stays effectively private to Majik. Pass `{ standard: true }` to derive the actual BIP-84 mainnet path instead — the address any standard wallet would show for the same mnemonic:
|
|
374
175
|
|
|
375
|
-
|
|
176
|
+
```typescript
|
|
177
|
+
// Majik's default (domain-separated, stored on the key)
|
|
178
|
+
const wif = key.getBitcoinWIF();
|
|
376
179
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
const isValid = await key.verify('my-password');
|
|
180
|
+
// The real BIP-84 mainnet key — recoverable in any standard wallet
|
|
181
|
+
const standardBtc = await MajikKey.deriveStandardBitcoinFromMnemonic(mnemonic);
|
|
380
182
|
```
|
|
381
183
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
#### `updateLabel(newLabel)`
|
|
385
|
-
Update the label of the Majik Key.
|
|
184
|
+
### Two Solana paths, on purpose
|
|
386
185
|
|
|
387
|
-
|
|
388
|
-
- `newLabel: string` - New label value
|
|
186
|
+
By default (`deriveSolanaKeypairFromEdSecretKey`), the Solana keypair is domain-separated from your Ed25519 message-signing key via `SHA256(edSeed || "MajikMessageSolanaSeed")`, so the same private key never secures two different protocols. You can opt into reusing your Ed25519 message-signing key directly instead:
|
|
389
187
|
|
|
390
|
-
|
|
188
|
+
```typescript
|
|
189
|
+
// Recommended: domain-separated Solana key
|
|
190
|
+
const solanaAddress = key.getSolanaAddress();
|
|
391
191
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
key.updateLabel('Work Account');
|
|
192
|
+
// Opt-in: reuse the Ed25519 message-signing key as-is (not recommended)
|
|
193
|
+
const reusedAddress = key.getSolanaAddress({ reuseMessageKey: true });
|
|
395
194
|
```
|
|
396
195
|
|
|
397
196
|
---
|
|
398
197
|
|
|
399
|
-
|
|
400
|
-
Change the passphrase used to encrypt the private key. Re-encrypts keys and triggers an auto-migration to KDF v2.
|
|
401
|
-
|
|
402
|
-
**Parameters:**
|
|
403
|
-
- `currentPassphrase: string` - Current passphrase
|
|
404
|
-
- `newPassphrase: string` - New passphrase
|
|
405
|
-
|
|
406
|
-
**Returns:** `Promise<this>` - This instance for chaining
|
|
407
|
-
|
|
408
|
-
**Throws:** `MajikKeyError` if current passphrase is incorrect
|
|
198
|
+
## Installation
|
|
409
199
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
await key.updatePassphrase('old-password', 'new-password');
|
|
200
|
+
```bash
|
|
201
|
+
npm install @majikah/majik-key
|
|
413
202
|
```
|
|
414
203
|
|
|
415
204
|
---
|
|
416
205
|
|
|
417
|
-
|
|
418
|
-
Get the private key (only when unlocked).
|
|
419
|
-
|
|
420
|
-
**Returns:** `CryptoKey | { raw: Uint8Array }` - The private key
|
|
421
|
-
|
|
422
|
-
**Throws:** `MajikKeyError` if the key is locked
|
|
423
|
-
|
|
424
|
-
**Example:**
|
|
425
|
-
```ts
|
|
426
|
-
const privateKey = key.getPrivateKey();
|
|
427
|
-
```
|
|
206
|
+
## Quick Start (Core Identity)
|
|
428
207
|
|
|
429
|
-
|
|
208
|
+
```typescript
|
|
209
|
+
import { MajikKey } from '@majikah/majik-key';
|
|
430
210
|
|
|
431
|
-
|
|
432
|
-
|
|
211
|
+
// 1. Generate & Create
|
|
212
|
+
const mnemonic = await MajikKey.generateMnemonic(); // 12 words (128-bit)
|
|
213
|
+
const key = await MajikKey.create(mnemonic, 'super-secure-passphrase', 'My PQ Account');
|
|
433
214
|
|
|
434
|
-
|
|
215
|
+
// 2. Access Identity
|
|
216
|
+
console.log('Fingerprint:', key.fingerprint);
|
|
217
|
+
console.log('Key ID:', key.id);
|
|
218
|
+
console.log('Unlocked?', key.isUnlocked); // true — create() returns an already-unlocked key
|
|
435
219
|
|
|
436
|
-
|
|
220
|
+
// 3. Lock to purge private key material from memory
|
|
221
|
+
key.lock();
|
|
437
222
|
|
|
438
|
-
|
|
439
|
-
|
|
223
|
+
// 4. Unlock again when cryptographic operations are needed
|
|
224
|
+
await key.unlock('super-secure-passphrase');
|
|
440
225
|
const privateKeyBase64 = key.getPrivateKeyBase64();
|
|
441
|
-
```
|
|
442
226
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
#### `toJSON()`
|
|
446
|
-
Export to JSON format (safe for storage).
|
|
447
|
-
|
|
448
|
-
**Returns:** `MajikKeyJSON` - JSON representation (private keys never included)
|
|
449
|
-
|
|
450
|
-
**Example:**
|
|
451
|
-
```ts
|
|
452
|
-
const json = key.toJSON();
|
|
453
|
-
localStorage.setItem('myKey', JSON.stringify(json));
|
|
227
|
+
// 5. Safe storage — toJSON()/toString() never include raw private keys
|
|
228
|
+
localStorage.setItem('myKey', key.toString());
|
|
454
229
|
```
|
|
455
230
|
|
|
456
231
|
---
|
|
457
232
|
|
|
458
|
-
|
|
459
|
-
Export to JSON string.
|
|
460
|
-
|
|
461
|
-
**Parameters:**
|
|
462
|
-
- `pretty?: boolean` - Whether to pretty-print. Default: false
|
|
463
|
-
|
|
464
|
-
**Returns:** `string` - JSON string representation
|
|
465
|
-
|
|
466
|
-
**Example:**
|
|
467
|
-
```ts
|
|
468
|
-
const jsonString = key.toString(true);
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
---
|
|
472
|
-
|
|
473
|
-
#### `toMnemonicJSON(mnemonic, passphrase?)`
|
|
474
|
-
Export to MnemonicJSON format.
|
|
475
|
-
|
|
476
|
-
**Parameters:**
|
|
477
|
-
- `mnemonic: string` - The BIP39 mnemonic phrase
|
|
478
|
-
- `passphrase?: string` - Optional passphrase
|
|
479
|
-
|
|
480
|
-
**Returns:** `MnemonicJSON` - MnemonicJSON object
|
|
481
|
-
|
|
482
|
-
**Throws:** `MajikKeyError` if the key is locked
|
|
483
|
-
|
|
484
|
-
**Example:**
|
|
485
|
-
```ts
|
|
486
|
-
const mnemonicData = key.toMnemonicJSON(mnemonic, 'encryption-phrase');
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
---
|
|
490
|
-
|
|
491
|
-
#### `exportMnemonicBackup(mnemonic)`
|
|
492
|
-
Export a mnemonic-encrypted backup.
|
|
493
|
-
|
|
494
|
-
**Parameters:**
|
|
495
|
-
- `mnemonic: string` - The original mnemonic phrase
|
|
496
|
-
|
|
497
|
-
**Returns:** `Promise<string>` - Base64-encoded backup string
|
|
498
|
-
|
|
499
|
-
**Throws:** `MajikKeyError` if the key is locked
|
|
500
|
-
|
|
501
|
-
**Example:**
|
|
502
|
-
```ts
|
|
503
|
-
const backup = await key.exportMnemonicBackup(mnemonic);
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
---
|
|
507
|
-
|
|
508
|
-
#### `toContact()`
|
|
509
|
-
Create a MajikContact from this Majik Key.
|
|
510
|
-
|
|
511
|
-
**Returns:** `MajikContact` - A MajikContact instance
|
|
512
|
-
|
|
513
|
-
**Example:**
|
|
514
|
-
```ts
|
|
515
|
-
const contact = key.toContact();
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
---
|
|
519
|
-
|
|
520
|
-
#### `toMajikMessageIdentity(user, options?)`
|
|
521
|
-
Convert to MajikMessageIdentity for use in Majik Message.
|
|
522
|
-
|
|
523
|
-
**Parameters:**
|
|
524
|
-
- `user: MajikUser` - MajikUser instance
|
|
525
|
-
- `options?: { label?: string, restricted?: boolean }` - Optional configuration
|
|
526
|
-
|
|
527
|
-
**Returns:** `Promise<MajikMessageIdentity>` - MajikMessageIdentity instance
|
|
528
|
-
|
|
529
|
-
**Example:**
|
|
530
|
-
```ts
|
|
531
|
-
const identity = await key.toMajikMessageIdentity(user, {
|
|
532
|
-
label: 'My Account',
|
|
533
|
-
restricted: false
|
|
534
|
-
});
|
|
535
|
-
```
|
|
233
|
+
## API Reference
|
|
536
234
|
|
|
537
|
-
|
|
235
|
+
### Static Methods (Lifecycle & Generation)
|
|
538
236
|
|
|
539
|
-
|
|
237
|
+
| Method | Parameters | Returns | Description |
|
|
238
|
+
| :--- | :--- | :--- | :--- |
|
|
239
|
+
| `create()` | `mnemonic`, `passphrase`, `label?`, `mnemonicLanguage?` | `Promise<MajikKey>` | Creates a new Argon2id-protected, fully post-quantum-capable account. |
|
|
240
|
+
| `fromJSON()` | `json` | `MajikKey` | Loads a locked key from safe JSON storage. |
|
|
241
|
+
| `fromMnemonicJSON()` | `mnemonicJson`, `passphrase`, `label?` | `Promise<MajikKey>` | Rebuilds a key straight from a portable seed export. |
|
|
242
|
+
| `importFromMnemonicBackup()` | `backup`, `mnemonic`, `passphrase`, `label?`, `mnemonicLanguage?` | `Promise<MajikKey>` | Full migration path — verifies the mnemonic, then re-derives and re-encrypts the complete identity with Argon2id. |
|
|
243
|
+
| `fromDangerousJSON()` | `json` | `MajikKey` | Reconstructs an already-unlocked key from a dangerous export. Server-side only — see warning below. |
|
|
244
|
+
| `generateMnemonic()` | `strength?` *(128 \| 256)*, `language?` | `Promise<string>` | Generates a 12- or 24-word BIP-39 phrase. |
|
|
245
|
+
| `validateMnemonic()` | `mnemonic` | `boolean` | Validates a BIP-39 mnemonic phrase. |
|
|
246
|
+
| `deriveStandardBitcoinFromMnemonic()` *(experimental)* | `mnemonic`, `mnemonicLanguage?` | `Promise<BitcoinKeypairMaterial>` | Derives the real BIP-84 mainnet Bitcoin key without needing a `MajikKey` instance. |
|
|
540
247
|
|
|
541
|
-
|
|
542
|
-
The unique identifier (fingerprint).
|
|
248
|
+
### Instance Methods (State & Management)
|
|
543
249
|
|
|
544
|
-
|
|
545
|
-
|
|
250
|
+
| Method | Parameters | Returns | Description |
|
|
251
|
+
| :--- | :--- | :--- | :--- |
|
|
252
|
+
| `unlock()` | `passphrase` | `Promise<this>` | Decrypts keys into memory. Chainable. |
|
|
253
|
+
| `lock()` | None | `this` | Purges all private key material (including cached Web3 keys) from memory. Chainable. |
|
|
254
|
+
| `verify()` | `passphrase` | `Promise<boolean>` | Tests a passphrase without keeping keys in memory or requiring an unlock. |
|
|
255
|
+
| `updatePassphrase()` | `currentPass`, `newPass` | `Promise<this>` | Re-encrypts every stored key under a new passphrase and migrates to KDF v2 if needed. |
|
|
256
|
+
| `migrate()` | `passphrase` | `Promise<this>` | Upgrades the X25519 key's KDF from v1 to v2 only — does **not** add ML-KEM/Ed25519/ML-DSA/Bitcoin keys. Use `importFromMnemonicBackup()` for a full upgrade. |
|
|
257
|
+
| `updateLabel()` | `newLabel` | `this` | Updates the human-readable account label. |
|
|
546
258
|
|
|
547
|
-
|
|
548
|
-
The public key.
|
|
259
|
+
### Export & Integration Methods
|
|
549
260
|
|
|
550
|
-
|
|
551
|
-
|
|
261
|
+
| Method | Returns | Description |
|
|
262
|
+
| :--- | :--- | :--- |
|
|
263
|
+
| `toJSON()` / `toString()` | `MajikKeyJSON` / `string` | Safe export for DB/LocalStorage. No raw keys. |
|
|
264
|
+
| `toDangerousJSON()` | `MajikKeyDangerousJSON` | ⚠️ Contains every raw private key. Server-side secret injection only — see warning below. |
|
|
265
|
+
| `toMnemonicJSON()` | `MnemonicJSON` | ⚠️ Contains the raw mnemonic words (and passphrase, if you pass one) in plaintext — a transport format, not an at-rest storage format. Requires the key to be unlocked. |
|
|
266
|
+
| `exportMnemonicBackup()` | `Promise<string>` | Encrypted backup string, decryptable only with the original mnemonic — used to verify a mnemonic before `importFromMnemonicBackup()` re-derives the identity. |
|
|
267
|
+
| `toContact()` | `MajikContact` | Extracts public identity data for sharing (the basis for Majik Universal ID). |
|
|
268
|
+
| `toMajikMessageIdentity()` | `Promise<MajikMessageIdentity>` | Formats the key for direct use in Majik Message. Requires a `MajikUser`. |
|
|
552
269
|
|
|
553
|
-
|
|
554
|
-
The user-defined label.
|
|
270
|
+
### Instance Getters
|
|
555
271
|
|
|
556
|
-
|
|
557
|
-
The mnemonic backup identifier.
|
|
272
|
+
*Public — available at any time, regardless of lock state:*
|
|
558
273
|
|
|
559
|
-
|
|
560
|
-
The creation timestamp.
|
|
274
|
+
`id`, `fingerprint`, `publicKey`, `publicKeyBase64`, `label`, `backup`, `timestamp`, `mnemonicLanguage`, `kdfVersion`, `isArgon2id`, `isLocked`, `isUnlocked`, `isFullyUpgraded`, `mlKemPublicKey`, `hasMlKem`, `edPublicKey`, `mlDsaPublicKey`, `hasSigningKeys`, `btcPublicKey`, `hasBitcoin`, `hasSolanaKeypair`, `hasBitcoinKeypair`, `metadata`.
|
|
561
275
|
|
|
562
|
-
|
|
563
|
-
Whether the key is currently locked.
|
|
276
|
+
*Restricted — throws `MajikKeyError` if locked (or if that key type isn't present, e.g. on an account not yet fully migrated):*
|
|
564
277
|
|
|
565
|
-
|
|
566
|
-
Whether the key is currently unlocked.
|
|
278
|
+
`getPrivateKey()`, `getPrivateKeyBase64()`, `getMlKemSecretKey()`, `getEdSecretKey()`, `getMlDsaSecretKey()`, `getBtcSecretKey()`.
|
|
567
279
|
|
|
568
|
-
|
|
569
|
-
Safe metadata object (no sensitive data).
|
|
280
|
+
### Web3 (Experimental)
|
|
570
281
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
// isLocked: false
|
|
580
|
-
// }
|
|
581
|
-
```
|
|
282
|
+
| Member | Returns | Notes |
|
|
283
|
+
| :--- | :--- | :--- |
|
|
284
|
+
| `web3` *(getter)* | `{ solana, bitcoin? } \| undefined` | `undefined` if locked or has no Ed25519 signing key. `bitcoin` is present only if the account has stored Bitcoin key material. |
|
|
285
|
+
| `getBitcoinKeypairMaterial()` | `BitcoinKeypairMaterial` | Raw Bitcoin keypair bytes. |
|
|
286
|
+
| `getBitcoinWIF()` | `string` | Wallet Import Format string, pastes into any standard Bitcoin wallet. |
|
|
287
|
+
| `getSolanaKeypairMaterial()` | `SolanaKeypairMaterial` | Raw Solana keypair bytes. |
|
|
288
|
+
| `getSolanaKeypair()` | `Promise<any>` | Real `@solana/kit` `KeyPairSigner`. Requires `@solana/kit`. |
|
|
289
|
+
| `getSolanaAddress()` | `string` | Base58 Solana address. No extra dependency required. |
|
|
582
290
|
|
|
583
291
|
---
|
|
584
292
|
|
|
585
293
|
## Usage Examples
|
|
586
294
|
|
|
587
|
-
###
|
|
295
|
+
### 1. Secure Backup & Recovery Workflow
|
|
588
296
|
|
|
589
|
-
```
|
|
297
|
+
```typescript
|
|
590
298
|
import { MajikKey } from '@majikah/majik-key';
|
|
591
299
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
// Save to storage
|
|
610
|
-
const json = key.toJSON();
|
|
611
|
-
localStorage.setItem('myKey', JSON.stringify(json));
|
|
612
|
-
|
|
613
|
-
return { key, mnemonic };
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
createKey();
|
|
300
|
+
// -- EXPORTING --
|
|
301
|
+
// ⚠️ jsonData contains the raw mnemonic (and passphrase, if provided) in
|
|
302
|
+
// plaintext. Treat this exactly like the mnemonic itself — encrypt the
|
|
303
|
+
// file yourself, or keep it offline. This is a transport format, not a
|
|
304
|
+
// safe-storage format.
|
|
305
|
+
const jsonData = key.toMnemonicJSON(mnemonic, 'password123');
|
|
306
|
+
const blob = new Blob([JSON.stringify(jsonData)], { type: "application/json" });
|
|
307
|
+
// Save blob to a secure location...
|
|
308
|
+
|
|
309
|
+
// -- RECOVERING --
|
|
310
|
+
const recoveredData = JSON.parse(await blob.text());
|
|
311
|
+
const recoveredKey = await MajikKey.importFromMnemonicBackup(
|
|
312
|
+
recoveredData.id,
|
|
313
|
+
recoveredData.seed.join(" "),
|
|
314
|
+
recoveredData.phrase,
|
|
315
|
+
'Recovered Key',
|
|
316
|
+
);
|
|
617
317
|
```
|
|
618
318
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
### Example 2: Lock/Unlock Pattern
|
|
622
|
-
|
|
623
|
-
```ts
|
|
624
|
-
import { MajikKey } from '@majikah/majik-key';
|
|
625
|
-
|
|
626
|
-
async function secureLockPattern() {
|
|
627
|
-
const json = localStorage.getItem('myKey');
|
|
628
|
-
const key = MajikKey.fromJSON(json);
|
|
629
|
-
|
|
630
|
-
// Key is locked by default when loaded from JSON
|
|
631
|
-
console.log('Locked:', key.isLocked); // true
|
|
632
|
-
|
|
633
|
-
try {
|
|
634
|
-
// This will throw an error
|
|
635
|
-
const privateKey = key.getPrivateKey();
|
|
636
|
-
} catch (error) {
|
|
637
|
-
console.log('❌ Cannot access private key when locked');
|
|
638
|
-
}
|
|
319
|
+
### 2. Password Verification Before Action
|
|
639
320
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
console.log('Unlocked:', key.isUnlocked); // true
|
|
321
|
+
```typescript
|
|
322
|
+
const key = MajikKey.fromJSON(storedJson);
|
|
643
323
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
// Lock again when done
|
|
652
|
-
key.lock();
|
|
653
|
-
console.log('🔒 Key locked again');
|
|
324
|
+
if (await key.verify('user-input-password')) {
|
|
325
|
+
await key.unlock('user-input-password');
|
|
326
|
+
// ... proceed with signing/encryption
|
|
327
|
+
key.lock(); // Always clean up!
|
|
328
|
+
} else {
|
|
329
|
+
throw new Error("Invalid passphrase");
|
|
654
330
|
}
|
|
655
|
-
|
|
656
|
-
secureLockPattern();
|
|
657
331
|
```
|
|
658
332
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
### Example 3: Backup and Recovery
|
|
662
|
-
|
|
663
|
-
```ts
|
|
664
|
-
import { MajikKey } from '@majikah/majik-key';
|
|
665
|
-
|
|
666
|
-
async function backupAndRecover() {
|
|
667
|
-
const mnemonic = MajikKey.generateMnemonic();
|
|
668
|
-
const key = await MajikKey.create(mnemonic, 'password123', 'Original Key');
|
|
669
|
-
|
|
670
|
-
//Download as Blob JSON File
|
|
671
|
-
|
|
672
|
-
const jsonData = await key.toMnemonicJSON(mnemonic, 'password123');
|
|
673
|
-
const jsonString = JSON.stringify(jsonData);
|
|
674
|
-
const blob = new Blob([jsonString], {
|
|
675
|
-
type: "application/json;charset=utf-8",
|
|
676
|
-
});
|
|
677
|
-
downloadBlob(
|
|
678
|
-
blob,
|
|
679
|
-
"json",
|
|
680
|
-
`${label} | ${key.id} | SEED KEY`,
|
|
681
|
-
);
|
|
682
|
-
|
|
333
|
+
### 3. Server-Side Secret Injection (Dangerous JSON)
|
|
683
334
|
|
|
335
|
+
`toDangerousJSON()` / `fromDangerousJSON()` skip encryption entirely — no KDF, no AES-GCM, instant reconstruction. This exists for one narrow case: injecting a pre-unlocked signing key into a server process, not for anything that touches a database, log, or the network.
|
|
684
336
|
|
|
685
|
-
|
|
337
|
+
```typescript
|
|
338
|
+
// At deploy time, generated once and stored in your secrets manager:
|
|
339
|
+
const dangerousJson = unlockedKey.toDangerousJSON();
|
|
686
340
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
seed: ["word1", "word2", ...],
|
|
691
|
-
phrase: 'password123',
|
|
692
|
-
};
|
|
693
|
-
|
|
694
|
-
const recoveredKey = await MajikKey.importFromMnemonicBackup(
|
|
695
|
-
jsonData.id,
|
|
696
|
-
seedArrayToString(jsonData.seed),
|
|
697
|
-
jsonData.phrase,
|
|
698
|
-
'Recovered Key'
|
|
699
|
-
);
|
|
700
|
-
|
|
701
|
-
console.log('✅ Key recovered!');
|
|
702
|
-
console.log('Same fingerprint:', key.fingerprint === recoveredKey.fingerprint);
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
backupAndRecover();
|
|
341
|
+
// At server boot:
|
|
342
|
+
const serverKey = MajikKey.fromDangerousJSON(process.env.MAJIK_SIGNING_KEY!);
|
|
343
|
+
// serverKey is already unlocked — no passphrase needed, no KDF cost.
|
|
706
344
|
```
|
|
707
345
|
|
|
346
|
+
### 4. Experimental Web3 Usage
|
|
708
347
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
```ts
|
|
714
|
-
import { MajikKey } from '@majikah/majik-key';
|
|
348
|
+
```typescript
|
|
349
|
+
// Bitcoin — key material is already derived and stored on any account
|
|
350
|
+
console.log('Bitcoin WIF:', key.getBitcoinWIF());
|
|
351
|
+
console.log('Bitcoin address:', await key.web3?.bitcoin?.getBitcoinAddress()); // needs @scure/btc-signer
|
|
715
352
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
// Must unlock first
|
|
721
|
-
await key.unlock('old-password');
|
|
722
|
-
|
|
723
|
-
// Change passphrase
|
|
724
|
-
await key.updatePassphrase('old-password', 'new-secure-password');
|
|
725
|
-
console.log('✅ Passphrase updated!');
|
|
726
|
-
|
|
727
|
-
// Save updated key
|
|
728
|
-
localStorage.setItem('myKey', JSON.stringify(key.toJSON()));
|
|
729
|
-
|
|
730
|
-
// Verify new passphrase works
|
|
731
|
-
key.lock();
|
|
732
|
-
await key.unlock('new-secure-password');
|
|
733
|
-
console.log('✅ New passphrase verified!');
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
changePassphrase();
|
|
353
|
+
// Solana — derived on demand from your Ed25519 signing key
|
|
354
|
+
console.log('Solana address:', key.getSolanaAddress()); // no extra dependency
|
|
355
|
+
const solanaSigner = await key.getSolanaKeypair(); // needs @solana/kit
|
|
737
356
|
```
|
|
738
357
|
|
|
739
358
|
---
|
|
740
359
|
|
|
741
|
-
|
|
360
|
+
## Security Best Practices
|
|
742
361
|
|
|
743
|
-
|
|
744
|
-
|
|
362
|
+
✅ **DO:**
|
|
363
|
+
- Call `.lock()` immediately after signing or decrypting payloads to free key material from memory.
|
|
364
|
+
- Use `mlKemPublicKey` for all new communication protocols to stay post-quantum ready.
|
|
365
|
+
- Keep `@scure/bip39` and the underlying crypto dependencies up to date.
|
|
366
|
+
- Treat any `toMnemonicJSON()` export, and the mnemonic itself, as the master secret — it recovers everything.
|
|
745
367
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
// Verify without unlocking
|
|
751
|
-
const isValid = await key.verify('user-entered-password');
|
|
752
|
-
|
|
753
|
-
if (isValid) {
|
|
754
|
-
console.log('✅ Passphrase is correct');
|
|
755
|
-
await key.unlock('user-entered-password');
|
|
756
|
-
// Proceed with operations...
|
|
757
|
-
} else {
|
|
758
|
-
console.log('❌ Invalid passphrase');
|
|
759
|
-
// Show error to user
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
verifyPassphrase();
|
|
764
|
-
```
|
|
368
|
+
❌ **DON'T:**
|
|
369
|
+
- Log `mnemonic` phrases, `privateKeyBase64`, or any `*SecretKeyBase64` value in production.
|
|
370
|
+
- Use `toDangerousJSON()` / `fromDangerousJSON()` outside of controlled, server-side secret injection.
|
|
371
|
+
- Store the output of `toMnemonicJSON()` unencrypted — it is not the same as `toJSON()`/`toString()`.
|
|
765
372
|
|
|
766
373
|
---
|
|
767
374
|
|
|
768
|
-
##
|
|
769
|
-
|
|
770
|
-
Majik Key is fully compatible with **Majik Message** as its seed phrase account implementation. Keys created with Majik Key can be directly imported into Majik Message.
|
|
771
|
-
|
|
772
|
-
### Importing to Majik Message
|
|
773
|
-
|
|
774
|
-
```ts
|
|
775
|
-
import { MajikKey } from '@majikah/majik-key';
|
|
776
|
-
|
|
777
|
-
async function importToMajikMessage() {
|
|
778
|
-
// Create or load a Majik Key
|
|
779
|
-
const mnemonic = MajikKey.generateMnemonic();
|
|
780
|
-
const key = await MajikKey.create(mnemonic, 'password', 'Message Account');
|
|
781
|
-
|
|
782
|
-
// Export to MnemonicJSON format for Majik Message
|
|
783
|
-
const mnemonicData = key.toMnemonicJSON(mnemonic, 'password');
|
|
784
|
-
const jsonString = JSON.stringify(mnemonicData);
|
|
375
|
+
## Ecosystem
|
|
785
376
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
// This mnemonicData can be imported directly into Majik Message
|
|
792
|
-
// as a seed phrase account
|
|
793
|
-
console.log('Import the saved JSON to Majik Message:', mnemonicData);
|
|
794
|
-
}
|
|
795
|
-
```
|
|
796
|
-
|
|
797
|
-
### Converting to Majik Message Identity
|
|
798
|
-
|
|
799
|
-
```ts
|
|
800
|
-
import { MajikKey } from '@majikah/majik-key';
|
|
801
|
-
import { MajikUser } from '@thezelijah/majik-user';
|
|
802
|
-
|
|
803
|
-
async function createMessageIdentity() {
|
|
804
|
-
const mnemonic = MajikKey.generateMnemonic();
|
|
805
|
-
const key = await MajikKey.create(mnemonic, 'password', 'Message Identity');
|
|
806
|
-
|
|
807
|
-
// Create/parse a MajikUser instance
|
|
808
|
-
const user = new MajikUser({
|
|
809
|
-
username: 'myusername',
|
|
810
|
-
// ... other user properties
|
|
811
|
-
});
|
|
812
|
-
|
|
813
|
-
// Convert to Majik Message Identity
|
|
814
|
-
const identity = await key.toMajikMessageIdentity(user, {
|
|
815
|
-
label: 'My Message Account',
|
|
816
|
-
restricted: false
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
console.log('Majik Message Identity created:', identity);
|
|
820
|
-
}
|
|
821
|
-
```
|
|
822
|
-
|
|
823
|
-
---
|
|
824
|
-
|
|
825
|
-
## Security Considerations
|
|
826
|
-
|
|
827
|
-
### Best Practices
|
|
828
|
-
|
|
829
|
-
1. **Never expose mnemonics**: Treat mnemonic phrases like root passwords. Never log or store them unencrypted.
|
|
830
|
-
|
|
831
|
-
2. **Lock when not in use**: Always call .lock() when private key access is no longer required to purge the heap.
|
|
832
|
-
|
|
833
|
-
3. **PQ Readiness**: For all new communication protocols, ensure you are utilizing the mlKemPublicKey.
|
|
834
|
-
|
|
835
|
-
Security Summary
|
|
836
|
-
- **Primary KDF**: Argon2id (64 / 3t / 4p).
|
|
837
|
-
|
|
838
|
-
- **Legacy KDF**: PBKDF2-SHA256 (250,000 iterations).
|
|
839
|
-
|
|
840
|
-
- **Encryption**: AES-256-GCM with unique salts and IVs.
|
|
841
|
-
|
|
842
|
-
- **Post-Quantum**: ML-KEM-768 (Lattice-based cryptography).
|
|
843
|
-
|
|
844
|
-
### What NOT to Do
|
|
845
|
-
|
|
846
|
-
❌ **DON'T** store mnemonics in code or version control
|
|
847
|
-
❌ **DON'T** transmit mnemonics over insecure channels
|
|
848
|
-
❌ **DON'T** use weak passphrases like "password123"
|
|
849
|
-
❌ **DON'T** share mnemonics or passphrases with anyone
|
|
850
|
-
❌ **DON'T** screenshot or photograph mnemonics
|
|
851
|
-
|
|
852
|
-
### What TO Do
|
|
853
|
-
|
|
854
|
-
✅ **DO** use password managers for mnemonic storage
|
|
855
|
-
✅ **DO** write mnemonics on paper and store securely
|
|
856
|
-
✅ **DO** use hardware security modules when possible
|
|
857
|
-
✅ **DO** test recovery procedures before relying on them
|
|
858
|
-
✅ **DO** keep multiple encrypted backups in different locations
|
|
859
|
-
|
|
860
|
-
---
|
|
861
|
-
|
|
862
|
-
### Tips & Reminders
|
|
863
|
-
|
|
864
|
-
#### For Developers
|
|
865
|
-
|
|
866
|
-
- **Remember**: Always validate user input before creating or unlocking keys.
|
|
867
|
-
|
|
868
|
-
- **Security**: Never log sensitive data (mnemonics, private keys, passphrases) in production.
|
|
869
|
-
|
|
870
|
-
- **Performance**: Lock keys when not in use to free memory and reduce attack surface.
|
|
871
|
-
|
|
872
|
-
- **Testing**: Test backup/recovery procedures in development before deploying to production.
|
|
873
|
-
|
|
874
|
-
- **Dependencies**: Keep `@scure/bip39` and other crypto dependencies up to date.
|
|
875
|
-
|
|
876
|
-
#### For Users
|
|
877
|
-
|
|
878
|
-
- **Backup**: Always keep multiple backups of your mnemonic phrase in secure locations.
|
|
879
|
-
|
|
880
|
-
- **Passphrase**: Use a strong, unique passphrase for each Majik Key.
|
|
881
|
-
|
|
882
|
-
- **Recovery**: Test your ability to recover keys from backups before you need to.
|
|
883
|
-
|
|
884
|
-
- **Organization**: Use meaningful labels to identify different keys.
|
|
885
|
-
- **Loss Prevention**: Losing your mnemonic phrase means permanent loss of access to your key.
|
|
377
|
+
- [Majik Signature Web App](https://signature.majikah.solutions)
|
|
378
|
+
- [Majik Signature on Microsoft Store](https://apps.microsoft.com/detail/9pl9g3xzvd1x)
|
|
379
|
+
- [Majik Signature Official Repository](https://github.com/Majikah/majik-signature)
|
|
380
|
+
- [Majikah Solutions](https://majikah.solutions)
|
|
886
381
|
|
|
887
382
|
---
|
|
888
383
|
|
|
889
|
-
##
|
|
890
|
-
|
|
891
|
-
### [Majik Message](https://message.majikah.solutions)
|
|
892
|
-
Secure messaging platform using Majik Keys
|
|
893
|
-
|
|
894
|
-
[Read more about Majik Message here](https://majikah.solutions/products/majik-message)
|
|
895
|
-
|
|
896
|
-
[](https://message.majikah.solutions)
|
|
897
|
-
|
|
898
|
-
> Click the image to try Majik Message live.
|
|
899
|
-
|
|
900
|
-
[Read Docs](https://majikah.solutions/products/majik-message/docs)
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
Also available on [Microsoft Store](https://apps.microsoft.com/detail/9pmjgvzzjspn) for free.
|
|
904
|
-
|
|
905
|
-
[Official Repository](https://github.com/Majikah/majik-message)
|
|
906
|
-
[SDK Library](https://www.npmjs.com/package/@majikah/majik-message)
|
|
384
|
+
## License & Author
|
|
907
385
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
## Contributing
|
|
911
|
-
|
|
912
|
-
If you want to contribute or help extend support to more platforms, reach out via email. All contributions are welcome!
|
|
913
|
-
|
|
914
|
-
---
|
|
915
|
-
|
|
916
|
-
## License
|
|
917
|
-
|
|
918
|
-
[Apache-2.0](LICENSE) — free for personal and commercial use.
|
|
919
|
-
|
|
920
|
-
---
|
|
921
|
-
## Author
|
|
922
|
-
|
|
923
|
-
Made with 💙 by [@thezelijah](https://github.com/jedlsf)
|
|
924
|
-
|
|
925
|
-
## About the Developer
|
|
926
|
-
|
|
927
|
-
- **Developer**: Josef Elijah Fabian
|
|
928
|
-
- **GitHub**: [https://github.com/jedlsf](https://github.com/jedlsf)
|
|
929
|
-
- **Project Repository**: [https://github.com/Majikah/majik-key](https://github.com/Majikah/majik-key)
|
|
930
|
-
|
|
931
|
-
---
|
|
386
|
+
**License:** [Apache-2.0](LICENSE) — free for personal and commercial use.
|
|
932
387
|
|
|
933
|
-
|
|
388
|
+
Developed by **Josef Elijah Fabian (Zelijah)** | [Majikah Solutions OPC](https://majikah.solutions/about)
|
|
934
389
|
|
|
935
|
-
- **
|
|
936
|
-
- **
|
|
390
|
+
- **GitHub:** [@jedlsf](https://github.com/jedlsf)
|
|
391
|
+
- **Website:** [https://www.thezelijah.world](https://www.thezelijah.world)
|
|
392
|
+
- **Email:** [business@thezelijah.world](mailto:business@thezelijah.world)
|