def keygen(self, seed, masks): assert len(seed) == self.n // 8 assert len(masks) >= 2 * self.tau sk = self.Gt(seed) sk = chunkbytes(sk, self.n // 8) L = list(map(self.F, sk)) H = lambda x, y, i: self.H(xor(x, masks[2*i]), xor(y, masks[2*i+1])) return root(hash_tree(H, L))
def keygen_pub(self, SK1, Q): addresses = [self.address(self.d - 1, 0, i) for i in range(1 << (self.h//self.d))] leafs = [self.wots_leaf(A, SK1, Q) for A in addresses] Qtree = Q[2 * ceil(log(self.wots.l, 2)):] H = lambda x, y, i: self.H(xor(x, Qtree[2*i]), xor(y, Qtree[2*i+1])) PK1 = root(hash_tree(H, leafs)) return PK1
def wots_leaf(self, address, SK1, masks): seed = self.Fa(address, SK1) pk_A = self.wots.keygen(seed, masks) def H(x, y, i): return self.H(xor(x, masks[2 * i]), xor(y, masks[2 * i + 1])) return root(l_tree(H, pk_A))
def keygen(self, seed, masks): assert len(seed) == self.n // 8 assert len(masks) >= 2 * self.tau sk = self.Gt(seed) sk = chunkbytes(sk, self.n // 8) L = list(map(self.F, sk)) H = lambda x, y, i: self.H(xor(x, masks[2 * i]), xor(y, masks[2 * i + 1])) return root(hash_tree(H, L))
def wots_path(self, a, SK1, Q, subh): ta = dict(a) leafs = [] for subleaf in range(1 << subh): ta['leaf'] = subleaf leafs.append(self.wots_leaf(self.address(**ta), SK1, Q)) Qtree = Q[2 * ceil(log(self.wots.l, 2)):] H = lambda x, y, i: self.H(xor(x, Qtree[2*i]), xor(y, Qtree[2*i+1])) tree = list(hash_tree(H, leafs)) return auth_path(tree, a['leaf']), root(tree)
def sign(self, m, seed, masks): assert len(m) == self.m // 8 assert len(seed) == self.n // 8 assert len(masks) >= 2 * self.tau sk = self.Gt(seed) sk = chunkbytes(sk, self.n // 8) L = list(map(self.F, sk)) H = lambda x, y, i: self.H(xor(x, masks[2*i]), xor(y, masks[2*i+1])) tree = hash_tree(H, L) trunk = list(itertools.islice(tree, 0, self.tau - self.x)) sigma_k = next(tree) M = self.message_indices(m) pk = root(tree) # the SPHINCS paper suggests to put sigma_k at the end of sigma # but the reference code places it at the front return ([(sk[Mi], auth_path(trunk, Mi)) for Mi in M] + [sigma_k], pk)
def sign(self, m, seed, masks): assert len(m) == self.m // 8 assert len(seed) == self.n // 8 assert len(masks) >= 2 * self.tau sk = self.Gt(seed) sk = chunkbytes(sk, self.n // 8) L = list(map(self.F, sk)) H = lambda x, y, i: self.H(xor(x, masks[2 * i]), xor(y, masks[2 * i + 1])) tree = hash_tree(H, L) trunk = list(itertools.islice(tree, 0, self.tau - self.x)) sigma_k = next(tree) M = self.message_indices(m) pk = root(tree) # the SPHINCS paper suggests to put sigma_k at the end of sigma # but the reference code places it at the front return ([(sk[Mi], auth_path(trunk, Mi)) for Mi in M] + [sigma_k], pk)
def verify(self, m, sig, masks): assert len(m) == self.m // 8 assert len(masks) >= 2 * self.tau M = self.message_indices(m) H = lambda x, y, i: self.H(xor(x, masks[2*i]), xor(y, masks[2*i+1])) sigma_k = sig[-1] for (sk, path), Mi in zip(sig, M): leaf = self.F(sk) r = construct_root(H, path, leaf, Mi) # there is an error in the SPHINCS paper for this formula, as it # states that y_i = floor(M_i / 2^tau - x) # rather than y_i = floor(M_i / 2^{tau - x}) yi = Mi // (1 << (self.tau - self.x)) if r != sigma_k[yi]: return False Qtop = masks[2*(self.tau - self.x):] H = lambda x, y, i: self.H(xor(x, Qtop[2*i]), xor(y, Qtop[2*i+1])) return root(hash_tree(H, sigma_k))
def verify(self, M, sig, PK): i, R1, sig_horst, *sig = sig PK1, Q = PK Qtree = Q[2 * ceil(log(self.wots.l, 2)):] D = self.Hdigest(R1, M) pk = pk_horst = self.horst.verify(D, sig_horst, Q) if pk_horst is False: return False subh = self.h // self.d H = lambda x, y, i: self.H(xor(x, Q[2*i]), xor(y, Q[2*i+1])) Ht = lambda x, y, i: self.H(xor(x, Qtree[2*i]), xor(y, Qtree[2*i+1])) for _ in range(self.d): wots_sig, wots_path, *sig = sig pk_wots = self.wots.verify(pk, wots_sig, Q) leaf = root(l_tree(H, pk_wots)) pk = construct_root(Ht, wots_path, leaf, i & 0x1f) i >>= subh return PK1 == pk
def verify(self, m, sig, masks): assert len(m) == self.m // 8 assert len(masks) >= 2 * self.tau M = self.message_indices(m) H = lambda x, y, i: self.H(xor(x, masks[2 * i]), xor(y, masks[2 * i + 1])) sigma_k = sig[-1] for (sk, path), Mi in zip(sig, M): leaf = self.F(sk) r = construct_root(H, path, leaf, Mi) # there is an error in the SPHINCS paper for this formula, as it # states that y_i = floor(M_i / 2^tau - x) # rather than y_i = floor(M_i / 2^{tau - x}) yi = Mi // (1 << (self.tau - self.x)) if r != sigma_k[yi]: return False Qtop = masks[2 * (self.tau - self.x):] H = lambda x, y, i: self.H(xor(x, Qtop[2 * i]), xor( y, Qtop[2 * i + 1])) return root(hash_tree(H, sigma_k))
def wots_leaf(self, address, SK1, masks): seed = self.Fa(address, SK1) pk_A = self.wots.keygen(seed, masks) H = lambda x, y, i: self.H(xor(x, masks[2*i]), xor(y, masks[2*i+1])) return root(l_tree(H, pk_A))