Ascon Security-Focused API Documentation
Algorithm: Ascon-128 / Ascon-128a
Type: Authenticated Encryption with Associated Data (AEAD) & Hashing
Security Level: 128-bit
Key Size: 128 bits (16 bytes)
Nonce Size: 128 bits (16 bytes)
Tag Size: 128 bits (16 bytes)
Standard: NIST Lightweight Cryptography Winner (2023)
Table of Contents
- Security Contract
- Attack Resistance Matrix
- Secure Usage Examples
- Common Mistakes
- Performance vs Security
- Platform-Specific Notes
- Compliance Information
Security Contract
encrypt(key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, associatedData?: Uint8Array): { ciphertext: Uint8Array, tag: Uint8Array }
Preconditions:
keyMUST be exactly 16 bytes (128 bits)nonceMUST be exactly 16 bytes (128 bits)nonceMUST be unique for each encryption with same keyplaintextcan be 0 to 2^64 - 1 bytesassociatedData(optional) can be 0 to 2^64 - 1 bytes
Postconditions:
- Returns
ciphertextof same length asplaintext - Returns
tagof exactly 16 bytes (128 bits) - Ciphertext indistinguishable from random without key
- Tag authenticates both ciphertext and associated data
- Side-channel resistant implementation
Side Effects:
- Minimal memory allocation (designed for constrained devices)
- Constant-time operations
- Low power consumption
decrypt(key: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array, tag: Uint8Array, associatedData?: Uint8Array): Uint8Array | null
Preconditions:
- All parameters must match encryption values exactly
tagMUST be exactly 16 bytes
Postconditions:
- Returns decrypted plaintext if authentication succeeds
- Returns
nullif authentication fails - No partial plaintext returned on failure
- Constant-time tag verification
Side Effects:
- Early abort on tag mismatch (safe)
- Memory cleared after use
hash(message: Uint8Array, outputLength?: number): Uint8Array
Preconditions:
messagecan be any lengthoutputLength(optional) default 32 bytes
Postconditions:
- Returns hash of specified length
- Cryptographically secure hash
- No length extension attacks
Side Effects:
- Minimal memory usage
- No timing variations
Attack Resistance Matrix
✅ Attacks Prevented
| Attack Type | Protection Mechanism | Security Level |
|---|---|---|
| Forgery | 128-bit authentication tag | 2^-128 probability |
| Key Recovery | Sponge construction security | 2^128 operations |
| State Recovery | Large internal state (320 bits) | Not feasible |
| Differential Cryptanalysis | 12 rounds (Ascon-128) | > 2^128 complexity |
| Linear Cryptanalysis | Strong S-box design | > 2^128 complexity |
| Cube Attacks | Sufficient rounds | No practical attacks |
| Side-Channel (Timing) | Bitsliced implementation | Constant-time |
| Power Analysis | Low power design | Reduced leakage |
| Fault Attacks | Redundancy in state | Detection possible |
❌ Attacks NOT Prevented
| Attack Type | Reason | Mitigation Required |
|---|---|---|
| Nonce Reuse | Deterministic encryption | Strict nonce management |
| Multi-Target | Birthday bound on tags | Limit encryptions per key |
| Quantum Attacks | Symmetric primitive | Use 256-bit variant |
| Replay Attacks | No built-in replay protection | Add timestamps |
| Key Commitment | Not built-in | Add key commitment if needed |
⚠️ Security Limitations
- 128-bit Security: May be insufficient for long-term security
- Nonce Size: Large nonce (128 bits) requires careful management
- Lightweight Focus: Optimized for efficiency over maximum security
- Single Key: No built-in key separation
Secure Usage Examples
Basic AEAD Encryption
import { Ascon } from '@metamui/ascon';
import { randomBytes } from '@metamui/random';
class SecureAscon {
static encrypt(
key: Uint8Array,
plaintext: Uint8Array,
associatedData?: Uint8Array
): {
nonce: Uint8Array;
ciphertext: Uint8Array;
tag: Uint8Array;
} {
if (key.length !== 16) {
throw new Error('Key must be 128 bits');
}
// Generate unique nonce
const nonce = randomBytes(16);
// Encrypt with Ascon-128
const { ciphertext, tag } = Ascon.encrypt(
key,
nonce,
plaintext,
associatedData
);
return { nonce, ciphertext, tag };
}
static decrypt(
key: Uint8Array,
nonce: Uint8Array,
ciphertext: Uint8Array,
tag: Uint8Array,
associatedData?: Uint8Array
): Uint8Array | null {
return Ascon.decrypt(
key,
nonce,
ciphertext,
tag,
associatedData
);
}
}
IoT Device Communication
class IoTSecureChannel {
private readonly deviceId: Uint8Array;
private messageCounter: bigint = 0n;
constructor(
private readonly sharedKey: Uint8Array,
deviceId: string
) {
this.deviceId = new TextEncoder().encode(deviceId);
}
sendMessage(
command: string,
payload: Uint8Array
): {
packet: Uint8Array;
} {
// Create nonce from device ID and counter
const nonce = new Uint8Array(16);
nonce.set(this.deviceId.slice(0, 8), 0);
const counterBytes = new Uint8Array(8);
for (let i = 7; i >= 0; i--) {
counterBytes[i] = Number(this.messageCounter & 0xFFn);
this.messageCounter >>= 8n;
}
nonce.set(counterBytes, 8);
this.messageCounter++;
// Create associated data
const associatedData = new TextEncoder().encode(
`${command}:${Date.now()}`
);
// Encrypt payload
const { ciphertext, tag } = Ascon.encrypt(
this.sharedKey,
nonce,
payload,
associatedData
);
// Build packet: command(1) || nonce(16) || tag(16) || ciphertext
const packet = new Uint8Array(
1 + 16 + 16 + ciphertext.length
);
packet[0] = command.charCodeAt(0);
packet.set(nonce, 1);
packet.set(tag, 17);
packet.set(ciphertext, 33);
return { packet };
}
receiveMessage(packet: Uint8Array): {
command: string;
payload: Uint8Array;
} | null {
if (packet.length < 33) {
return null; // Too short
}
const command = String.fromCharCode(packet[0]);
const nonce = packet.slice(1, 17);
const tag = packet.slice(17, 33);
const ciphertext = packet.slice(33);
// Verify counter is newer
const receivedCounter = new DataView(
nonce.buffer,
nonce.byteOffset + 8
).getBigUint64(0);
if (receivedCounter <= this.messageCounter) {
return null; // Replay attack
}
// Decrypt and verify
const associatedData = new TextEncoder().encode(
`${command}:${Date.now()}`
);
const payload = Ascon.decrypt(
this.sharedKey,
nonce,
ciphertext,
tag,
associatedData
);
if (payload === null) {
return null; // Authentication failed
}
// Update counter
this.messageCounter = receivedCounter;
return { command, payload };
}
}
Lightweight File Encryption
class AsconFileEncryption {
static async encryptFile(
key: Uint8Array,
inputFile: File,
chunkSize: number = 4096 // 4KB chunks for low memory
): Promise<Uint8Array> {
const chunks: Uint8Array[] = [];
const fileNonce = randomBytes(16);
// File metadata as associated data
const metadata = new TextEncoder().encode(JSON.stringify({
name: inputFile.name,
size: inputFile.size,
type: inputFile.type,
modified: inputFile.lastModified
}));
const reader = inputFile.stream().getReader();
let chunkIndex = 0;
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
// Derive per-chunk nonce
const chunkNonce = new Uint8Array(fileNonce);
new DataView(chunkNonce.buffer).setUint32(12, chunkIndex++);
// Encrypt chunk
const { ciphertext, tag } = Ascon.encrypt(
key,
chunkNonce,
value,
metadata
);
// Store tag + ciphertext
const chunk = new Uint8Array(16 + ciphertext.length);
chunk.set(tag, 0);
chunk.set(ciphertext, 16);
chunks.push(chunk);
}
// Build final file: fileNonce || metadataLen || metadata || chunks
const metadataLenBytes = new Uint8Array(4);
new DataView(metadataLenBytes.buffer).setUint32(0, metadata.length);
const totalSize = 16 + 4 + metadata.length +
chunks.reduce((sum, chunk) => sum + chunk.length, 0);
const encrypted = new Uint8Array(totalSize);
let offset = 0;
encrypted.set(fileNonce, offset);
offset += 16;
encrypted.set(metadataLenBytes, offset);
offset += 4;
encrypted.set(metadata, offset);
offset += metadata.length;
for (const chunk of chunks) {
encrypted.set(chunk, offset);
offset += chunk.length;
}
return encrypted;
} finally {
reader.releaseLock();
}
}
}
Ascon-Hash for Key Derivation
class AsconKDF {
static deriveKey(
password: string,
salt: Uint8Array,
outputLength: number = 32
): Uint8Array {
// Use Ascon-Hash for key derivation
const passwordBytes = new TextEncoder().encode(password);
// Combine password and salt
const input = new Uint8Array(passwordBytes.length + salt.length);
input.set(passwordBytes, 0);
input.set(salt, passwordBytes.length);
// Hash with Ascon
const hash = Ascon.hash(input, outputLength);
// Clear sensitive data
input.fill(0);
return hash;
}
static async deriveMultipleKeys(
masterKey: Uint8Array,
context: string,
keyCount: number
): Promise<Uint8Array[]> {
const keys: Uint8Array[] = [];
for (let i = 0; i < keyCount; i++) {
const info = new TextEncoder().encode(`${context}-${i}`);
const input = new Uint8Array(masterKey.length + info.length);
input.set(masterKey, 0);
input.set(info, masterKey.length);
keys.push(Ascon.hash(input, 16)); // 128-bit keys
input.fill(0);
}
return keys;
}
}
Common Mistakes
❌ Nonce Reuse
// NEVER DO THIS - Catastrophic failure
class BrokenAscon {
private static FIXED_NONCE = new Uint8Array(16); // All zeros!
static encrypt(key: Uint8Array, data: Uint8Array): Uint8Array {
// Reusing nonce breaks security completely
const { ciphertext, tag } = Ascon.encrypt(
key,
this.FIXED_NONCE, // NEVER DO THIS!
data
);
return new Uint8Array([...ciphertext, ...tag]);
}
}
❌ Insufficient Nonce Randomness
// BAD - Predictable nonce
function weakNonce(): Uint8Array {
const nonce = new Uint8Array(16);
const time = Date.now();
new DataView(nonce.buffer).setBigUint64(0, BigInt(time));
// Only 8 bytes of entropy!
return nonce;
}
// GOOD - Full randomness
function strongNonce(): Uint8Array {
return randomBytes(16);
}
❌ Tag Truncation
// WRONG - Reducing security
function insecureEncrypt(key: Uint8Array, data: Uint8Array): {
ciphertext: Uint8Array;
shortTag: Uint8Array;
} {
const { ciphertext, tag } = Ascon.encrypt(key, randomBytes(16), data);
// NEVER truncate the tag!
return {
ciphertext,
shortTag: tag.slice(0, 8) // Only 64 bits - INSECURE!
};
}
❌ Using for Password Hashing
// WRONG - Ascon-Hash not designed for passwords
function badPasswordHash(password: string): Uint8Array {
return Ascon.hash(new TextEncoder().encode(password));
}
// CORRECT - Use proper password hashing
import { Argon2 } from '@metamui/argon2';
async function goodPasswordHash(password: string): Promise<Uint8Array> {
const salt = randomBytes(16);
return Argon2.hash(password, salt);
}
Performance vs Security
Variant Comparison
| Variant | Rate | Security | Use Case |
|---|---|---|---|
| Ascon-128 | 64 bits/round | Standard | General purpose |
| Ascon-128a | 128 bits/round | Standard | Higher throughput |
| Ascon-Hash | - | Collision resistant | Hashing |
| Ascon-Xof | - | Variable output | Key derivation |
Performance Characteristics
// Benchmark results (approximate)
// Platform: ARM Cortex-M4 @ 48MHz
//
// Ascon-128 AEAD:
// - Setup: ~500 cycles
// - Per byte: ~15 cycles
// - Tag generation: ~800 cycles
//
// Compared to AES-GCM:
// - 3x less ROM
// - 5x less RAM
// - Similar speed on small messages
// - Better on constrained devices
Optimization for Microcontrollers
class AsconMCU {
// Bit-sliced implementation for 32-bit MCUs
static optimizedEncrypt(
key: Uint8Array,
nonce: Uint8Array,
plaintext: Uint8Array
): { ciphertext: Uint8Array; tag: Uint8Array } {
// Use 32-bit word operations
const state = new Uint32Array(10); // 320-bit state
// Initialize with key and nonce
// ... (bit-sliced operations)
// Process data in 64-bit blocks (Ascon-128)
// ... (optimized for 32-bit MCU)
return Ascon.encrypt(key, nonce, plaintext);
}
}
Platform-Specific Notes
Embedded Systems
- Designed specifically for constrained devices
- Low RAM requirements (< 1KB)
- No dynamic memory allocation
- Power analysis resistant
JavaScript/TypeScript
- Pure JS implementation efficient
- No SIMD required
- WebAssembly can improve performance
- Suitable for IoT web interfaces
Hardware
- Efficient hardware implementations exist
- Low gate count (< 10K GE)
- High throughput/area ratio
- Side-channel resistant designs available
Mobile
- Battery-efficient encryption
- Suitable for BLE/NFC protocols
- Low memory footprint
- Fast enough for real-time communication
Compliance Information
Standards and Recognition
- NIST LWC Winner: Selected February 2023
- CAESAR Finalist: Authenticated encryption competition
- ISO/IEC: Standardization in progress
- Academic: Extensive peer review since 2014
Security Analysis
- 7+ years of public scrutiny
- No practical attacks found
- Conservative design margins
- Proven security bounds
Recommended Use Cases
✅ Ideal for:
- IoT devices and sensors
- Embedded systems
- Mobile applications
- Battery-powered devices
- Real-time communication
- Lightweight protocols
❌ Not recommended for:
- Long-term archival (use 256-bit variants)
- Post-quantum requirements
- Maximum security requirements (use AES-256-GCM)
Implementation Requirements
- Must use unique nonces
- Must verify full authentication tag
- Should use constant-time implementation
- Should clear key material after use
Security Notice: Ascon is the NIST Lightweight Cryptography standard. It provides excellent security for constrained environments. Always ensure proper nonce management. Report issues to security@metamui.id
Last Updated: 2025-07-06
**Version: 3.0.0
License: BSL-1.1
Security Analysis
Threat Model: Ascon-128 Threat Model
The comprehensive threat analysis covers:
- Algorithm-specific attack vectors
- Implementation vulnerabilities
- Side-channel considerations
- Quantum resistance analysis (where applicable)
- Deployment recommendations
For complete security analysis and risk assessment, see the dedicated threat model documentation.