Module refinery.lib.crypto.maru

Maru hash implementation; it matches the C implementation found in Donut.

Expand source code Browse git
"""
Maru hash implementation; it matches the C implementation found in Donut.
"""
from __future__ import annotations

import struct

from refinery.lib.crypto.speck import (
    speck_encrypt32,
    speck_key_schedule_064_128,
)

MARU_MAX_STR = 64
MARU_BLK_LEN = 16
MARU_HASH_LEN = 8


def swap_dwords(value: int) -> int:
    return (value & 0xFFFFFFFF) << 32 | (value & 0xFFFFFFFF00000000) >> 32


def speck_operation(v: bytes, m: bytes) -> int:
    rk = speck_key_schedule_064_128(m)
    h_bytes = speck_encrypt32(v, rk, 27)
    h_int = int.from_bytes(h_bytes, "little")
    return swap_dwords(h_int)


def maru32(value: bytes, seed: int) -> int:
    m = bytearray(MARU_BLK_LEN)
    h = seed
    input_length = len(value)
    idx = 0
    length = 0
    end = False
    while not end:
        if length == input_length or length == MARU_MAX_STR:
            m[idx:] = bytes(MARU_BLK_LEN - idx)
            m[idx] = 0x80
            if idx >= MARU_BLK_LEN - 4:
                h_swapped = swap_dwords(h)
                h_bytes = h_swapped.to_bytes(8, "little")
                h ^= speck_operation(h_bytes, bytes(m))
                m[:] = bytes(MARU_BLK_LEN)
            struct.pack_into('<I', m, MARU_BLK_LEN - 4, length * 8)
            idx = MARU_BLK_LEN
            end = True
        else:
            m[idx] = value[length]
            length += 1
            idx += 1
        if idx == MARU_BLK_LEN:
            h_swapped = swap_dwords(h)
            h_bytes = h_swapped.to_bytes(8, "little")
            h ^= speck_operation(h_bytes, bytes(m))
            idx = 0
    return h


def maru32digest(value: bytes, seed: int) -> bytes:
    return maru32(value, seed).to_bytes(8, 'big')

Functions

def swap_dwords(value)
Expand source code Browse git
def swap_dwords(value: int) -> int:
    return (value & 0xFFFFFFFF) << 32 | (value & 0xFFFFFFFF00000000) >> 32
def speck_operation(v, m)
Expand source code Browse git
def speck_operation(v: bytes, m: bytes) -> int:
    rk = speck_key_schedule_064_128(m)
    h_bytes = speck_encrypt32(v, rk, 27)
    h_int = int.from_bytes(h_bytes, "little")
    return swap_dwords(h_int)
def maru32(value, seed)
Expand source code Browse git
def maru32(value: bytes, seed: int) -> int:
    m = bytearray(MARU_BLK_LEN)
    h = seed
    input_length = len(value)
    idx = 0
    length = 0
    end = False
    while not end:
        if length == input_length or length == MARU_MAX_STR:
            m[idx:] = bytes(MARU_BLK_LEN - idx)
            m[idx] = 0x80
            if idx >= MARU_BLK_LEN - 4:
                h_swapped = swap_dwords(h)
                h_bytes = h_swapped.to_bytes(8, "little")
                h ^= speck_operation(h_bytes, bytes(m))
                m[:] = bytes(MARU_BLK_LEN)
            struct.pack_into('<I', m, MARU_BLK_LEN - 4, length * 8)
            idx = MARU_BLK_LEN
            end = True
        else:
            m[idx] = value[length]
            length += 1
            idx += 1
        if idx == MARU_BLK_LEN:
            h_swapped = swap_dwords(h)
            h_bytes = h_swapped.to_bytes(8, "little")
            h ^= speck_operation(h_bytes, bytes(m))
            idx = 0
    return h
def maru32digest(value, seed)
Expand source code Browse git
def maru32digest(value: bytes, seed: int) -> bytes:
    return maru32(value, seed).to_bytes(8, 'big')