BLAKE3 Security-Focused API Documentation

Version: 1.0
Last Updated: 2025-07-05
**Security Classification:
PUBLIC
Author: Phantom (phantom@metamui.id)

Overview

BLAKE3 is a cryptographic hash function that combines the security of BLAKE2 with significantly improved performance through parallelism. It supports arbitrary output lengths, keyed hashing (MAC), key derivation, and extensible output (XOF). BLAKE3 is the fastest secure hash function on modern hardware.

Security Level: 128-bit (collision), 256-bit (preimage)
Default Output Size: 32 bytes (configurable)
Performance: >5 GB/s on modern CPUs

Security Warnings ⚠️

  1. Not for Passwords: Use Argon2 for password hashing, not BLAKE3
  2. Key Size: When using keyed mode, keys MUST be exactly 32 bytes
  3. Context Strings: Use domain separation to prevent cross-protocol attacks
  4. Parallel Safety: Internal parallelism, safe for concurrent use
  5. No Length Hiding: Output length can reveal input length in some modes

API Functions

hash(data: bytes, length: int = 32) -> bytes

Security Contract:

Attack Resistance: | Attack Type | Protected | Notes | |————-|———–|——-| | Collision | ✅ | 2^128 operations minimum | | Preimage | ✅ | 2^min(256,8*outlen) operations | | Second Preimage | ✅ | 2^256 operations | | Length Extension | ✅ | Tree structure prevents | | Timing Attack | ✅ | Constant time operations | | Parallel Collision | ✅ | Tree mode prevents |

Secure Usage Example:

# SECURE: Content addressing with verification
def store_content(data: bytes) -> str:
    # Generate content ID
    content_hash = blake3.hash(data)
    content_id = content_hash.hex()
    
    # Store with integrity
    storage[content_id] = {
        'data': data,
        'hash': content_hash,
        'algorithm': 'blake3',
        'timestamp': time.time()
    }
    
    return content_id

def retrieve_content(content_id: str) -> bytes:
    stored = storage[content_id]
    
    # Verify integrity
    computed_hash = blake3.hash(stored['data'])
    if not constant_time_compare(computed_hash, stored['hash']):
        raise IntegrityError("Content corrupted")
    
    return stored['data']

# SECURE: Variable-length output for KDF
def derive_keys(master_secret: bytes) -> dict:
    # Generate multiple keys from one hash
    extended = blake3.hash(master_secret, length=96)
    
    return {
        'encryption_key': extended[0:32],
        'mac_key': extended[32:64],
        'iv': extended[64:80],
        'session_id': extended[80:96]
    }

Common Mistakes:

# INSECURE: Using for passwords
password_hash = blake3.hash(password.encode())  # NO!

# INSECURE: Short outputs reduce security
short_hash = blake3.hash(data, length=8)  # Only 64 bits!

# INSECURE: No domain separation
key1 = blake3.hash(master + b"encryption")
key2 = blake3.hash(master + b"mac")  # Use derive_key instead!

keyed_hash(key: bytes[32], data: bytes, length: int = 32) -> bytes

Security Contract:

Attack Resistance: | Attack Type | Protected | Notes | |————-|———–|——-| | Forgery | ✅ | 2^128 operations minimum | | Key Recovery | ✅ | 2^256 operations | | Collision | ✅ | Under same key: 2^128 | | Timing Attack | ✅ | Constant time | | Multi-target | ✅ | Security per key maintained |

Security Requirements:

Secure Usage Example:

// SECURE: Message authentication
struct MessageAuthenticator {
    key: [u8; 32],
}

impl MessageAuthenticator {
    fn new() -> Result<Self, Error> {
        let key = generate_random_key()?;
        Ok(Self { key })
    }
    
    fn create_authenticated_message(&self, msg: &[u8]) -> Vec<u8> {
        // Generate MAC
        let mac = blake3::keyed_hash(&self.key, msg);
        
        // Append MAC to message
        [msg, &mac].concat()
    }
    
    fn verify_message(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
        if data.len() < 32 {
            return Err(Error::InvalidMessage);
        }
        
        // Split message and MAC
        let (msg, received_mac) = data.split_at(data.len() - 32);
        
        // Verify MAC
        let computed_mac = blake3::keyed_hash(&self.key, msg);
        
        if constant_time_eq(&computed_mac, received_mac) {
            Ok(msg.to_vec())
        } else {
            Err(Error::AuthenticationFailed)
        }
    }
}

// SECURE: Key rotation
fn rotate_key(old_key: &[u8; 32]) -> Result<[u8; 32], Error> {
    // Derive new key from old (one-way)
    let new_key = blake3::derive_key("key-rotation-v1", old_key);
    
    // Securely erase old key
    secure_zero(old_key);
    
    Ok(new_key)
}

derive_key(context: str, material: bytes, length: int = 32) -> bytes

Security Contract:

