mirror of
https://github.com/status-im/research.git
synced 2025-02-10 14:06:56 +00:00
added more mining tests
This commit is contained in:
parent
5fa3ac65c4
commit
fbd45634cf
104
mining/arpow_miner.py
Normal file
104
mining/arpow_miner.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
from pyethereum import utils
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
def sha3(x):
|
||||||
|
return utils.decode_int(utils.zunpad(utils.sha3(x)))
|
||||||
|
|
||||||
|
|
||||||
|
class SeedObj():
|
||||||
|
def __init__(self, seed):
|
||||||
|
self.seed = seed
|
||||||
|
self.a = 3**160
|
||||||
|
self.c = 7**80
|
||||||
|
self.n = 2**256 - 4294968273 # secp256k1n, why not
|
||||||
|
|
||||||
|
def rand(self, r):
|
||||||
|
self.seed = (self.seed * self.a + self.c) % self.n
|
||||||
|
return self.seed % r
|
||||||
|
|
||||||
|
|
||||||
|
def encode_int(x):
|
||||||
|
o = ''
|
||||||
|
for _ in range(8):
|
||||||
|
o = chr(x % 256) + o
|
||||||
|
x //= 256
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
ops = {
|
||||||
|
"plus": lambda x, y: (x + y) % 2**64,
|
||||||
|
"times": lambda x, y: (x * y) % 2**64,
|
||||||
|
"xor": lambda x, y: x ^ y,
|
||||||
|
"and": lambda x, y: x & y,
|
||||||
|
"or": lambda x, y: x | y,
|
||||||
|
"not": lambda x, y: 2**64-1-x,
|
||||||
|
"nxor": lambda x, y: (2**64-1-x) ^ y,
|
||||||
|
"rshift": lambda x, y: x >> (y % 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def gentape(W, H, SEED):
|
||||||
|
s = SeedObj(SEED)
|
||||||
|
tape = []
|
||||||
|
for i in range(H):
|
||||||
|
op = ops.keys()[s.rand(len(ops))]
|
||||||
|
r = s.rand(100)
|
||||||
|
if r < 20 and i > 20:
|
||||||
|
x1 = tape[-r]["x1"]
|
||||||
|
else:
|
||||||
|
x1 = s.rand(W)
|
||||||
|
x2 = s.rand(W)
|
||||||
|
tape.append({"op": op, "x1": x1, "x2": x2})
|
||||||
|
return tape
|
||||||
|
|
||||||
|
|
||||||
|
def runtape(TAPE, SEED, params):
|
||||||
|
s = SeedObj(SEED)
|
||||||
|
# Fill up tape inputs
|
||||||
|
mem = [0] * params["w"]
|
||||||
|
for i in range(params["w"]):
|
||||||
|
mem[i] = s.rand(2**64)
|
||||||
|
# Direction variable (run forwards or backwards)
|
||||||
|
dir = 1
|
||||||
|
# Run the tape on the inputs
|
||||||
|
for i in range(params["h"] // 100):
|
||||||
|
for j in (range(100) if dir == 1 else range(99, -1, -1)):
|
||||||
|
t = TAPE[i * 100 + j]
|
||||||
|
mem[t["x1"]] = ops[t["op"]](mem[t["x1"]], mem[t["x2"]])
|
||||||
|
# 16% of the time, we flip the order of the next 100 ops;
|
||||||
|
# this adds in conditional branching
|
||||||
|
if 2 < mem[t["x1"]] % 37 < 9:
|
||||||
|
dir *= -1
|
||||||
|
return sha3(''.join(encode_int(x) for x in mem))
|
||||||
|
|
||||||
|
|
||||||
|
def PoWVerify(header, nonce, params):
|
||||||
|
tape = gentape(params["w"], params["h"],
|
||||||
|
sha3(header + encode_int(nonce // params["n"])))
|
||||||
|
h = runtape(tape, sha3(header + encode_int(nonce)), params)
|
||||||
|
print h
|
||||||
|
return h < 2**256 / params["diff"]
|
||||||
|
|
||||||
|
|
||||||
|
def PoWRun(header, params):
|
||||||
|
# actual randomness, so that miners don't overlap
|
||||||
|
nonce = random.randrange(2**50) * params["n"]
|
||||||
|
tape = None
|
||||||
|
while 1:
|
||||||
|
print nonce
|
||||||
|
if nonce % params["n"] == 0:
|
||||||
|
tape = gentape(params["w"], params["h"],
|
||||||
|
sha3(header + encode_int(nonce // params["n"])))
|
||||||
|
h = runtape(tape, sha3(header + encode_int(nonce)), params)
|
||||||
|
if h < 2**256 / params["diff"]:
|
||||||
|
return nonce
|
||||||
|
else:
|
||||||
|
nonce += 1
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"w": 100,
|
||||||
|
"h": 15000, # generally, w*log(w) at least
|
||||||
|
"diff": 2**24, # initial
|
||||||
|
"n": 1000
|
||||||
|
}
|
128
mining/hashimoto.py
Normal file
128
mining/hashimoto.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
try:
|
||||||
|
shathree = __import__('sha3')
|
||||||
|
except:
|
||||||
|
shathree = __import__('python_sha3')
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
def sha3(x):
|
||||||
|
return decode_int(shathree.sha3_256(x).digest()) #
|
||||||
|
|
||||||
|
|
||||||
|
def decode_int(s):
|
||||||
|
o = 0
|
||||||
|
for i in range(len(s)):
|
||||||
|
o = o * 256 + ord(s[i])
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
def encode_int(x):
|
||||||
|
o = ''
|
||||||
|
for _ in range(32):
|
||||||
|
o = chr(x % 256) + o
|
||||||
|
x //= 256
|
||||||
|
return o
|
||||||
|
|
||||||
|
P = 2**256 - 4294968273
|
||||||
|
|
||||||
|
|
||||||
|
def produce_dag(params, seed):
|
||||||
|
o = [sha3(seed)]
|
||||||
|
init = o[0]
|
||||||
|
picker = 1
|
||||||
|
for i in range(1, params["n"]):
|
||||||
|
x = 0
|
||||||
|
picker = (picker * init) % P
|
||||||
|
curpicker = picker
|
||||||
|
for j in range(params["k"]):
|
||||||
|
x |= o[curpicker % i]
|
||||||
|
curpicker >>= 10
|
||||||
|
o.append((x * x) % P) # use any "hash function" here
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
def quick_calc(params, seed, pos):
|
||||||
|
init = sha3(seed)
|
||||||
|
known = {0: init}
|
||||||
|
|
||||||
|
def calc(p):
|
||||||
|
if p not in known:
|
||||||
|
picker = pow(init, p, P)
|
||||||
|
x = 0
|
||||||
|
for j in range(params["k"]):
|
||||||
|
x |= calc(picker % p)
|
||||||
|
picker >>= 10
|
||||||
|
known[p] = (x * x) % P
|
||||||
|
return known[p]
|
||||||
|
|
||||||
|
o = calc(pos)
|
||||||
|
print 'Calculated pos %d with %d accesses' % (pos, len(known))
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
def hashimoto(daggerset, params, header, nonce):
|
||||||
|
rand = sha3(header+encode_int(nonce))
|
||||||
|
mix = 0
|
||||||
|
for i in range(40):
|
||||||
|
shifted_A = rand >> i
|
||||||
|
dag = daggerset[shifted_A % params["numdags"]]
|
||||||
|
mix ^= dag[(shifted_A // params["numdags"]) % params["n"]]
|
||||||
|
return mix ^ rand
|
||||||
|
|
||||||
|
|
||||||
|
def get_daggerset(params, block):
|
||||||
|
if block.number == 0:
|
||||||
|
return [produce_dag(params, i) for i in range(params["numdags"])]
|
||||||
|
elif block.number % params["epochtime"]:
|
||||||
|
return get_daggerset(block.parent)
|
||||||
|
else:
|
||||||
|
o = get_daggerset(block.parent)
|
||||||
|
o[sha3(block.parent.nonce) % params["numdags"]] = \
|
||||||
|
produce_dag(params, sha3(block.parent.nonce))
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
def mine(daggerset, params, header):
|
||||||
|
nonce = random.randrange(2**50)
|
||||||
|
orignonce = nonce
|
||||||
|
origtime = time.time()
|
||||||
|
while 1:
|
||||||
|
h = hashimoto(daggerset, params, header, nonce)
|
||||||
|
if h <= 2**256 / params["diff"]:
|
||||||
|
noncediff = nonce - orignonce
|
||||||
|
timediff = time.time() - origtime
|
||||||
|
print 'Found nonce: %d, tested %d nonces in %f seconds (%f per sec)' % \
|
||||||
|
(nonce, noncediff, timediff, noncediff / timediff)
|
||||||
|
return nonce
|
||||||
|
nonce += 1
|
||||||
|
|
||||||
|
|
||||||
|
def verify(daggerset, params, header, nonce):
|
||||||
|
return hashimoto(daggerset, params, header, nonce) \
|
||||||
|
<= 2**256 / params["diff"]
|
||||||
|
|
||||||
|
|
||||||
|
def light_hashimoto(seedset, params, header, nonce):
|
||||||
|
rand = sha3(header+encode_int(nonce))
|
||||||
|
mix = 0
|
||||||
|
for i in range(40):
|
||||||
|
shifted_A = rand >> i
|
||||||
|
seed = seedset[shifted_A % params["numdags"]]
|
||||||
|
# can further optimize with cross-round memoization
|
||||||
|
mix ^= quick_calc(params, seed,
|
||||||
|
(shifted_A // params["numdags"]) % params["n"])
|
||||||
|
return mix ^ rand
|
||||||
|
|
||||||
|
|
||||||
|
def light_verify(seedset, params, header, nonce):
|
||||||
|
return light_hashimoto(seedset, params, header, nonce) \
|
||||||
|
<= 2**256 / params["diff"]
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"numdags": 40,
|
||||||
|
"n": 250000,
|
||||||
|
"diff": 2**14,
|
||||||
|
"epochtime": 100,
|
||||||
|
"k": 3
|
||||||
|
}
|
411
mining/python_sha3.py
Normal file
411
mining/python_sha3.py
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
#! /usr/bin/env python
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
# The Keccak sponge function was designed by Guido Bertoni, Joan Daemen,
|
||||||
|
# Michaël Peeters and Gilles Van Assche. For more information, feedback or
|
||||||
|
# questions, please refer to their website: http://keccak.noekeon.org/
|
||||||
|
#
|
||||||
|
# Based on the implementation by Renaud Bauvin,
|
||||||
|
# from http://keccak.noekeon.org/KeccakInPython-3.0.zip
|
||||||
|
#
|
||||||
|
# Modified by Moshe Kaplan to be hashlib-compliant
|
||||||
|
#
|
||||||
|
# To the extent possible under law, the implementer has waived all copyright
|
||||||
|
# and related or neighboring rights to the source code in this file.
|
||||||
|
# http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
|
def sha3_224(data=None):
|
||||||
|
return Keccak(c=448, r=1152, n=224, data=data)
|
||||||
|
|
||||||
|
|
||||||
|
def sha3_256(data=None):
|
||||||
|
return Keccak(c=512, r=1088, n=256, data=data)
|
||||||
|
|
||||||
|
|
||||||
|
def sha3_384(data=None):
|
||||||
|
return Keccak(c=768, r=832, n=384, data=data)
|
||||||
|
|
||||||
|
|
||||||
|
def sha3_512(data=None):
|
||||||
|
return Keccak(c=1024, r=576, n=512, data=data)
|
||||||
|
|
||||||
|
|
||||||
|
class KeccakError(Exception):
|
||||||
|
"""Custom error Class used in the Keccak implementation"""
|
||||||
|
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return repr(self.value)
|
||||||
|
|
||||||
|
|
||||||
|
class Keccak:
|
||||||
|
def __init__(self, r, c, n, data=None):
|
||||||
|
# Initialize the constants used throughout Keccak
|
||||||
|
# bitrate
|
||||||
|
self.r = r
|
||||||
|
# capacity
|
||||||
|
self.c = c
|
||||||
|
# output size
|
||||||
|
self.n = n
|
||||||
|
|
||||||
|
self.b = r + c
|
||||||
|
# b = 25*w
|
||||||
|
self.w = self.b // 25
|
||||||
|
# 2**l = w
|
||||||
|
self.l = int(math.log(self.w, 2))
|
||||||
|
|
||||||
|
self.n_r = 12 + 2 * self.l
|
||||||
|
|
||||||
|
self.block_size = r
|
||||||
|
self.digest_size = n
|
||||||
|
|
||||||
|
# Initialize the state of the sponge
|
||||||
|
# The state is made up of 25 words, each word being w bits.
|
||||||
|
self.S = [[0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0]]
|
||||||
|
|
||||||
|
# A string of hexchars, where each char represents 4 bits.
|
||||||
|
self.buffered_data = ""
|
||||||
|
|
||||||
|
# Store the calculated digest.
|
||||||
|
# We'll only apply padding and recalculate the hash if it's modified.
|
||||||
|
self.last_digest = None
|
||||||
|
|
||||||
|
if data:
|
||||||
|
self.update(data)
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
|
||||||
|
## Round constants
|
||||||
|
RC = [0x0000000000000001,
|
||||||
|
0x0000000000008082,
|
||||||
|
0x800000000000808A,
|
||||||
|
0x8000000080008000,
|
||||||
|
0x000000000000808B,
|
||||||
|
0x0000000080000001,
|
||||||
|
0x8000000080008081,
|
||||||
|
0x8000000000008009,
|
||||||
|
0x000000000000008A,
|
||||||
|
0x0000000000000088,
|
||||||
|
0x0000000080008009,
|
||||||
|
0x000000008000000A,
|
||||||
|
0x000000008000808B,
|
||||||
|
0x800000000000008B,
|
||||||
|
0x8000000000008089,
|
||||||
|
0x8000000000008003,
|
||||||
|
0x8000000000008002,
|
||||||
|
0x8000000000000080,
|
||||||
|
0x000000000000800A,
|
||||||
|
0x800000008000000A,
|
||||||
|
0x8000000080008081,
|
||||||
|
0x8000000000008080,
|
||||||
|
0x0000000080000001,
|
||||||
|
0x8000000080008008]
|
||||||
|
|
||||||
|
## Rotation offsets
|
||||||
|
r = [[0, 36, 3, 41, 18],
|
||||||
|
[1, 44, 10, 45, 2],
|
||||||
|
[62, 6, 43, 15, 61],
|
||||||
|
[28, 55, 25, 21, 56],
|
||||||
|
[27, 20, 39, 8, 14]]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def Round(A, RCfixed, w):
|
||||||
|
"""Perform one round of computation as defined in the Keccak-f permutation
|
||||||
|
|
||||||
|
A: current state (5x5 matrix)
|
||||||
|
RCfixed: value of round constant to use (integer)
|
||||||
|
"""
|
||||||
|
|
||||||
|
#Initialization of temporary variables
|
||||||
|
B = [[0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0]]
|
||||||
|
C = [0, 0, 0, 0, 0]
|
||||||
|
D = [0, 0, 0, 0, 0]
|
||||||
|
|
||||||
|
#Theta step
|
||||||
|
for x in range(5):
|
||||||
|
C[x] = A[x][0] ^ A[x][1] ^ A[x][2] ^ A[x][3] ^ A[x][4]
|
||||||
|
|
||||||
|
for x in range(5):
|
||||||
|
D[x] = C[(x - 1) % 5] ^ _rot(C[(x + 1) % 5], 1, w)
|
||||||
|
|
||||||
|
for x in range(5):
|
||||||
|
for y in range(5):
|
||||||
|
A[x][y] = A[x][y] ^ D[x]
|
||||||
|
|
||||||
|
#Rho and Pi steps
|
||||||
|
for x in range(5):
|
||||||
|
for y in range(5):
|
||||||
|
B[y][(2 * x + 3 * y) % 5] = _rot(A[x][y], Keccak.r[x][y], w)
|
||||||
|
|
||||||
|
#Chi step
|
||||||
|
for x in range(5):
|
||||||
|
for y in range(5):
|
||||||
|
A[x][y] = B[x][y] ^ ((~B[(x + 1) % 5][y]) & B[(x + 2) % 5][y])
|
||||||
|
|
||||||
|
#Iota step
|
||||||
|
A[0][0] = A[0][0] ^ RCfixed
|
||||||
|
|
||||||
|
return A
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def KeccakF(A, n_r, w):
|
||||||
|
"""Perform Keccak-f function on the state A
|
||||||
|
|
||||||
|
A: 5x5 matrix containing the state, where each entry is a string of hexchars that is 'w' bits long
|
||||||
|
n_r: number of rounds
|
||||||
|
w: word size
|
||||||
|
"""
|
||||||
|
|
||||||
|
for i in xrange(n_r):
|
||||||
|
A = Keccak.Round(A, Keccak.RC[i] % (1 << w), w)
|
||||||
|
|
||||||
|
return A
|
||||||
|
|
||||||
|
### Padding rule
|
||||||
|
# This is a disgusting piece of code. Clean it.
|
||||||
|
@staticmethod
|
||||||
|
def pad10star1(M, n):
|
||||||
|
"""Pad M with the pad10*1 padding rule to reach a length multiple of r bits
|
||||||
|
|
||||||
|
M: message pair (length in bits, string of hex characters ('9AFC...')
|
||||||
|
n: length in bits (must be a multiple of 8)
|
||||||
|
Example: pad10star1([60, 'BA594E0FB9EBBD30'],8) returns 'BA594E0FB9EBBD93'
|
||||||
|
"""
|
||||||
|
|
||||||
|
[my_string_length, my_string] = M
|
||||||
|
|
||||||
|
# Check the parameter n
|
||||||
|
if n % 8 != 0:
|
||||||
|
raise KeccakError.KeccakError("n must be a multiple of 8")
|
||||||
|
|
||||||
|
# Check the length of the provided string
|
||||||
|
if len(my_string) % 2 != 0:
|
||||||
|
#Pad with one '0' to reach correct length (don't know test
|
||||||
|
#vectors coding)
|
||||||
|
my_string += '0'
|
||||||
|
if my_string_length > (len(my_string) // 2 * 8):
|
||||||
|
raise KeccakError.KeccakError("the string is too short to contain the number of bits announced")
|
||||||
|
|
||||||
|
nr_bytes_filled = my_string_length // 8
|
||||||
|
nbr_bits_filled = my_string_length % 8
|
||||||
|
l = my_string_length % n
|
||||||
|
if ((n - 8) <= l <= (n - 2)):
|
||||||
|
if (nbr_bits_filled == 0):
|
||||||
|
my_byte = 0
|
||||||
|
else:
|
||||||
|
my_byte = int(my_string[nr_bytes_filled * 2:nr_bytes_filled * 2 + 2], 16)
|
||||||
|
my_byte = (my_byte >> (8 - nbr_bits_filled))
|
||||||
|
my_byte = my_byte + 2 ** (nbr_bits_filled) + 2 ** 7
|
||||||
|
my_byte = "%02X" % my_byte
|
||||||
|
my_string = my_string[0:nr_bytes_filled * 2] + my_byte
|
||||||
|
else:
|
||||||
|
if (nbr_bits_filled == 0):
|
||||||
|
my_byte = 0
|
||||||
|
else:
|
||||||
|
my_byte = int(my_string[nr_bytes_filled * 2:nr_bytes_filled * 2 + 2], 16)
|
||||||
|
my_byte = (my_byte >> (8 - nbr_bits_filled))
|
||||||
|
my_byte = my_byte + 2 ** (nbr_bits_filled)
|
||||||
|
my_byte = "%02X" % my_byte
|
||||||
|
my_string = my_string[0:nr_bytes_filled * 2] + my_byte
|
||||||
|
while((8 * len(my_string) // 2) % n < (n - 8)):
|
||||||
|
my_string = my_string + '00'
|
||||||
|
my_string = my_string + '80'
|
||||||
|
|
||||||
|
return my_string
|
||||||
|
|
||||||
|
def update(self, arg):
|
||||||
|
# Update the hash object with the string arg. Repeated calls are equivalent to a single call with the concatenation of all the arguments: m.update(a); m.update(b) is equivalent to m.update(a+b). arg is a normal bytestring.
|
||||||
|
|
||||||
|
self.last_digest = None
|
||||||
|
# Convert the data into a workable format, and add it to the buffer
|
||||||
|
self.buffered_data += arg.encode('hex')
|
||||||
|
|
||||||
|
# Absorb any blocks we can:
|
||||||
|
if len(self.buffered_data) * 4 >= self.r:
|
||||||
|
extra_bits = len(self.buffered_data) * 4 % self.r
|
||||||
|
|
||||||
|
# An exact fit!
|
||||||
|
if extra_bits == 0:
|
||||||
|
P = self.buffered_data
|
||||||
|
self.buffered_data = ""
|
||||||
|
else:
|
||||||
|
# Slice it up into the first r*a bits, for some constant a>=1, and the remaining total-r*a bits.
|
||||||
|
P = self.buffered_data[:-extra_bits // 4]
|
||||||
|
self.buffered_data = self.buffered_data[-extra_bits // 4:]
|
||||||
|
|
||||||
|
#Absorbing phase
|
||||||
|
for i in xrange((len(P) * 8 // 2) // self.r):
|
||||||
|
to_convert = P[i * (2 * self.r // 8):(i + 1) * (2 * self.r // 8)] + '00' * (self.c // 8)
|
||||||
|
P_i = _convertStrToTable(to_convert, self.w, self.b)
|
||||||
|
|
||||||
|
# First apply the XOR to the state + block
|
||||||
|
for y in xrange(5):
|
||||||
|
for x in xrange(5):
|
||||||
|
self.S[x][y] = self.S[x][y] ^ P_i[x][y]
|
||||||
|
# Then apply the block permutation, Keccak-F
|
||||||
|
self.S = Keccak.KeccakF(self.S, self.n_r, self.w)
|
||||||
|
|
||||||
|
def digest(self):
|
||||||
|
"""Return the digest of the strings passed to the update() method so far.
|
||||||
|
|
||||||
|
This is a string of digest_size bytes which may contain non-ASCII
|
||||||
|
characters, including null bytes."""
|
||||||
|
|
||||||
|
if self.last_digest:
|
||||||
|
return self.last_digest
|
||||||
|
|
||||||
|
# UGLY WARNING
|
||||||
|
# Handle bytestring/hexstring conversions
|
||||||
|
M = _build_message_pair(self.buffered_data.decode('hex'))
|
||||||
|
|
||||||
|
# First finish the padding and force the final update:
|
||||||
|
self.buffered_data = Keccak.pad10star1(M, self.r)
|
||||||
|
self.update('')
|
||||||
|
# UGLY WARNING over
|
||||||
|
|
||||||
|
assert len(self.buffered_data) == 0, "Why is there data left in the buffer? %s with length %d" % (self.buffered_data, len(self.buffered_data) * 4)
|
||||||
|
|
||||||
|
# Squeezing time!
|
||||||
|
Z = ''
|
||||||
|
outputLength = self.n
|
||||||
|
while outputLength > 0:
|
||||||
|
string = _convertTableToStr(self.S, self.w)
|
||||||
|
# Read the first 'r' bits of the state
|
||||||
|
Z = Z + string[:self.r * 2 // 8]
|
||||||
|
outputLength -= self.r
|
||||||
|
if outputLength > 0:
|
||||||
|
S = KeccakF(S, verbose)
|
||||||
|
|
||||||
|
self.last_digest = Z[:2 * self.n // 8].decode('hex')
|
||||||
|
return self.last_digest
|
||||||
|
|
||||||
|
def hexdigest(self):
|
||||||
|
"""Like digest() except the digest is returned as a string of hex digits
|
||||||
|
|
||||||
|
This may be used to exchange the value safely in email or other
|
||||||
|
non-binary environments."""
|
||||||
|
return self.digest().encode('hex')
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
# First initialize whatever can be done normally
|
||||||
|
duplicate = Keccak(c=self.c, r=self.r, n=self.n)
|
||||||
|
# Then copy over the state.
|
||||||
|
for i in xrange(5):
|
||||||
|
for j in xrange(5):
|
||||||
|
duplicate.S[i][j] = self.S[i][j]
|
||||||
|
# and any other stored data
|
||||||
|
duplicate.buffered_data = self.buffered_data
|
||||||
|
duplicate.last_digest = self.last_digest
|
||||||
|
return duplicate
|
||||||
|
|
||||||
|
|
||||||
|
## Generic utility functions
|
||||||
|
|
||||||
|
def _build_message_pair(data):
|
||||||
|
hex_data = data.encode('hex')
|
||||||
|
size = len(hex_data) * 4
|
||||||
|
return (size, hex_data)
|
||||||
|
|
||||||
|
|
||||||
|
def _rot(x, shift_amount, length):
|
||||||
|
"""Rotate x shift_amount bits to the left, considering the \
|
||||||
|
string of bits is length bits long"""
|
||||||
|
|
||||||
|
shift_amount = shift_amount % length
|
||||||
|
return ((x >> (length - shift_amount)) + (x << shift_amount)) % (1 << length)
|
||||||
|
|
||||||
|
### Conversion functions String <-> Table (and vice-versa)
|
||||||
|
|
||||||
|
|
||||||
|
def _fromHexStringToLane(string):
|
||||||
|
"""Convert a string of bytes written in hexadecimal to a lane value"""
|
||||||
|
|
||||||
|
#Check that the string has an even number of characters i.e. whole number of bytes
|
||||||
|
if len(string) % 2 != 0:
|
||||||
|
raise KeccakError.KeccakError("The provided string does not end with a full byte")
|
||||||
|
|
||||||
|
#Perform the conversion
|
||||||
|
temp = ''
|
||||||
|
nrBytes = len(string) // 2
|
||||||
|
for i in xrange(nrBytes):
|
||||||
|
offset = (nrBytes - i - 1) * 2
|
||||||
|
temp += string[offset:offset + 2]
|
||||||
|
return int(temp, 16)
|
||||||
|
|
||||||
|
|
||||||
|
def _fromLaneToHexString(lane, w):
|
||||||
|
"""Convert a lane value to a string of bytes written in hexadecimal"""
|
||||||
|
|
||||||
|
laneHexBE = (("%%0%dX" % (w // 4)) % lane)
|
||||||
|
#Perform the conversion
|
||||||
|
temp = ''
|
||||||
|
nrBytes = len(laneHexBE) // 2
|
||||||
|
for i in xrange(nrBytes):
|
||||||
|
offset = (nrBytes - i - 1) * 2
|
||||||
|
temp += laneHexBE[offset:offset + 2]
|
||||||
|
return temp.upper()
|
||||||
|
|
||||||
|
|
||||||
|
def _convertStrToTable(string, w, b):
|
||||||
|
"""Convert a string of hex-chars to its 5x5 matrix representation
|
||||||
|
|
||||||
|
string: string of bytes of hex-coded bytes (e.g. '9A2C...')"""
|
||||||
|
|
||||||
|
# Check that the input paramaters are expected
|
||||||
|
if w % 8 != 0:
|
||||||
|
raise KeccakError("w is not a multiple of 8")
|
||||||
|
|
||||||
|
# Each character in the string represents 4 bits.
|
||||||
|
# The string should have exactly 'b' bits.
|
||||||
|
if len(string) * 4 != b:
|
||||||
|
raise KeccakError.KeccakError("string can't be divided in 25 blocks of w bits\
|
||||||
|
i.e. string must have exactly b bits")
|
||||||
|
|
||||||
|
#Convert
|
||||||
|
output = [[0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0]]
|
||||||
|
|
||||||
|
bits_per_char = 2 * w // 8
|
||||||
|
for x in xrange(5):
|
||||||
|
for y in xrange(5):
|
||||||
|
# Each entry will have b/25=w bits.
|
||||||
|
offset = (5 * y + x) * bits_per_char
|
||||||
|
# Store the data into the associated word.
|
||||||
|
hexstring = string[offset:offset + bits_per_char]
|
||||||
|
output[x][y] = _fromHexStringToLane(hexstring)
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def _convertTableToStr(table, w):
|
||||||
|
"""Convert a 5x5 matrix representation to its string representation"""
|
||||||
|
|
||||||
|
#Check input format
|
||||||
|
if w % 8 != 0:
|
||||||
|
raise KeccakError.KeccakError("w is not a multiple of 8")
|
||||||
|
if (len(table) != 5) or any(len(row) != 5 for row in table):
|
||||||
|
raise KeccakError.KeccakError("table must be 5x5")
|
||||||
|
|
||||||
|
#Convert
|
||||||
|
output = [''] * 25
|
||||||
|
for x in xrange(5):
|
||||||
|
for y in xrange(5):
|
||||||
|
output[5 * y + x] = _fromLaneToHexString(table[x][y], w)
|
||||||
|
output = ''.join(output).upper()
|
||||||
|
return output
|
Loading…
x
Reference in New Issue
Block a user