Skip to content

Commit

Permalink
Merge pull request #12 from sea212/getter_and_default_functions
Browse files Browse the repository at this point in the history
[WIP] Implement __functions__ and getter
  • Loading branch information
sea212 committed Feb 28, 2019
2 parents 64f7b4b + e64379a commit c6b68cf
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 59 deletions.
31 changes: 27 additions & 4 deletions tests/test_wots.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,35 @@

from winternitz.signatures import WOTS, WOTSPLUS

# rom unittest import assertTrue, assertFalse

__author__ = "Harald Heckmann"
__copyright__ = "Harald Heckmann"
__license__ = "mit"


def test_init():
# write some serious tests
_ = WOTS(16)
_ = WOTSPLUS(16)
def test_underscore_functions_and_getter():
# TODO: write some serious tests
# Init
wots = WOTS(4)
wots2 = WOTS(6)
wotsp = WOTSPLUS(4)
wotsp2 = WOTSPLUS(6)

# Object representation
_ = wots.__repr__()
_ = wots.__str__()
_ = wotsp.__repr__()
_ = wotsp2.__str__() # noqa: F841

# Equality checks
assert wots == wots
assert wotsp == wotsp
assert not (wots == wots2)
assert not (wotsp == wotsp2)

# Not equality checks
assert wots != wots2
assert wotsp != wotsp2
assert not (wots != wots)
assert not (wotsp != wotsp)
232 changes: 177 additions & 55 deletions winternitz/signatures.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,153 @@
from abc import ABCMeta, abstractmethod
from hashlib import sha256
from typing import List, Optional
from math import ceil, floor, log2
from os import urandom
from typing import Any, List, Optional

__author__ = "Harald Heckmann"
__copyright__ = "Harald Heckmann"
__license__ = "mit"

# Paper describing WOTS: https://eprint.iacr.org/2011/191.pdf
class WOTS(object):
def __init__(w: int, hashalgo: List[int] = sha256, digestsize=256,
privkey: Optional[List[bytes]] = None,
pubkey: Optional[List[bytes]] = None):
# TODO

# Cases: privkey = None and pub is set. set self.privkey to None
# privkey set pubkey set just derive new pubkey
# privkey not set pubkey not set just generate priv and derive pub
pass
# Abstract definition of OTS class
class AbstractOTS(object, metaclass=ABCMeta):
@abstractmethod
def sign(message: bytes) -> dict:
raise NotImplementedError("sign is essential for WOTS signatures")

def __repr__():
# TODO
pass
@abstractmethod
def verify(message: bytes, signature: List[bytes]) -> bool:
raise NotImplementedError("verify is essential for WOTS signatures")

def __str__():
# TODO
pass
@abstractmethod
def __eq__(self, obj: Any) -> bool:
raise NotImplementedError("Equality checks are required")

def __eq__():
# TODO
pass
@abstractmethod
def __ne__(self, obj: Any) -> bool:
raise NotImplementedError("Non-equality checks are required")


# Paper describing WOTS: https://eprint.iacr.org/2011/191.pdf
# "On the Security of the Winternitz One-Time Signature Scheme"
# Note: This Class calculates with the logarithm to the base 2 of the
# Winternitz parameter. This is dues to the fact, that machine store numbers
# in binary representation, numbers like w = 5 therefore cannot be realized
# and result in w = 2**ceil(log2(w)), which would be w = 8.
# Other than that it is easier to caluclate.
class WOTS(AbstractOTS):
def __init__(self,
w_log2: int,
hash_class: Any = sha256, # TODO: correct Type
digestsize: int = 256,
privkey: Optional[List[bytes]] = None,
pubkey: Optional[List[bytes]] = None) -> None:

if w_log2 > digestsize:
raise ValueError("The winternitz parameter must be lower than\
the digestsize of the hashalgorithm")

self.__w_log2 = w_log2

# Calculate number of message keys, checksum keys and total keys
self.__msg_key_count = ceil(digestsize / w_log2)
self.__cs_key_count = ceil((floor(log2(self.__msg_key_count)) +
1 + w_log2) / w_log2)
self.__key_count = self.__msg_key_count + self.__cs_key_count

# Hashing algorithm
self.__hash_class = hash_class
self.__digest_size = digestsize

# Keys
self.__privkey = []
self.__pubkey = []
hash_bytes = int(ceil(digestsize / 8))

# generate random privkey, derive pubkey
if privkey is None and pubkey is None:
privkey = [pkey for pkey in urandom(hash_bytes)]
self._derivePubKey()
# derive pubkey
elif privkey is not None:
if len(privkey) != self.__key_count:
raise ValueError("Provided private key length does not match to\
provided winternitz parameter")

self.__privkey = privkey.copy()
self._derivePubkey()
# set pubkey
else:
if len(pubkey) != self.__cs_key_count:
raise ValueError("Provided private key length does not match to\
provided winternitz parameter")