Attack Resistance: | Attack Type | Protected | Notes | |————-|———–|——-| | Cross-protocol | ✅ | Context separation | | Related-key | ✅ | Independent derivation | | Weak material | ⚠️ | Output limited by input entropy | | Side-channel | ✅ | Constant time |

Best Practices:

Secure Usage Example:

// SECURE: Hierarchical key derivation
class KeyHierarchy {
    private masterKey: Uint8Array;
    
    constructor(masterKey: Uint8Array) {
        if (masterKey.length !== 32) {
            throw new Error("Master key must be 32 bytes");
        }
        this.masterKey = masterKey;
    }
    
    deriveKey(purpose: string, index: number): Uint8Array {
        // Create unique context with purpose and index
        const context = `MyApp-v1-${purpose}-${index}`;
        
        // Derive purpose-specific key
        return blake3.deriveKey(context, this.masterKey, 32);
    }
    
    // Derive keys for different purposes
    getEncryptionKey(userId: string): Uint8Array {
        const userBytes = new TextEncoder().encode(userId);
        const combined = concatenate(this.masterKey, userBytes);
        return blake3.deriveKey("encryption-per-user-v1", combined, 32);
    }
    
    getSigningKey(documentId: string): Uint8Array {
        const docBytes = new TextEncoder().encode(documentId);
        const combined = concatenate(this.masterKey, docBytes);
        return blake3.deriveKey("signing-per-document-v1", combined, 32);
    }
}

// SECURE: Protocol-specific derivation
function deriveProtocolKeys(sharedSecret: Uint8Array): ProtocolKeys {
    // Each protocol gets independent keys
    return {
        clientEncrypt: blake3.deriveKey(
            "MyProtocol-v2-client-encrypt",
            sharedSecret,
            32
        ),
        serverEncrypt: blake3.deriveKey(
            "MyProtocol-v2-server-encrypt",
            sharedSecret,
            32
        ),
        clientMAC: blake3.deriveKey(
            "MyProtocol-v2-client-mac",
            sharedSecret,
            32
        ),
        serverMAC: blake3.deriveKey(
            "MyProtocol-v2-server-mac",
            sharedSecret,
            32
        ),
    };
}

create_hasher(key?: bytes[32]) -> Blake3Hasher

Security Contract:

Streaming Benefits:

Secure Usage Example:

# SECURE: Streaming large files with progress
def hash_large_file_with_progress(filepath: str, callback=None):
    hasher = blake3.create_hasher()
    total_size = os.path.getsize(filepath)
    bytes_processed = 0
    
    with open(filepath, 'rb') as f:
        while chunk := f.read(1024 * 1024):  # 1MB chunks
            hasher.update(chunk)
            bytes_processed += len(chunk)
            
            if callback:
                callback(bytes_processed / total_size)
    
    return hasher.finalize()

# SECURE: Tree hashing for parallel processing
class ParallelTreeHasher:
    def __init__(self):
        self.chunk_size = 1024 * 1024  # 1MB
        
    def hash_file_parallel(self, filepath: str) -> bytes:
        # Split file into chunks
        chunks = self._split_file(filepath)
        
        # Hash chunks in parallel
        with ThreadPoolExecutor() as executor:
            chunk_hashes = executor.map(blake3.hash, chunks)
        
        # Combine chunk hashes
        tree_hasher = blake3.create_hasher()
        for chunk_hash in chunk_hashes:
            tree_hasher.update(chunk_hash)
        
        return tree_hasher.finalize()

update(data: bytes) -> void

Security Contract:

Security Notes:

finalize(length: int = 32) -> bytes

Security Contract:

XOF Properties:

Secure Usage Example:

// SECURE: Using XOF for multiple outputs
fn generate_crypto_values(seed: &[u8]) -> CryptoValues {
    let mut hasher = Blake3::new();
    hasher.update(b"MyApp-CryptoValues-v1");
    hasher.update(seed);
    
    // Generate different lengths for different purposes
    CryptoValues {
        session_key: hasher.finalize_xof(32),     // 256-bit key
        nonce: hasher.finalize_xof(12),           // 96-bit nonce
        auth_tag: hasher.finalize_xof(16),        // 128-bit tag
        commitment: hasher.finalize_xof(32),      // 256-bit commitment
    }
}

Security Best Practices

Domain Separation

class DomainSeparatedHasher:
    """Prevent cross-protocol attacks with domain separation"""
    
    def __init__(self, app_name: str, version: str):
        self.domain = f"{app_name}-{version}"
    
    def hash_user_data(self, user_id: str, data: bytes) -> bytes:
        hasher = blake3.create_hasher()
        hasher.update(f"{self.domain}-user-data".encode())
        hasher.update(user_id.encode())
        hasher.update(data)
        return hasher.finalize()
    
    def hash_system_data(self, component: str, data: bytes) -> bytes:
        hasher = blake3.create_hasher()
        hasher.update(f"{self.domain}-system-data".encode())
        hasher.update(component.encode())
        hasher.update(data)
        return hasher.finalize()

