BigBlocks Docs
Components/Security

BapEncryptionSuite

Comprehensive encryption toolkit using Bitcoin Attestation Protocol (BAP) identities for secure communication and data protection

A powerful encryption component that leverages Bitcoin Attestation Protocol (BAP) identities for end-to-end encryption, enabling secure communication and data protection using Bitcoin cryptographic keys. Perfect for building secure messaging, file sharing, and confidential data storage applications.

View Component Preview →

Installation

npx bigblocks add bap-encryption-suite

Import

import { BapEncryptionSuite } from 'bigblocks';

Props

PropTypeRequiredDefaultDescription
bapInstanceExtendedBAPYes-The BAP instance for encryption/decryption
recipientBapIdExtendedBAPNo-Recipient BAP instance for asymmetric encryption
onEncrypted(result: EncryptedData) => voidNo-Callback when content is encrypted
onDecrypted(result: DecryptedData) => voidNo-Callback when content is decrypted
allowedTypesEncryptionContentType[]No['message', 'file', 'json']Allowed content types
useType42booleanNofalseUse Type 42 key derivation
variant'surface' | 'ghost' | 'classic'No'surface'Theme variant
size'1' | '2' | '3'No'2'Component size

Type Definitions

interface EncryptedData {
  encrypted: string;
  type: EncryptionContentType;
  mode: EncryptionMode;
  recipientPublicKey?: string;
  timestamp: Date;
  metadata?: Record<string, unknown>;
}

interface DecryptedData {
  content: string | ArrayBuffer;
  type: EncryptionContentType;
  decryptedAt: Date;
  senderAddress?: string;
  metadata?: Record<string, unknown>;
}

type EncryptionMode = 'symmetric' | 'asymmetric';
type EncryptionContentType = 'message' | 'file' | 'json';

Basic Usage

import { BapEncryptionSuite, useBitcoinAuth } from 'bigblocks';

export default function BasicEncryption() {
  const { bapInstance } = useBitcoinAuth();

  const handleEncrypted = (result: EncryptedData) => {
    console.log('Encrypted:', result);
    // Store or transmit encrypted data
  };

  const handleDecrypted = (result: DecryptedData) => {
    console.log('Decrypted:', result);
    // Display decrypted content
  };

  return (
    <BapEncryptionSuite
      bapInstance={bapInstance}
      onEncrypted={handleEncrypted}
      onDecrypted={handleDecrypted}
    />
  );
}

Advanced Usage

Secure Messaging System

import { BapEncryptionSuite, useBitcoinAuth } from 'bigblocks';
import { useState, useEffect } from 'react';

interface SecureMessage {
  id: string;
  sender: string;
  recipient: string;
  encrypted: string;
  timestamp: Date;
  read: boolean;
}