for elem in filter(lambda t: len(t) != hash_bytes, pubkey):
raise ValueError("Length of public key hashes does not match\
with the provided digestsize")

self.__pubkey = pubkey.copy()

def __repr__(self) -> str:
repr = "winternitz.signatures.WOTS(w_log2={}, hash_class={}, " + \
"digestsize={}, "
repr = repr.format(self.w, str(self.hashclass.__module__) +
"." + str(self.hashclass.__qualname__),
self.digestsize)

if self.privkey is None:
return repr + "pubkey=" + str(self.pubkey) + ")"
else:
return repr + "privkey=" + str(self.privkey) + ")"

def __str__(self) -> str:
fstr = "Package: winternitz\nLibrary: signatures\nClass: WOTS\n" + \
"Winternitz Parameter (log2): {}\nHash algorithm: {}\n" + \
"Digest size: {}\n"
fstr += "Private key:\n" if self.privkey is not None else \
"Public key:\n"

for idx, key in enumerate(self.privkey if self.privkey is not None
else self.pubkey):
fstr += "\t[{}] {}\n".format(idx, key)

return fstr.format(self.w, self.hashclass.__qualname__,
self.digestsize)

def __eq__(self, obj: Any) -> bool:
return isinstance(obj, self.__class__) and self.w == obj.w and \
self.hashclass == obj.hashclass and self.digestsize == \
obj.digestsize and self.privkey == obj.privkey and \
self.pubkey == obj.pubkey

def __ne__(self, obj: Any) -> bool:
return not self.__eq__(obj)

@property
def privkey(self):
# TODO: get privkey
pass
def privkey(self) -> List[bytes]:
return self.__privkey.copy()

@property
def pubkey(self):
# TODO: get privkey
pass
def pubkey(self) -> List[bytes]:
return self.__pubkey.copy()

@property
def w(self):
# TODO: get Winternitz parameter
pass
def w(self) -> int:
return self.__w_log2

@property
def hashalgo(self):
# TODO: get hashalgorithm
pass
def hashclass(self) -> Any: # TODO: correct Type
return self.__hash_class

@property
def digestsize(self):
# TODO: get digestsize
def digestsize(self) -> int:
return self.__digest_size

def _derivePubKey(self) -> None:
# TODO:
pass

def sign(message: bytes)\
-> dict:
def sign(message: bytes) -> dict:
# TODO: implement sign algorithm
# Check if privkey none, throw exception if it is
pass
Expand All @@ -63,39 +158,66 @@ def verify(message: bytes, signature: List[bytes]) -> bool:


# Paper descirbing WOTS+: https://eprint.iacr.org/2017/965.pdf
# "W-OTS+ – Shorter Signatures for Hash-BasedSignature Schemes"
class WOTSPLUS(WOTS):
def __init__(w: int, privkey: Optional[List[bytes]] = None,
hashalgo: List[int] = sha256, digestsize=256,
def __init__(self,
w_log2: int,
hash_class: Any = sha256, # TODO: correct Type
# TODO: Pseudo Random Function for Key and BM derivation
digestsize: int = 256,
privkey: Optional[List[bytes]] = None,
pubkey: Optional[List[bytes]] = None,
seed: Optional[bytes] = None):
super().__init__(w, privkey, hashalgo, digestsize)
# TODO
# Only init seed if it is not None
pass

def __repr__():
# TODO
pass
super().__init__(w_log2, hash_class=hash_class,
digestsize=digestsize, privkey=privkey,
pubkey=pubkey)

if seed is None:
seed = urandom(int(ceil(digestsize/8)))

def __str__():
# TODO
self.__seed = seed
# TODO: store prf
pass

def __eq__():
# TODO
def __repr__(self) -> str:
# TODO: add prf
return super().__repr__().replace("WOTS", "WOTSPLUS")[:-1] + \
", seed=" + str(self.seed) + ")"

def __str__(self) -> str:
# TODO: add prf
sstr = super().__str__().replace("WOTS", "WOTSPLUS")
strsplit = sstr.split("Public key:" if self.privkey is None else
"Private key:")
result = strsplit[0] + "Seed: " + str(self.__seed) + \
("\nPublic key: " if self.privkey is None else
"\nPrivate key: ") + strsplit[1]

return result

def __eq__(self, obj) -> bool:
# TODO: add prf
return super().__eq__(obj) and isinstance(obj, self.__class__) and \
self.seed == obj.seed

def __ne__(self, obj) -> bool:
return not self.__eq__(obj)

def _derivePubKey(self) -> None:
# TODO:
pass

@property
def seed():
# TODO: get seed
pass
def seed(self) -> bytes:
return self.__seed

@property
def prf():
def prf(self):
# TODO: get pseudo random function
pass

def sign(message: bytes)\
-> dict:
def sign(message: bytes) -> dict:
# TODO: implement sign algorithm
# Check if privkey none, throw exception if it is
pass
Expand Down

0 comments on commit c6b68cf

Please sign in to comment.