🥯Bagel

Cryptography

Understanding Bagel's cryptographic implementation

Cryptographic Implementation

Bagel uses advanced cryptography to provide privacy and security for your transactions. This document explains the key cryptographic functions and processes used in Bagel.

Key Types and Derivation

Viewing and Spending Keys

The core of Bagel's privacy model is the separation of viewing and spending keys:

  • Viewing Keys: Used to discover and identify stealth payments sent to you
  • Spending Keys: Used to control and spend funds from stealth addresses

Both keys are derived deterministically from your wallet signature:

export async function deriveKeysFromSignature(signature: string | Uint8Array) {
  // Use HMAC-SHA256 to derive deterministic seeds for viewing and spending keys
  const viewingSeed = hmac(sha256, Buffer.from("bagel-viewing-key"), signatureBytes);
  const spendingSeed = hmac(sha256, Buffer.from("bagel-spending-key"), signatureBytes);
 
  // Generate key pairs from the seeds
  const viewingPrivateKey = viewingSeed.slice(0, 32);
  const viewingPublicKey = ed25519.getPublicKey(viewingPrivateKey);
 
  const spendingPrivateKey = spendingSeed.slice(0, 32);
  const spendingPublicKey = ed25519.getPublicKey(spendingPrivateKey);
  
  // Returns hex-encoded key pairs
  return { viewingKeys, spendingKeys };
}

Ephemeral Keys

For each transaction, a new ephemeral key pair is generated:

export async function generateEphemeralKeyPair() {
  // Generate a random 32-byte private key
  const ephemeralSecret = randomBytes(32);
  // Derive the public key from the private key
  const ephemeralPublic = ed25519.getPublicKey(ephemeralSecret);
 
  return {
    ephemeralSecret: bytesToHex(ephemeralSecret),
    ephemeralPublic: bytesToHex(ephemeralPublic),
  };
}

Key Exchange and Shared Secrets

Bagel uses X25519 Diffie-Hellman key exchange to create shared secrets:

export async function computeSharedSecret(privateKey: string, publicKey: string) {
  // Convert Ed25519 keys to X25519 format for Diffie-Hellman key exchange
  const x25519PublicKey = edPublicKeyToX25519(publicBytes);
  const x25519PrivateKey = edPrivateKeyToX25519(privateBytes);
 
  // Compute the shared secret using X25519 key exchange
  const sharedSecret = x25519.getSharedSecret(
    x25519PrivateKey,
    x25519PublicKey,
  );
  
  return bytesToHex(sharedSecret);
}

Stealth Address Creation

The core privacy feature of Bagel is the creation of unique stealth addresses for each transaction:

export async function createSecureStealthAddress(
  sharedSecret: string,
  spendingPublicKey: string,
) {
  // Create a tag for identifying the stealth address
  const tag = sha256(concatBytes(secretBytes, spendingPubBytes));
 
  // Combine shared secret and spending public key to get a deterministic seed
  const combinedInput = concatBytes(secretBytes, spendingPubBytes);
  
  // Generate a deterministic private key using HMAC-SHA256
  const stealthPrivateKeyBytes = hmac(
    sha256,
    Buffer.from("bagel-stealth-key"),
    combinedInput,
  );
  
  // Create a Solana keypair from this seed
  const stealthKeypair = Keypair.fromSeed(seed);
  
  return {
    stealthAddress,
    stealthPublicKey,
    stealthPrivateKey,
    tag,
  };
}

Stealth Address Recovery

Recipients can recover stealth addresses sent to them using their keys:

export async function reconstructSecureStealthKeypair(
  sharedSecret: string,
  spendingPublicKey: string,
  spendingPrivateKey: string,
): Promise<{ keypair: Keypair; tag: string }>

This function:

  1. Validates the spending key pair
  2. Creates the same tag as in stealth address creation
  3. Derives the same stealth keypair using identical HMAC-SHA256 parameters
  4. Returns the reconstructed keypair, which can access funds sent to the stealth address

Message Signing and Verification

Bagel includes utilities for signing and verifying messages:

export async function signMessage(message: string, privateKey: string) {
  const privateKeyBytes = hexToBytes(privateKey);
  const messageBytes = new TextEncoder().encode(message);
  const signature = ed25519.sign(messageBytes, privateKeyBytes);
  return bytesToHex(signature);
}
 
export async function verifySignature(
  message: string,
  signature: string,
  publicKey: string,
) {
  const signatureBytes = hexToBytes(signature);
  const publicKeyBytes = hexToBytes(publicKey);
  const messageBytes = new TextEncoder().encode(message);
  return ed25519.verify(signatureBytes, messageBytes, publicKeyBytes);
}

Data Encryption and Decryption

In addition to stealth address functionality, Bagel provides utilities for general data encryption:

export async function encryptData(data: string, password: string): Promise<string> {
  // Generate a random salt for PBKDF2
  const salt = crypto.getRandomValues(new Uint8Array(16));
  // Generate a random IV for AES-GCM
  const iv = crypto.getRandomValues(new Uint8Array(12));
  // Derive the encryption key from the password
  const key = await deriveEncryptionKey(password, salt);
  // Encrypt the data using AES-GCM
  const encryptedBuffer = await crypto.subtle.encrypt(
    { name: "AES-GCM", iv },
    key,
    dataBuffer,
  );
  // Return base64-encoded result
  // ...
}

Libraries and Standards

Bagel uses the following cryptographic libraries and standards:

  • Noble Curves (@noble/curves) - For Ed25519 and X25519 operations
  • Noble Hashes (@noble/hashes) - For SHA-256, HMAC, and utility functions
  • Web Crypto API - For encryption operations (AES-GCM, PBKDF2)
  • Solana Web3.js - For Solana-specific keypair operations

All cryptographic operations follow industry best practices for secure implementation.

On this page