← Back to Skills
Security

cifer-security

tip-citron By tip-citron 👁 22 views ▲ 0 votes

Implement quantum-resistant encryption using the CIFER SDK (cifer-sdk npm package)

GitHub
---
name: cifer-sdk
description: Implement quantum-resistant encryption using the CIFER SDK (cifer-sdk npm package). Covers SDK initialization, wallet setup, secret creation, text encryption/decryption, and file encryption/decryption on any supported chain (Ethereum, Sepolia, Ternoa). Use when the user mentions CIFER, cifer-sdk, quantum-resistant encryption, ML-KEM, secret creation, or encrypted payloads/files with blockchain.
---

# CIFER SDK — Complete Integration Guide

## Overview

CIFER SDK provides quantum-resistant encryption (ML-KEM-768 + AES-256-GCM) for blockchain apps. Secrets are on-chain key pairs: public key on IPFS, private key sharded across enclaves.

**Package**: `cifer-sdk` (npm)
**Chains**: Ethereum Mainnet (1), Sepolia (11155111), Ternoa (752025)
**Blackbox URL**: `https://cifer-blackbox.ternoa.dev:3010`

For the full API reference, see [reference.md](reference.md).

---

## Quick Setup

```bash
npm install cifer-sdk ethers dotenv
```

`package.json` must have `"type": "module"` for ESM imports.

```javascript
import 'dotenv/config';
import { createCiferSdk, keyManagement, blackbox } from 'cifer-sdk';
import { Wallet, JsonRpcProvider } from 'ethers';
```

---

## Step 1: Initialize SDK

```javascript
const sdk = await createCiferSdk({
  blackboxUrl: 'https://cifer-blackbox.ternoa.dev:3010',
});

const chainId = 1; // Ethereum Mainnet (or 11155111 for Sepolia, 752025 for Ternoa)
const controllerAddress = sdk.getControllerAddress(chainId);
const rpcUrl = sdk.getRpcUrl(chainId);
```

`sdk.getSupportedChainIds()` returns all available chains.

## Step 2: Create Wallet Signer (Server-Side)

```javascript
const provider = new JsonRpcProvider(rpcUrl);
const wallet = new Wallet(process.env.PRIVATE_KEY, provider);

// Signer adapter — this is what the SDK expects
const signer = {
  async getAddress() { return wallet.address; },
  async signMessage(message) { return wallet.signMessage(message); },
};
```

For browser wallets, use the built-in adapter instead:
```javascript
import { Eip1193SignerAdapter } from 'cifer-sdk';
const signer = new Eip1193SignerAdapter(window.ethereum);
```

## Step 3: Create a Secret

A secret costs a fee in native token. Check balance first.

```javascript
const fee = await keyManagement.getSecretCreationFee({
  chainId, controllerAddress, readClient: sdk.readClient,
});

const txIntent = keyManagement.buildCreateSecretTx({ chainId, controllerAddress, fee });

const tx = await wallet.sendTransaction({
  to: txIntent.to,
  data: txIntent.data,
  value: txIntent.value,
});
const receipt = await tx.wait();
const secretId = keyManagement.extractSecretIdFromReceipt(receipt.logs);
```

## Step 4: Wait for Secret Sync

After creation, the enclave cluster generates keys (~30-120s on mainnet).

```javascript
let ready = false;
while (!ready) {
  ready = await keyManagement.isSecretReady(
    { chainId, controllerAddress, readClient: sdk.readClient },
    secretId,
  );
  if (!ready) await new Promise(r => setTimeout(r, 5000));
}
```

Or read the full state:
```javascript
const state = await keyManagement.getSecret(
  { chainId, controllerAddress, readClient: sdk.readClient },
  secretId,
);
// state.owner, state.delegate, state.isSyncing, state.publicKeyCid
```

## Step 5: Encrypt Text

```javascript
const encrypted = await blackbox.payload.encryptPayload({
  chainId,
  secretId,
  plaintext: 'Your secret message',
  signer,
  readClient: sdk.readClient,
  blackboxUrl: sdk.blackboxUrl,
});
// Returns: { cifer, encryptedMessage }
```

## Step 6: Decrypt Text

Caller must be secret owner or delegate.

```javascript
const decrypted = await blackbox.payload.decryptPayload({
  chainId,
  secretId,
  encryptedMessage: encrypted.encryptedMessage,
  cifer: encrypted.cifer,
  signer,
  readClient: sdk.readClient,
  blackboxUrl: sdk.blackboxUrl,
});
// Returns: { decryptedMessage }
```

## Step 7: Encrypt File

File operations are async jobs. Works with `Blob` in Node.js 18+.

```javascript
import { readFile, writeFile } from 'fs/promises';

const buffer = await readFile('myfile.pdf');
const blob = new Blob([buffer], { type: 'application/pdf' });

// Start encrypt job
const job = await blackbox.files.encryptFile({
  chainId, secretId, file: blob, signer,
  readClient: sdk.readClient, blackboxUrl: sdk.blackboxUrl,
});

// Poll until done
const status = await blackbox.jobs.pollUntilComplete(job.jobId, sdk.blackboxUrl, {
  intervalMs: 2000,
  maxAttempts: 120,
  onProgress: (j) => console.log(`${j.progress}%`),
});

// Download encrypted .cifer file (no auth needed for encrypt jobs)
const encBlob = await blackbox.jobs.download(job.jobId, { blackboxUrl: sdk.blackboxUrl });
await writeFile('myfile.pdf.cifer', Buffer.from(await encBlob.arrayBuffer()));
```

