# coding=utf-8
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
from pyqrllib.pyqrllib import bin2hstr, getRandomSeed, str2bin, bin2mnemonic, mnemonic2bin, XmssFast # noqa
[docs]class XMSS(object):
# FIXME: Getters are only temporarily. Delete everything or use properties
def __init__(self, tree_height, seed=None, _xmssfast=None):
"""
:param
tree_height: height of the tree to generate. number of OTS keypairs=2**tree_height
:param seed:
>>> from qrl.crypto.doctest_data import *; XMSS(4, xmss_test_seed1)._xmss.getHeight()
4
>>> from qrl.crypto.doctest_data import *; XMSS(4, xmss_test_seed1)._xmss.getSecretKeySize()
132
>>> from qrl.crypto.doctest_data import *; XMSS(4, xmss_test_seed1)._xmss.getSignatureSize()
2308
>>> from qrl.crypto.doctest_data import *; len(XMSS(4, xmss_test_seed1)._xmss.getSK()) == XMSS(4, xmss_test_seed1)._xmss.getSecretKeySize()
True
>>> from qrl.crypto.doctest_data import *; bin2hstr( XMSS(4, xmss_test_seed1)._xmss.getPK() )
'26b3bcc104d686ecfd9fdea7b1963384339121430fbe056cab7c3048ea3e4c4e51ec21420dd061739e4637fd74517a46f86f89e0fb83f2526fafafe356e564ff'
>>> from qrl.crypto.doctest_data import *; bin2hstr( XMSS(4, xmss_test_seed1)._xmss.getSK() ) == xmss_sk_expected1
True
>>> from qrl.crypto.doctest_data import *; bin2hstr( XMSS(4, xmss_test_seed1)._xmss.getRoot() )
'26b3bcc104d686ecfd9fdea7b1963384339121430fbe056cab7c3048ea3e4c4e'
>>> from qrl.crypto.doctest_data import *; bin2hstr( XMSS(4, xmss_test_seed1)._xmss.getPKSeed() )
'51ec21420dd061739e4637fd74517a46f86f89e0fb83f2526fafafe356e564ff'
>>> from qrl.crypto.doctest_data import *; XMSS(4, xmss_test_seed1)._xmss.getIndex()
0
>>> from qrl.crypto.doctest_data import *; bin2hstr( XMSS(4, xmss_test_seed1)._xmss.getSKSeed() )
'5f2eb95ccf6a0e3e7f472c32d234340c20b3fd379dc28b710affcc0cb2afa57b'
>>> from qrl.crypto.doctest_data import *; bin2hstr( XMSS(4, xmss_test_seed1)._xmss.getSKPRF() )
'3aa40c0f99459afe7efe72eb9517ee8ded49ccd51dab72ebf6bc37d73240bb3a'
>>> from qrl.crypto.doctest_data import *; XMSS(4, xmss_test_seed1)._xmss.getAddress('Q')
'Q1d651431536359202ce7095757e3ed66f579a6eab488ac1331486f207c91604016b6a443'
>>> from qrl.crypto.doctest_data import *; bin2hstr( XMSS(4, xmss_test_seed2)._xmss.getPK() ) # doctest: +SKIP
''
"""
self._type = 'XMSS'
if _xmssfast is not None:
self._xmss = _xmssfast
self._seed = self._xmss.getSeed()
else:
# TODO: This is the old code, probably it should be removed
if seed is None:
# FIXME: Improve seed generation
self._seed = getRandomSeed(48, '')
else:
if isinstance(seed, str):
self._seed = str2bin(seed)
else:
self._seed = seed
self._xmss = XmssFast(self._seed, tree_height)
self.addresses = [(0, self.get_address(), self.get_number_signatures())]
@property
def height(self):
return self._xmss.getHeight()
def _sk(self):
# FIXME: Move to property
"""
>>> from qrl.crypto.doctest_data import *; bin2hstr(XMSS(4, xmss_test_seed1)._sk()) == xmss_sk_expected1
True
>>> from qrl.crypto.doctest_data import *; bin2hstr(XMSS(4, xmss_test_seed2)._sk()) == xmss_sk_expected2
True
"""
return bytes(self._xmss.getSK())
[docs] def pk(self):
# FIXME: Move to property
"""
>>> from qrl.crypto.doctest_data import *; bin2hstr(XMSS(4, xmss_test_seed1).pk()) == xmss_pk_expected1
True
>>> from qrl.crypto.doctest_data import *; bin2hstr(XMSS(4, xmss_test_seed2).pk()) == xmss_pk_expected2
True
"""
return bytes(self._xmss.getPK())
[docs] def get_number_signatures(self):
# FIXME: Move to property
"""
:return:
:rtype:
>>> from qrl.crypto.doctest_data import *; XMSS(4, xmss_test_seed1).get_number_signatures()
16
>>> from qrl.crypto.doctest_data import *; XMSS(4, xmss_test_seed2).get_number_signatures()
16
"""
# type: () -> int
return 2 ** self._xmss.getHeight()
[docs] def get_remaining_signatures(self):
# FIXME: Move to property
"""
:return:
:rtype:
>>> from qrl.crypto.doctest_data import *; XMSS(4, xmss_test_seed1).get_remaining_signatures()
16
>>> from qrl.crypto.doctest_data import *; XMSS(4, xmss_test_seed2).get_remaining_signatures()
16
"""
return self.get_number_signatures() - self._xmss.getIndex()
[docs] def get_mnemonic(self):
# FIXME: Move to property
"""
:return:
:rtype:
>>> from qrl.crypto.doctest_data import *; XMSS(4, hstr2bin(xmss_mnemonic_seed1)).get_mnemonic() == xmss_mnemonic_test1
True
>>> from qrl.crypto.doctest_data import *; XMSS(4, hstr2bin(xmss_mnemonic_seed2)).get_mnemonic() == xmss_mnemonic_test2
True
>>> from qrl.crypto.doctest_data import *; XMSS(4, mnemonic2bin(xmss_mnemonic_test1)).get_mnemonic() == xmss_mnemonic_test1
True
>>> from qrl.crypto.doctest_data import *; XMSS(4, mnemonic2bin(xmss_mnemonic_test2)).get_mnemonic() == xmss_mnemonic_test2
True
"""
return bin2mnemonic(self._xmss.getSeed())
[docs] def get_address(self)->bytes:
# FIXME: Move to property
return bytes(self._xmss.getAddress('Q').encode())
[docs] def get_type(self):
# FIXME: Move to property
# type: () -> str
return self._type
[docs] def get_index(self):
# FIXME: Move to property
"""
:return:
:rtype:
>>> from qrl.crypto.doctest_data import *; XMSS(4, xmss_test_seed1).get_index()
0
>>> from qrl.crypto.doctest_data import *; XMSS(4, xmss_test_seed2).get_index()
0
>>> from qrl.crypto.doctest_data import *
>>> xmss = XMSS(4, xmss_test_seed2)
>>> s = xmss.SIGN(str2bin("test"))
>>> xmss.get_index()
1
"""
# type: () -> int
return self._xmss.getIndex()
[docs] def set_index(self, new_index):
"""
:return:
:rtype:
>>> from qrl.crypto.doctest_data import *
>>> xmss = XMSS(4, xmss_test_seed1)
>>> xmss.set_index(1)
>>> xmss.get_index()
1
>>> from qrl.crypto.doctest_data import *
>>> xmss = XMSS(4, xmss_test_seed1)
>>> xmss.set_index(10)
>>> xmss.get_index()
10
"""
self._xmss.setIndex(new_index)
[docs] def get_hexseed(self):
# FIXME: Move to property
"""
:return:
:rtype:
>>> from qrl.crypto.doctest_data import *; XMSS(4, xmss_test_seed1).get_hexseed()
'303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030'
>>> from qrl.crypto.doctest_data import *; XMSS(4, xmss_test_seed2).get_hexseed()
'333133313331333133313331333133313331333133313331333133313331333133313331333133313331333133313331'
"""
return bin2hstr(self._seed)
[docs] def get_seed(self):
# FIXME: Move to property
"""
:return:
:rtype:
>>> from qrl.crypto.doctest_data import *; bin2hstr( XMSS(4, xmss_test_seed1).get_seed() )
'303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030'
>>> from qrl.crypto.doctest_data import *; bin2hstr( XMSS(4, xmss_test_seed2).get_seed() )
'333133313331333133313331333133313331333133313331333133313331333133313331333133313331333133313331'
"""
return self._seed
[docs] def get_seed_public(self):
# FIXME: Move to property
"""
:return:
:rtype:
>>> from qrl.crypto.doctest_data import *; bin2hstr( XMSS(4, xmss_test_seed1).get_seed_private() )
'5f2eb95ccf6a0e3e7f472c32d234340c20b3fd379dc28b710affcc0cb2afa57b'
>>> from qrl.crypto.doctest_data import *; bin2hstr( XMSS(4, xmss_test_seed2).get_seed_private() )
'ad70ef34f316aaadcbf16a64b1b381db731eb53d833745c0d3eaa1e24cf728a2'
"""
return bytes(self._xmss.getPKSeed())
[docs] def get_seed_private(self):
# FIXME: Move to property
"""
:return:
:rtype:
>>> from qrl.crypto.doctest_data import *; bin2hstr( XMSS(4, xmss_test_seed1).get_seed_public() )
'51ec21420dd061739e4637fd74517a46f86f89e0fb83f2526fafafe356e564ff'
>>> from qrl.crypto.doctest_data import *; bin2hstr( XMSS(4, xmss_test_seed2).get_seed_public() )
'df2355c48096f2351e4d04db57b326c355345552d31b75a65ac18b1f6d7c7875'
"""
return bytes(self._xmss.getSKSeed())
[docs] @staticmethod
# NOTE: USED EXTERNALLY!!!
def VERIFY(message: bytes, signature: bytes, pk: bytes):
"""
Verify an xmss sig with shorter PK
same function but verifies using shorter signature where PK: {root, hex(_public_SEED)}
# main verification function..
:param pk:
:type pk:
:param message:
:param signature:
:return:
>>> from qrl.crypto.doctest_data import *; XMSS.VERIFY( str2bin("test_message"), hstr2bin(xmss_sign_expected1), hstr2bin(xmss_pk_expected1))
True
>>> from qrl.crypto.doctest_data import *; XMSS.VERIFY( str2bin("test_messagex"), hstr2bin(xmss_sign_expected1), hstr2bin(xmss_pk_expected1))
False
>>> from qrl.crypto.doctest_data import *; XMSS.VERIFY( str2bin("test_message"), hstr2bin(xmss_sign_expected2), hstr2bin(xmss_pk_expected2))
True
>>> from qrl.crypto.doctest_data import *; XMSS.VERIFY( str2bin("test_messagex"), hstr2bin(xmss_sign_expected2), hstr2bin(xmss_pk_expected2))
False
"""
try:
return XmssFast.verify(message, signature, pk)
except ValueError:
return False
[docs] def SIGN(self, message):
# type: (bytes) -> bytes
"""
:param message:
:return:
>>> from qrl.crypto.doctest_data import *; bin2hstr(XMSS(4, xmss_test_seed1).SIGN(str2bin("test_message"))) == xmss_sign_expected1
True
>>> from qrl.crypto.doctest_data import *; bin2hstr(XMSS(4, xmss_test_seed2).SIGN(str2bin("test_message"))) == xmss_sign_expected2
True
"""
return bytes(self._xmss.sign(message))
[docs] def list_addresses(self):
"""
List the addresses derived in the main tree
:return:
"""
addr_arr = []
for addr in self.addresses:
addr_arr.append(addr[1])
return addr_arr
if __name__ == "__main__":
import doctest
doctest.testmod()