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.
Installation
npx bigblocks add bap-encryption-suite
Import
import { BapEncryptionSuite } from 'bigblocks';
Props
Prop | Type | Required | Default | Description |
---|---|---|---|---|
bapInstance | ExtendedBAP | Yes | - | The BAP instance for encryption/decryption |
recipientBapId | ExtendedBAP | No | - | Recipient BAP instance for asymmetric encryption |
onEncrypted | (result: EncryptedData) => void | No | - | Callback when content is encrypted |
onDecrypted | (result: DecryptedData) => void | No | - | Callback when content is decrypted |
allowedTypes | EncryptionContentType[] | No | ['message', 'file', 'json'] | Allowed content types |
useType42 | boolean | No | false | Use 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
- Key Management: Never expose private keys
- Perfect Forward Secrecy: Use ephemeral keys when possible
- Metadata Privacy: Be careful what metadata you include
- Size Limits: Implement reasonable file size limits
- 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
Related Components
- BapFileSigner - Sign files with BAP identity
- BapKeyRotation - Rotate encryption keys
- ShamirSecretSharing - Split secrets securely
- Type42KeyDerivation - Advanced key derivation
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>
);
}