# -*- coding: utf-8 -*-
"""Hierarchical Signatures
For reference see RFC 8554, section 6.
"""
from .lms import LMS_Priv, LMS_Pub
from .utils import INVALID, FAILURE
from .utils import u32str, strTou32
[docs]class HSS_Pub:
"""A class used to hold the public key of Hierarchical Signatures (HSS)
This hierarchical scheme uses LMS as a component.
For a reference see RFC 8554, section 6.
Args:
pubkey (bytes): u32str(L) || LMS Public Key[0]
Raises:
INVALID: If the public is invalid.
"""
def __init__(self, pubkey):
if len(pubkey) < 4:
raise INVALID
self.L = strTou32(pubkey[:4])
self.pub = LMS_Pub(pubkey[4:])
[docs] def verify(self, message, signature):
"""Signature Verification of HSS
Tries to verify the signature of a message with the public key associated
with the class.
Args:
message (bytes, BufferedReader): Message to be verified with `signature`
signature (bytes): Signature belonging to the `message`
Raises:
INVALID: If signature is invalid.
"""
Nspk = strTou32(signature[:4])
if Nspk+1 != self.L:
raise INVALID
signature = signature[4:]
key = self.pub
for i in range(Nspk):
l = LMS_Pub._len_signature(signature)
lms_sig = signature[:l]
signature = signature[l:]
l = LMS_Pub._len_pubkey(signature)
lms_pub = signature[:l]
key.verify(lms_pub, lms_sig)
signature = signature[l:]
key = LMS_Pub(lms_pub)
key.verify(message, signature)
[docs] def get_pubkey(self):
return u32str(self.L) + self.pub.get_pubkey()
[docs] def info(self):
return f"Height L: {self.L}\n" + self.pub.info()
[docs]class HSS_Priv:
"""A class used to hold the private key of Hierarchical Signatures (HSS)
For a reference see RFC 8554, section 6.
This class can be used to generate the belonging public key `HSS_Pub`.
Args:
lmstypecodes (:obj:`list` of :obj:`LMS_ALGORITHM_TYPE`): List of enumeration of Leighton-Micali Signatures (LMS) algorithm types
otstypecode (LMOTS_ALGORITHM_TYPE): Enumeration of Leighton-Micali One-Time-Signatures (LMOTS) algorithm types
num_cores (int, None, optional): the number of CPU cores used for key generation, None=all cores
"""
def __init__(self, lmstypecodes, otstypecode, num_cores=None):
self.lmstypecodes = lmstypecodes
self.otstypecode = otstypecode
self.L = len(lmstypecodes)
self.priv = [LMS_Priv(self.lmstypecodes[0], self.otstypecode, num_cores)]
self.avail_signatures = self.priv[0].get_avail_signatures()
self.pub = [self.priv[0].gen_pub()]
self.sig = []
for i in range(1, self.L):
self.priv.append(LMS_Priv(self.lmstypecodes[i], self.otstypecode, num_cores))
self.avail_signatures *= self.priv[-1].get_avail_signatures()
self.pub.append(self.priv[-1].gen_pub())
self.sig.append(self.priv[-2].sign(self.pub[-1].get_pubkey()))
[docs] def sign(self, message):
"""Signature Generation of HSS
Signs a message with the private key associated with the class.
Args:
message (bytes, BufferedReader): Message to be signed
Raises:
FAILURE: If a signature has already been computed, or for other
technical reason
Returns:
bytes: The signature to `message`.
"""
d = self.L
while self.priv[d-1].get_avail_signatures() == 0:
d -= 1
if d == 0:
raise FAILURE("Private keys exhausted.")
for i in range(d, self.L):
self.priv[i] = LMS_Priv(self.lmstypecodes[i], self.otstypecode)
self.pub[i] = self.priv[i].gen_pub()
self.sig[i-1] = self.priv[i-1].sign(self.pub[i].get_pubkey())
signature = u32str(self.L-1)
for i in range(self.L-1):
signature += self.sig[i] + self.pub[i+1].get_pubkey() # signed_pub_key
self.avail_signatures -= 1
return signature + self.priv[-1].sign(message)
[docs] def gen_pub(self):
"""Computes the public key associated with the private key in this class.
Returns:
HSS_Pub: The public key belonging to this private key.
"""
return HSS_Pub(u32str(self.L) + self.pub[0].get_pubkey())
[docs] def get_avail_signatures(self):
"""Computes the numbers of availalbe signatures.
Every invokation of the 'sign'-Method reduces this number by one.
Returns:
int: The remaining number of signatures that can be generated.
"""
return self.avail_signatures
[docs] def info(self):
return f"""\
lmotstype = {self.otstypecode.name}
lmstypes = {[t.name for t in self.lmstypecodes]}
available signatures = {self.get_avail_signatures()}
"""