export default function SecureMessaging() {
  const { bapInstance, address } = useBitcoinAuth();
  const [messages, setMessages] = useState<SecureMessage[]>([]);
  const [recipientBap, setRecipientBap] = useState<ExtendedBAP | null>(null);
  const [selectedMessage, setSelectedMessage] = useState<SecureMessage | null>(null);

  // Load recipient's BAP profile
  const loadRecipientBap = async (recipientAddress: string) => {
    try {
      const response = await fetch(`/api/bap/profile/${recipientAddress}`);
      const bapData = await response.json();
      setRecipientBap(bapData);
    } catch (error) {
      console.error('Failed to load recipient BAP:', error);
    }
  };

  // Send encrypted message
  const handleSendMessage = async (result: EncryptedData) => {
    if (!recipientBap) return;

    const message: SecureMessage = {
      id: crypto.randomUUID(),
      sender: address,
      recipient: recipientBap.address,
      encrypted: result.encrypted,
      timestamp: result.timestamp,
      read: false
    };

    // Send to backend
    await fetch('/api/messages', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Auth-Token': await generateAuthToken()
      },
      body: JSON.stringify(message)
    });

    console.log('Message sent securely');
  };

  // Decrypt received message
  const handleDecryptMessage = (result: DecryptedData) => {
    if (result.type === 'message') {
      alert(`Message from ${result.senderAddress}: ${result.content}`);
      
      // Mark as read
      if (selectedMessage) {
        markMessageAsRead(selectedMessage.id);
      }
    }
  };

  return (
    <div className="secure-messaging">
      <h2>Secure BAP Messaging</h2>
      
      <div className="recipient-selector">
        <input
          type="text"
          placeholder="Recipient Bitcoin address"
          onChange={(e) => loadRecipientBap(e.target.value)}
        />
      </div>

      {recipientBap && (
        <BapEncryptionSuite
          bapInstance={bapInstance}
          recipientBapId={recipientBap}
          onEncrypted={handleSendMessage}
          allowedTypes={['message']}
          variant="surface"
          size="2"
        />
      )}

      <div className="message-list">
        <h3>Encrypted Messages</h3>
        {messages.map((msg) => (
          <div 
            key={msg.id} 
            className={`message ${msg.read ? 'read' : 'unread'}`}
            onClick={() => setSelectedMessage(msg)}
          >
            <span>From: {msg.sender}</span>
            <span>{new Date(msg.timestamp).toLocaleString()}</span>
          </div>
        ))}
      </div>

      {selectedMessage && (
        <BapEncryptionSuite
          bapInstance={bapInstance}
          onDecrypted={handleDecryptMessage}
          allowedTypes={['message']}
          variant="ghost"
        />
      )}
    </div>
  );
}

Encrypted File Sharing

import { BapEncryptionSuite, useBitcoinAuth } from 'bigblocks';
import { useState } from 'react';

interface EncryptedFile {
  id: string;
  filename: string;
  size: number;
  mimeType: string;
  encrypted: string;
  owner: string;
  sharedWith: string[];
  createdAt: Date;
}

export default function EncryptedFileSharing() {
  const { bapInstance, address } = useBitcoinAuth();
  const [files, setFiles] = useState<EncryptedFile[]>([]);
  const [selectedFile, setSelectedFile] = useState<File | null>(null);
  const [recipients, setRecipients] = useState<string[]>([]);
  const [decryptedFiles, setDecryptedFiles] = useState<Map<string, Blob>>(new Map());

  const handleFileEncryption = async (result: EncryptedData) => {
    if (!selectedFile || result.type !== 'file') return;

    const encryptedFile: EncryptedFile = {
      id: crypto.randomUUID(),
      filename: selectedFile.name,
      size: selectedFile.size,
      mimeType: selectedFile.type,
      encrypted: result.encrypted,
      owner: address,
      sharedWith: recipients,
      createdAt: new Date()
    };

    // Upload encrypted file
    const response = await fetch('/api/files/encrypted', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Auth-Token': await generateAuthToken()
      },
      body: JSON.stringify(encryptedFile)
    });

    if (response.ok) {
      setFiles([...files, encryptedFile]);
      setSelectedFile(null);
      alert(`File encrypted and shared with ${recipients.length} recipients`);
    }
  };

  const handleFileDecryption = (result: DecryptedData) => {
    if (result.type !== 'file' || !selectedFile) return;

    // Convert ArrayBuffer to Blob
    const blob = new Blob([result.content as ArrayBuffer], {
      type: selectedFile.mimeType
    });

    // Store decrypted file
    setDecryptedFiles(new Map(decryptedFiles.set(selectedFile.id, blob)));

    // Create download link
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = selectedFile.filename;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div className="encrypted-file-sharing">
      <h2>Encrypted File Sharing</h2>

      <div className="upload-section">
        <h3>Upload & Encrypt File</h3>
        
        <input
          type="file"
          onChange={(e) => setSelectedFile(e.target.files?.[0] || null)}
        />

        <div className="recipients">
          <h4>Share with (BAP IDs):</h4>
          <textarea
            placeholder="Enter BAP IDs, one per line"
            onChange={(e) => setRecipients(e.target.value.split('\n').filter(Boolean))}
          />
        </div>

        {selectedFile && (
          <BapEncryptionSuite
            bapInstance={bapInstance}
            onEncrypted={handleFileEncryption}
            allowedTypes={['file']}
            useType42={true}
            variant="surface"
            size="3"
          />
        )}
      </div>

      <div className="file-list">
        <h3>Encrypted Files</h3>
        {files.map((file) => (
          <div key={file.id} className="encrypted-file">
            <div className="file-info">
              <span className="filename">{file.filename}</span>
              <span className="size">{(file.size / 1024).toFixed(2)} KB</span>
              <span className="date">{file.createdAt.toLocaleDateString()}</span>
            </div>
            
            {(file.owner === address || file.sharedWith.includes(address)) && (
              <button onClick={() => setSelectedFile(file)}>
                Decrypt & Download
              </button>
            )}
          </div>
        ))}
      </div>
    </div>
  );
}

