Deoxys-II Security-Focused API Documentation
Algorithm: Deoxys-II-256-128
Type: Authenticated Encryption with Associated Data (AEAD)
Security Level: 128-bit
Nonce Size: 120 bits (15 bytes)
Key Size: 256 bits (32 bytes)
Tag Size: 128 bits (16 bytes)
Standard: CAESAR Competition Finalist
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 32 bytes (256 bits)nonceMUST be exactly 15 bytes (120 bits)nonceMUST be unique for each encryption with the same keyplaintextcan be 0 to 2^64 - 1 bytesassociatedData(optional) can be 0 to 2^64 - 1 bytes- All inputs MUST be valid byte arrays
Postconditions:
- Returns
ciphertextof same length asplaintext - Returns
tagof exactly 16 bytes (128 bits) ciphertextis indistinguishable from random without the keytagauthenticates bothciphertextandassociatedData- Original inputs remain unchanged
Side Effects:
- May allocate memory for output buffers
- No observable timing variations based on data content
decrypt(key: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array, tag: Uint8Array, associatedData?: Uint8Array): Uint8Array | null
Preconditions:
keyMUST be exactly 32 bytes (256 bits)nonceMUST be exactly 15 bytes (120 bits)tagMUST be exactly 16 bytes (128 bits)ciphertextcan be 0 to 2^64 - 1 bytesassociatedData(optional) must match encryption value exactly
Postconditions:
- Returns decrypted
plaintextif authentication succeeds - Returns
nullif authentication fails - No partial plaintext is ever returned on failure
- Constant-time tag verification
Side Effects:
- May allocate memory for output buffer
- Timing independent of tag comparison result
Attack Resistance Matrix
✅ Attacks Prevented
| Attack Type | Protection Mechanism | Security Level |
|---|---|---|
| Forgery | 128-bit authentication tag | 2^-128 probability |
| Chosen Ciphertext | AEAD construction with tag verification | Complete protection |
| Nonce Misuse | Deterministic encryption for same nonce | Reveals plaintext equality only |
| Side-Channel (Timing) | Constant-time operations | No key-dependent branches |
| Related-Key Attacks | Strong key schedule | 256-bit security |
| Differential Cryptanalysis | AES-based with strong mixing | > 2^128 complexity |
| Linear Cryptanalysis | Multiple AES rounds | > 2^128 complexity |
| Padding Oracle | No padding required | Not applicable |
❌ Attacks NOT Prevented
| Attack Type | Reason | Mitigation Required |
|---|---|---|
| Nonce Reuse | Same nonce reveals XOR of plaintexts | Implement nonce management |
| Key Reuse Across Domains | No domain separation | Use different keys per context |
| Fault Injection | No built-in fault detection | Hardware countermeasures |
| Power Analysis (DPA) | Implementation-dependent | Use masked implementations |
| Replay Attacks | No built-in replay protection | Add sequence numbers |
| Traffic Analysis | Ciphertext length reveals plaintext length | Add padding scheme |
⚠️ Security Limitations
- Nonce Size: 120-bit nonce allows ~2^60 encryptions before birthday collision
- Single Key: No built-in key rotation mechanism
- No Forward Secrecy: Compromised key reveals all past messages
- Deterministic: Same key/nonce/plaintext produces same ciphertext
Secure Usage Examples
Basic Encryption/Decryption
import { DeoxysII } from '@metamui/deoxys-ii';
import { randomBytes } from '@metamui/random';
// Generate a random key (store securely!)
const key = randomBytes(32);
// CRITICAL: Generate unique nonce for EVERY encryption
const nonce = randomBytes(15); // 120 bits
// Your sensitive data
const plaintext = new TextEncoder().encode('Secret message');
const associatedData = new TextEncoder().encode('metadata');
// Encrypt
const { ciphertext, tag } = DeoxysII.encrypt(
key,
nonce,
plaintext,
associatedData
);
// Store/transmit: nonce || ciphertext || tag
const message = new Uint8Array(nonce.length + ciphertext.length + tag.length);
message.set(nonce, 0);
message.set(ciphertext, nonce.length);
message.set(tag, nonce.length + ciphertext.length);
// Decrypt
const receivedNonce = message.slice(0, 15);
const receivedCiphertext = message.slice(15, -16);
const receivedTag = message.slice(-16);
const decrypted = DeoxysII.decrypt(
key,
receivedNonce,
receivedCiphertext,
receivedTag,
associatedData
);
if (decrypted === null) {
throw new Error('Authentication failed - data tampered!');
}
Secure Nonce Management
class SecureDeoxysII {
private counter: bigint = 0n;
private readonly maxCounter = (1n << 96n) - 1n; // Reserve 24 bits for randomness
constructor(private readonly key: Uint8Array) {
if (key.length !== 32) {
throw new Error('Key must be 32 bytes');
}
}
encrypt(plaintext: Uint8Array, associatedData?: Uint8Array): {
nonce: Uint8Array;
ciphertext: Uint8Array;
tag: Uint8Array;
} {
// Generate nonce: 96-bit counter || 24-bit random
if (this.counter > this.maxCounter) {
throw new Error('Nonce counter exhausted - generate new key');
}
const nonce = new Uint8Array(15);
const counterBytes = new Uint8Array(12);
const randomBytes = crypto.getRandomValues(new Uint8Array(3));
// Write counter as big-endian
let c = this.counter++;
for (let i = 11; i >= 0; i--) {
counterBytes[i] = Number(c & 0xFFn);
c >>= 8n;
}
nonce.set(counterBytes, 0);
nonce.set(randomBytes, 12);
const { ciphertext, tag } = DeoxysII.encrypt(
this.key,
nonce,
plaintext,
associatedData
);
return { nonce, ciphertext, tag };
}
}
Streaming Large Files
async function encryptFile(
key: Uint8Array,
inputFile: File,
chunkSize: number = 64 * 1024 // 64KB chunks
): AsyncGenerator<Uint8Array> {
const fileId = randomBytes(8);
let chunkIndex = 0;
const reader = inputFile.stream().getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
// Create unique nonce for each chunk
const nonce = new Uint8Array(15);
nonce.set(fileId, 0); // 8 bytes file ID
const indexBytes = new Uint8Array(7);
let idx = chunkIndex++;
for (let i = 6; i >= 0; i--) {
indexBytes[i] = idx & 0xFF;
idx >>= 8;
}
nonce.set(indexBytes, 8);
// Include chunk metadata in associated data
const metadata = new Uint8Array(16);
new DataView(metadata.buffer).setBigUint64(0, BigInt(chunkIndex - 1));
new DataView(metadata.buffer).setBigUint64(8, BigInt(value.length));
const { ciphertext, tag } = DeoxysII.encrypt(
key,
nonce,
value,
metadata
);
// Yield: nonce || ciphertext || tag
const chunk = new Uint8Array(15 + ciphertext.length + 16);
chunk.set(nonce, 0);
chunk.set(ciphertext, 15);
chunk.set(tag, 15 + ciphertext.length);
yield chunk;
}
} finally {
reader.releaseLock();
}
}
Common Mistakes
❌ CRITICAL: Nonce Reuse
// NEVER DO THIS - Catastrophic security failure!
const fixedNonce = new Uint8Array(15); // All zeros
function insecureEncrypt(key: Uint8Array, data: Uint8Array): Uint8Array {
// Using same nonce breaks security completely!
const { ciphertext, tag } = DeoxysII.encrypt(key, fixedNonce, data);
return new Uint8Array([...ciphertext, ...tag]);
}
// If attacker gets two ciphertexts with same nonce:
// ciphertext1 XOR ciphertext2 = plaintext1 XOR plaintext2
❌ Insufficient Nonce Randomness
// BAD: Predictable nonce generation
function badNonceGeneration(): Uint8Array {
const nonce = new Uint8Array(15);
const timestamp = Date.now();
new DataView(nonce.buffer).setBigUint64(0, BigInt(timestamp));
// Only 8 bytes of entropy - collisions likely!
return nonce;
}
// GOOD: Cryptographically random nonce
function goodNonceGeneration(): Uint8Array {
return crypto.getRandomValues(new Uint8Array(15));
}
❌ Ignoring Authentication Failures
// NEVER DO THIS - Opens door to attacks
function insecureDecrypt(key: Uint8Array, data: Uint8Array): Uint8Array {
const nonce = data.slice(0, 15);
const ciphertext = data.slice(15, -16);
const tag = data.slice(-16);
const result = DeoxysII.decrypt(key, nonce, ciphertext, tag);
// WRONG: Returning partial data on failure
if (result === null) {
console.warn('Authentication failed, returning ciphertext');
return ciphertext; // NEVER DO THIS!
}
return result;
}
// CORRECT: Always fail completely
function secureDecrypt(key: Uint8Array, data: Uint8Array): Uint8Array {
const result = DeoxysII.decrypt(key, nonce, ciphertext, tag);
if (result === null) {
throw new Error('Authentication failed - data integrity compromised');
}
return result;
}
❌ Key Derivation Mistakes
// BAD: Weak key derivation
const weakKey = new TextEncoder().encode('my-password-123').slice(0, 32);
// GOOD: Proper key derivation
import { Argon2 } from '@metamui/argon2';
async function deriveKey(password: string, salt: Uint8Array): Promise<Uint8Array> {
return await Argon2.hash(password, salt, {
outputLength: 32,
timeCost: 3,
memoryCost: 65536,
parallelism: 4
});
}
Performance vs Security
Optimization Guidelines
| Optimization | Security Impact | Recommendation |
|---|---|---|
| Parallel Processing | None - inherently parallel | ✅ Use for large data |
| Nonce Precomputation | None if unique | ✅ Acceptable |
| Key Caching | Increases key exposure time | ⚠️ Limit cache duration |
| Tag Truncation | Reduces security exponentially | ❌ Never truncate |
| Skip Authentication | Complete security failure | ❌ Never skip |
Performance Characteristics
// Benchmark results (approximate)
// Platform: Intel i7-10700K @ 3.8GHz
//
// Small messages (< 1KB): ~2-3 μs
// Medium messages (1MB): ~1-2 ms
// Large messages (100MB): ~100-150 ms
//
// Throughput: ~800-1000 MB/s
Platform-Specific Notes
TypeScript/JavaScript
- Use
crypto.getRandomValues()for nonce generation - Consider WebCrypto API for hardware acceleration
- Avoid string conversions in hot paths
Python
- Use
secrets.token_bytes(15)for secure nonces - NumPy arrays can improve performance for large data
- Consider
cryptographylibrary for comparison
Rust
- Use
getrandomcrate for nonce generation - Zero-copy operations with
&[u8]slices - Consider
zeroizefor key cleanup
Swift
- Use
SecRandomCopyBytesfor nonces - Leverage Accelerate framework if available
- Careful with Data vs [UInt8] conversions
Kotlin/JVM
- Use
SecureRandomfor nonce generation - ByteArray allocations can be expensive
- Consider ByteBuffer for zero-copy operations
WASM
- Ensure proper random source is available
- Memory growth can impact performance
- Consider chunked processing for large data
Compliance Information
Standards Compliance
- CAESAR Competition: Finalist in authenticated encryption category
- NIST: Not standardized but follows AEAD security definitions
- RFC 5116: Compliant with AEAD interface requirements
Security Certifications
- No formal FIPS certification (not NIST standardized)
- Extensive third-party cryptanalysis during CAESAR
- Academic security proofs available
Regulatory Considerations
- Export Control: Check local cryptography export regulations
- Key Size: 256-bit keys may have restrictions in some jurisdictions
- Patent Status: Believed to be patent-free
Recommended Use Cases
✅ Ideal for:
- High-performance AEAD requirements
- Parallel processing environments
- Nonce-misuse resistant applications
- IoT and embedded systems
❌ Not recommended for:
- FIPS-required environments
- Long-term archival (use standardized algorithms)
- Environments requiring format-preserving encryption
Security Notice: This implementation has been professionally audited. However, always perform your own security assessment before production use. Report security issues to security@metamui.id
Last Updated: 2025-07-06
**Version: 3.0.0
License: BSL-1.1
Security Analysis
Threat Model: Deoxys-II 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.