Module refinery.lib.speck

This implementation of the SPECK cipher is based on the SPECK Implementation Guide by the authors of SPECK.

Expand source code Browse git
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
This implementation of the SPECK cipher is based on the
[SPECK Implementation Guide](https://nsacyber.github.io/simon-speck/implementations/ImplementationGuide1.1.pdf) by the authors of SPECK.
"""
import struct

from typing import Tuple, List

from refinery.lib.crypto import (
    rotl32 as ROL32,
    rotr32 as ROR32,
    rotl64 as ROL64,
    rotr64 as ROR64,
)


SPECK_ROUNDS = {
    "64_96": 26,
    "64_128": 27,
    "128_128": 32,
    "128_192": 33,
    "128_256": 34,
}


def make_dword(x: int) -> int:
    return x & 0xFFFFFFFF


def make_qword(x: int) -> int:
    return x & 0xFFFFFFFFFFFFFFFF


def words_to_bytes(words: List[int], word_size=32) -> bytes:
    numwords = len(words)
    if word_size == 32:
        bytes_out = struct.pack("<" + "I" * numwords, *words[::-1])
    elif word_size == 64:
        bytes_out = struct.pack("<" + "Q" * numwords, *words[::-1])
    return bytes_out


def bytes_to_words(bytes_in: bytes, word_size=32) -> List[int]:
    numbytes = len(bytes_in)
    if word_size == 32:
        numwords = numbytes // 4
        words = struct.unpack("<" + "I" * numwords, bytes_in)
    elif word_size == 64:
        numwords = numbytes // 8
        words = struct.unpack("<" + "Q" * numwords, bytes_in)
    return list(words[::-1])


def Speck6496KeySchedule(key: bytearray) -> List[int]:
    """
    Calculate the round key rk for SPECK 64/96
    """
    k = bytes_to_words(key)
    rk = []
    C = k[0]
    B = k[1]
    A = k[2]
    for i in range(0, SPECK_ROUNDS["64_96"], 2):
        rk.append(A)
        B, A = speck_encrypt_round32(B, A, i)
        rk.append(A)
        C, A = speck_encrypt_round32(C, A, i + 1)
    return rk


def Speck64128KeySchedule(key: bytearray) -> List[int]:
    """
    Calculate the round key rk for SPECK 64/128
    """
    k = bytes_to_words(key)
    rk = []
    D = k[0]
    C = k[1]
    B = k[2]
    A = k[3]
    for i in range(0, SPECK_ROUNDS["64_128"], 3):
        rk.append(A)
        B, A = speck_encrypt_round32(B, A, i)
        rk.append(A)
        C, A = speck_encrypt_round32(C, A, i + 1)
        rk.append(A)
        D, A = speck_encrypt_round32(D, A, i + 2)
    return rk


def Speck128128KeySchedule(key: bytearray) -> List[int]:
    """
    Calculate the round key rk for SPECK 128/128
    """
    k = bytes_to_words(key, 64)
    rk = []
    B = k[0]
    A = k[1]
    for i in range(0, SPECK_ROUNDS["128_128"]):
        rk.append(A)
        B, A = speck_encrypt_round64(B, A, i)
    rk.append(A)
    return rk


def Speck128192KeySchedule(key: bytearray) -> List[int]:
    """
    Calculate the round key rk for SPECK 128/192
    """
    k = bytes_to_words(key, 64)
    rk = []
    C = k[0]
    B = k[1]
    A = k[2]
    for i in range(0, SPECK_ROUNDS["128_192"] - 1, 2):
        rk.append(A)
        B, A = speck_encrypt_round64(B, A, i)
        rk.append(A)
        C, A = speck_encrypt_round64(C, A, i + 1)
    rk.append(A)
    return rk


def Speck128256KeySchedule(key: bytearray) -> List[int]:
    """
    Calculate the round key rk for SPECK 128/256
    """
    k = bytes_to_words(key, 64)
    rk = []
    D = k[0]
    C = k[1]
    B = k[2]
    A = k[3]
    for i in range(0, SPECK_ROUNDS["128_256"] - 1, 3):
        rk.append(A)
        B, A = speck_encrypt_round64(B, A, i)
        rk.append(A)
        C, A = speck_encrypt_round64(C, A, i + 1)
        rk.append(A)
        D, A = speck_encrypt_round64(D, A, i + 2)
    rk.append(A)
    return rk


def speck_encrypt_round32(x: int, y: int, k: int) -> Tuple[int]:
    x = make_dword(ROR32(x, 8) + y) ^ k
    y = ROL32(y, 3) ^ x
    return x, y


def speck_encrypt_round64(x: int, y: int, k: int) -> Tuple[int]:
    x = make_qword(ROR64(x, 8) + y) ^ k
    y = ROL64(y, 3) ^ x
    return x, y


def speck_decrypt_round32(x: int, y: int, k: int) -> Tuple[int]:
    y = ROR32(y ^ x, 3)
    x = ROL32(make_dword((x ^ k) - y), 8)
    return x, y


def speck_decrypt_round64(x: int, y: int, k: int) -> Tuple[int]:
    y = ROR64(y ^ x, 3)
    x = ROL64(make_qword((x ^ k) - y), 8)
    return x, y


def _internal_speck_encrypt32(plaintext: List[int], rk: List[int], rounds: int) -> List[int]:
    cipher = plaintext
    for i in range(0, rounds):
        cipher[0], cipher[1] = speck_encrypt_round32(cipher[0], cipher[1], rk[i])
    return cipher


def _internal_speck_encrypt64(plaintext: List[int], rk: List[int], rounds: int) -> List[int]:
    cipher = plaintext
    for i in range(0, rounds):
        cipher[0], cipher[1] = speck_encrypt_round64(cipher[0], cipher[1], rk[i])
    return cipher


def _internal_speck_decrypt32(cipher: List[int], rk: List[int], rounds: int) -> List[int]:
    plaintext = cipher
    for i in range(rounds - 1, -1, -1):
        plaintext[0], plaintext[1] = speck_decrypt_round32(plaintext[0], plaintext[1], rk[i])
    return plaintext


def _internal_speck_decrypt64(cipher: List[int], rk: List[int], rounds: int) -> List[int]:
    plaintext = cipher
    for i in range(rounds - 1, -1, -1):
        plaintext[0], plaintext[1] = speck_decrypt_round64(plaintext[0], plaintext[1], rk[i])
    return plaintext


def speck_encrypt32(plaintext: bytearray, rk: List[int], rounds: int) -> bytes:
    pt_words = bytes_to_words(plaintext)
    cipher = _internal_speck_encrypt32(pt_words, rk, rounds)
    return words_to_bytes(cipher)


def speck_encrypt64(plaintext: bytearray, rk: List[int], rounds: int) -> bytes:
    pt_words = bytes_to_words(plaintext, 64)
    cipher = _internal_speck_encrypt64(pt_words, rk, rounds)
    return words_to_bytes(cipher, 64)


def speck_decrypt32(cipher: bytearray, rk: List[int], rounds: int) -> bytes:
    ct_words = bytes_to_words(cipher)
    plaintext = _internal_speck_decrypt32(ct_words, rk, rounds)
    return words_to_bytes(plaintext)


def speck_decrypt64(cipher: bytearray, rk: List[int], rounds: int) -> bytes:
    ct_words = bytes_to_words(cipher, 64)
    plaintext = _internal_speck_decrypt64(ct_words, rk, rounds)
    return words_to_bytes(plaintext, 64)

Functions

def make_dword(x)
Expand source code Browse git
def make_dword(x: int) -> int:
    return x & 0xFFFFFFFF
def make_qword(x)
Expand source code Browse git
def make_qword(x: int) -> int:
    return x & 0xFFFFFFFFFFFFFFFF
def words_to_bytes(words, word_size=32)
Expand source code Browse git
def words_to_bytes(words: List[int], word_size=32) -> bytes:
    numwords = len(words)
    if word_size == 32:
        bytes_out = struct.pack("<" + "I" * numwords, *words[::-1])
    elif word_size == 64:
        bytes_out = struct.pack("<" + "Q" * numwords, *words[::-1])
    return bytes_out
def bytes_to_words(bytes_in, word_size=32)
Expand source code Browse git
def bytes_to_words(bytes_in: bytes, word_size=32) -> List[int]:
    numbytes = len(bytes_in)
    if word_size == 32:
        numwords = numbytes // 4
        words = struct.unpack("<" + "I" * numwords, bytes_in)
    elif word_size == 64:
        numwords = numbytes // 8
        words = struct.unpack("<" + "Q" * numwords, bytes_in)
    return list(words[::-1])
def Speck6496KeySchedule(key)

Calculate the round key rk for SPECK 64/96

Expand source code Browse git
def Speck6496KeySchedule(key: bytearray) -> List[int]:
    """
    Calculate the round key rk for SPECK 64/96
    """
    k = bytes_to_words(key)
    rk = []
    C = k[0]
    B = k[1]
    A = k[2]
    for i in range(0, SPECK_ROUNDS["64_96"], 2):
        rk.append(A)
        B, A = speck_encrypt_round32(B, A, i)
        rk.append(A)
        C, A = speck_encrypt_round32(C, A, i + 1)
    return rk
def Speck64128KeySchedule(key)

Calculate the round key rk for SPECK 64/128

Expand source code Browse git
def Speck64128KeySchedule(key: bytearray) -> List[int]:
    """
    Calculate the round key rk for SPECK 64/128
    """
    k = bytes_to_words(key)
    rk = []
    D = k[0]
    C = k[1]
    B = k[2]
    A = k[3]
    for i in range(0, SPECK_ROUNDS["64_128"], 3):
        rk.append(A)
        B, A = speck_encrypt_round32(B, A, i)
        rk.append(A)
        C, A = speck_encrypt_round32(C, A, i + 1)
        rk.append(A)
        D, A = speck_encrypt_round32(D, A, i + 2)
    return rk
def Speck128128KeySchedule(key)

Calculate the round key rk for SPECK 128/128

Expand source code Browse git
def Speck128128KeySchedule(key: bytearray) -> List[int]:
    """
    Calculate the round key rk for SPECK 128/128
    """
    k = bytes_to_words(key, 64)
    rk = []
    B = k[0]
    A = k[1]
    for i in range(0, SPECK_ROUNDS["128_128"]):
        rk.append(A)
        B, A = speck_encrypt_round64(B, A, i)
    rk.append(A)
    return rk
def Speck128192KeySchedule(key)

Calculate the round key rk for SPECK 128/192

Expand source code Browse git
def Speck128192KeySchedule(key: bytearray) -> List[int]:
    """
    Calculate the round key rk for SPECK 128/192
    """
    k = bytes_to_words(key, 64)
    rk = []
    C = k[0]
    B = k[1]
    A = k[2]
    for i in range(0, SPECK_ROUNDS["128_192"] - 1, 2):
        rk.append(A)
        B, A = speck_encrypt_round64(B, A, i)
        rk.append(A)
        C, A = speck_encrypt_round64(C, A, i + 1)
    rk.append(A)
    return rk
def Speck128256KeySchedule(key)

Calculate the round key rk for SPECK 128/256

Expand source code Browse git
def Speck128256KeySchedule(key: bytearray) -> List[int]:
    """
    Calculate the round key rk for SPECK 128/256
    """
    k = bytes_to_words(key, 64)
    rk = []
    D = k[0]
    C = k[1]
    B = k[2]
    A = k[3]
    for i in range(0, SPECK_ROUNDS["128_256"] - 1, 3):
        rk.append(A)
        B, A = speck_encrypt_round64(B, A, i)
        rk.append(A)
        C, A = speck_encrypt_round64(C, A, i + 1)
        rk.append(A)
        D, A = speck_encrypt_round64(D, A, i + 2)
    rk.append(A)
    return rk
def speck_encrypt_round32(x, y, k)
Expand source code Browse git
def speck_encrypt_round32(x: int, y: int, k: int) -> Tuple[int]:
    x = make_dword(ROR32(x, 8) + y) ^ k
    y = ROL32(y, 3) ^ x
    return x, y
def speck_encrypt_round64(x, y, k)
Expand source code Browse git
def speck_encrypt_round64(x: int, y: int, k: int) -> Tuple[int]:
    x = make_qword(ROR64(x, 8) + y) ^ k
    y = ROL64(y, 3) ^ x
    return x, y
def speck_decrypt_round32(x, y, k)
Expand source code Browse git
def speck_decrypt_round32(x: int, y: int, k: int) -> Tuple[int]:
    y = ROR32(y ^ x, 3)
    x = ROL32(make_dword((x ^ k) - y), 8)
    return x, y
def speck_decrypt_round64(x, y, k)
Expand source code Browse git
def speck_decrypt_round64(x: int, y: int, k: int) -> Tuple[int]:
    y = ROR64(y ^ x, 3)
    x = ROL64(make_qword((x ^ k) - y), 8)
    return x, y
def speck_encrypt32(plaintext, rk, rounds)
Expand source code Browse git
def speck_encrypt32(plaintext: bytearray, rk: List[int], rounds: int) -> bytes:
    pt_words = bytes_to_words(plaintext)
    cipher = _internal_speck_encrypt32(pt_words, rk, rounds)
    return words_to_bytes(cipher)
def speck_encrypt64(plaintext, rk, rounds)
Expand source code Browse git
def speck_encrypt64(plaintext: bytearray, rk: List[int], rounds: int) -> bytes:
    pt_words = bytes_to_words(plaintext, 64)
    cipher = _internal_speck_encrypt64(pt_words, rk, rounds)
    return words_to_bytes(cipher, 64)
def speck_decrypt32(cipher, rk, rounds)
Expand source code Browse git
def speck_decrypt32(cipher: bytearray, rk: List[int], rounds: int) -> bytes:
    ct_words = bytes_to_words(cipher)
    plaintext = _internal_speck_decrypt32(ct_words, rk, rounds)
    return words_to_bytes(plaintext)
def speck_decrypt64(cipher, rk, rounds)
Expand source code Browse git
def speck_decrypt64(cipher: bytearray, rk: List[int], rounds: int) -> bytes:
    ct_words = bytes_to_words(cipher, 64)
    plaintext = _internal_speck_decrypt64(ct_words, rk, rounds)
    return words_to_bytes(plaintext, 64)