Blind chain signature verification
- also adds serialisation, deserialisation and optional metadata
This commit is contained in:
parent
32bb2a6e44
commit
213519bd93
77
src/pyelliptic/blindchain.py
Normal file
77
src/pyelliptic/blindchain.py
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
"""
|
||||||
|
Blind signature chain with a top level CA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from eccblind import ECCBlind
|
||||||
|
|
||||||
|
try:
|
||||||
|
import msgpack
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
import umsgpack as msgpack
|
||||||
|
except ImportError:
|
||||||
|
import fallback.umsgpack.umsgpack as msgpack
|
||||||
|
|
||||||
|
from eccblind import ECCBlind
|
||||||
|
|
||||||
|
def encode_datetime(obj):
|
||||||
|
"""
|
||||||
|
Method to format time
|
||||||
|
"""
|
||||||
|
return {'Time': int(obj.strftime("%s"))}
|
||||||
|
|
||||||
|
|
||||||
|
class ECCBlindChain(object): # pylint: disable=too-many-instance-attributes
|
||||||
|
"""
|
||||||
|
# Class for ECC Blind Chain signature functionality
|
||||||
|
"""
|
||||||
|
chain = []
|
||||||
|
ca = []
|
||||||
|
|
||||||
|
def __init__(self, chain=None):
|
||||||
|
if chain is not None:
|
||||||
|
self.chain = chain
|
||||||
|
|
||||||
|
def serialize(self):
|
||||||
|
data = msgpack.packb(self.chain)
|
||||||
|
return data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def deserialize(self, chain):
|
||||||
|
"""
|
||||||
|
Deserialize the data using msgpack
|
||||||
|
"""
|
||||||
|
data = msgpack.unpackb(chain)
|
||||||
|
return ECCBlindChain(data)
|
||||||
|
|
||||||
|
def add_level(self, pubkey, metadata, signature):
|
||||||
|
self.chain.append((pubkey, metadata, signature))
|
||||||
|
|
||||||
|
def add_ca(self, ca):
|
||||||
|
pubkey, metadata = ECCBlind.deserialize(ca)
|
||||||
|
self.ca.append(pubkey)
|
||||||
|
|
||||||
|
def verify(self, msg, value):
|
||||||
|
lastpubkey = None
|
||||||
|
retval = False
|
||||||
|
for level in reversed(self.chain):
|
||||||
|
pubkey, metadata, signature = level
|
||||||
|
verifier_obj = ECCBlind(pubkey=pubkey, metadata)
|
||||||
|
if not lastpubkey:
|
||||||
|
retval = verifier_obj.verify(msg, signature, value)
|
||||||
|
else:
|
||||||
|
retval = verifier_obj.verify(lastpubkey, signature, value)
|
||||||
|
if not reval:
|
||||||
|
break
|
||||||
|
lastpubkey = pubkey
|
||||||
|
if retval:
|
||||||
|
retval = False
|
||||||
|
for ca in self.ca:
|
||||||
|
match = True
|
||||||
|
for i in range(4):
|
||||||
|
if lastpubkey[i] != ca[i]:
|
||||||
|
match = False
|
||||||
|
break
|
||||||
|
if match:
|
||||||
|
return True
|
||||||
|
return retval
|
|
@ -10,9 +10,52 @@ http://www.isecure-journal.com/article_39171_47f9ec605dd3918c2793565ec21fcd7a.pd
|
||||||
# variable names are based on the math in the paper, so they don't conform
|
# variable names are based on the math in the paper, so they don't conform
|
||||||
# to PEP8
|
# to PEP8
|
||||||
|
|
||||||
|
from hashlib import sha256
|
||||||
|
import time
|
||||||
|
|
||||||
|
try:
|
||||||
|
import msgpack
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
import umsgpack as msgpack
|
||||||
|
except ImportError:
|
||||||
|
import fallback.umsgpack.umsgpack as msgpack
|
||||||
|
|
||||||
from .openssl import OpenSSL
|
from .openssl import OpenSSL
|
||||||
|
|
||||||
|
|
||||||
|
class Metadata(object):
|
||||||
|
"""
|
||||||
|
Pubkey metadata
|
||||||
|
"""
|
||||||
|
def __init__(self, exp=0, value=0):
|
||||||
|
self.exp = 0
|
||||||
|
self.value = 0
|
||||||
|
if exp:
|
||||||
|
self.exp = exp
|
||||||
|
if value:
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def serialize(self):
|
||||||
|
if self.exp or self.value:
|
||||||
|
return [self.exp, self.value]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def deserialize(self, data):
|
||||||
|
exp, value = data
|
||||||
|
return Medatadata(exp, value)
|
||||||
|
|
||||||
|
def verify(self, value):
|
||||||
|
if self.value and value > self.value:
|
||||||
|
return False
|
||||||
|
if self.exp:
|
||||||
|
if time.time() > self.exp:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class ECCBlind(object): # pylint: disable=too-many-instance-attributes
|
class ECCBlind(object): # pylint: disable=too-many-instance-attributes
|
||||||
"""
|
"""
|
||||||
Class for ECC blind signature functionality
|
Class for ECC blind signature functionality
|
||||||
|
@ -75,7 +118,21 @@ class ECCBlind(object): # pylint: disable=too-many-instance-attributes
|
||||||
OpenSSL.EC_POINT_get_affine_coordinates_GFp(group, F, x0, y0, ctx)
|
OpenSSL.EC_POINT_get_affine_coordinates_GFp(group, F, x0, y0, ctx)
|
||||||
return x0
|
return x0
|
||||||
|
|
||||||
def __init__(self, curve="secp256k1", pubkey=None):
|
@staticmethod
|
||||||
|
def deserialize(self, data):
|
||||||
|
pubkey_deserialized, meta = msgpack.unpackb(data)
|
||||||
|
if meta:
|
||||||
|
obj = ECCBlind(pubkey=pubkey_deserialized, metadata=meta)
|
||||||
|
else:
|
||||||
|
obj = ECCBlind(pubkey=pubkey_deserialized)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def serialize(self):
|
||||||
|
data = (self.pubkey, self.metadata.serialize)
|
||||||
|
retval = msgpack.packb(data)
|
||||||
|
return retval
|
||||||
|
|
||||||
|
def __init__(self, curve="secp256k1", pubkey=None, metadata=None):
|
||||||
self.ctx = OpenSSL.BN_CTX_new()
|
self.ctx = OpenSSL.BN_CTX_new()
|
||||||
|
|
||||||
if pubkey:
|
if pubkey:
|
||||||
|
@ -101,6 +158,14 @@ class ECCBlind(object): # pylint: disable=too-many-instance-attributes
|
||||||
self.iO = OpenSSL.EC_POINT_new(self.group)
|
self.iO = OpenSSL.EC_POINT_new(self.group)
|
||||||
OpenSSL.EC_POINT_set_to_infinity(self.group, self.iO)
|
OpenSSL.EC_POINT_set_to_infinity(self.group, self.iO)
|
||||||
|
|
||||||
|
if metadata:
|
||||||
|
self._set_metadata(self, metadata)
|
||||||
|
else:
|
||||||
|
self.metadata = Metadata()
|
||||||
|
|
||||||
|
def _set_metadata(self, metadata):
|
||||||
|
self.metadata = Metadata.deserialise(metadata)
|
||||||
|
|
||||||
def signer_init(self):
|
def signer_init(self):
|
||||||
"""
|
"""
|
||||||
Init signer
|
Init signer
|
||||||
|
@ -119,6 +184,7 @@ class ECCBlind(object): # pylint: disable=too-many-instance-attributes
|
||||||
Requester creates a new signing request
|
Requester creates a new signing request
|
||||||
"""
|
"""
|
||||||
self.R = R
|
self.R = R
|
||||||
|
msghash = sha256(msg)
|
||||||
|
|
||||||
# Requester: 3 random blinding factors
|
# Requester: 3 random blinding factors
|
||||||
self.F = OpenSSL.EC_POINT_new(self.group)
|
self.F = OpenSSL.EC_POINT_new(self.group)
|
||||||
|
@ -151,7 +217,7 @@ class ECCBlind(object): # pylint: disable=too-many-instance-attributes
|
||||||
|
|
||||||
# Requester: Blinding (m' = br(m) + a)
|
# Requester: Blinding (m' = br(m) + a)
|
||||||
self.m = OpenSSL.BN_new()
|
self.m = OpenSSL.BN_new()
|
||||||
OpenSSL.BN_bin2bn(msg, len(msg), self.m)
|
OpenSSL.BN_bin2bn(msghash, len(msghash), self.m)
|
||||||
|
|
||||||
self.m_ = OpenSSL.BN_new()
|
self.m_ = OpenSSL.BN_new()
|
||||||
OpenSSL.BN_mod_mul(self.m_, self.b, self.r, self.n, self.ctx)
|
OpenSSL.BN_mod_mul(self.m_, self.b, self.r, self.n, self.ctx)
|
||||||
|
@ -180,14 +246,15 @@ class ECCBlind(object): # pylint: disable=too-many-instance-attributes
|
||||||
self.signature = (s, self.F)
|
self.signature = (s, self.F)
|
||||||
return self.signature
|
return self.signature
|
||||||
|
|
||||||
def verify(self, msg, signature):
|
def verify(self, msg, signature, value=0):
|
||||||
"""
|
"""
|
||||||
Verify signature with certifier's pubkey
|
Verify signature with certifier's pubkey
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# convert msg to BIGNUM
|
# convert msg to BIGNUM
|
||||||
self.m = OpenSSL.BN_new()
|
self.m = OpenSSL.BN_new()
|
||||||
OpenSSL.BN_bin2bn(msg, len(msg), self.m)
|
msghash = sha256(msg)
|
||||||
|
OpenSSL.BN_bin2bn(msghash, len(msghash), self.m)
|
||||||
|
|
||||||
# init
|
# init
|
||||||
s, self.F = signature
|
s, self.F = signature
|
||||||
|
@ -206,5 +273,8 @@ class ECCBlind(object): # pylint: disable=too-many-instance-attributes
|
||||||
retval = OpenSSL.EC_POINT_cmp(self.group, lhs, rhs, self.ctx)
|
retval = OpenSSL.EC_POINT_cmp(self.group, lhs, rhs, self.ctx)
|
||||||
if retval == -1:
|
if retval == -1:
|
||||||
raise RuntimeError("EC_POINT_cmp returned an error")
|
raise RuntimeError("EC_POINT_cmp returned an error")
|
||||||
else:
|
elif retval != 0:
|
||||||
return retval == 0
|
return False
|
||||||
|
elif self.metadata:
|
||||||
|
return self.metadata.verify(value)
|
||||||
|
return True
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
Test for ECC blind signatures
|
Test for ECC blind signatures
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
from ctypes import cast, c_char_p
|
from ctypes import cast, c_char_p
|
||||||
|
|
||||||
from pybitmessage.pyelliptic.eccblind import ECCBlind
|
from pybitmessage.pyelliptic.eccblind import ECCBlind, Metadata
|
||||||
|
from pybitmessage.pyelliptic.eccblindchain import ECCBlindChain
|
||||||
from pybitmessage.pyelliptic.openssl import OpenSSL
|
from pybitmessage.pyelliptic.openssl import OpenSSL
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,3 +52,47 @@ class TestBlindSig(unittest.TestCase):
|
||||||
# (5) Verification
|
# (5) Verification
|
||||||
verifier_obj = ECCBlind(pubkey=signer_obj.pubkey)
|
verifier_obj = ECCBlind(pubkey=signer_obj.pubkey)
|
||||||
self.assertTrue(verifier_obj.verify(msg, signature))
|
self.assertTrue(verifier_obj.verify(msg, signature))
|
||||||
|
|
||||||
|
# Serialization and deserialisation
|
||||||
|
pk = signer_obj.serialize()
|
||||||
|
pko = ECCBlind.deserialize(pk)
|
||||||
|
self.assertTrue(pko.verify(msg, signature))
|
||||||
|
|
||||||
|
def test_blind_sig_chain(self):
|
||||||
|
"""Test blind signature chain using a random certifier key and a random message"""
|
||||||
|
|
||||||
|
test_levels = 5
|
||||||
|
value = 1
|
||||||
|
msg = os.urandom(1024)
|
||||||
|
|
||||||
|
chain = ECCBlindChain()
|
||||||
|
ca = ECCBlind()
|
||||||
|
signer_obj = ca
|
||||||
|
signer_pubkey = signer_obj.serialize()
|
||||||
|
|
||||||
|
for level in range(test_levels):
|
||||||
|
if level == 0:
|
||||||
|
metadata = Metadata(exp=int(time.time()) + 100,
|
||||||
|
value=value).serialize()
|
||||||
|
requester_obj = ECCBlind(pubkey=signer_obj.pubkey,
|
||||||
|
metadata=metadata)
|
||||||
|
else:
|
||||||
|
requester_obj = ECCBlind(pubkey=signer_obj.pubkey)
|
||||||
|
point_r = signer_obj.signer_init()
|
||||||
|
|
||||||
|
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,
|
||||||
|
signer_pubkey)
|
||||||
|
signature_blinded = signer_obj.blind_sign(msg_blinded)
|
||||||
|
signature = requester_obj.unblind(signature_blinded)
|
||||||
|
chain.add_level(signer_obj.pubkey,
|
||||||
|
signer_obj.metadata.serialize,
|
||||||
|
signature)
|
||||||
|
signer_obj = requester_obj
|
||||||
|
signer_pubkey = requester_obj.serialize()
|
||||||
|
sigchain = chain.serialize()
|
||||||
|
verifychain = ECCBlindChain.deserialize(sigchain)
|
||||||
|
self.assertTrue(verifychain.verify(msg, value))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user