# -*- coding: utf-8 -*- import unittest from collections import OrderedDict from django.conf import settings import os from nacl import signing from nacl.exceptions import BadSignatureError import mock from ..crypto import HMAC, Ed25519, generate_randomness from ..models import SQRLNut from ..utils import Base64 TESTING_MODULE = 'sqrl.crypto' class TestHMAC(unittest.TestCase): def setUp(self): super(TestHMAC, self).setUp() self.nut = SQRLNut(session_key='0123456789') self.data = OrderedDict([ ('ver', '1'), ('tif', 0), ('mac', 'value'), ('qry', 'foo?nut=bar'), ]) self.hmac = HMAC(self.nut, self.data) def test_init(self): hmac = HMAC(mock.sentinel.nut, mock.sentinel.data) self.assertEqual(hmac.nut, mock.sentinel.nut) self.assertEqual(hmac.data, mock.sentinel.data) def test_sign_data(self): temp = str(settings.SECRET_KEY) settings.SECRET_KEY = "foo" signature = self.hmac.sign_data() self.assertIsInstance(signature, bytes) self.assertEqual(len(signature), 20) self.assertEqual( signature, b'R\xfc\xb2\xbd\x12\x85\xae\xb0>\xdd\xed\x16P\xc2\x82\xae\x06\x0c\xc5\xd3' ) settings.SECRET_KEY = str(temp) @mock.patch(TESTING_MODULE + '.salted_hmac') def test_sign_data_mock(self, mock_salted_hmac): signature = self.hmac.sign_data() self.assertEqual( signature, mock_salted_hmac.return_value.digest.return_value ) mock_salted_hmac.assert_called_once_with( self.nut.session_key, Base64.encode(b'ver=1\r\n' b'tif=0\r\n' b'qry=foo?nut=bar\r\n') ) def test_sign_data_not_dict(self): with self.assertRaises(AssertionError): HMAC(mock.sentinel.nut, mock.sentinel.data).sign_data() @mock.patch.object(HMAC, 'sign_data') def test_is_signature_valid(self, mock_sign_data): mock_sign_data.return_value = 'foo_signature' self.assertTrue(self.hmac.is_signature_valid('foo_signature')) self.assertFalse(self.hmac.is_signature_valid('foo-signature')) def test_validation_loop(self): signature = self.hmac.sign_data() self.assertTrue(self.hmac.is_signature_valid(signature)) self.assertFalse(self.hmac.is_signature_valid(b'a' + signature[:-1])) class TestEd25519(unittest.TestCase): def setUp(self): super(TestEd25519, self).setUp() self.signing_key = (b'\xbbH\xdfx\xed\xc5\xdbR\x94\xe4\xff\xa6~5\xbb\xbd\xf2\x16&' b'\xfc\x89\x8a\xc8\\\\\xeb\xea\x91Db~Hm+b\x88\xf2\x10\xfb:H' b'\xe4\xfb0\x00\r\xe7n|\xa64\x05m@\xc8\xef"\x07k{O\xf0\xff%') self.verifying_key = (b'm+b\x88\xf2\x10\xfb:H\xe4\xfb0\x00\r\xe7n|\xa64\x05m@' b'\xc8\xef"\x07k{O\xf0\xff%') self.data = b'data' self.sig = Ed25519(self.verifying_key, self.signing_key, self.data) def test_init(self): sig = Ed25519(mock.sentinel.pub_key, mock.sentinel.priv_key, mock.sentinel.msg) self.assertEqual(sig.public_key, mock.sentinel.pub_key) self.assertEqual(sig.private_key, mock.sentinel.priv_key) self.assertEqual(sig.msg, mock.sentinel.msg) def test_sign_data(self): signature = self.sig.sign_data() self.assertIsInstance(signature, bytes) self.assertEqual(len(signature), 64) self.assertEqual( signature, b'\xac\xe0\x81\xc4\xd5\x7f\xd4\xe3\xc1\x03>\x0f\x90\xb5\x9eG<\xe0\xd41' b'\x1cZ\xd7\x15F\xba\xdeS/\xfa\xbbL\x9bh\x8dn;\xcfP\xb1\x16\x14&d\xde' b'\x97\x145\x90N[\xb9\xfc\x8e\x8a\x9e\xd2=\xad\x84\xcd\xf1\x93\x06' ) @mock.patch('nacl.signing.SigningKey') def test_sign_data_mock(self, mock_signing_key): signature = self.sig.sign_data() self.assertEqual(signature, mock_signing_key.return_value.sign.return_value.signature) mock_signing_key.assert_called_once_with(self.sig.private_key) mock_signing_key.return_value.sign.assert_called_once_with(self.sig.msg) def test_is_signature_valid(self): signature = self.sig.sign_data() self.assertTrue(self.sig.is_signature_valid(signature)) self.assertFalse(self.sig.is_signature_valid(b'a' + signature[:-1])) @mock.patch('nacl.signing.VerifyKey') def test_is_signature_mock(self, mock_verifying_key): is_valid = self.sig.is_signature_valid(mock.sentinel.signature) self.assertTrue(is_valid) mock_verifying_key.assert_called_once_with(self.sig.public_key) mock_verifying_key.return_value.verify.assert_called_once_with( self.data, mock.sentinel.signature ) @mock.patch('nacl.signing.VerifyKey') def test_is_signature_mock_assertion_error(self, mock_verifying_key): mock_verifying_key.return_value.verify.side_effect = AssertionError is_valid = self.sig.is_signature_valid(mock.sentinel.signature) self.assertFalse(is_valid) mock_verifying_key.assert_called_once_with(self.sig.public_key) mock_verifying_key.return_value.verify.assert_called_once_with( self.data, mock.sentinel.signature ) @mock.patch('nacl.signing.VerifyKey') def test_is_signature_mock_bad_signature_error(self, mock_verifying_key): mock_verifying_key.return_value.verify.side_effect = BadSignatureError is_valid = self.sig.is_signature_valid(mock.sentinel.signature) self.assertFalse(is_valid) mock_verifying_key.assert_called_once_with(self.sig.public_key) mock_verifying_key.return_value.verify.assert_called_once_with( self.data, mock.sentinel.signature ) class TestUtils(unittest.TestCase): @mock.patch(TESTING_MODULE + '.bytearray', create=True) @mock.patch.object(Base64, 'encode') @mock.patch.object(os, 'urandom') def test_generate_randomness_mock(self, mock_urandom, mock_encode, mock_bytearray): _mock_bytearray = mock.MagicMock() def _bytearray(a): list(a) return _mock_bytearray(a) mock_bytearray.side_effect = _bytearray randomness = generate_randomness() self.assertEqual(randomness, mock_encode.return_value) self.assertEqual(mock_urandom.call_count, 1) mock_urandom.assert_called_with(32) mock_encode.assert_called_once_with(_mock_bytearray.return_value) def test_generate_randomness(self): randomness = generate_randomness() self.assertIsInstance(randomness, str)