Skip to content
CyberXplained

SPECK Encryption

AdvancedSymmetric

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

VariantBlock Size (bits)Key Size (bits)Rounds
Speck32/64326422
Speck48/72487222
Speck48/96489623
Speck64/96649626
Speck64/1286412827
Speck96/96969628
Speck96/1449614429
Speck128/12812812832
Speck128/19212819233
Speck128/25612825634

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)72
24 bits (Speck48)83
32 bits (Speck64)83
48 bits (Speck96)83
64 bits (Speck128)83

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

0
1
2
3
4
5
6
7
Rotating right by 8

Y Register

0
1
2
3
4
5
6
7

Round Structure

Input
ROR
ADD
XOR
ROL
XOR
Output

Round Key

K0
K1
K2
K3
K4
K5
K6
K7

ARX Operations

XYROR 8ADDXOR KROL 3XOR XX'Y'K0
SlowFast

SPECK Simulator

Try encrypting and decrypting with SPECK

SPECK Simulator

SPECK Encryption Simulator

1 (Faster, Insecure)1327 (Standard)

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.