# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals
from collections import OrderedDict
from random import SystemRandom
import ed25519
from django.utils.crypto import constant_time_compare, salted_hmac
from .utils import Base64, Encoder
random = SystemRandom()
[docs]class HMAC(object):
"""
Utility class for generating and verifying HMAC signatures.
This class relies on Django's built in :func:`salted_hmac`
to compute actual HMAC values by using ``SECRET_KEY`` as key.
Parameters
----------
nut : SQRLNut
Nut from which necessary data is extracted to add a salt value
to the HMAC input data.
Currently only :attr:`.models.SQRLNut.session_key` is used.
data : OrderedDict
Dict for which to either compute or validate HMAC signature.
"""
def __init__(self, nut, data):
self.nut = nut
self.data = data
[docs] def sign_data(self):
"""
Generate HMAC signature for the provided data.
Note
----
``max`` key is ignored in the input data if that key is present.
Returns
-------
bytes
Binary signature of the data
"""
assert isinstance(self.data, OrderedDict)
encoded = Encoder.base64_dumps(OrderedDict(
(k, v) for k, v in self.data.items()
if k != 'mac'
))
signature = salted_hmac(self.nut.session_key, encoded).digest()
return signature
[docs] def is_signature_valid(self, other_signature):
"""
Check if the ``other_signature`` is a valid signature for the
provided data and the nut.
Returns
-------
bool
Boolean indicating whether validation has succeeded.
"""
expected_signature = self.sign_data()
return constant_time_compare(expected_signature, other_signature)
[docs]class Ed25519(object):
"""
Utility class for signing and verifying ed25519 signatures.
More information about ed25519 can be found at `<http://ed25519.cr.yp.to/>`_.
Parameters
----------
public_key : bytes
Key used for verifying signature.
private_key : bytes
Key used for signing data.
msg : bytes
Binary data for which to generate the signature.
"""
def __init__(self, public_key, private_key, msg):
self.public_key = public_key
self.private_key = private_key
self.msg = msg
[docs] def is_signature_valid(self, other_signature):
"""
Check if ``other_signature`` is a valid signature for the provided message.
Returns
-------
bool
Boolean indicating whether validation has succeeded.
"""
try:
vk = ed25519.VerifyingKey(self.public_key)
vk.verify(other_signature, self.msg)
except (AssertionError, ed25519.BadSignatureError):
return False
else:
return True
[docs] def sign_data(self):
"""
Generate ed25519 signature for the provided data.
Returns
-------
bytes
ed25519 signature
"""
sk = ed25519.SigningKey(self.private_key)
return sk.sign(self.msg)
[docs]def generate_randomness(bytes=32):
"""
Generate random sample of specified size ``bytes``.
Parameters
----------
bytes : int, optional
Number of bytes to generate random sample
Returns
-------
str
:meth:`.Base64.encode` encoded random sample
"""
return Base64.encode(bytearray(random.getrandbits(8) for i in range(bytes)))