The Simon and Speck Algorithms
Simon and Speck are two lightweight block ciphers released by the NSA in 2013 for tiny devices. Learn how Simon gets by on AND, rotate, and XOR, how Speck uses add, rotate, and XOR, and why they became some of the most debated ciphers ever published.
Interactive Simon and Speck Encryption
🔐 Simon and Speck Encryption
The Simon and Speck Algorithms
Introduction
Simon and Speck are a pair of block ciphers designed by the United States National Security Agency and released to the public in June 2013. They are built for the smallest, cheapest devices: sensors, tags, and other Internet of Things hardware that cannot afford the memory or power that a cipher like AES needs.
The two ciphers are siblings with different strengths. Simon is tuned for hardware and uses only AND, rotation, and XOR. Speck is tuned for software and uses addition, rotation, and XOR. Both come in ten sizes, with blocks from 32 to 128 bits and keys from 64 to 256 bits. They are also two of the most controversial ciphers ever published, and the reasons say a lot about trust in cryptography.
Table of Contents
- History and Controversy
- Naming and Variants
- How Speck Works
- How Simon Works
- Word Order
- A Worked Example
- Python Implementation
- Limitations
- Security Status
- FAQ
- References
History and Controversy
Six NSA cryptographers designed both ciphers: Ray Beaulieu, Douglas Shors, Jason Smith, Stefan Treatman-Clark, Bryan Weeks, and Louis Wingers. The agency said it expected some US federal agencies to need a cipher that worked well across a wide variety of Internet of Things devices while keeping an acceptable level of security.
The NSA’s involvement made people wary. Early efforts to standardize the ciphers at ISO failed, because expert delegates from several countries, including Germany, Japan, and Israel, opposed them. They worried that the NSA might be pushing the ciphers while knowing about weaknesses. The ciphers were later standardized by ISO anyway. In October 2018 they became part of the RFID air interface standards, as ISO 29167-21 for Simon and ISO 29167-22 for Speck. Second editions of both were published in March 2026.
Speck also had a short life in the Linux kernel. Google added it in kernel 4.17 so that low-end Android devices without AES hardware could still encrypt their storage. After a backlash over the NSA’s role, Speck was removed again in kernel 4.20, and Google built a different design called Adiantum for those devices. No attack on the full ciphers has been published, but the episode shows that trust in a cipher depends on more than its math.
Naming and Variants
A variant is named Simon2n/mn or Speck2n/mn. The block is two words of n bits each, and the key is m words of n bits. So Speck64/128 has a 64-bit block made of two 32-bit words, and a 128-bit key made of four 32-bit words.
| Block (bits) | Key (bits) | Word size | Simon rounds | Speck rounds |
|---|---|---|---|---|
| 32 | 64 | 16 | 32 | 22 |
| 48 | 72 | 24 | 36 | 22 |
| 48 | 96 | 24 | 36 | 23 |
| 64 | 96 | 32 | 42 | 26 |
| 64 | 128 | 32 | 44 | 27 |
| 96 | 96 | 48 | 52 | 28 |
| 96 | 144 | 48 | 54 | 29 |
| 128 | 128 | 64 | 68 | 32 |
| 128 | 192 | 64 | 69 | 33 |
| 128 | 256 | 64 | 72 | 34 |
Small blocks such as 32 bits are meant for very constrained uses. For anything with a lot of data, the 128-bit block versions are the sensible choice.
How Speck Works
Speck is an ARX cipher: it uses Add, Rotate, and XOR on two words, x and y. A single round looks like this:
x = ((x >>> alpha) + y) ^ k
y = (y <<< beta) ^ x
The rotation amounts are alpha = 8 and beta = 3 for most variants, and alpha = 7 and beta = 2 for the 32-bit block. Addition is modulo 2ⁿ, and k is the round key for that round. That is the whole round: one rotation, one addition, one XOR, a second rotation, and a second XOR.
Speck’s key schedule reuses the same round function. The key is split into one word k[0] and m - 1 words l[0], l[1], .... Each new round key comes from running a Speck round on the key words, using the round number as the “key”:
l[i+m-1] = (k[i] + (l[i] >>> alpha)) ^ i
k[i+1] = (k[i] <<< beta) ^ l[i+m-1]
Interactive Visualizer
The visualizer above runs this exact algorithm for all twenty variants. Choose Speck or Simon and a size. The defaults are the official test vectors from the NSA’s paper, so the first result you see is the published ciphertext. Each row of the log shows x, y, and the round key for one round.
How Simon Works
Simon is a Feistel-style cipher, but its round function is unusual: it contains no addition and no S-box. The only nonlinear operation is a bitwise AND. A single round looks like this:
f(x) = ((x <<< 1) & (x <<< 8)) ^ (x <<< 2)
(x, y) = (y ^ f(x) ^ k, x)
The word x is rotated three different ways, and two of the rotations are ANDed together. That AND is the cipher’s only source of nonlinearity. The result is XORed with y and the round key, and the words swap places. A round is very cheap in hardware, because AND, rotation, and XOR cost almost nothing in circuit area.
Simon’s key schedule uses constants. Each variant uses one of five fixed 62-bit sequences, named z0 to z4, and a constant c made of all ones except the lowest two bits. The key words are expanded like this, where m is the number of key words:
tmp = k[i+m-1] >>> 3
if m == 4: tmp = tmp ^ k[i+1]
tmp = tmp ^ (tmp >>> 1)
k[i+m] = c ^ z[i mod 62] ^ k[i] ^ tmp
For example, Simon64/128 uses z3, whose first bits are 11011011. The sequence repeats every 62 rounds, which matters for the variants with more than 62 rounds.
Word Order
The NSA paper prints everything as words, most significant first. The key is written as (l[m-2], ..., l[0], k[0]) and the block is written as (x, y). So Speck64/128’s key 1b1a1918 13121110 0b0a0908 03020100 means that k[0] = 03020100, l[0] = 0b0a0908, l[1] = 13121110, and l[2] = 1b1a1918.
Libraries that work on bytes have to choose how bytes become words, and not all of them follow the paper’s order. Always check which convention a test vector uses. The code and the visualizer here use the paper’s word order and take hex strings of concatenated words, so the defaults can be compared directly with the published vectors.
A Worked Example
Speck64/128. The key is 1b1a1918 13121110 0b0a0908 03020100 and the plaintext words are x = 3b726574 and y = 7475432d. The first round key is k[0] = 03020100, and the next two are k[1] = 131d0309 and k[2] = bbd80d53.
Round 1 rotates x right by 8, which gives 743b7265. Adding y gives e8b0b592, and XORing k[0] gives the new x = ebb2b492. Then y rotates left by 3 to a3aa196b, and XORing the new x gives y = 4818adf9. After round 2 the words are x = c81963a4 and y = 88dc0c6e, and after round 13 they are x = a5c92ed2 and y = f52957b8. After all 27 rounds the ciphertext is:
8c6fa548 454e028b
Speck32/64. The smallest variant has a well-known vector. With key 1918 1110 0908 0100 and plaintext 6574 694c, the ciphertext is a86842f2.
Simon64/128. The same key words give the round keys k[0] = 03020100, k[1] = 0b0a0908, k[2] = 13121110, and k[3] = 1b1a1918. The first expanded key is k[4] = 70a011c3. The plaintext words are x = 656b696c and y = 20646e75.
In round 1, x rotated left by 1 is cad6d2d8, and rotated left by 8 it is 6b696c65. The AND of those two is 4a404040. Rotating x left by 2 gives 95ada5b1, so f(x) = dfede5f1. Then the new x = y ^ f(x) ^ k[0] = fc8b8a84, and the new y is the old x, which is 656b696c. After 44 rounds the ciphertext is:
44c8fc20 b9dfa07a
Simon32/64. With key 1918 1110 0908 0100 and plaintext 6565 6877, the ciphertext is c69be9bb.
All of these values are published in the NSA paper’s test vectors.
Python Implementation
This is a complete Simon and Speck: all twenty variants, key schedules, encryption, and decryption. The five Simon z sequences and the round counts were checked against an independent implementation and the published tables.
# simon_speck.py
#
# Simon and Speck, the NSA's lightweight block ciphers (2013).
# Both come in ten variants named Simon2n/mn: a block of two n-bit words and
# a key of m words. Speck is an ARX cipher (add, rotate, XOR). Simon is a
# Feistel cipher that uses only AND, rotate and XOR. This code takes and
# returns words as integers, in the order the paper prints them: the key as
# (l[m-2], ..., l[0], k[0]) and the block as (x, y).
# (block bits, key bits) -> (rounds, index of the z sequence)
SIMON = {
(32, 64): (32, 0),
(48, 72): (36, 0),
(48, 96): (36, 1),
(64, 96): (42, 2),
(64, 128): (44, 3),
(96, 96): (52, 2),
(96, 144): (54, 3),
(128, 128): (68, 2),
(128, 192): (69, 3),
(128, 256): (72, 4),
}
# (block bits, key bits) -> rounds
SPECK = {
(32, 64): 22,
(48, 72): 22,
(48, 96): 23,
(64, 96): 26,
(64, 128): 27,
(96, 96): 28,
(96, 144): 29,
(128, 128): 32,
(128, 192): 33,
(128, 256): 34,
}
# Simon's five 62-bit constant sequences, as the paper prints them.
Z = {
0: "11111010001001010110000111001101111101000100101011000011100110",
1: "10001110111110010011000010110101000111011111001001100001011010",
2: "10101111011100000011010010011000101000010001111110010110110011",
3: "11011011101011000110010111100000010010001010011100110100001111",
4: "11010001111001101011011000100000010111000011001010010011101111",
}
def rol(x, r, n):
return ((x << r) | (x >> (n - r))) & ((1 << n) - 1)
def ror(x, r, n):
return rol(x, n - r, n)
# ---- Speck ----
def speck_params(block_bits, key_bits):
n = block_bits // 2
m = key_bits // n
alpha, beta = (7, 2) if n == 16 else (8, 3)
return n, m, alpha, beta, SPECK[(block_bits, key_bits)]
def speck_round_keys(key_words, block_bits, key_bits):
n, m, alpha, beta, rounds = speck_params(block_bits, key_bits)
mask = (1 << n) - 1
words = key_words[::-1] # k[0], l[0], l[1], ...
k, l = [words[0]], list(words[1:])
for i in range(rounds - 1):
l.append(((k[i] + ror(l[i], alpha, n)) & mask) ^ i)
k.append(rol(k[i], beta, n) ^ l[i + m - 1])
return k
def speck_encrypt(key_words, block, block_bits, key_bits):
n, m, alpha, beta, rounds = speck_params(block_bits, key_bits)
mask = (1 << n) - 1
x, y = block
for k in speck_round_keys(key_words, block_bits, key_bits):
x = ((ror(x, alpha, n) + y) & mask) ^ k
y = rol(y, beta, n) ^ x
return x, y
def speck_decrypt(key_words, block, block_bits, key_bits):
n, m, alpha, beta, rounds = speck_params(block_bits, key_bits)
mask = (1 << n) - 1
x, y = block
for k in reversed(speck_round_keys(key_words, block_bits, key_bits)):
y = ror(y ^ x, beta, n)
x = rol(((x ^ k) - y) & mask, alpha, n)
return x, y
# ---- Simon ----
def simon_round_keys(key_words, block_bits, key_bits):
n = block_bits // 2
m = key_bits // n
rounds, zi = SIMON[(block_bits, key_bits)]
mask = (1 << n) - 1
k = key_words[::-1] # k[0], k[1], ..., k[m-1]
for i in range(rounds - m):
tmp = ror(k[i + m - 1], 3, n)
if m == 4:
tmp ^= k[i + 1]
tmp ^= ror(tmp, 1, n)
# c = 2^n - 4, so c XOR k[i] is the same as (NOT k[i]) XOR 3
k.append(mask ^ k[i] ^ tmp ^ int(Z[zi][i % 62]) ^ 3)
return k
def simon_f(x, n):
return (rol(x, 1, n) & rol(x, 8, n)) ^ rol(x, 2, n)
def simon_encrypt(key_words, block, block_bits, key_bits):
n = block_bits // 2
x, y = block
for k in simon_round_keys(key_words, block_bits, key_bits):
x, y = y ^ simon_f(x, n) ^ k, x
return x, y
def simon_decrypt(key_words, block, block_bits, key_bits):
n = block_bits // 2
x, y = block
for k in reversed(simon_round_keys(key_words, block_bits, key_bits)):
x, y = y, x ^ simon_f(y, n) ^ k
return x, y
def words(hex_string, word_bits):
"""Split a hex string into integers of word_bits each, most significant first."""
step = word_bits // 4
return [int(hex_string[i:i + step], 16) for i in range(0, len(hex_string), step)]
if __name__ == "__main__":
# The test vectors for the 64-bit-block, 128-bit-key variants, from the paper
key = words("1b1a191813121110" "0b0a090803020100", 32)
for name, enc, dec, plain, expected in (
("Speck64/128", speck_encrypt, speck_decrypt, "3b7265747475432d", "8c6fa548454e028b"),
("Simon64/128", simon_encrypt, simon_decrypt, "656b696c20646e75", "44c8fc20b9dfa07a"),
):
block = tuple(words(plain, 32))
cipher = enc(key, block, 64, 128)
print(f"{name}: plaintext {plain} -> ciphertext {''.join('%08x' % w for w in cipher)}"
f" recovered {''.join('%08x' % w for w in dec(key, cipher, 64, 128))}")
Running it produces this output:
Speck64/128: plaintext 3b7265747475432d -> ciphertext 8c6fa548454e028b recovered 3b7265747475432d
Simon64/128: plaintext 656b696c20646e75 -> ciphertext 44c8fc20b9dfa07a recovered 656b696c20646e75
I checked this code two ways before writing it up. It reproduces all twenty official test vectors from the NSA paper, one for each variant of each cipher, so every key size and every z sequence is exercised. It also agrees with an independent open-source Python implementation by the author known as inmcm on 500 random keys and blocks, 25 for each of the twenty variants.
For Fun: Both Ciphers in 34 Lines
This is the same spirit as the compact bonus sections elsewhere on this site. It is not for learning the algorithm from. This version squeezes the 150-line implementation above into 34 lines. The tables share a line each, and every key schedule and block routine is tightened. It needs no imports and no other files.
SIMON={(32,64):(32,0),(48,72):(36,0),(48,96):(36,1),(64,96):(42,2),(64,128):(44,3),(96,96):(52,2),(96,144):(54,3),(128,128):(68,2),(128,192):(69,3),(128,256):(72,4)};SPECK={(32,64):22,(48,72):22,(48,96):23,(64,96):26,(64,128):27,(96,96):28,(96,144):29,(128,128):32,(128,192):33,(128,256):34};Z={0:"11111010001001010110000111001101111101000100101011000011100110",1:"10001110111110010011000010110101000111011111001001100001011010",2:"10101111011100000011010010011000101000010001111110010110110011",3:"11011011101011000110010111100000010010001010011100110100001111",4:"11010001111001101011011000100000010111000011001010010011101111"}
rol=lambda x,r,n: ((x<<r)|(x>>(n-r)))&((1<<n)-1);ror=lambda x,r,n: rol(x,n-r,n)
def speck_params(b,k): n=b//2;m=k//n;a,be=(7,2) if n==16 else (8,3);return n,m,a,be,SPECK[(b,k)]
def speck_round_keys(kw,b,k):
n,m,a,be,r=speck_params(b,k);mask=(1<<n)-1;w=kw[::-1];kk=[w[0]];l=list(w[1:])
for i in range(r-1): l.append(((kk[i]+ror(l[i],a,n))&mask)^i);kk.append(rol(kk[i],be,n)^l[i+m-1])
return kk
def speck_encrypt(kw,bl,b,k):
n,m,a,be,r=speck_params(b,k);mask=(1<<n)-1;x,y=bl
for kk in speck_round_keys(kw,b,k): x=((ror(x,a,n)+y)&mask)^kk;y=rol(y,be,n)^x
return x,y
def speck_decrypt(kw,bl,b,k):
n,m,a,be,r=speck_params(b,k);mask=(1<<n)-1;x,y=bl
for kk in reversed(speck_round_keys(kw,b,k)): y=ror(y^x,be,n);x=rol(((x^kk)-y)&mask,a,n)
return x,y
def simon_round_keys(kw,b,k):
n=b//2;m=k//n;r,zi=SIMON[(b,k)];mask=(1<<n)-1;kk=kw[::-1]
for i in range(r-m):
t=ror(kk[i+m-1],3,n)
if m==4: t^=kk[i+1]
t^=ror(t,1,n);kk.append(mask^kk[i]^t^int(Z[zi][i%62])^3)
return kk
def simon_f(x,n): return (rol(x,1,n)&rol(x,8,n))^rol(x,2,n)
def simon_encrypt(kw,bl,b,k):
n=b//2;x,y=bl
for kk in simon_round_keys(kw,b,k): x,y=y^simon_f(x,n)^kk,x
return x,y
def simon_decrypt(kw,bl,b,k):
n=b//2;x,y=bl
for kk in reversed(simon_round_keys(kw,b,k)): x,y=y,x^simon_f(y,n)^kk
return x,y
def words(h,wb): s=wb//4;return [int(h[i:i+s],16) for i in range(0,len(h),s)]
key=words("1b1a1918131211100b0a090803020100",32)
for name,enc,dec,plain,expected in (("Speck64/128",speck_encrypt,speck_decrypt,"3b7265747475432d","8c6fa548454e028b"),("Simon64/128",simon_encrypt,simon_decrypt,"656b696c20646e75","44c8fc20b9dfa07a")):
block=tuple(words(plain,32));cipher=enc(key,block,64,128);print(f"{name}: plaintext {plain} -> ciphertext {''.join('%08x'%w for w in cipher)} recovered {''.join('%08x'%w for w in dec(key,cipher,64,128))}")
Running it prints the same two lines as the readable version, with the NSA paper’s Speck64/128 and Simon64/128 ciphertexts:
Speck64/128: plaintext 3b7265747475432d -> ciphertext 8c6fa548454e028b recovered 3b7265747475432d
Simon64/128: plaintext 656b696c20646e75 -> ciphertext 44c8fc20b9dfa07a recovered 656b696c20646e75
I checked it against the paper and the readable code. It reproduces all twenty official test vectors, in both directions. On 500 random keys and blocks, 25 for each of the twenty variants, encryption matched the readable code every time, and decryption recovered every block. The round tables and the five z sequences are identical.
Limitations
This is a faithful teaching version of the ciphers, not a production one:
- Single block only. There is no mode of operation for longer messages and no padding for partial blocks.
- Small blocks are weak. The 32-bit and 48-bit block sizes limit how much data one key can safely protect. The 128-bit variants are the sensible choice.
- No side-channel hardening. Python integers are not constant-time. The ciphers themselves have no table lookups, which helps real implementations.
- Trust questions remain. The ciphers’ origin and the standardization history are a reason some projects avoid them.
Security Status
As of this writing, no successful attack on the full-round version of any variant of Simon is known, and Speck has a similar record. The best published attacks use differential cryptanalysis and get through about 70 to 75 percent of the rounds of most variants. Even those attacks are only marginally faster than brute force. The designers aimed for a security margin of about 30 percent, similar to AES-128, so these results fit their plan. Research on reduced-round versions, including machine-learning-assisted distinguishers, continues.
The debate is about trust, not about a break. The ciphers are compact and fast, which suits constrained devices. But because they came from the NSA, and because the designers have said little about how the design was chosen, many experts prefer ciphers with a longer public design history. For general use, AES or ChaCha20 are the usual choices, and Simon and Speck are mostly of interest for tiny devices and for study.
FAQ
What is the difference between Simon and Speck?
Simon is a Feistel-style cipher tuned for hardware that uses AND, rotation, and XOR. Speck is an ARX cipher tuned for software that uses addition, rotation, and XOR. They share a naming scheme and a set of block and key sizes.
Are Simon and Speck secure?
No successful attack on the full-round versions is known. The best attacks reach about 70 to 75 percent of the rounds. The main concern is trust in the designers, not a known weakness.
Why were they controversial?
The NSA designed them and released few details about the design process. Delegates from several countries objected to standardizing them at ISO, and Speck was removed from the Linux kernel after a backlash. Both were later standardized by ISO for RFID.
What does ARX mean?
It stands for Add, Rotate, XOR. Speck is a pure ARX cipher. ChaCha20 and Threefish are other well-known ARX designs.
Which variant should I use?
If you must use them, prefer a 128-bit block such as Speck128/128 or Simon128/128. The small block sizes are meant for very constrained cases and are not suitable for protecting much data.
References
-
Beaulieu, R., Shors, D., Smith, J., Treatman-Clark, S., Weeks, B., and Wingers, L. “The SIMON and SPECK Families of Lightweight Block Ciphers.” IACR Cryptology ePrint Archive, 2013/404. Available at: https://eprint.iacr.org/2013/404
-
Beaulieu, R. et al. “SIMON and SPECK: Block Ciphers for the Internet of Things.” IACR Cryptology ePrint Archive, 2015/585.
-
Crowley, P. and Biggers, E. “Adiantum: Length-Preserving Encryption for Entry-Level Processors.” IACR Transactions on Symmetric Cryptology, 2018.
-
Wikipedia. “Simon (cipher).” Available at: https://en.wikipedia.org/wiki/Simon_(cipher)
-
Wikipedia. “Speck (cipher).” Available at: https://en.wikipedia.org/wiki/Speck_(cipher)