SPECK Encryption
At a Glance
- Block sizes: 32, 48, 64, 96, 128 bits
- Key sizes: 64, 72, 96, 128, 144, 192, 256 bits
- ARX structure (Add-Rotate-XOR)
- Feistel-like network
- Optimized for software implementation
About SPECK
Designed by the NSA in 2013 for IoT and resource-constrained devices
SPECK 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 software implementations on resource-constrained devices such as IoT sensors, RFID tags, and other embedded systems where processing power, memory, and energy are limited.
SPECK was released alongside SIMON, another lightweight cipher that was optimized for hardware implementations, while SPECK was optimized for software. Together, they provide efficient encryption options for the growing Internet of Things (IoT) ecosystem. SPECK's design prioritizes simplicity and efficiency while maintaining a reasonable security margin.
Key Features
- Extremely small code size and memory footprint
- Fast execution on 8-bit, 16-bit, and 32-bit processors
- Simple ARX (Add-Rotate-XOR) operations for efficiency
- Flexible with multiple block and key size options
- Optimized for software implementations
- Low energy consumption, ideal for battery-powered devices
- Competitive performance compared to other lightweight ciphers
Technical Specifications
| Variant | Block Size (bits) | Key Size (bits) | Rounds |
|---|---|---|---|
| Speck32/64 | 32 | 64 | 22 |
| Speck48/72 | 48 | 72 | 22 |
| Speck48/96 | 48 | 96 | 23 |
| Speck64/96 | 64 | 96 | 26 |
| Speck64/128 | 64 | 128 | 27 |
| Speck96/96 | 96 | 96 | 28 |
| Speck96/144 | 96 | 144 | 29 |
| Speck128/128 | 128 | 128 | 32 |
| Speck128/192 | 128 | 192 | 33 |
| Speck128/256 | 128 | 256 | 34 |
How SPECK Works
SPECK uses a Feistel-like structure with ARX (Add-Rotate-XOR) operations, which are simple and efficient on most processors. The cipher operates on two words of data in each round.
1. Round Function
Each round of SPECK applies a series of operations to the two words of the state:
x = ROR(x, α) x = (x + y) mod 2^n x = x ⊕ k y = ROL(y, β) y = y ⊕ x
Where α and β are rotation constants (α=8, β=3 for most variants), and n is the word size.
2. Key Schedule
The key schedule in SPECK is similar to the encryption process, making it efficient to implement:
l[i+m-1] = (ROR(l[i], α) + k[i]) ⊕ i k[i+1] = ROL(k[i], β) ⊕ l[i+m-1]
Where m is the number of key words, and i is the round number.
3. Operations
SPECK uses only three basic operations:
- +: Addition modulo 2n
- ⊕: Bitwise XOR
- ROL/ROR: Rotate Left/Right
These simple operations are fast on most processors, including resource-constrained devices.
Rotation Constants
| Block Size | α (Right Rotation) | β (Left Rotation) |
|---|---|---|
| 16 bits (Speck32) | 7 | 2 |
| 24 bits (Speck48) | 8 | 3 |
| 32 bits (Speck64) | 8 | 3 |
| 48 bits (Speck96) | 8 | 3 |
| 64 bits (Speck128) | 8 | 3 |
Encryption and Decryption Process
Encryption
function encrypt(plaintext, roundKeys): x, y = split_plaintext(plaintext) for i = 0 to rounds-1: x = ROR(x, α) x = (x + y) mod 2^n x = x ⊕ roundKeys[i] y = ROL(y, β) y = y ⊕ x return combine(x, y)
Decryption
function decrypt(ciphertext, roundKeys): x, y = split_ciphertext(ciphertext) for i = rounds-1 down to 0: y = y ⊕ x y = ROR(y, β) x = x ⊕ roundKeys[i] x = (x - y) mod 2^n x = ROL(x, α) return combine(x, y)
Security Considerations
SPECK offers a reasonable security margin while maintaining 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
SPECK is particularly well-suited for software implementations in constrained environments:
- IoT devices and sensors with limited processing power
- RFID tags and smart cards
- Embedded systems with 8-bit, 16-bit, or 32-bit processors
- Battery-powered devices where energy efficiency is critical
- Applications requiring small code size and memory footprint
- Performs better in software than SIMON (its hardware-optimized counterpart from the NSA)
- Competitive with other lightweight ciphers like PRESENT, CLEFIA, and LEA in terms of performance
- More efficient than AES on 8-bit and 16-bit platforms, though AES may be preferred when hardware acceleration is available
SPECK Animation
Visual representation of the SPECK encryption process
SPECK Animation
SPECK Visualization
Round 1/5 - Right Rotation (ROR)
Rotate X right by 8 bits
State Visualization
X Register
Y Register
Round Structure
Round Key
ARX Operations
SPECK Simulator
Try encrypting and decrypting with SPECK
SPECK Simulator
SPECK Encryption Simulator
SPECK Implementation
JavaScript implementation of the SPECK algorithm
SPECK Implementation
// SPECK Lightweight Block Cipher Implementation in JavaScript
/**
* SPECK block cipher implementation
*
* This is a JavaScript implementation of the SPECK lightweight block cipher
* designed by the NSA for resource-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
*/
// Rotation constants for different word sizes
const ROTATION_CONSTANTS = {
16: { alpha: 7, beta: 2 }, // For Speck32/64
24: { alpha: 8, beta: 3 }, // For Speck48/72, Speck48/96
32: { alpha: 8, beta: 3 }, // For Speck64/96, Speck64/128
48: { alpha: 8, beta: 3 }, // For Speck96/96, Speck96/144
64: { alpha: 8, beta: 3 }, // For Speck128/128, Speck128/192, Speck128/256
};
// Number of rounds for each variant
const ROUNDS = {
"Speck32/64": 22,
"Speck48/72": 22,
"Speck48/96": 23,
"Speck64/96": 26,
"Speck64/128": 27,
"Speck96/96": 28,
"Speck96/144": 29,
"Speck128/128": 32,
"Speck128/192": 33,
"Speck128/256": 34,
};
/**
* 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 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)) & mask;
}
/**
* 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;
}
/**
* Generate round keys for SPECK
* @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 { alpha, beta } = ROTATION_CONSTANTS[wordSize];
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)));
}
const roundKeys = [keyParts[0]];
const l = keyParts.slice(1);
for (let i = 0; i < rounds - 1; i++) {
// Key schedule
let li = l[i % (keyWords - 1)];
let ki = roundKeys[i];
// l[i] = (ROR(l[i], alpha) + k[i]) XOR i
li = ror(li, alpha, wordSize);
li = (li + ki) & wordMask;
li = li ^ BigInt(i);
// k[i+1] = ROL(k[i], beta) XOR l[i]
ki = rol(ki, beta, wordSize);
ki = ki ^ li;
roundKeys.push(ki);
l.push(li);
}
return roundKeys;
}
/**
* Encrypt a block using SPECK
* @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 { alpha, beta } = ROTATION_CONSTANTS[wordSize];
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++) {
// x = ROR(x, alpha)
x = ror(x, alpha, wordSize);
// x = (x + y) mod 2^n
x = (x + y) & wordMask;
// x = x XOR k
x = x ^ roundKeys[i];
// y = ROL(y, beta)
y = rol(y, beta, wordSize);
// y = y XOR x
y = y ^ x;
}
// Combine the two words
return bigIntToHex(x, wordSizeInHex) + bigIntToHex(y, wordSizeInHex);
}
/**
* Decrypt a block using SPECK
* @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 { alpha, beta } = ROTATION_CONSTANTS[wordSize];
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--) {
// y = y XOR x
y = y ^ x;
// y = ROR(y, beta)
y = ror(y, beta, wordSize);
// x = x XOR k
x = x ^ roundKeys[i];
// x = (x - y) mod 2^n
x = (x - y) & wordMask;
// x = ROL(x, alpha)
x = rol(x, alpha, wordSize);
}
// Combine the two words
return bigIntToHex(x, wordSizeInHex) + bigIntToHex(y, wordSizeInHex);
}
/**
* Encrypt a message using SPECK in ECB mode
* @param {string} message - Message to encrypt
* @param {string} key - Key as a hex string
* @param {string} variant - SPECK variant (e.g., "Speck64/128")
* @param {boolean} isHex - Whether the message is already in hex format
* @returns {string} - Encrypted message as a hex string
*/
function encryptSpeck(message, key, variant = "Speck64/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] || 27; // Default to Speck64/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 SPECK in ECB mode
* @param {string} ciphertext - Ciphertext as a hex string
* @param {string} key - Key as a hex string
* @param {string} variant - SPECK variant (e.g., "Speck64/128")
* @param {boolean} toHex - Whether to return the result as hex or convert to string
* @returns {string} - Decrypted message
*/
function decryptSpeck(ciphertext, key, variant = "Speck64/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] || 27; // Default to Speck64/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 = {
encryptSpeck,
decryptSpeck,
encryptBlock,
decryptBlock,
generateRoundKeys,
stringToHex,
hexToString
};
Implementation Notes
- This is a pure JavaScript implementation of SPECK for educational purposes.
- Uses BigInt for handling various word sizes (16, 24, 32, 48, and 64 bits).
- Implements all SPECK variants with their respective parameters.
- The implementation follows the original specification by the NSA.
- 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 ARX (Add-Rotate-XOR) operations make SPECK efficient on software platforms.
- For production use, consider using established cryptographic libraries.
- This implementation is optimized for readability rather than performance.