## Step 8: Decrypt File

```javascript
const encBuffer = await readFile('myfile.pdf.cifer');
const encBlob = new Blob([encBuffer]);

const decJob = await blackbox.files.decryptFile({
  chainId, secretId, file: encBlob, signer,
  readClient: sdk.readClient, blackboxUrl: sdk.blackboxUrl,
});

const decStatus = await blackbox.jobs.pollUntilComplete(decJob.jobId, sdk.blackboxUrl, {
  intervalMs: 2000, maxAttempts: 120,
});

// Download decrypted file (auth REQUIRED for decrypt jobs)
const decBlob = await blackbox.jobs.download(decJob.jobId, {
  blackboxUrl: sdk.blackboxUrl,
  chainId, secretId, signer, readClient: sdk.readClient,
});
await writeFile('myfile-decrypted.pdf', Buffer.from(await decBlob.arrayBuffer()));
```

---

## List Existing Secrets

```javascript
const secrets = await keyManagement.getSecretsByWallet(
  { chainId, controllerAddress, readClient: sdk.readClient },
  wallet.address,
);
// secrets.owned: bigint[]   — secrets you own
// secrets.delegated: bigint[] — secrets delegated to you
```

## Delegation

Set a delegate (can decrypt but not encrypt or modify):
```javascript
const txIntent = keyManagement.buildSetDelegateTx({
  chainId, controllerAddress, secretId, newDelegate: '0xDelegateAddress',
});
await wallet.sendTransaction({ to: txIntent.to, data: txIntent.data });
```

Remove delegation:
```javascript
const txIntent = keyManagement.buildRemoveDelegationTx({
  chainId, controllerAddress, secretId,
});
```

---

## Important Notes

- **Minimum SDK version**: Use `[email protected]` or later. Earlier versions had incorrect function selectors.
- **Payload size limit**: Text encryption max ~16KB (`encryptPayload`). Use file encryption for larger data.
- **Block freshness**: The SDK auto-retries up to 3 times if the block number becomes stale.
- **Secret sync time**: ~30-60s on Ternoa, ~60-120s on Ethereum mainnet.
- **Auth for file download**: Encrypt job downloads need no auth. Decrypt job downloads require signer + readClient.
- **Fee**: Secret creation requires a fee in native token (e.g. ~0.0005 ETH on mainnet). Query `getSecretCreationFee()` first.
- **Private keys**: Never expose private keys in frontend code. Use server-side signer for Node.js.

## Error Handling

```javascript
import { isCiferError, isBlockStaleError } from 'cifer-sdk';

try {
  await blackbox.payload.encryptPayload({ ... });
} catch (error) {
  if (isBlockStaleError(error)) {
    // RPC returning stale blocks, SDK already retried 3x
  } else if (error instanceof SecretNotReadyError) {
    // Wait and retry
  } else if (isCiferError(error)) {
    console.error(error.code, error.message);
  }
}
```

## Complete Minimal Example

```javascript
import 'dotenv/config';
import { createCiferSdk, keyManagement, blackbox } from 'cifer-sdk';
import { Wallet, JsonRpcProvider } from 'ethers';

const sdk = await createCiferSdk({ blackboxUrl: 'https://cifer-blackbox.ternoa.dev:3010' });
const chainId = 1;
const controllerAddress = sdk.getControllerAddress(chainId);
const provider = new JsonRpcProvider(sdk.getRpcUrl(chainId));
const wallet = new Wallet(process.env.PRIVATE_KEY, provider);
const signer = {
  async getAddress() { return wallet.address; },
  async signMessage(msg) { return wallet.signMessage(msg); },
};

// Create secret
const fee = await keyManagement.getSecretCreationFee({ chainId, controllerAddress, readClient: sdk.readClient });
const txIntent = keyManagement.buildCreateSecretTx({ chainId, controllerAddress, fee });
const tx = await wallet.sendTransaction({ to: txIntent.to, data: txIntent.data, value: txIntent.value });
const receipt = await tx.wait();
const secretId = keyManagement.extractSecretIdFromReceipt(receipt.logs);

// Wait for sync
let ready = false;
while (!ready) {
  ready = await keyManagement.isSecretReady({ chainId, controllerAddress, readClient: sdk.readClient }, secretId);
  if (!ready) await new Promise(r => setTimeout(r, 5000));
}

// Encrypt & decrypt
const enc = await blackbox.payload.encryptPayload({
  chainId, secretId, plaintext: 'Hello CIFER!', signer,
  readClient: sdk.readClient, blackboxUrl: sdk.blackboxUrl,
});
const dec = await blackbox.payload.decryptPayload({
  chainId, secretId, encryptedMessage: enc.encryptedMessage, cifer: enc.cifer,
  signer, readClient: sdk.readClient, blackboxUrl: sdk.blackboxUrl,
});
console.log(dec.decryptedMessage); // "Hello CIFER!"
```
security

Comments

Sign in to leave a comment

Loading comments...