/ src / pyelliptic / tests / test_blindsig.py
test_blindsig.py
  1  """
  2  Test for ECC blind signatures
  3  """
  4  import os
  5  import unittest
  6  from hashlib import sha256
  7  
  8  try:
  9      from pyelliptic import ECCBlind, ECCBlindChain, OpenSSL
 10  except ImportError:
 11      from pybitmessage.pyelliptic import ECCBlind, ECCBlindChain, OpenSSL
 12  
 13  # pylint: disable=protected-access
 14  
 15  
 16  class TestBlindSig(unittest.TestCase):
 17      """
 18      Test case for ECC blind signature
 19      """
 20      def test_blind_sig(self):
 21          """Test full sequence using a random certifier key and a random message"""
 22          # See page 127 of the paper
 23          # (1) Initialization
 24          signer_obj = ECCBlind()
 25          point_r = signer_obj.signer_init()
 26          self.assertEqual(len(signer_obj.pubkey()), 35)
 27  
 28          # (2) Request
 29          requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
 30          # only 64 byte messages are planned to be used in Bitmessage
 31          msg = os.urandom(64)
 32          msg_blinded = requester_obj.create_signing_request(point_r, msg)
 33          self.assertEqual(len(msg_blinded), 32)
 34  
 35          # check
 36          self.assertNotEqual(sha256(msg).digest(), msg_blinded)
 37  
 38          # (3) Signature Generation
 39          signature_blinded = signer_obj.blind_sign(msg_blinded)
 40          assert isinstance(signature_blinded, bytes)
 41          self.assertEqual(len(signature_blinded), 32)
 42  
 43          # (4) Extraction
 44          signature = requester_obj.unblind(signature_blinded)
 45          assert isinstance(signature, bytes)
 46          self.assertEqual(len(signature), 65)
 47  
 48          self.assertNotEqual(signature, signature_blinded)
 49  
 50          # (5) Verification
 51          verifier_obj = ECCBlind(pubkey=signer_obj.pubkey())
 52          self.assertTrue(verifier_obj.verify(msg, signature))
 53  
 54      def test_is_odd(self):
 55          """Test our implementation of BN_is_odd"""
 56          for _ in range(1024):
 57              obj = ECCBlind()
 58              x = OpenSSL.BN_new()
 59              y = OpenSSL.BN_new()
 60              OpenSSL.EC_POINT_get_affine_coordinates(
 61                  obj.group, obj.Q, x, y, None)
 62              self.assertEqual(OpenSSL.BN_is_odd(y),
 63                               OpenSSL.BN_is_odd_compatible(y))
 64  
 65      def test_serialize_ec_point(self):
 66          """Test EC point serialization/deserialization"""
 67          for _ in range(1024):
 68              try:
 69                  obj = ECCBlind()
 70                  obj2 = ECCBlind()
 71                  randompoint = obj.Q
 72                  serialized = obj._ec_point_serialize(randompoint)
 73                  secondpoint = obj2._ec_point_deserialize(serialized)
 74                  x0 = OpenSSL.BN_new()
 75                  y0 = OpenSSL.BN_new()
 76                  OpenSSL.EC_POINT_get_affine_coordinates(obj.group,
 77                                                          randompoint, x0,
 78                                                          y0, obj.ctx)
 79                  x1 = OpenSSL.BN_new()
 80                  y1 = OpenSSL.BN_new()
 81                  OpenSSL.EC_POINT_get_affine_coordinates(obj2.group,
 82                                                          secondpoint, x1,
 83                                                          y1, obj2.ctx)
 84  
 85                  self.assertEqual(OpenSSL.BN_cmp(y0, y1), 0)
 86                  self.assertEqual(OpenSSL.BN_cmp(x0, x1), 0)
 87                  self.assertEqual(OpenSSL.EC_POINT_cmp(obj.group, randompoint,
 88                                                        secondpoint, None), 0)
 89              finally:
 90                  OpenSSL.BN_free(x0)
 91                  OpenSSL.BN_free(x1)
 92                  OpenSSL.BN_free(y0)
 93                  OpenSSL.BN_free(y1)
 94                  del obj
 95                  del obj2
 96  
 97      def test_serialize_bn(self):
 98          """Test Bignum serialization/deserialization"""
 99          for _ in range(1024):