Common Patterns

Multi-Recipient Encryption

import { BapEncryptionSuite, useBitcoinAuth } from 'bigblocks';

export default function MultiRecipientEncryption() {
  const { bapInstance } = useBitcoinAuth();
  const [recipients, setRecipients] = useState<ExtendedBAP[]>([]);
  const [message, setMessage] = useState('');

  const loadRecipients = async (addresses: string[]) => {
    const bapProfiles = await Promise.all(
      addresses.map(addr => fetch(`/api/bap/profile/${addr}`).then(r => r.json()))
    );
    setRecipients(bapProfiles.filter(Boolean));
  };

  const handleMultiEncryption = async (baseResult: EncryptedData) => {
    // Encrypt for each recipient
    for (const recipient of recipients) {
      const encryptedCopy = {
        ...baseResult,
        recipientPublicKey: recipient.publicKey,
        metadata: {
          ...baseResult.metadata,
          recipientAddress: recipient.address
        }
      };

      await sendToRecipient(recipient.address, encryptedCopy);
    }

    alert(`Message encrypted and sent to ${recipients.length} recipients`);
  };

  return (
    <div className="multi-recipient">
      <h3>Broadcast Encrypted Message</h3>
      
      <textarea
        value={message}
        onChange={(e) => setMessage(e.target.value)}
        placeholder="Enter message to encrypt"
      />

      <input
        type="text"
        placeholder="Recipient addresses (comma-separated)"
        onChange={(e) => loadRecipients(e.target.value.split(',').map(s => s.trim()))}
      />

      <div className="recipient-list">
        {recipients.map((r) => (
          <span key={r.address} className="recipient-badge">
            {r.name || r.address.slice(0, 8)}...
          </span>
        ))}
      </div>

      {recipients.length > 0 && (
        <BapEncryptionSuite
          bapInstance={bapInstance}
          recipientBapId={recipients[0]} // Use first recipient as template
          onEncrypted={handleMultiEncryption}
          allowedTypes={['message']}
        />
      )}
    </div>
  );
}

Encrypted JSON Data Storage

import { BapEncryptionSuite, useBitcoinAuth } from 'bigblocks';

interface SensitiveData {
  apiKeys: Record<string, string>;
  passwords: Record<string, string>;
  privateNotes: string[];
  financialData: {
    accounts: Array<{ name: string; balance: number }>;
    transactions: Array<{ date: string; amount: number }>;
  };
}