Keyed Hashing Patterns

/// Secure token generation with BLAKE3
pub struct TokenGenerator {
    key: [u8; 32],
    counter: AtomicU64,
}

impl TokenGenerator {
    pub fn new() -> Result<Self, Error> {
        Ok(Self {
            key: generate_random_bytes()?,
            counter: AtomicU64::new(0),
        })
    }
    
    pub fn generate_token(&self, purpose: &str) -> String {
        let count = self.counter.fetch_add(1, Ordering::SeqCst);
        
        let mut data = Vec::new();
        data.extend_from_slice(purpose.as_bytes());
        data.extend_from_slice(&count.to_le_bytes());
        data.extend_from_slice(&current_timestamp().to_le_bytes());
        
        let token = blake3::keyed_hash(&self.key, &data);
        base64url::encode(&token)
    }
    
    pub fn verify_token(&self, token: &str, purpose: &str) -> bool {
        // In practice, store issued tokens for verification
        // This is a simplified example
        true
    }
}

Performance Optimization

Parallel Processing

def parallel_merkle_tree(leaves: List[bytes]) -> bytes:
    """Build Merkle tree using BLAKE3's parallelism"""
    
    if not leaves:
        return blake3.hash(b"empty-tree")
    
    # First level: hash leaves in parallel
    # BLAKE3 automatically uses SIMD/threading
    level = [blake3.hash(leaf) for leaf in leaves]
    
    while len(level) > 1:
        next_level = []
        
        # Process pairs in parallel
        for i in range(0, len(level), 2):
            if i + 1 < len(level):
                combined = level[i] + level[i + 1]
            else:
                combined = level[i]
            
            next_level.append(blake3.hash(combined))
        
        level = next_level
    
    return level[0]

Performance Metrics

Operation Input Size Single-thread Multi-thread Notes
hash() 1 KB 200 MB/s N/A Small data
hash() 1 MB 3 GB/s 8 GB/s Parallel kicks in
hash() 1 GB 3 GB/s 15 GB/s Full parallelism
keyed_hash() Any ~Same as hash() ~Same Minimal overhead
derive_key() Any ~Same as hash() N/A Single-threaded

Platform-Specific Security Notes

Python

Rust

TypeScript/JavaScript

Swift

Kotlin

Common Integration Patterns

Content-Defined Chunking

def content_defined_chunks(data: bytes, target_size: int = 1024*1024):
    """Split data using BLAKE3 rolling hash"""
    window_size = 48
    chunks = []
    start = 0
    
    hasher = blake3.create_hasher()
    
    for i in range(len(data)):
        # Update rolling hash
        if i >= window_size:
            # BLAKE3 doesn't support rolling, so rehash window
            window = data[i-window_size+1:i+1]
            h = blake3.hash(window, length=8)
            
            # Check if this is a chunk boundary
            threshold = (1 << 64) // target_size
            if int.from_bytes(h, 'little') < threshold:
                chunks.append(data[start:i+1])
                start = i + 1
    
    # Don't forget the last chunk
    if start < len(data):
        chunks.append(data[start:])
    
    return chunks

Commitment Schemes

pub struct Blake3Commitment {
    commitment: [u8; 32],
}

impl Blake3Commitment {
    pub fn commit(value: &[u8]) -> (Self, [u8; 32]) {
        // Generate random nonce
        let nonce: [u8; 32] = random();
        
        // Create commitment
        let mut hasher = Blake3::new();
        hasher.update(&nonce);
        hasher.update(value);
        let commitment = hasher.finalize();
        
        (Self { commitment: commitment.into() }, nonce)
    }
    
    pub fn verify(&self, value: &[u8], nonce: &[u8; 32]) -> bool {
        let mut hasher = Blake3::new();
        hasher.update(nonce);
        hasher.update(value);
        let check = hasher.finalize();
        
        constant_time_eq(&self.commitment, check.as_bytes())
    }
}

Security Auditing

Verification Checklist

Code Review Patterns

# ✅ GOOD: Keyed hash for MAC
mac = blake3.keyed_hash(key, message)

# ❌ BAD: Hash without key for MAC
mac = blake3.hash(key + message)

# ✅ GOOD: Domain separation
key1 = blake3.derive_key("app-v1-encrypt", master)
key2 = blake3.derive_key("app-v1-sign", master)

# ❌ BAD: No domain separation
key1 = blake3.hash(master + b"1")
key2 = blake3.hash(master + b"2")

Security Analysis

Threat Model: BLAKE3 Threat Model

The comprehensive threat analysis covers:

For complete security analysis and risk assessment, see the dedicated threat model documentation.

References

  1. BLAKE3 Specification
  2. BLAKE3 Paper
  3. Official Implementations

Support

Security Issues: security@metamui.id
Documentation Updates: phantom@metamui.id
Vulnerability Disclosure: See SECURITY.md


Document Version: 1.0
Review Cycle: Quarterly
Next Review: 2025-04-05
Classification: PUBLIC