2019-08-26 17:46:25 +02:00
|
|
|
"""
|
|
|
|
Test for ECC blind signatures
|
|
|
|
"""
|
|
|
|
import os
|
|
|
|
import unittest
|
2020-03-29 14:51:55 +02:00
|
|
|
from hashlib import sha256
|
2019-08-26 17:46:25 +02:00
|
|
|
|
2021-02-02 20:51:48 +01:00
|
|
|
from pybitmessage.pyelliptic import ECCBlind, ECCBlindChain, OpenSSL
|
2019-08-26 17:46:25 +02:00
|
|
|
|
2020-03-29 14:51:55 +02:00
|
|
|
# pylint: disable=protected-access
|
|
|
|
|
2019-08-26 17:46:25 +02:00
|
|
|
|
|
|
|
class TestBlindSig(unittest.TestCase):
|
|
|
|
"""
|
|
|
|
Test case for ECC blind signature
|
|
|
|
"""
|
|
|
|
def test_blind_sig(self):
|
|
|
|
"""Test full sequence using a random certifier key and a random message"""
|
2019-08-27 23:11:42 +02:00
|
|
|
# See page 127 of the paper
|
|
|
|
# (1) Initialization
|
|
|
|
signer_obj = ECCBlind()
|
|
|
|
point_r = signer_obj.signer_init()
|
2020-03-29 14:51:55 +02:00
|
|
|
self.assertEqual(len(signer_obj.pubkey()), 35)
|
2019-08-27 23:11:42 +02:00
|
|
|
|
|
|
|
# (2) Request
|
2020-03-29 14:51:55 +02:00
|
|
|
requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
|
2019-08-27 23:11:42 +02:00
|
|
|
# only 64 byte messages are planned to be used in Bitmessage
|
2019-08-26 17:46:25 +02:00
|
|
|
msg = os.urandom(64)
|
2019-08-27 23:11:42 +02:00
|
|
|
msg_blinded = requester_obj.create_signing_request(point_r, msg)
|
2020-03-29 14:51:55 +02:00
|
|
|
self.assertEqual(len(msg_blinded), 32)
|
2019-08-27 23:11:42 +02:00
|
|
|
|
2019-08-28 13:21:44 +02:00
|
|
|
# check
|
2020-03-29 14:51:55 +02:00
|
|
|
self.assertNotEqual(sha256(msg).digest(), msg_blinded)
|
2019-08-28 13:21:44 +02:00
|
|
|
|
2019-08-27 23:11:42 +02:00
|
|
|
# (3) Signature Generation
|
|
|
|
signature_blinded = signer_obj.blind_sign(msg_blinded)
|
2021-02-02 20:51:48 +01:00
|
|
|
assert isinstance(signature_blinded, bytes)
|
2020-03-29 14:51:55 +02:00
|
|
|
self.assertEqual(len(signature_blinded), 32)
|
2019-08-27 23:11:42 +02:00
|
|
|
|
|
|
|
# (4) Extraction
|
|
|
|
signature = requester_obj.unblind(signature_blinded)
|
2021-02-02 20:51:48 +01:00
|
|
|
assert isinstance(signature, bytes)
|
2020-03-29 14:51:55 +02:00
|
|
|
self.assertEqual(len(signature), 65)
|
2019-08-27 23:11:42 +02:00
|
|
|
|
2020-03-29 14:51:55 +02:00
|
|
|
self.assertNotEqual(signature, signature_blinded)
|
2019-08-28 13:21:44 +02:00
|
|
|
|
2019-08-27 23:11:42 +02:00
|
|
|
# (5) Verification
|
2020-03-29 14:51:55 +02:00
|
|
|
verifier_obj = ECCBlind(pubkey=signer_obj.pubkey())
|
2019-08-27 23:11:42 +02:00
|
|
|
self.assertTrue(verifier_obj.verify(msg, signature))
|
2019-12-25 22:09:17 +01:00
|
|
|
|
2020-03-29 14:51:55 +02:00
|
|
|
def test_is_odd(self):
|
|
|
|
"""Test our implementation of BN_is_odd"""
|
|
|
|
for _ in range(1024):
|
|
|
|
obj = ECCBlind()
|
|
|
|
x = OpenSSL.BN_new()
|
|
|
|
y = OpenSSL.BN_new()
|
|
|
|
OpenSSL.EC_POINT_get_affine_coordinates(
|
|
|
|
obj.group, obj.Q, x, y, 0)
|
|
|
|
self.assertEqual(OpenSSL.BN_is_odd(y),
|
|
|
|
OpenSSL.BN_is_odd_compatible(y))
|
|
|
|
|
|
|
|
def test_serialize_ec_point(self):
|
|
|
|
"""Test EC point serialization/deserialization"""
|
|
|
|
for _ in range(1024):
|
|
|
|
try:
|
|
|
|
obj = ECCBlind()
|
|
|
|
obj2 = ECCBlind()
|
|
|
|
randompoint = obj.Q
|
|
|
|
serialized = obj._ec_point_serialize(randompoint)
|
|
|
|
secondpoint = obj2._ec_point_deserialize(serialized)
|
|
|
|
x0 = OpenSSL.BN_new()
|
|
|
|
y0 = OpenSSL.BN_new()
|
|
|
|
OpenSSL.EC_POINT_get_affine_coordinates(obj.group,
|
|
|
|
randompoint, x0,
|
|
|
|
y0, obj.ctx)
|
|
|
|
x1 = OpenSSL.BN_new()
|
|
|
|
y1 = OpenSSL.BN_new()
|
|
|
|
OpenSSL.EC_POINT_get_affine_coordinates(obj2.group,
|
|
|
|
secondpoint, x1,
|
|
|
|
y1, obj2.ctx)
|
|
|
|
|
|
|
|
self.assertEqual(OpenSSL.BN_cmp(y0, y1), 0)
|
|
|
|
self.assertEqual(OpenSSL.BN_cmp(x0, x1), 0)
|
|
|
|
self.assertEqual(OpenSSL.EC_POINT_cmp(obj.group, randompoint,
|
|
|
|
secondpoint, 0), 0)
|
|
|
|
finally:
|
|
|
|
OpenSSL.BN_free(x0)
|
|
|
|
OpenSSL.BN_free(x1)
|
|
|
|
OpenSSL.BN_free(y0)
|
|
|
|
OpenSSL.BN_free(y1)
|
|
|
|
del obj
|
|
|
|
del obj2
|
|
|
|
|
|
|
|
def test_serialize_bn(self):
|
|
|
|
"""Test Bignum serialization/deserialization"""
|
|
|
|
for _ in range(1024):
|
|
|
|
obj = ECCBlind()
|
|
|
|
obj2 = ECCBlind()
|
|
|
|
randomnum = obj.d
|
|
|
|
serialized = obj._bn_serialize(randomnum)
|
|
|
|
secondnum = obj2._bn_deserialize(serialized)
|
|
|
|
self.assertEqual(OpenSSL.BN_cmp(randomnum, secondnum), 0)
|
|
|
|
|
|
|
|
def test_blind_sig_many(self):
|
|
|
|
"""Test a lot of blind signatures"""
|
|
|
|
for _ in range(1024):
|
|
|
|
self.test_blind_sig()
|
|
|
|
|
|
|
|
def test_blind_sig_value(self):
|
|
|
|
"""Test blind signature value checking"""
|
|
|
|
signer_obj = ECCBlind(value=5)
|
|
|
|
point_r = signer_obj.signer_init()
|
|
|
|
requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
|
|
|
|
msg = os.urandom(64)
|
|
|
|
msg_blinded = requester_obj.create_signing_request(point_r, msg)
|
|
|
|
signature_blinded = signer_obj.blind_sign(msg_blinded)
|
|
|
|
signature = requester_obj.unblind(signature_blinded)
|
|
|
|
verifier_obj = ECCBlind(pubkey=signer_obj.pubkey())
|
|
|
|
self.assertFalse(verifier_obj.verify(msg, signature, value=8))
|
|
|
|
|
|
|
|
def test_blind_sig_expiration(self):
|
|
|
|
"""Test blind signature expiration checking"""
|
|
|
|
signer_obj = ECCBlind(year=2020, month=1)
|
|
|
|
point_r = signer_obj.signer_init()
|
|
|
|
requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
|
|
|
|
msg = os.urandom(64)
|
|
|
|
msg_blinded = requester_obj.create_signing_request(point_r, msg)
|
|
|
|
signature_blinded = signer_obj.blind_sign(msg_blinded)
|
|
|
|
signature = requester_obj.unblind(signature_blinded)
|
|
|
|
verifier_obj = ECCBlind(pubkey=signer_obj.pubkey())
|
|
|
|
self.assertFalse(verifier_obj.verify(msg, signature))
|
2019-12-25 22:09:17 +01:00
|
|
|
|
2020-03-29 14:51:55 +02:00
|
|
|
def test_blind_sig_chain(self): # pylint: disable=too-many-locals
|
2019-12-25 22:09:17 +01:00
|
|
|
"""Test blind signature chain using a random certifier key and a random message"""
|
|
|
|
|
2020-03-29 14:51:55 +02:00
|
|
|
test_levels = 4
|
|
|
|
msg = os.urandom(1024)
|
|
|
|
|
|
|
|
ca = ECCBlind()
|
|
|
|
signer_obj = ca
|
|
|
|
|
|
|
|
output = bytearray()
|
|
|
|
|
|
|
|
for level in range(test_levels):
|
|
|
|
if not level:
|
|
|
|
output.extend(ca.pubkey())
|
|
|
|
requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
|
|
|
|
child_obj = ECCBlind()
|
|
|
|
point_r = signer_obj.signer_init()
|
|
|
|
pubkey = child_obj.pubkey()
|
|
|
|
|
|
|
|
if level == test_levels - 1:
|
|
|
|
msg_blinded = requester_obj.create_signing_request(point_r,
|
|
|
|
msg)
|
|
|
|
else:
|
|
|
|
msg_blinded = requester_obj.create_signing_request(point_r,
|
|
|
|
pubkey)
|
|
|
|
signature_blinded = signer_obj.blind_sign(msg_blinded)
|
|
|
|
signature = requester_obj.unblind(signature_blinded)
|
|
|
|
if level != test_levels - 1:
|
|
|
|
output.extend(pubkey)
|
|
|
|
output.extend(signature)
|
|
|
|
signer_obj = child_obj
|
2021-02-02 20:51:48 +01:00
|
|
|
verifychain = ECCBlindChain(ca=ca.pubkey(), chain=bytes(output))
|
2020-03-29 14:51:55 +02:00
|
|
|
self.assertTrue(verifychain.verify(msg=msg, value=1))
|
|
|
|
|
|
|
|
def test_blind_sig_chain_wrong_ca(self): # pylint: disable=too-many-locals
|
|
|
|
"""Test blind signature chain with an unlisted ca"""
|
|
|
|
|
|
|
|
test_levels = 4
|
2019-12-25 22:09:17 +01:00
|
|
|
msg = os.urandom(1024)
|
|
|
|
|
2020-03-29 14:51:55 +02:00
|
|
|
ca = ECCBlind()
|
|
|
|
fake_ca = ECCBlind()
|
|
|
|
signer_obj = fake_ca
|
|
|
|
|
|
|
|
output = bytearray()
|
|
|
|
|
|
|
|
for level in range(test_levels):
|
|
|
|
requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
|
|
|
|
child_obj = ECCBlind()
|
|
|
|
if not level:
|
|
|
|
# unlisted CA, but a syntactically valid pubkey
|
|
|
|
output.extend(fake_ca.pubkey())
|
|
|
|
point_r = signer_obj.signer_init()
|
|
|
|
pubkey = child_obj.pubkey()
|
|
|
|
|
|
|
|
if level == test_levels - 1:
|
|
|
|
msg_blinded = requester_obj.create_signing_request(point_r,
|
|
|
|
msg)
|
|
|
|
else:
|
|
|
|
msg_blinded = requester_obj.create_signing_request(point_r,
|
|
|
|
pubkey)
|
|
|
|
signature_blinded = signer_obj.blind_sign(msg_blinded)
|
|
|
|
signature = requester_obj.unblind(signature_blinded)
|
|
|
|
if level != test_levels - 1:
|
|
|
|
output.extend(pubkey)
|
|
|
|
output.extend(signature)
|
|
|
|
signer_obj = child_obj
|
|
|
|
verifychain = ECCBlindChain(ca=ca.pubkey(), chain=str(output))
|
|
|
|
self.assertFalse(verifychain.verify(msg, 1))
|
|
|
|
|
|
|
|
def test_blind_sig_chain_wrong_msg(self): # pylint: disable=too-many-locals
|
|
|
|
"""Test blind signature chain with a fake message"""
|
|
|
|
|
|
|
|
test_levels = 4
|
|
|
|
msg = os.urandom(1024)
|
|
|
|
fake_msg = os.urandom(1024)
|
|
|
|
|
2019-12-25 22:09:17 +01:00
|
|
|
ca = ECCBlind()
|
|
|
|
signer_obj = ca
|
2020-03-29 14:51:55 +02:00
|
|
|
|
|
|
|
output = bytearray()
|
2019-12-25 22:09:17 +01:00
|
|
|
|
|
|
|
for level in range(test_levels):
|
2020-03-29 14:51:55 +02:00
|
|
|
if not level:
|
|
|
|
output.extend(ca.pubkey())
|
|
|
|
requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
|
|
|
|
child_obj = ECCBlind()
|
|
|
|
point_r = signer_obj.signer_init()
|
|
|
|
pubkey = child_obj.pubkey()
|
|
|
|
|
|
|
|
if level == test_levels - 1:
|
|
|
|
msg_blinded = requester_obj.create_signing_request(point_r,
|
|
|
|
msg)
|
2019-12-25 22:09:17 +01:00
|
|
|
else:
|
2020-03-29 14:51:55 +02:00
|
|
|
msg_blinded = requester_obj.create_signing_request(point_r,
|
|
|
|
pubkey)
|
|
|
|
signature_blinded = signer_obj.blind_sign(msg_blinded)
|
|
|
|
signature = requester_obj.unblind(signature_blinded)
|
|
|
|
if level != test_levels - 1:
|
|
|
|
output.extend(pubkey)
|
|
|
|
output.extend(signature)
|
|
|
|
signer_obj = child_obj
|
|
|
|
verifychain = ECCBlindChain(ca=ca.pubkey(), chain=str(output))
|
|
|
|
self.assertFalse(verifychain.verify(fake_msg, 1))
|
|
|
|
|
|
|
|
def test_blind_sig_chain_wrong_intermediary(self): # pylint: disable=too-many-locals
|
|
|
|
"""Test blind signature chain using a fake intermediary pubkey"""
|
|
|
|
|
|
|
|
test_levels = 4
|
|
|
|
msg = os.urandom(1024)
|
|
|
|
wrong_level = 2
|
|
|
|
|
|
|
|
ca = ECCBlind()
|
|
|
|
signer_obj = ca
|
|
|
|
fake_intermediary = ECCBlind()
|
|
|
|
|
|
|
|
output = bytearray()
|
|
|
|
|
|
|
|
for level in range(test_levels):
|
|
|
|
if not level:
|
|
|
|
output.extend(ca.pubkey())
|
|
|
|
requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
|
|
|
|
child_obj = ECCBlind()
|
2019-12-25 22:09:17 +01:00
|
|
|
point_r = signer_obj.signer_init()
|
2020-03-29 14:51:55 +02:00
|
|
|
pubkey = child_obj.pubkey()
|
2019-12-25 22:09:17 +01:00
|
|
|
|
|
|
|
if level == test_levels - 1:
|
|
|
|
msg_blinded = requester_obj.create_signing_request(point_r,
|
|
|
|
msg)
|
|
|
|
else:
|
2020-03-29 14:51:55 +02:00
|
|
|
msg_blinded = requester_obj.create_signing_request(point_r,
|
|
|
|
pubkey)
|
2019-12-25 22:09:17 +01:00
|
|
|
signature_blinded = signer_obj.blind_sign(msg_blinded)
|
|
|
|
signature = requester_obj.unblind(signature_blinded)
|
2020-03-29 14:51:55 +02:00
|
|
|
if level == wrong_level:
|
|
|
|
output.extend(fake_intermediary.pubkey())
|
|
|
|
elif level != test_levels - 1:
|
|
|
|
output.extend(pubkey)
|
|
|
|
output.extend(signature)
|
|
|
|
signer_obj = child_obj
|
|
|
|
verifychain = ECCBlindChain(ca=ca.pubkey(), chain=str(output))
|
|
|
|
self.assertFalse(verifychain.verify(msg, 1))
|