export default function EncryptedDataVault() {
  const { bapInstance } = useBitcoinAuth();
  const [vaultData, setVaultData] = useState<SensitiveData | null>(null);
  const [encryptedVault, setEncryptedVault] = useState<string | null>(null);

  const handleVaultEncryption = async (result: EncryptedData) => {
    if (result.type !== 'json') return;

    // Store encrypted vault
    const response = await fetch('/api/vault', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Auth-Token': await generateAuthToken()
      },
      body: JSON.stringify({
        encrypted: result.encrypted,
        timestamp: result.timestamp,
        metadata: result.metadata
      })
    });

    if (response.ok) {
      setEncryptedVault(result.encrypted);
      alert('Vault encrypted and saved');
    }
  };

  const handleVaultDecryption = (result: DecryptedData) => {
    if (result.type !== 'json') return;

    try {
      const data = JSON.parse(result.content as string) as SensitiveData;
      setVaultData(data);
      console.log('Vault decrypted successfully');
    } catch (error) {
      console.error('Failed to parse decrypted data:', error);
    }
  };

  return (
    <div className="encrypted-vault">
      <h2>Encrypted Data Vault</h2>

      <div className="vault-editor">
        <h3>Vault Contents</h3>
        <textarea
          value={JSON.stringify(vaultData, null, 2)}
          onChange={(e) => {
            try {
              setVaultData(JSON.parse(e.target.value));
            } catch {}
          }}
          rows={15}
          cols={50}
        />
      </div>

      <div className="vault-actions">
        <BapEncryptionSuite
          bapInstance={bapInstance}
          onEncrypted={handleVaultEncryption}
          onDecrypted={handleVaultDecryption}
          allowedTypes={['json']}
          useType42={true}
          variant="surface"
        />
      </div>

      {encryptedVault && (
        <div className="encrypted-preview">
          <h4>Encrypted Vault (Preview)</h4>
          <code>{encryptedVault.slice(0, 100)}...</code>
        </div>
      )}
    </div>
  );
}

Type 42 Key Derivation

import { BapEncryptionSuite, useBitcoinAuth } from 'bigblocks';

export default function Type42Encryption() {
  const { bapInstance } = useBitcoinAuth();
  const [useType42, setUseType42] = useState(true);
  const [derivationPath, setDerivationPath] = useState('');

  const handleType42Encryption = (result: EncryptedData) => {
    console.log('Encrypted with Type 42:', {
      encrypted: result.encrypted,
      keyDerivation: result.metadata?.type42Path
    });

    // Type 42 provides deterministic key derivation
    // Useful for hierarchical access control
  };

  return (
    <div className="type42-encryption">
      <h3>Type 42 Key Derivation</h3>
      
      <label>
        <input
          type="checkbox"
          checked={useType42}
          onChange={(e) => setUseType42(e.target.checked)}
        />
        Enable Type 42 Derivation
      </label>

      {useType42 && (
        <input
          type="text"
          placeholder="Custom derivation path (optional)"
          value={derivationPath}
          onChange={(e) => setDerivationPath(e.target.value)}
        />
      )}

      <BapEncryptionSuite
        bapInstance={bapInstance}
        onEncrypted={handleType42Encryption}
        useType42={useType42}
        variant="classic"
      />

      <div className="type42-info">
        <p>Type 42 Benefits:</p>
        <ul>
          <li>Deterministic key generation</li>
          <li>Hierarchical access control</li>
          <li>Perfect forward secrecy</li>
          <li>Key rotation capability</li>
        </ul>
      </div>
    </div>
  );
}

Encryption with Metadata

import { BapEncryptionSuite, useBitcoinAuth } from 'bigblocks';

export default function MetadataEncryption() {
  const { bapInstance, address } = useBitcoinAuth();
  const [tags, setTags] = useState<string[]>([]);
  const [expiresIn, setExpiresIn] = useState(24); // hours

  const handleEncryptionWithMetadata = (result: EncryptedData) => {
    const enrichedResult = {
      ...result,
      metadata: {
        ...result.metadata,
        sender: address,
        tags: tags,
        expiresAt: new Date(Date.now() + expiresIn * 60 * 60 * 1000),
        version: '1.0',
        app: 'BigBlocks Secure'
      }
    };

    // Store with metadata for filtering/searching
    storeEncryptedWithMetadata(enrichedResult);
  };

  return (
    <div className="metadata-encryption">
      <h3>Encrypt with Metadata</h3>

      <div className="metadata-inputs">
        <input
          type="text"
          placeholder="Tags (comma-separated)"
          onChange={(e) => setTags(e.target.value.split(',').map(s => s.trim()))}
        />

        <label>
          Expires in:
          <select value={expiresIn} onChange={(e) => setExpiresIn(Number(e.target.value))}>
            <option value={1}>1 hour</option>
            <option value={24}>24 hours</option>
            <option value={168}>1 week</option>
            <option value={720}>30 days</option>
          </select>
        </label>
      </div>

      <BapEncryptionSuite
        bapInstance={bapInstance}
        onEncrypted={handleEncryptionWithMetadata}
        variant="surface"
      />
    </div>
  );
}

