forked from Adam-Vandervorst/PyBHV
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvanilla.py
More file actions
118 lines (87 loc) · 4.25 KB
/
vanilla.py
File metadata and controls
118 lines (87 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
from .abstract import *
import random
from sys import version_info
class VanillaPermutation(MemoizedPermutation):
_permutations: 'dict[int | tuple[int, ...], Self]' = {}
def __init__(self, indices):
self.data = indices
@classmethod
def random(cls) -> 'VanillaPermutation':
p = list(range(DIMENSION//8))
random.shuffle(p)
return VanillaPermutation(p)
def __mul__(self, other: 'VanillaPermutation') -> 'VanillaPermutation':
return VanillaPermutation([self.data[other.data[i]] for i in range(DIMENSION//8)])
def __invert__(self) -> 'VanillaPermutation':
p = [None]*(DIMENSION//8)
for i, j in enumerate(self.data):
p[j] = i
return VanillaPermutation(p)
def __call__(self, hv: 'VanillaBHV') -> 'VanillaBHV':
return hv.permute_bytes(self)
VanillaPermutation.IDENTITY = VanillaPermutation(list(range(DIMENSION//8)))
class VanillaBHV(AbstractBHV):
def __init__(self, data: bytes):
self.data: bytes = data
@classmethod
def rand(cls) -> 'VanillaBHV':
return VanillaBHV(random.randbytes(DIMENSION//8))
@classmethod
def random(cls, active: float) -> 'VanillaBHV':
assert 0. <= active <= 1.
return VanillaBHV(bytes([
(random.random() < active) | (random.random() < active) << 1 | (random.random() < active) << 2 | (random.random() < active) << 3 |
(random.random() < active) << 4 | (random.random() < active) << 5 | (random.random() < active) << 6 | (random.random() < active) << 7
for _ in range(DIMENSION//8)]))
@classmethod
def level(cls, active_fraction: float) -> 'VanillaBHV':
return VanillaBHV.from_int(cls.ONE.to_int() >> (DIMENSION - int(active_fraction*DIMENSION)))
def roll_bytes(self, n: int) -> 'VanillaBHV':
assert abs(n) < DIMENSION//8, "only supports DIMENSION/8 rolls"
return VanillaBHV(self.data[n:] + self.data[:n])
def roll_bits(self, n: int) -> 'VanillaBHV':
assert abs(n) < DIMENSION, "only supports DIMENSION rolls"
n = (DIMENSION + n) % DIMENSION
return self.from_int(((self.to_int() << (DIMENSION - n)) & self.ONE.to_int()) | (self.to_int() >> n))
def swap_even_odd(self) -> 'VanillaBHV':
return self.EVEN.select(self.roll_bits(1), self.roll_bits(-1))
def permute_bytes(self, permutation: 'VanillaPermutation') -> 'VanillaBHV':
return VanillaBHV(bytes([self.data[i] for i in permutation.data]))
def permute(self, permutation_id: 'int | tuple[int, ...]') -> 'VanillaBHV':
return self.permute_bytes(VanillaPermutation.get(permutation_id))
def rehash(self) -> 'VanillaBHV':
return VanillaBHV(hashlib.shake_256(self.data).digest(DIMENSION//8))
def __eq__(self, other: 'VanillaBHV') -> bool:
return self.data == other.data
def __xor__(self, other: 'VanillaBHV') -> 'VanillaBHV':
return self.from_int(self.to_int() ^ other.to_int())
def __and__(self, other: 'VanillaBHV') -> 'VanillaBHV':
return self.from_int(self.to_int() & other.to_int())
def __or__(self, other: 'VanillaBHV') -> 'VanillaBHV':
return self.from_int(self.to_int() | other.to_int())
if version_info[1] >= 10:
def active(self) -> int:
return self.to_int().bit_count()
else:
def active(self) -> int:
return bin(self.to_int()).count("1")
def to_int(self) -> int:
return int.from_bytes(self.data, "little", signed=False)
@classmethod
def from_int(cls, i: int):
return VanillaBHV(i.to_bytes(DIMENSION//8, "little", signed=False))
def to_bytes(self):
return self.data
@classmethod
def from_bytes(cls, bs):
return cls(bs)
def bitstring(self) -> str:
return bin(self.to_int())[2:].rjust(DIMENSION, "0")[::-1]
@classmethod
def from_bitstring(cls, s: str) -> Self:
return cls.from_int(int(s[::-1], 2))
VanillaBHV.ZERO = VanillaBHV(bytes([0 for _ in range(DIMENSION//8)]))
VanillaBHV.ONE = VanillaBHV(bytes([0xff for _ in range(DIMENSION//8)]))
VanillaBHV._FEISTAL_SUBKEYS = VanillaBHV.nrand2(VanillaBHV._FEISTAL_ROUNDS, 4)
VanillaBHV.EVEN = VanillaBHV(bytes([0x55 for _ in range(DIMENSION//8)]))
VanillaBHV.ODD = VanillaBHV(bytes([0xaa for _ in range(DIMENSION//8)]))