NTRU Prime Security-Focused API Documentation
Algorithm: NTRU Prime (sntrup761)
Type: Post-Quantum Key Encapsulation Mechanism
Security Level: ~128-bit quantum, ~200-bit classical
Specification: NTRU Prime Round 3
Security Contract
ntruprime.generateKeypair(): { publicKey: Uint8Array, secretKey: Uint8Array }
Preconditions:
- Secure randomness available
- 32 bytes entropy minimum
Postconditions:
- Public key: 1158 bytes
- Secret key: 1763 bytes
- No decryption failures by design
Side Effects:
- Consumes entropy
- Deterministic key generation from seed
ntruprime.encapsulate(publicKey: Uint8Array): { ciphertext: Uint8Array, sharedSecret: Uint8Array }
Preconditions:
- Public key: Valid 1158-byte NTRU Prime key
- Internal randomness: 32 bytes
Postconditions:
- Ciphertext: 1039 bytes
- Shared secret: 32 bytes
- IND-CCA2 secure
Side Effects:
- Uses system randomness
- No decryption failures possible
ntruprime.decapsulate(ciphertext: Uint8Array, secretKey: Uint8Array): Uint8Array
Preconditions:
- Ciphertext: Any 1039 bytes
- Secret key: Valid 1763-byte key
Postconditions:
- Always returns 32-byte shared secret
- Implicit rejection (no explicit failures)
- Constant-time execution
Side Effects:
- None - pure function
- Deterministic output
Attack Resistance Matrix
| Attack Type | Resistance | Notes |
|---|---|---|
| Quantum (Grover) | ✅ 2^128 | NIST Level III |
| Quantum (Shor) | ✅ Strong | Lattice-based |
| Decryption Failure | ✅ None | By design |
| CCA2 | ✅ Proven | Fujisaki-Okamoto |
| Side-Channel | ✅ Strong | Simple operations |
| Patent Issues | ✅ None | Public domain |
| Backdoors | ✅ None | Transparent design |
Secure Usage Examples
✅ CORRECT: Basic NTRU Prime KEM
import { ntruprime } from 'metamui-ntruprime';
// Generate keypair
const keypair = ntruprime.generateKeypair();
// Encapsulation
const { ciphertext, sharedSecret } = ntruprime.encapsulate(
keypair.publicKey
);
// Decapsulation
const recovered = ntruprime.decapsulate(
ciphertext,
keypair.secretKey
);
// sharedSecret === recovered
// Always use KDF
const sessionKey = kdf.derive(
sharedSecret,
"ntruprime-session",
32
);
✅ CORRECT: Streamlined NTRU Prime
import { ntruprime } from 'metamui-ntruprime';
// Streamlined variant (sntrup761)
class StreamlinedNTRU {
private keypair: NTRUPrimeKeypair;
constructor() {
// Streamlined = no decryption failures
this.keypair = ntruprime.sntrup761.generateKeypair();
}
// Encapsulate with confirmation
encapsulateWithConfirmation(
publicKey: Uint8Array
): {
ciphertext: Uint8Array;
sharedSecret: Uint8Array;
confirmation: Uint8Array;
} {
const { ciphertext, sharedSecret } =
ntruprime.sntrup761.encapsulate(publicKey);
// Key confirmation value
const confirmation = kdf.derive(
sharedSecret,
concat(b"confirm", ciphertext),
16
);
return { ciphertext, sharedSecret, confirmation };
}
}
✅ CORRECT: NTRU Prime + ChaCha20 KEM-DEM
import { ntruprime } from 'metamui-ntruprime';
import { chacha20poly1305 } from 'metamui-chacha20-poly1305';
class NTRUPrimeBox {
// Efficient public-key encryption
static seal(
message: Uint8Array,
recipientPublicKey: Uint8Array
): Uint8Array {
// KEM
const { ciphertext, sharedSecret } =
ntruprime.encapsulate(recipientPublicKey);
// Derive keys
const keys = kdf.derive(
sharedSecret,
"ntruprime-box-v1",
64
);
const encKey = keys.slice(0, 32);
const authKey = keys.slice(32, 64);
// DEM
const encrypted = chacha20poly1305.seal(
message,
encKey,
ciphertext.slice(0, 24) // Use part of ciphertext as nonce
);
return concat(ciphertext, encrypted);
}
static open(
sealedBox: Uint8Array,
secretKey: Uint8Array
): Uint8Array {
// Split KEM and DEM parts
const ciphertext = sealedBox.slice(0, 1039);
const encrypted = sealedBox.slice(1039);
// KEM
const sharedSecret = ntruprime.decapsulate(
ciphertext,
secretKey
);
// Derive keys
const keys = kdf.derive(
sharedSecret,
"ntruprime-box-v1",
64
);
const encKey = keys.slice(0, 32);
// DEM
return chacha20poly1305.open(
encrypted,
encKey,
ciphertext.slice(0, 24)
);
}
}
✅ CORRECT: Hybrid with X25519
import { ntruprime } from 'metamui-ntruprime';
import { x25519 } from 'metamui-x25519';
// Belt and suspenders approach
class HybridNTRUX25519 {
static encapsulate(
ntruPublicKey: Uint8Array,
x25519PublicKey: Uint8Array
): {
ciphertext: Uint8Array;
sharedSecret: Uint8Array;
} {
// NTRU Prime KEM
const ntru = ntruprime.encapsulate(ntruPublicKey);
// X25519 ephemeral
const ephemeral = x25519.generateKeypair();
const ecdh = x25519.computeSharedSecret(
x25519PublicKey,
ephemeral.secretKey
);
// Combine ciphertexts
const ciphertext = concat(
ntru.ciphertext, // 1039 bytes
ephemeral.publicKey // 32 bytes
);
// Combine secrets securely
const sharedSecret = kdf.derive(
concat(
new Uint8Array([0x00]), // Domain separator
ntru.sharedSecret,
new Uint8Array([0x01]), // Domain separator
ecdh
),
"hybrid-ntru-x25519",
32
);
return { ciphertext, sharedSecret };
}
}
Common Mistakes to Avoid
❌ WRONG: Using Non-Streamlined Variant
// NEVER DO THIS - Original NTRU has decryption failures
const keypair = ntruprime.ntru761.generateKeypair(); // Not streamlined!
// ✅ CORRECT: Use streamlined variant
const keypair = ntruprime.sntrup761.generateKeypair(); // Streamlined
❌ WRONG: Comparing Ciphertexts
// NEVER DO THIS - Ciphertexts are randomized
const ct1 = ntruprime.encapsulate(publicKey).ciphertext;
const ct2 = ntruprime.encapsulate(publicKey).ciphertext;
if (ct1 === ct2) { // Always false!
// ...
}
// ✅ CORRECT: Compare shared secrets after decapsulation
const ss1 = ntruprime.decapsulate(ct1, secretKey);
const ss2 = ntruprime.decapsulate(ct2, secretKey);
// ss1 and ss2 are different (randomized KEM)
❌ WRONG: Small Polynomial Attacks
// NEVER DO THIS - Don't try to optimize polynomials
function weakKeyGen() {
// Trying to use "small" polynomials
const f = new Int8Array(761).fill(1); // Weak!
const g = new Int8Array(761).fill(1); // Weak!
// ...
}
// ✅ CORRECT: Use proper key generation
const keypair = ntruprime.generateKeypair(); // Secure randomness
Implementation Security
Polynomial Multiplication
// Constant-time polynomial multiplication
class NTRUPrimePoly {
static multiply(
f: Int8Array,
g: Int8Array,
p: number
): Int8Array {
const n = 761;
const result = new Int8Array(n);
// Schoolbook multiplication (constant-time)
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
const k = (i + j) % n;
result[k] = modp(
result[k] + f[i] * g[j],
p
);
}
}
return result;
}
// Constant-time modular reduction
static modp(x: number, p: number): number {
// Avoid division (timing attack)
const q = Math.floor(x * INVERSE_P);
return x - q * p;
}
}
Sorting Network (Constant-Time)
// Decode uses constant-time sorting
class NTRUPrimeSort {
static sort32(x: Uint32Array): void {
const n = x.length;
// Odd-even merge sort network
for (let p = 1; p < n; p *= 2) {
for (let k = p; k >= 1; k /= 2) {
for (let j = k % p; j < n - k; j += 2 * k) {
for (let i = 0; i < Math.min(k, n - j - k); i++) {
if ((Math.floor((i + j) / (2 * p)) ===
Math.floor((i + j + k) / (2 * p)))) {
this.compareSwap(x, i + j, i + j + k);
}
}
}
}
}
}
// Constant-time compare and swap
static compareSwap(x: Uint32Array, i: number, j: number): void {
const xi = x[i];
const xj = x[j];
const b = xi > xj ? 1 : 0;
x[i] = b ? xj : xi;
x[j] = b ? xi : xj;
}
}
Platform-Specific Notes
Performance Characteristics
| Platform | Encapsulation | Decapsulation | |———-|—————|—————| | x86-64 AVX2 | 60 μs | 90 μs | | ARM Cortex-A72 | 150 μs | 200 μs | | WASM (Chrome) | 400 μs | 600 μs |
Memory Requirements
- Key generation: ~10KB stack
- Encapsulation: ~5KB stack
- Decapsulation: ~8KB stack
- No heap allocation required
Advantages Over ML-KEM
| Feature | NTRU Prime | ML-KEM |
|---|---|---|
| Ciphertext Size | 1039 bytes | 1088 bytes |
| Decryption Failures | None | <2^-164 |
| Design Simplicity | Simpler | Complex |
| Patent Status | Clear | Clear |
| Standardization | Alternative | NIST |
Security Checklist
- Use sntrup761 (streamlined) variant
- Apply KDF to all shared secrets
- Include domain separation
- Use fresh randomness for each encapsulation
- Clear secret keys after use
- Consider hybrid with X25519
- Verify public key size (1158 bytes)
- Monitor for cryptanalysis updates
Security Analysis
Threat Model: NTRU Prime 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.