Authentication Requirements

BapEncryptionSuite requires full BAP authentication:

import { 
  BitcoinAuthProvider, 
  BitcoinQueryProvider,
  BapEncryptionSuite
} from 'bigblocks';

function App() {
  return (
    <BitcoinQueryProvider>
      <BitcoinAuthProvider config={{ 
        apiUrl: '/api',
        bapEnabled: true // Required for BAP features
      }}>
        <YourEncryptionApp />
      </BitcoinAuthProvider>
    </BitcoinQueryProvider>
  );
}

API Integration

Encryption Endpoints

// Encrypt data
POST /api/bap/encrypt
{
  content: string | ArrayBuffer;
  type: 'message' | 'file' | 'json';
  recipientPublicKey?: string;
  useType42?: boolean;
  metadata?: Record<string, any>;
}

Response:
{
  encrypted: string;
  type: string;
  mode: 'symmetric' | 'asymmetric';
  timestamp: string;
  metadata: Record<string, any>;
}

// Decrypt data
POST /api/bap/decrypt
{
  encrypted: string;
  type: 'message' | 'file' | 'json';
  senderPublicKey?: string;
  useType42?: boolean;
}

Response:
{
  content: string | ArrayBuffer;
  type: string;
  decryptedAt: string;
  senderAddress?: string;
  metadata?: Record<string, any>;
}

Backend Implementation

