SIMON Encryption
At a Glance
- Block sizes: 32, 48, 64, 96, 128 bits
- Key sizes: 64, 72, 96, 128, 144, 192, 256 bits
- Feistel network structure
- Bitwise operations only (AND, XOR, shifts)
- Optimized for hardware implementation
About SIMON
Designed by the NSA in 2013 for hardware-constrained environments
SIMON is a family of lightweight block ciphers designed by the National Security Agency (NSA) and published in 2013. It was specifically created to perform well in hardware implementations on resource-constrained devices such as RFID tags, sensor networks, and other embedded systems where gate count, power consumption, and energy efficiency are critical constraints.
SIMON was released alongside SPECK, another lightweight cipher that was optimized for software implementations, while SIMON was optimized for hardware. Together, they provide efficient encryption options for the growing Internet of Things (IoT) ecosystem. SIMON's design prioritizes simplicity and hardware efficiency while maintaining a reasonable security margin.
Key Features
- Extremely small hardware footprint (minimal gate count)
- Low power consumption, ideal for passive devices
- Simple operations: bitwise AND, XOR, and circular shifts
- Flexible with multiple block and key size options
- Optimized for hardware implementations
- No S-boxes or table lookups required
- Competitive performance compared to other hardware-oriented ciphers
Technical Specifications
| Variant | Block Size (bits) | Key Size (bits) | Rounds |
|---|---|---|---|
| Simon32/64 | 32 | 64 | 32 |
| Simon48/72 | 48 | 72 | 36 |
| Simon48/96 | 48 | 96 | 36 |
| Simon64/96 | 64 | 96 | 42 |
| Simon64/128 | 64 | 128 | 44 |
| Simon96/96 | 96 | 96 | 52 |
| Simon96/144 | 96 | 144 | 54 |
| Simon128/128 | 128 | 128 | 68 |
| Simon128/192 | 128 | 192 | 69 |
| Simon128/256 | 128 | 256 | 72 |
How SIMON Works
SIMON uses a balanced Feistel network structure with a simple round function that consists of bitwise operations. The cipher operates on two words of data in each round.
1. Round Function
Each round of SIMON applies a series of operations to the two words of the state:
t = x x = y ⊕ f(x) ⊕ k y = t
\ Where f(x) = ((x <<< 1) & (x <<< 8)) ⊕ (x <<< 2), and k is the round key.
2. Key Schedule
The key schedule in SIMON generates round keys using a sequence of operations:
tmp = k[i+m-1] >>> 3 k[i+m] = k[i] ⊕ tmp ⊕ (tmp >>> 1) ⊕ c ⊕ (z_j)i
Where m is the number of key words, c is a constant, and z_j is a sequence.
3. Operations
SIMON uses only three basic operations:
- &: Bitwise AND
- ⊕: Bitwise XOR
- <<<: Circular left shift
These simple operations are very efficient in hardware implementations.
Round Function Details
The core of SIMON's round function is the f function, which applies bitwise operations to one of the state words:
f(x) = ((x <<< 1) & (x <<< 8)) ⊕ (x <<< 2)
This function provides both confusion and diffusion properties through the combination of circular shifts and the non-linear AND operation. The specific shift amounts (1, 8, and 2) were carefully chosen to provide good cryptographic properties.
Encryption and Decryption Process
Encryption
function encrypt(plaintext, roundKeys): x, y = split_plaintext(plaintext) for i = 0 to rounds-1: t = x x = y ⊕ ((x << 1) & (x << 8)) ⊕ (x << 2) ⊕ roundKeys[i] y = t return combine(x, y)
Decryption
function decrypt(ciphertext, roundKeys): x, y = split_ciphertext(ciphertext) for i = rounds-1 down to 0: t = y y = x ⊕ ((y << 1) & (y << 8)) ⊕ (y << 2) ⊕ roundKeys[i] x = t return combine(x, y)
Security Considerations
SIMON offers a reasonable security margin while maintaining hardware efficiency:
- Designed to resist differential and linear cryptanalysis
- The number of rounds for each variant was chosen to provide adequate security margins against known attacks
- Some academic analyses have found reduced-round attacks, but none that compromise the full cipher
- As with any NSA-designed cipher, some cryptographers have expressed concerns about potential backdoors
- The simple design makes it easier to analyze for weaknesses compared to more complex ciphers
- Side-channel attacks (timing, power analysis) should be considered in practical implementations
Applications and Comparisons
SIMON is particularly well-suited for hardware implementations in constrained environments:
- RFID tags and smart cards with limited gate count
- Sensor networks with extreme power constraints
- Hardware-based security modules
- ASIC and FPGA implementations
- Applications requiring minimal power consumption
- Performs better in hardware than SPECK (its software-optimized counterpart from the NSA)
- Competitive with other hardware-oriented lightweight ciphers like PRESENT, KATAN, and LED
- More efficient than AES in hardware-constrained environments, though AES may be preferred when security is the primary concern
- Comparison to SPECK: SIMON uses fewer operations per round but requires more rounds overall
SIMON Animation
Visual representation of the SIMON encryption process
SIMON Animation
SIMON Visualization
Round 1/5 - Function f(x)
Apply f(x) = ((x <<< 1) & (x <<< 8)) ⊕ (x <<< 2)
State Visualization
X Register
Y Register
Round Structure
Round Key
Function f(x)
f(x) = ((x <<< 1) & (x <<< 8)) ⊕ (x <<< 2)
Feistel Network Structure
SIMON Simulator
Try encrypting and decrypting with SIMON
SIMON Simulator
SIMON Encryption Simulator
SIMON Implementation
JavaScript implementation of the SIMON algorithm
SIMON Implementation
// SIMON Lightweight Block Cipher Implementation in JavaScript
/**
* SIMON block cipher implementation
*
* This is a JavaScript implementation of the SIMON lightweight block cipher
* designed by the NSA for hardware-constrained devices.
*
* Reference: "The SIMON and SPECK Lightweight Block Ciphers" by
* Ray Beaulieu, Douglas Shors, Jason Smith, Stefan Treatman-Clark,
* Bryan Weeks, and Louis Wingers
*/
// Number of rounds for each variant
const ROUNDS = {
"Simon32/64": 32,
"Simon48/72": 36,
"Simon48/96": 36,
"Simon64/96": 42,
"Simon64/128": 44,
"Simon96/96": 52,
"Simon96/144": 54,
"Simon128/128": 68,
"Simon128/192": 69,
"Simon128/256": 72,
};
// Constants for the key schedule
const Z = [
[1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0],
[1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0],
[1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1],
[1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1],
[1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1],
];
/**
* Convert a hexadecimal string to a BigInt
* @param {string} hex - Hexadecimal string
* @returns {BigInt} - BigInt representation
*/
function hexToBigInt(hex) {
return BigInt(`0x${hex}`);
}
/**
* Convert a BigInt to a hexadecimal string with specified length
* @param {BigInt} num - BigInt number
* @param {number} length - Desired length of the hex string
* @returns {string} - Hexadecimal string
*/
function bigIntToHex(num, length) {
return num.toString(16).padStart(length, '0');
}
/**
* Rotate left (circular left shift)
* @param {BigInt} x - Value to rotate
* @param {number} n - Number of bits to rotate
* @param {number} wordSize - Word size in bits
* @returns {BigInt} - Rotated value
*/
function rol(x, n, wordSize) {
const mask = (BigInt(1) << BigInt(wordSize)) - BigInt(1);
return ((x << BigInt(n)) | (x >> BigInt(wordSize - n))) & mask;
}
/**
* Rotate right (circular right shift)
* @param {BigInt} x - Value to rotate
* @param {number} n - Number of bits to rotate
* @param {number} wordSize - Word size in bits
* @returns {BigInt} - Rotated value
*/
function ror(x, n, wordSize) {
const mask = (BigInt(1) << BigInt(wordSize)) - BigInt(1);
return ((x >> BigInt(n)) | (x << BigInt(wordSize - n))) & mask;
}
/**
* Generate round keys for SIMON
* @param {string} key - Key as a hex string
* @param {number} rounds - Number of rounds
* @param {Object} params - Parameters including wordSize and keySize
* @returns {Array<BigInt>} - Array of round keys
*/
function generateRoundKeys(key, rounds, params) {
const { wordSize, keySize } = params;
const keyWords = keySize / wordSize;
const wordSizeInHex = wordSize / 4;
const wordMask = (BigInt(1) << BigInt(wordSize)) - BigInt(1);
// Split key into words
const keyParts = [];
for (let i = 0; i < keyWords; i++) {
const start = i * wordSizeInHex;
const end = start + wordSizeInHex;
keyParts.push(hexToBigInt(key.slice(start, end)));
}
// Determine which Z sequence to use based on key words
let zIdx = 0;
if (keyWords === 4) zIdx = 2;
else if (keyWords === 3) zIdx = 1;
else if (keyWords === 2) {
if (wordSize === 16) zIdx = 0;
else zIdx = 2;
}
const roundKeys = [...keyParts];
const c = BigInt((1 << wordSize) - 4); // constant c = 2^n - 4
for (let i = keyWords; i < rounds; i++) {
let tmp = ror(roundKeys[i - 1], 3, wordSize);
if (keyWords === 4) tmp = tmp ^ roundKeys[i - 3];
tmp = tmp ^ ror(tmp, 1, wordSize);
const zBit = BigInt(Z[zIdx][(i - keyWords) % 62]);
roundKeys[i] = (c ^ zBit ^ roundKeys[i - keyWords] ^ tmp) & wordMask;
}
return roundKeys;
}
/**
* Function f used in SIMON
* @param {BigInt} x - Input value
* @param {number} wordSize - Word size in bits
* @returns {BigInt} - Result of f(x)
*/
function f(x, wordSize) {
const wordMask = (BigInt(1) << BigInt(wordSize)) - BigInt(1);
const x1 = rol(x, 1, wordSize);
const x8 = rol(x, 8, wordSize);
const x2 = rol(x, 2, wordSize);
return (x1 & x8) ^ x2;
}
/**
* Encrypt a block using SIMON
* @param {string} block - Block as a hex string
* @param {Array<BigInt>} roundKeys - Round keys
* @param {Object} params - Parameters including wordSize
* @returns {string} - Encrypted block as a hex string
*/
function encryptBlock(block, roundKeys, params) {
const { wordSize } = params;
const wordSizeInHex = wordSize / 4;
const wordMask = (BigInt(1) << BigInt(wordSize)) - BigInt(1);
// Split block into two words
let x = hexToBigInt(block.slice(0, wordSizeInHex));
let y = hexToBigInt(block.slice(wordSizeInHex));
// Apply rounds
for (let i = 0; i < roundKeys.length; i++) {
const temp = x;
x = y ^ f(x, wordSize) ^ roundKeys[i];
y = temp;
}
// Combine the two words
return bigIntToHex(x, wordSizeInHex) + bigIntToHex(y, wordSizeInHex);
}
/**
* Decrypt a block using SIMON
* @param {string} block - Block as a hex string
* @param {Array<BigInt>} roundKeys - Round keys
* @param {Object} params - Parameters including wordSize
* @returns {string} - Decrypted block as a hex string
*/
function decryptBlock(block, roundKeys, params) {
const { wordSize } = params;
const wordSizeInHex = wordSize / 4;
const wordMask = (BigInt(1) << BigInt(wordSize)) - BigInt(1);
// Split block into two words
let x = hexToBigInt(block.slice(0, wordSizeInHex));
let y = hexToBigInt(block.slice(wordSizeInHex));
// Apply rounds in reverse
for (let i = roundKeys.length - 1; i >= 0; i--) {
const temp = y;
y = x;
x = temp ^ f(y, wordSize) ^ roundKeys[i];
}
// Combine the two words
return bigIntToHex(x, wordSizeInHex) + bigIntToHex(y, wordSizeInHex);
}
/**
* Encrypt a message using SIMON in ECB mode
* @param {string} message - Message to encrypt
* @param {string} key - Key as a hex string
* @param {string} variant - SIMON variant (e.g., "Simon64/128")
* @param {boolean} isHex - Whether the message is already in hex format
* @returns {string} - Encrypted message as a hex string
*/
function encryptSimon(message, key, variant = "Simon64/128", isHex = false) {
// Get parameters for the variant
const [blockSizeStr, keySizeStr] = variant.substring(5).split('/');
const blockSize = parseInt(blockSizeStr);
const keySize = parseInt(keySizeStr);
const wordSize = blockSize / 2;
const blockSizeInHex = blockSize / 4;
const rounds = ROUNDS[variant] || 44; // Default to Simon64/128 rounds
// Convert message to hex if needed
const messageHex = isHex ? message : stringToHex(message);
// Pad the message to a multiple of block size
const paddedMessage = padHex(messageHex, blockSizeInHex);
// Generate round keys
const roundKeys = generateRoundKeys(key, rounds, { wordSize, keySize });
let ciphertext = '';
// Encrypt each block
for (let i = 0; i < paddedMessage.length; i += blockSizeInHex) {
const block = paddedMessage.substr(i, blockSizeInHex);
const encryptedBlock = encryptBlock(block, roundKeys, { wordSize });
ciphertext += encryptedBlock;
}
return ciphertext;
}
/**
* Decrypt a message using SIMON in ECB mode
* @param {string} ciphertext - Ciphertext as a hex string
* @param {string} key - Key as a hex string
* @param {string} variant - SIMON variant (e.g., "Simon64/128")
* @param {boolean} toHex - Whether to return the result as hex or convert to string
* @returns {string} - Decrypted message
*/
function decryptSimon(ciphertext, key, variant = "Simon64/128", toHex = false) {
// Get parameters for the variant
const [blockSizeStr, keySizeStr] = variant.substring(5).split('/');
const blockSize = parseInt(blockSizeStr);
const keySize = parseInt(keySizeStr);
const wordSize = blockSize / 2;
const blockSizeInHex = blockSize / 4;
const rounds = ROUNDS[variant] || 44; // Default to Simon64/128 rounds
// Generate round keys
const roundKeys = generateRoundKeys(key, rounds, { wordSize, keySize });
let plaintext = '';
// Decrypt each block
for (let i = 0; i < ciphertext.length; i += blockSizeInHex) {
const block = ciphertext.substr(i, blockSizeInHex);
const decryptedBlock = decryptBlock(block, roundKeys, { wordSize });
plaintext += decryptedBlock;
}
// Convert from hex to string if requested
return toHex ? plaintext : hexToString(plaintext);
}
/**
* Convert a string to a hex string
* @param {string} str - Input string
* @returns {string} - Hex string
*/
function stringToHex(str) {
let hex = '';
for (let i = 0; i < str.length; i++) {
const charCode = str.charCodeAt(i);
hex += charCode.toString(16).padStart(2, '0');
}
return hex;
}
/**
* Convert a hex string to a string
* @param {string} hex - Hex string
* @returns {string} - Decoded string
*/
function hexToString(hex) {
let str = '';
for (let i = 0; i < hex.length; i += 2) {
const charCode = parseInt(hex.substr(i, 2), 16);
if (charCode) {
str += String.fromCharCode(charCode);
}
}
return str;
}
/**
* Pad a hex string to a multiple of specified length
* @param {string} hex - Hex string to pad
* @param {number} blockSize - Block size in hex characters
* @returns {string} - Padded hex string
*/
function padHex(hex, blockSize) {
const paddingLength = blockSize - (hex.length % blockSize);
if (paddingLength < blockSize) {
return hex + '0'.repeat(paddingLength);
}
return hex;
}
// Export the functions
module.exports = {
encryptSimon,
decryptSimon,
encryptBlock,
decryptBlock,
generateRoundKeys,
stringToHex,
hexToString
};Implementation Notes
- This is a pure JavaScript implementation of SIMON for educational purposes.
- Uses BigInt for handling various word sizes (16, 24, 32, 48, and 64 bits).
- Implements all SIMON variants with their respective parameters.
- The implementation follows the original specification by the NSA.
- SIMON is optimized for hardware implementations, while its companion SPECK is optimized for software.
- For simplicity, this uses ECB mode which is not recommended for secure applications.
- In a real-world scenario, you would use a more secure mode of operation like CBC or CTR.
- The round function uses simple bitwise operations (AND, XOR, rotations) that map efficiently to hardware.
- For production use, consider using established cryptographic libraries.
- This implementation is optimized for readability rather than performance.