> For the complete documentation index, see [llms.txt](https://docs.qsafewallet.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.qsafewallet.com/technical-details/backup-and-recovery.md).

# Backup and Recovery

### 1. **Automatic Backups**

Backups are created automatically when accounts or settings are modified. These backups are **encrypted locally using ML-KEM before being uploaded** to a remote server.

#### Why ML-KEM?

* **NIST-standardized lattice-based algorithm** .
* Resistant to attacks from both classical and quantum computers.
* Used to securely encrypt sensitive data like account records and recovery information.

***

#### Encryption Flow: ML-KEM + XSalsa20-Poly1305

The wallet uses a **hybrid encryption model** :

1. A **shared secret** is generated using ML-KEM.
2. The actual data is encrypted using **XSalsa20-Poly1305** , a symmetric cipher.
3. The shared secret is then encapsulated using the recipient's **ML-KEM public key** .

```typescript
{
  "capsule": "0x...",          // ML-KEM ciphertext (encapsulated shared secret)
  "symmetricCiphertext": "0x...",  // XSalsa20-Poly1305 encrypted payload
  "nonce": "0x..."             // Random nonce used for symmetric encryption
}
```

```typescript
async encryptBackup(backupBuffer: Buffer, encryptionPublicKey: string): Promise<string> {
  const recipientPubKeyBuffer = hexToBuffer(encryptionPublicKey);

  // Step 1: Generate shared secret using ML-KEM encapsulation
  const { cipherText: capsule, sharedSecret } = ml_kem768.encapsulate(recipientPubKeyBuffer);

  // Step 2: Generate random nonce for symmetric encryption
  const nonce = crypto.randomBytes(24); // 24-byte nonce for XSalsa20

  // Step 3: Encrypt payload using XSalsa20-Poly1305
  const symmetricCiphertext = secretbox(backupBuffer, nonce, sharedSecret);

  // Step 4: Return structured JSON blob
  return JSON.stringify({
    capsule: bufferToHex(capsule),
    symmetricCiphertext: bufferToHex(symmetricCiphertext),
    nonce: bufferToHex(nonce),
  });
}
```

#### Uploading the Backup

After encryption, the backup is signed using **SLH-DSA** to ensure authenticity before upload.

```typescript
const signatureHex = await kr.sign(messageBuffer, {
  basePath: QL1evmNetworks.ql1evm.basePath,
  signerType: SignerType.slh_dsaevm,
  pathIndex: 0,
  walletType: WalletType.mnemonic,
});
```

The backup and signature are sent via an authenticated API call:

```typescript
await fetch(`${BACKUP_URL}backups/${account.publicKey}/users/${state.userId}`, {
  method: 'POST',
  headers: HEADERS,
  body: JSON.stringify({
    signature: signatureHex,
    payload: bufferToHex(Buffer.from(encryptedPayload)),
  }),
});
```

### 2. **Manual Recovery**

There are two main ways to restore a wallet:

***

#### Option A: **Recovery via Mnemonic Phrase (12/24 Words)**

**Process:**

1. User enters their **12/24-word mnemonic** during recovery.
2. The wallet regenerates:
   * **SLH-DSA signing keypair** (for Quranium Chain).
   * **ML-KEM encryption keypair** (for decrypting backups).
3. All accounts are re-created deterministically based on derivation paths.

```typescript
const entropy = Buffer.from(mnemonicToEntropy(mnemonic), "hex");
const seed96 = shake256.create({ dkLen: 96 }).update(entropy).digest();
const keys = slh.slh_dsa_shake_256f.keygen(seed96);
```

#### Option B: **Restore from Cloud Backup**

Use this option if you want to restore specific encrypted backups (e.g., saved settings or additional accounts).

**Requirements:**

* **ML-KEM Private Key** : Either regenerated from the mnemonic or exported earlier.
* **Encrypted Backup File** : Must contain the `capsule`, `symmetricCiphertext`, and `nonce`.

**Decryption Steps:**

1. Use the **ML-KEM private key** to extract the shared secret from the capsule.
2. Decrypt the symmetric ciphertext using the shared secret and nonce.

```typescript
async decryptBackup(encryptedMessageStr: string, encryptionKeypair: KeyPair): Promise<string> {
  const encryptedData = JSON.parse(encryptedMessageStr);
  const capsule = hexToBuffer(encryptedData.capsule);
  const symmetricCiphertext = hexToBuffer(encryptedData.symmetricCiphertext);
  const nonce = hexToBuffer(encryptedData.nonce);

  // Step 1: Decapsulate shared secret using ML-KEM
  const sharedSecret = ml_kem768.decapsulate(capsule, hexToBuffer(encryptionKeypair.privateKey));

  // Step 2: Decrypt symmetric data
  const decrypted = secretbox.open(symmetricCiphertext, nonce, sharedSecret);

  return bufferToHex(Buffer.from(decrypted));
}
```
