BLAKE2s HASH
32-bit Optimized High-Performance Hash Function
🎯 Overview
BLAKE2s is the 32-bit optimized variant of BLAKE2, specifically designed for embedded systems, mobile devices, and 32-bit platforms. It delivers exceptional performance while maintaining strong cryptographic security, making it ideal for resource-constrained environments.
✨ Key Features
32-bit Optimized
Designed specifically for embedded systems and 32-bit platforms
Variable Output
Configurable digest size from 1 to 32 bytes
Built-in MAC Mode
Native keyed hashing with up to 32-byte keys
Small Footprint
Minimal RAM and code size requirements
Domain Separation
Salt and personalization for different contexts
Tree Hashing
Supports parallel and tree hashing modes
🎯 Common Use Cases
🔌 Embedded Systems
IoT devices, microcontrollers, and resource-constrained platforms
📱 Mobile Applications
Efficient hashing on mobile devices and ARM processors
⛏️ Proof of Work
Cryptocurrency mining optimized for GPU architectures
✅ Fast Checksums
Rapid integrity checking with compact digest sizes
🔑 Lightweight KDF
Key derivation for constrained devices and protocols
✍️ Embedded Signatures
Hash function for signature schemes in embedded systems
🔧 Algorithm Parameters
Output Size
Range: 1-32 bytes
Default: 32 bytes (256 bits)
Common: 16 bytes (128 bits)
Key Size (MAC Mode)
Range: 0-32 bytes
Recommended: 16+ bytes
Purpose: Message authentication
Salt
Size: 8 bytes
Default: All zeros
Use: Randomization and uniqueness
Personalization
Size: 8 bytes
Default: All zeros
Use: Domain separation
Tree Parameters
Fanout: 0-255 (default: 1)
Depth: 0-255 (default: 1)
Use: Parallel processing
Node Parameters
Offset: 0-2^48
Depth: 0-255
Use: Tree structure
🔒 Security Properties
Collision Resistance
2^128 for 32-byte output, 2^64 for 16-byte output
Preimage Resistance
2^256 for 32-byte output, 2^128 for 16-byte output
Second Preimage
2^256 resistance for maximum security
Keyed Mode Security
2^128 security for MAC usage with proper keys
Indifferentiability
Proven indifferentiable from random oracle
Length Extension
Immune to length extension attacks by design
- Second Preimage Resistance: 2^256
- Keyed Mode Security: 2^128 for MAC usage
- Side-Channel Resistance: No data-dependent operations
Implementation
Python Example
from metamui_crypto import BLAKE2s
import os
# Basic hashing
blake2s = BLAKE2s()
hash_value = blake2s.hash(b"Hello, World!")
print(f"BLAKE2s hash: {hash_value.hex()}")
# Custom digest size
blake2s_128 = BLAKE2s(digest_size=16) # 128-bit output
hash_128 = blake2s_128.hash(b"Hello, World!")
print(f"BLAKE2s-128: {hash_128.hex()}")
# Keyed hashing (MAC mode)
key = os.urandom(32)
blake2s_mac = BLAKE2s(key=key)
mac = blake2s_mac.hash(b"Authenticated message")
print(f"BLAKE2s MAC: {mac.hex()}")
# Incremental hashing
hasher = BLAKE2s()
hasher.update(b"Hello, ")
hasher.update(b"World!")
final_hash = hasher.finalize()
# Personalization for domain separation
blake2s_iot = BLAKE2s(personalization=b"IoTAuth1")
blake2s_mobile = BLAKE2s(personalization=b"MobileAp")
# Salt for randomization
salt = os.urandom(8)
blake2s_salted = BLAKE2s(salt=salt)
salted_hash = blake2s_salted.hash(b"Data to hash")
Advanced Usage
# Embedded system authentication
class EmbeddedAuth:
def __init__(self, device_key: bytes):
self.device_key = device_key
self.hasher = BLAKE2s(key=device_key, digest_size=16)
def generate_token(self, timestamp: int, nonce: bytes) -> bytes:
"""Generate compact authentication token"""
message = timestamp.to_bytes(4, 'little') + nonce
return self.hasher.hash(message)
def verify_token(self, token: bytes, timestamp: int, nonce: bytes) -> bool:
"""Verify authentication token"""
expected = self.generate_token(timestamp, nonce)
return token == expected
# Lightweight file integrity
class CompactIntegrity:
def __init__(self):
self.hasher = BLAKE2s(digest_size=16) # 128-bit checksums
def checksum_file(self, filepath: str) -> bytes:
"""Compute compact file checksum"""
hasher = BLAKE2s(digest_size=16)
with open(filepath, 'rb') as f:
# Small chunks for embedded systems
while chunk := f.read(512):
hasher.update(chunk)
return hasher.finalize()
def verify_file(self, filepath: str, expected: bytes) -> bool:
"""Verify file integrity"""
actual = self.checksum_file(filepath)
return actual == expected
# IoT sensor data signing
class SensorSigner:
def __init__(self, sensor_id: str, secret: bytes):
self.sensor_id = sensor_id
self.secret = secret
personalization = f"SENS{sensor_id[:4]}".encode()[:8]
self.hasher = BLAKE2s(key=secret, personalization=personalization)
def sign_reading(self, temperature: float, humidity: float,
timestamp: int) -> dict:
"""Sign sensor reading"""
# Pack data efficiently
data = struct.pack('<ffI', temperature, humidity, timestamp)
signature = self.hasher.hash(data)
return {
'sensor_id': self.sensor_id,
'temperature': temperature,
'humidity': humidity,
'timestamp': timestamp,
'signature': signature.hex()
}
def verify_reading(self, reading: dict) -> bool:
"""Verify signed reading"""
data = struct.pack('<ffI',
reading['temperature'],
reading['humidity'],
reading['timestamp'])
expected = self.hasher.hash(data)
return expected.hex() == reading['signature']
# Memory-constrained password hashing
class LightweightPassword:
def __init__(self, iterations=1000):
self.iterations = iterations
def hash_password(self, password: str, salt: bytes = None) -> tuple:
"""Lightweight password hashing for embedded systems"""
if salt is None:
salt = os.urandom(8)
# Initial hash with salt
hasher = BLAKE2s(salt=salt, personalization=b"Password")
current = hasher.hash(password.encode())
# Simple iteration (less memory than PBKDF2/Argon2)
for i in range(self.iterations):
hasher = BLAKE2s(key=current[:32])
current = hasher.hash(salt + i.to_bytes(2, 'little'))
return current, salt
# Merkle tree for embedded systems
class CompactMerkleTree:
def __init__(self):
self.hasher = BLAKE2s(digest_size=16) # 128-bit nodes
def hash_leaf(self, data: bytes) -> bytes:
"""Hash leaf node"""
return self.hasher.hash(b"\x00" + data)
def hash_internal(self, left: bytes, right: bytes) -> bytes:
"""Hash internal node"""
return self.hasher.hash(b"\x01" + left + right)
def compute_root(self, leaves: list) -> bytes:
"""Compute Merkle root with minimal memory"""
if not leaves:
return self.hasher.hash(b"")
# Hash leaves
current_level = [self.hash_leaf(leaf) for leaf in leaves]
# Build tree level by level
while len(current_level) > 1:
next_level = []
# Process pairs
for i in range(0, len(current_level) - 1, 2):
parent = self.hash_internal(
current_level[i],
current_level[i + 1]
)
next_level.append(parent)
# Handle odd leaf
if len(current_level) % 2 == 1:
next_level.append(current_level[-1])
current_level = next_level
return current_level[0]
Implementation Details
# BLAKE2s core operations (simplified)
class BLAKE2sCore:
def __init__(self):
self.h = [ # Initial hash values (32-bit)
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
]
self.t = [0, 0] # Counter (64-bit as two 32-bit)
self.f = [0, 0] # Finalization flags
def G(self, v, a, b, c, d, x, y):
"""BLAKE2s G function (32-bit operations)"""
v[a] = (v[a] + v[b] + x) & 0xFFFFFFFF
v[d] = self.rotr32(v[d] ^ v[a], 16)
v[c] = (v[c] + v[d]) & 0xFFFFFFFF
v[b] = self.rotr32(v[b] ^ v[c], 12)
v[a] = (v[a] + v[b] + y) & 0xFFFFFFFF
v[d] = self.rotr32(v[d] ^ v[a], 8)
v[c] = (v[c] + v[d]) & 0xFFFFFFFF
v[b] = self.rotr32(v[b] ^ v[c], 7)
def compress(self, block, last=False):
"""Compression function for 32-bit words"""
v = self.h[:] + [
0x6a09e667, 0xbb67ae85,
0x3c6ef372, 0xa54ff53a
]
v[12] ^= self.t[0]
v[13] ^= self.t[1]
if last:
v[14] ^= 0xFFFFFFFF
# 10 rounds of mixing (vs 12 for BLAKE2b)
for round in range(10):
# Column step
self.G(v, 0, 4, 8, 12, m[sigma[round][0]], m[sigma[round][1]])
self.G(v, 1, 5, 9, 13, m[sigma[round][2]], m[sigma[round][3]])
self.G(v, 2, 6, 10, 14, m[sigma[round][4]], m[sigma[round][5]])
self.G(v, 3, 7, 11, 15, m[sigma[round][6]], m[sigma[round][7]])
# Diagonal step
self.G(v, 0, 5, 10, 15, m[sigma[round][8]], m[sigma[round][9]])
self.G(v, 1, 6, 11, 12, m[sigma[round][10]], m[sigma[round][11]])
self.G(v, 2, 7, 8, 13, m[sigma[round][12]], m[sigma[round][13]])
self.G(v, 3, 4, 9, 14, m[sigma[round][14]], m[sigma[round][15]])
# Update hash values
for i in range(8):
self.h[i] ^= v[i] ^ v[i + 8]
def rotr32(self, x, n):
"""32-bit rotate right"""
return ((x >> n) | (x << (32 - n))) & 0xFFFFFFFF
Security Considerations
Best Practices
- Output Size Selection
- Use full 256-bit output for maximum security
- 128-bit output acceptable for non-critical uses
- Avoid outputs smaller than 128 bits for security
- Key Management
- Generate keys using secure random source
- Use different keys for different purposes
- Rotate keys regularly in long-running systems
- Embedded Security
- Clear sensitive data from memory after use
- Use constant-time comparisons for MACs
- Implement rate limiting for authentication
- Resource Constraints
- Balance security with available resources
- Consider power analysis attacks on embedded devices
- Use appropriate iteration counts for password hashing
Common Pitfalls
# DON'T: Use very short outputs for security
blake2s_weak = BLAKE2s(digest_size=8) # Only 64 bits!
# WRONG: Birthday attack in 2^32 operations
weak_mac = blake2s_weak.hash(data)
# DO: Use adequate output size
blake2s_secure = BLAKE2s(digest_size=16) # 128 bits minimum
secure_mac = blake2s_secure.hash(data)
# DON'T: Use predictable personalization
device_id = "12345"
# WRONG: Predictable personalization
blake2s = BLAKE2s(personalization=device_id.encode()[:8])
# DO: Use properly formatted personalization
blake2s = BLAKE2s(personalization=b"DEV12345") # Fixed 8 bytes
# DON'T: Ignore timing attacks in embedded systems
def verify_mac(computed, expected):
# WRONG: Early return leaks timing information
for i in range(len(computed)):
if computed[i] != expected[i]:
return False
return True
# DO: Use constant-time comparison
def verify_mac_secure(computed, expected):
if len(computed) != len(expected):
return False
result = 0
for x, y in zip(computed, expected):
result |= x ^ y
return result == 0
Performance Characteristics
Benchmarks
| Operation | Data Size | Platform | Throughput | Time |
|---|---|---|---|---|
| BLAKE2s-256 | 64 B | ARM Cortex-M4 | 12.8 MB/s | 5.0 μs |
| BLAKE2s-256 | 1 KB | ARM Cortex-M4 | 45.2 MB/s | 22.1 μs |
| BLAKE2s-256 | 64 B | x86-32 | 89.6 MB/s | 0.71 μs |
| BLAKE2s-256 | 1 KB | x86-32 | 412 MB/s | 2.43 μs |
| BLAKE2s-128 | 1 KB | ARM Cortex-M4 | 46.8 MB/s | 21.4 μs |
| Keyed Mode | 1 KB | ARM Cortex-M4 | 44.9 MB/s | 22.3 μs |
Memory Usage
| Component | RAM Usage | Code Size |
|---|---|---|
| State Structure | 64 bytes | - |
| Working Variables | 64 bytes | - |
| Stack (typical) | 128 bytes | - |
| Code (ARM Thumb) | - | ~2 KB |
| Code (x86) | - | ~3 KB |
| Total Typical | 256 bytes | 2-3 KB |
Performance vs Other Hash Functions
| Algorithm | ARM Cortex-M4 (1KB) | Relative Speed |
|---|---|---|
| BLAKE2s | 45.2 MB/s | 1.00x (baseline) |
| SHA-256 | 18.7 MB/s | 0.41x |
| SHA-1 | 24.3 MB/s | 0.54x |
| MD5 | 31.2 MB/s | 0.69x |
| BLAKE2b | 28.9 MB/s | 0.64x |
Optimization Strategies
# Efficient batch processing for embedded systems
class BatchProcessor:
def __init__(self, batch_size=16):
self.batch_size = batch_size
self.hasher = BLAKE2s(digest_size=16)
def process_batch(self, items):
"""Process items in batches to reduce overhead"""
results = []
for i in range(0, len(items), self.batch_size):
batch = items[i:i + self.batch_size]
batch_hash = self.hasher.hash(b''.join(batch))
results.append(batch_hash)
return results
# Memory-efficient streaming for large data
def hash_stream_embedded(stream, chunk_size=256):
"""Hash stream with minimal memory usage"""
hasher = BLAKE2s()
total_bytes = 0
while True:
chunk = stream.read(chunk_size)
if not chunk:
break
hasher.update(chunk)
total_bytes += len(chunk)
# Yield progress for UI updates
yield total_bytes
yield hasher.finalize()
# Power-efficient hashing with sleep
class PowerEfficientHasher:
def __init__(self, power_mode='balanced'):
self.hasher = BLAKE2s()
self.sleep_ms = {
'low_power': 10,
'balanced': 1,
'performance': 0
}[power_mode]
def hash_with_throttle(self, data, chunk_size=1024):
"""Hash data with power throttling"""
import time
for i in range(0, len(data), chunk_size):
chunk = data[i:i + chunk_size]
self.hasher.update(chunk)
if self.sleep_ms > 0:
time.sleep(self.sleep_ms / 1000)
return self.hasher.finalize()
Use Cases
1. IoT Device Authentication
class IoTDeviceAuth:
def __init__(self, device_secret: bytes):
self.secret = device_secret
self.counter = 0
def generate_auth_packet(self, data: bytes) -> bytes:
"""Generate authenticated packet for IoT communication"""
# Increment counter for replay protection
self.counter += 1
# Pack header efficiently
header = struct.pack('<HI',
0x1234, # Protocol version
self.counter)
# Compute MAC
hasher = BLAKE2s(key=self.secret, digest_size=16)
hasher.update(header)
hasher.update(data)
mac = hasher.finalize()
# Return packet: header + data + mac
return header + data + mac
def verify_auth_packet(self, packet: bytes) -> tuple:
"""Verify and extract authenticated packet"""
if len(packet) < 22: # 6 (header) + 16 (MAC)
return False, None
header = packet[:6]
data = packet[6:-16]
mac = packet[-16:]
# Verify protocol version
version, counter = struct.unpack('<HI', header)
if version != 0x1234:
return False, None
# Verify counter (prevent replay)
if counter <= self.counter:
return False, None
# Verify MAC
hasher = BLAKE2s(key=self.secret, digest_size=16)
hasher.update(header)
hasher.update(data)
expected_mac = hasher.finalize()
if not constant_time_compare(mac, expected_mac):
return False, None
# Update counter
self.counter = counter
return True, data
2. Firmware Verification
class FirmwareVerifier:
def __init__(self, public_key: bytes):
self.public_key = public_key
self.chunk_size = 512 # Small chunks for embedded
def compute_firmware_hash(self, firmware: bytes) -> bytes:
"""Compute firmware hash with metadata"""
hasher = BLAKE2s(personalization=b"FW_HASH1")
# Add firmware metadata
header = struct.pack('<III',
len(firmware), # Size
0x01020304, # Version
0x20240101) # Build date
hasher.update(header)
# Hash firmware in chunks
for i in range(0, len(firmware), self.chunk_size):
chunk = firmware[i:i + self.chunk_size]
hasher.update(chunk)
return hasher.finalize()
def verify_firmware(self, firmware: bytes, signature: bytes) -> bool:
"""Verify firmware signature"""
# Compute hash
fw_hash = self.compute_firmware_hash(firmware)
# Verify signature (simplified - use real crypto)
hasher = BLAKE2s(key=self.public_key)
expected = hasher.hash(fw_hash)
return expected[:16] == signature
class SecureBootloader:
def __init__(self):
self.trusted_hashes = {} # Hash -> version mapping
def add_trusted_firmware(self, version: str, firmware: bytes):
"""Add trusted firmware hash"""
hasher = BLAKE2s(digest_size=16) # Compact hashes
fw_hash = hasher.hash(firmware)
self.trusted_hashes[fw_hash] = version
def verify_and_boot(self, firmware: bytes) -> bool:
"""Verify firmware before booting"""
hasher = BLAKE2s(digest_size=16)
fw_hash = hasher.hash(firmware)
if fw_hash in self.trusted_hashes:
version = self.trusted_hashes[fw_hash]
print(f"Booting trusted firmware v{version}")
return True
print("Firmware verification failed!")
return False
3. Sensor Network Security
class SensorNetwork:
def __init__(self, network_key: bytes):
self.network_key = network_key
self.nodes = {}
def register_node(self, node_id: str) -> bytes:
"""Register sensor node and derive its key"""
# Derive node-specific key
hasher = BLAKE2s(key=self.network_key)
node_key = hasher.hash(f"NODE:{node_id}".encode())
self.nodes[node_id] = {
'key': node_key,
'sequence': 0
}
return node_key
def create_sensor_packet(self, node_id: str, sensor_data: dict) -> bytes:
"""Create authenticated sensor packet"""
if node_id not in self.nodes:
raise ValueError("Unknown node")
node = self.nodes[node_id]
node['sequence'] += 1
# Pack sensor data efficiently
packet = struct.pack('<H', node['sequence']) # Sequence number
packet += struct.pack('<f', sensor_data['temperature'])
packet += struct.pack('<f', sensor_data['humidity'])
packet += struct.pack('<I', sensor_data['timestamp'])
# Add MAC
hasher = BLAKE2s(key=node['key'], digest_size=8) # Short MAC
hasher.update(packet)
mac = hasher.finalize()
return node_id.encode()[:8] + packet + mac
def verify_sensor_packet(self, packet: bytes) -> dict:
"""Verify and parse sensor packet"""
if len(packet) < 26: # 8 + 14 + 8
raise ValueError("Invalid packet size")
node_id = packet[:8].decode().strip('\x00')
data = packet[8:-8]
mac = packet[-8:]
if node_id not in self.nodes:
raise ValueError("Unknown node")
# Verify MAC
node = self.nodes[node_id]
hasher = BLAKE2s(key=node['key'], digest_size=8)
hasher.update(data)
expected_mac = hasher.finalize()
if mac != expected_mac:
raise ValueError("MAC verification failed")
# Parse data
seq = struct.unpack('<H', data[0:2])[0]
# Verify sequence (prevent replay)
if seq <= node['sequence']:
raise ValueError("Replay attack detected")
node['sequence'] = seq
return {
'node_id': node_id,
'sequence': seq,
'temperature': struct.unpack('<f', data[2:6])[0],
'humidity': struct.unpack('<f', data[6:10])[0],
'timestamp': struct.unpack('<I', data[10:14])[0]
}
4. Lightweight Blockchain
class LightBlock:
def __init__(self, index, timestamp, data, previous_hash):
self.index = index
self.timestamp = timestamp
self.data = data
self.previous_hash = previous_hash
self.nonce = 0
self.hash = None
def compute_hash(self):
"""Compute block hash using BLAKE2s"""
hasher = BLAKE2s(digest_size=16) # 128-bit hashes
# Pack block data
hasher.update(struct.pack('<I', self.index))
hasher.update(struct.pack('<I', self.timestamp))
hasher.update(self.previous_hash)
hasher.update(self.data.encode() if isinstance(self.data, str) else self.data)
hasher.update(struct.pack('<I', self.nonce))
return hasher.finalize()
def mine_block(self, difficulty=2):
"""Simple proof of work"""
target = b'\x00' * difficulty
while True:
self.hash = self.compute_hash()
if self.hash[:difficulty] == target:
break
self.nonce += 1
# Yield periodically for cooperative multitasking
if self.nonce % 1000 == 0:
yield self.nonce
yield self.hash
class LightBlockchain:
def __init__(self):
self.chain = []
self.difficulty = 2
# Create genesis block
genesis = LightBlock(0, 0, "Genesis", b'\x00' * 16)
genesis.hash = genesis.compute_hash()
self.chain.append(genesis)
def add_block(self, data):
"""Add new block to chain"""
previous = self.chain[-1]
block = LightBlock(
len(self.chain),
int(time.time()),
data,
previous.hash
)
# Mine block
for progress in block.mine_block(self.difficulty):
if isinstance(progress, int):
print(f"Mining... {progress} hashes")
else:
block.hash = progress
break
self.chain.append(block)
return block
def verify_chain(self):
"""Verify blockchain integrity"""
for i in range(1, len(self.chain)):
current = self.chain[i]
previous = self.chain[i-1]
# Verify hash
if current.compute_hash() != current.hash:
return False
# Verify link
if current.previous_hash != previous.hash:
return False
# Verify proof of work
if current.hash[:self.difficulty] != b'\x00' * self.difficulty:
return False
return True
Comparison with Other Hash Functions
BLAKE2s vs BLAKE2b
| Feature | BLAKE2s | BLAKE2b |
|---|---|---|
| Word Size | 32-bit | 64-bit |
| Output Size | 1-32 bytes | 1-64 bytes |
| Block Size | 64 bytes | 128 bytes |
| Rounds | 10 | 12 |
| Best Platform | 32-bit, embedded | 64-bit servers |
BLAKE2s vs SHA-256 (Embedded)
| Feature | BLAKE2s | SHA-256 |
|---|---|---|
| Speed (Cortex-M4) | 45.2 MB/s | 18.7 MB/s |
| Code Size | ~2 KB | ~3 KB |
| RAM Usage | 256 bytes | 320 bytes |
| Keyed Mode | Native | Needs HMAC |
| Flexibility | High | Low |
When to Use BLAKE2s
# Use BLAKE2s for embedded/32-bit systems
if platform == "embedded" or architecture == "32-bit":
hasher = BLAKE2s() # Optimized for platform
# Use BLAKE2b for servers/64-bit systems
elif platform == "server" or architecture == "64-bit":
hasher = BLAKE2b() # Better performance
# Use BLAKE2s for small digests
if required_size <= 32:
hasher = BLAKE2s(digest_size=required_size)
# Use BLAKE2s for memory-constrained environments
if available_ram < 1024: # Less than 1KB RAM
hasher = BLAKE2s() # Smaller working set
Migration Guide
From MD5 (Embedded Systems)
# Before: MD5 on embedded system
import hashlib
checksum = hashlib.md5(data).digest() # 128-bit, insecure
# After: BLAKE2s with same output size
from metamui_crypto import BLAKE2s
hasher = BLAKE2s(digest_size=16) # 128-bit, secure
checksum = hasher.hash(data)
# Migration wrapper for gradual transition
class HashMigrator:
def __init__(self, use_blake2s=True):
self.use_blake2s = use_blake2s
def compute_checksum(self, data):
if self.use_blake2s:
return BLAKE2s(digest_size=16).hash(data)
else:
return hashlib.md5(data).digest()
From SHA-1 (IoT Devices)
# Before: SHA-1 for IoT authentication
import hashlib
import hmac
mac = hmac.new(key, data, hashlib.sha1).digest() # 160-bit
# After: BLAKE2s keyed mode
from metamui_crypto import BLAKE2s
hasher = BLAKE2s(key=key, digest_size=20) # 160-bit compatible
mac = hasher.hash(data)
# Backward compatible implementation
def compute_mac(key, data, algorithm='blake2s'):
if algorithm == 'blake2s':
return BLAKE2s(key=key, digest_size=20).hash(data)
elif algorithm == 'hmac-sha1':
return hmac.new(key, data, hashlib.sha1).digest()
From CRC32 (Checksums)
# Before: CRC32 for file checksums
import zlib
crc = zlib.crc32(data) # 32-bit, non-cryptographic
# After: BLAKE2s for secure checksums
from metamui_crypto import BLAKE2s
hasher = BLAKE2s(digest_size=4) # 32-bit output
checksum = hasher.hash(data)
# Or use longer output for better security
hasher = BLAKE2s(digest_size=8) # 64-bit, much more secure
checksum = hasher.hash(data)
Test Vectors
BLAKE2s-256 Test Vectors
# Test Vector 1: Empty input
blake2s = BLAKE2s()
assert blake2s.hash(b"").hex() == "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9"
# Test Vector 2: "abc"
assert blake2s.hash(b"abc").hex() == "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"
# Test Vector 3: One million 'a's
data = b"a" * 1000000
assert blake2s.hash(data).hex() == "bec0c0e6cde5b67acb73b81f79a67a4079ae1c60dac9d2661af18e9f8b50dfa5"
# Test Vector 4: "The quick brown fox jumps over the lazy dog"
message = b"The quick brown fox jumps over the lazy dog"
assert blake2s.hash(message).hex() == "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812"
BLAKE2s Keyed Mode Test Vectors
# Test Vector 5: Keyed hashing
key = bytes.fromhex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
blake2s_keyed = BLAKE2s(key=key)
# Empty input with key
assert blake2s_keyed.hash(b"").hex() == "48a8997da407876b3d79c0d92325ad3b89cbb754d86ab71aee047ad345fd2c49"
# "abc" with key
assert blake2s_keyed.hash(b"abc").hex() == "c19012cc2aaf0dc3d8e5c45a1b79114d2df42abb2a410bf54be09e891af06ff8"
BLAKE2s-128 Test Vectors
# Test Vector 6: BLAKE2s-128
blake2s_128 = BLAKE2s(digest_size=16)
# Empty input
assert blake2s_128.hash(b"").hex() == "64550d6ffe2c0a01a14aba1eade0200c"
# "abc"
assert blake2s_128.hash(b"abc").hex() == "aa4938119b1dc7b87cbad0ffd200d0ae"
# With personalization
blake2s_personal = BLAKE2s(digest_size=16, personalization=b"ZcashSig")
assert blake2s_personal.hash(b"").hex() == "f9a8a6843a5ba3db2a7977f9e112f8e8"
Incremental Hashing Test
# Test Vector 7: Incremental vs one-shot
data = b"The quick brown fox jumps over the lazy dog"
# One-shot
blake2s = BLAKE2s()
one_shot = blake2s.hash(data)
# Incremental
hasher = BLAKE2s()
hasher.update(b"The quick brown ")
hasher.update(b"fox jumps over ")
hasher.update(b"the lazy dog")
incremental = hasher.finalize()
assert one_shot == incremental
🔗 Related Algorithms
📋 Standards & References
🏛️ RFC 7693
The BLAKE2 Cryptographic Hash and MAC - Official specification
📄 BLAKE2 Paper
Original academic specification and cryptanalysis
💾 Reference Code
Official BLAKE2 reference implementations
🔒 CBOR Security
RFC 8152 - CBOR Object Signing uses BLAKE2