100              obj = ECCBlind()
101              obj2 = ECCBlind()
102              randomnum = obj.d
103              serialized = obj._bn_serialize(randomnum)
104              secondnum = obj2._bn_deserialize(serialized)
105              self.assertEqual(OpenSSL.BN_cmp(randomnum, secondnum), 0)
106  
107      def test_blind_sig_many(self):
108          """Test a lot of blind signatures"""
109          for _ in range(1024):
110              self.test_blind_sig()
111  
112      def test_blind_sig_value(self):
113          """Test blind signature value checking"""
114          signer_obj = ECCBlind(value=5)
115          point_r = signer_obj.signer_init()
116          requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
117          msg = os.urandom(64)
118          msg_blinded = requester_obj.create_signing_request(point_r, msg)
119          signature_blinded = signer_obj.blind_sign(msg_blinded)
120          signature = requester_obj.unblind(signature_blinded)
121          verifier_obj = ECCBlind(pubkey=signer_obj.pubkey())
122          self.assertFalse(verifier_obj.verify(msg, signature, value=8))
123  
124      def test_blind_sig_expiration(self):
125          """Test blind signature expiration checking"""
126          signer_obj = ECCBlind(year=2020, month=1)
127          point_r = signer_obj.signer_init()
128          requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
129          msg = os.urandom(64)
130          msg_blinded = requester_obj.create_signing_request(point_r, msg)
131          signature_blinded = signer_obj.blind_sign(msg_blinded)
132          signature = requester_obj.unblind(signature_blinded)
133          verifier_obj = ECCBlind(pubkey=signer_obj.pubkey())
134          self.assertFalse(verifier_obj.verify(msg, signature))
135  
136      def test_blind_sig_chain(self):  # pylint: disable=too-many-locals
137          """Test blind signature chain using a random certifier key and a random message"""
138  
139          test_levels = 4
140          msg = os.urandom(1024)
141  
142          ca = ECCBlind()
143          signer_obj = ca
144  
145          output = bytearray()
146  
147          for level in range(test_levels):
148              if not level:
149                  output.extend(ca.pubkey())
150              requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
151              child_obj = ECCBlind()
152              point_r = signer_obj.signer_init()
153              pubkey = child_obj.pubkey()
154  
155              if level == test_levels - 1:
156                  msg_blinded = requester_obj.create_signing_request(point_r,
157                                                                     msg)
158              else:
159                  msg_blinded = requester_obj.create_signing_request(point_r,
160                                                                     pubkey)
161              signature_blinded = signer_obj.blind_sign(msg_blinded)
162              signature = requester_obj.unblind(signature_blinded)
163              if level != test_levels - 1:
164                  output.extend(pubkey)
165              output.extend(signature)
166              signer_obj = child_obj
167          verifychain = ECCBlindChain(ca=ca.pubkey(), chain=bytes(output))
168          self.assertTrue(verifychain.verify(msg=msg, value=1))
169  
170      def test_blind_sig_chain_wrong_ca(self):  # pylint: disable=too-many-locals
171          """Test blind signature chain with an unlisted ca"""
172  
173          test_levels = 4
174          msg = os.urandom(1024)
175  
176          ca = ECCBlind()
177          fake_ca = ECCBlind()
178          signer_obj = fake_ca
179  
180          output = bytearray()
181  
182          for level in range(test_levels):
183              requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
184              child_obj = ECCBlind()
185              if not level:
186                  # unlisted CA, but a syntactically valid pubkey
187                  output.extend(fake_ca.pubkey())
188              point_r = signer_obj.signer_init()
189              pubkey = child_obj.pubkey()
190  
191              if level == test_levels - 1:
192                  msg_blinded = requester_obj.create_signing_request(point_r,
193                                                                     msg)
194              else:
195                  msg_blinded = requester_obj.create_signing_request(point_r,
196                                                                     pubkey)
197              signature_blinded = signer_obj.blind_sign(msg_blinded)
198              signature = requester_obj.unblind(signature_blinded)
199              if level != test_levels - 1:
200                  output.extend(pubkey)
201              output.extend(signature)
202              signer_obj = child_obj
203          verifychain = ECCBlindChain(ca=ca.pubkey(), chain=bytes(output))
204          self.assertFalse(verifychain.verify(msg, 1))
205  
206      def test_blind_sig_chain_wrong_msg(self):  # pylint: disable=too-many-locals
207          """Test blind signature chain with a fake message"""
208  
209          test_levels = 4
210          msg = os.urandom(1024)
211          fake_msg = os.urandom(1024)
212  
213          ca = ECCBlind()
214          signer_obj = ca
215  
216          output = bytearray()
217  
218          for level in range(test_levels):
219              if not level:
220                  output.extend(ca.pubkey())
221              requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
222              child_obj = ECCBlind()
223              point_r = signer_obj.signer_init()
224              pubkey = child_obj.pubkey()
225  
226              if level == test_levels - 1:
227                  msg_blinded = requester_obj.create_signing_request(point_r,
228                                                                     msg)
229              else:
230                  msg_blinded = requester_obj.create_signing_request(point_r,
231                                                                     pubkey)
232              signature_blinded = signer_obj.blind_sign(msg_blinded)
233              signature = requester_obj.unblind(signature_blinded)
234              if level != test_levels - 1:
235                  output.extend(pubkey)
236              output.extend(signature)
237              signer_obj = child_obj
238          verifychain = ECCBlindChain(ca=ca.pubkey(), chain=bytes(output))
239          self.assertFalse(verifychain.verify(fake_msg, 1))
240  
241      def test_blind_sig_chain_wrong_intermediary(self):  # pylint: disable=too-many-locals
242          """Test blind signature chain using a fake intermediary pubkey"""
243  
244          test_levels = 4
245          msg = os.urandom(1024)
246          wrong_level = 2
247  
248          ca = ECCBlind()
249          signer_obj = ca
250          fake_intermediary = ECCBlind()
251  
252          output = bytearray()
253  
254          for level in range(test_levels):
255              if not level:
256                  output.extend(ca.pubkey())
257              requester_obj = ECCBlind(pubkey=signer_obj.pubkey())
258              child_obj = ECCBlind()
259              point_r = signer_obj.signer_init()
260              pubkey = child_obj.pubkey()
261  
262              if level == test_levels - 1:
263                  msg_blinded = requester_obj.create_signing_request(point_r,
264                                                                     msg)
265              else:
266                  msg_blinded = requester_obj.create_signing_request(point_r,
267                                                                     pubkey)
268              signature_blinded = signer_obj.blind_sign(msg_blinded)
269              signature = requester_obj.unblind(signature_blinded)
270              if level == wrong_level:
271                  output.extend(fake_intermediary.pubkey())
272              elif level != test_levels - 1:
273                  output.extend(pubkey)
274              output.extend(signature)
275              signer_obj = child_obj
276          verifychain = ECCBlindChain(ca=ca.pubkey(), chain=bytes(output))
277          self.assertFalse(verifychain.verify(msg, 1))