// BAP encryption handler
app.post('/api/bap/encrypt', authenticate, async (req, res) => {
  const { content, type, recipientPublicKey, useType42, metadata } = req.body;
  const { bapId } = req.user;
  
  try {
    // Get user's BAP instance
    const bap = await getBapInstance(bapId);
    
    // Perform encryption
    const encrypted = await bap.encrypt(content, {
      type,
      recipientPublicKey,
      useType42,
      metadata: {
        ...metadata,
        encryptedBy: bapId,
        encryptedAt: new Date().toISOString()
      }
    });
    
    // Log for audit
    await logEncryption({
      bapId,
      type,
      size: content.length,
      recipient: recipientPublicKey,
      timestamp: new Date()
    });
    
    res.json(encrypted);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

Security Considerations

Encryption Best Practices

  1. Key Management: Never expose private keys
  2. Perfect Forward Secrecy: Use ephemeral keys when possible
  3. Metadata Privacy: Be careful what metadata you include
  4. Size Limits: Implement reasonable file size limits
  5. Access Control: Verify recipient identity before encryption

Common Security Patterns

// Secure key verification
const verifyRecipient = async (recipientAddress: string) => {
  const profile = await fetchBapProfile(recipientAddress);
  
  // Verify profile signature
  if (!await verifyBapSignature(profile)) {
    throw new Error('Invalid recipient profile');
  }
  
  // Check if profile is recent
  const age = Date.now() - new Date(profile.updatedAt).getTime();
  if (age > 30 * 24 * 60 * 60 * 1000) { // 30 days
    console.warn('Recipient profile may be outdated');
  }
  
  return profile;
};

// Implement encryption policies
const encryptionPolicy = {
  maxFileSize: 50 * 1024 * 1024, // 50MB
  allowedTypes: ['message', 'file', 'json'],
  requireType42ForFiles: true,
  metadataWhitelist: ['tags', 'expiresAt', 'version']
};

Error Handling

Common Error Scenarios

import { BapEncryptionSuite } from 'bigblocks';

export default function ErrorHandling() {
  const handleEncryptionError = (error: Error) => {
    if (error.message.includes('Invalid recipient')) {
      alert('Recipient BAP profile not found or invalid');
    } else if (error.message.includes('File too large')) {
      alert('File exceeds maximum size limit');
    } else if (error.message.includes('Unsupported type')) {
      alert('This content type cannot be encrypted');
    } else if (error.message.includes('Key derivation failed')) {
      alert('Type 42 key derivation error - check BAP configuration');
    } else {
      alert(`Encryption failed: ${error.message}`);
    }
  };

  const handleDecryptionError = (error: Error) => {
    if (error.message.includes('Invalid ciphertext')) {
      alert('Data is corrupted or not properly encrypted');
    } else if (error.message.includes('Wrong key')) {
      alert('You do not have permission to decrypt this data');
    } else if (error.message.includes('Expired')) {
      alert('This encrypted data has expired');
    } else {
      alert(`Decryption failed: ${error.message}`);
    }
  };

  return (
    <BapEncryptionSuite
      bapInstance={bapInstance}
      onEncrypted={(result) => {
        try {
          processEncrypted(result);
        } catch (error) {
          handleEncryptionError(error);
        }
      }}
      onDecrypted={(result) => {
        try {
          processDecrypted(result);
        } catch (error) {
          handleDecryptionError(error);
        }
      }}
    />
  );
}

Performance Optimization

Large File Handling

// Chunk large files for efficient encryption
const chunkFile = async (file: File, chunkSize = 1024 * 1024) => {
  const chunks: Blob[] = [];
  let offset = 0;
  
  while (offset < file.size) {
    const chunk = file.slice(offset, offset + chunkSize);
    chunks.push(chunk);
    offset += chunkSize;
  }
  
  return chunks;
};

// Encrypt chunks in parallel
const encryptLargeFile = async (file: File, bap: ExtendedBAP) => {
  const chunks = await chunkFile(file);
  
  const encryptedChunks = await Promise.all(
    chunks.map((chunk, index) => 
      bap.encrypt(chunk, {
        type: 'file',
        metadata: { 
          chunkIndex: index, 
          totalChunks: chunks.length,
          filename: file.name
        }
      })
    )
  );
  
  return {
    chunks: encryptedChunks,
    metadata: {
      filename: file.name,
      size: file.size,
      chunks: chunks.length
    }
  };
};

Styling

/* Custom styling for encryption suite */
.bap-encryption-suite {
  padding: 1rem;
  border-radius: 8px;
  background: var(--surface-color);
}

.encryption-mode-toggle {
  display: flex;
  gap: 1rem;
  margin-bottom: 1rem;
}

.encryption-input {
  width: 100%;
  min-height: 100px;
  margin-bottom: 1rem;
}

.encryption-actions {
  display: flex;
  gap: 0.5rem;
}

.encryption-result {
  margin-top: 1rem;
  padding: 1rem;
  background: var(--code-bg);
  border-radius: 4px;
  word-break: break-all;
}

Troubleshooting

Encryption not working

  • Verify BAP instance is properly initialized
  • Check recipient has valid BAP profile
  • Ensure content type is supported
  • Verify size limits aren't exceeded

Decryption failing

  • Confirm you have the correct private key
  • Check if data was encrypted for your public key
  • Verify data integrity (not corrupted)
  • Ensure metadata matches expected format

Type 42 issues

  • BAP instance must support Type 42
  • Derivation paths must be valid
  • Check for conflicting key derivations

Performance problems

  • Chunk large files before encryption
  • Use web workers for heavy computation
  • Implement proper caching strategies

API Reference

BapEncryptionSuite Props

interface BapEncryptionSuiteProps {
  bapInstance: ExtendedBAP;
  recipientBapId?: ExtendedBAP;
  onEncrypted?: (result: EncryptedData) => void;
  onDecrypted?: (result: DecryptedData) => void;
  allowedTypes?: EncryptionContentType[];
  useType42?: boolean;
  variant?: 'surface' | 'ghost' | 'classic';
  size?: '1' | '2' | '3';
}

Hook Usage

import { useBapEncryption } from 'bigblocks';

function EncryptionHookExample() {
  const { encrypt, decrypt, isEncrypting, isDecrypting } = useBapEncryption({
    useType42: true
  });

  const handleEncrypt = async (content: string) => {
    const result = await encrypt(content, {
      type: 'message',
      recipientPublicKey: 'recipient-key'
    });
    console.log('Encrypted:', result);
  };

  return (
    <button onClick={() => handleEncrypt('Secret message')} disabled={isEncrypting}>
      {isEncrypting ? 'Encrypting...' : 'Encrypt'}
    </button>
  );
}