This repository has been archived on 2024-12-02. You can view files and clone it, but cannot push or open issues or pull requests.
PyBitmessage-2024-12-02/src/pyelliptic/eccblind.py
Peter Surda 213519bd93
Blind chain signature verification
- also adds serialisation, deserialisation and optional metadata
2020-03-14 10:20:06 +08:00

281 lines
8.0 KiB
Python

#!/usr/bin/env python
"""
ECC blind signature functionality based on
"An Efficient Blind Signature Scheme
Based on the Elliptic CurveDiscrete Logarithm Problem" by Morteza Nikooghadama
<mnikooghadam@sbu.ac.ir> and Ali Zakerolhosseini <a-zaker@sbu.ac.ir>,
http://www.isecure-journal.com/article_39171_47f9ec605dd3918c2793565ec21fcd7a.pdf
"""
# variable names are based on the math in the paper, so they don't conform
# 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
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 for ECC blind signature functionality
"""
# init
k = None
R = None
keypair = None
F = None
Q = None
a = None
b = None
c = None
binv = None
r = None
m = None
m_ = None
s_ = None
signature = None
@staticmethod
def ec_get_random(group, ctx):
"""
Random point from finite field
"""
order = OpenSSL.BN_new()
OpenSSL.EC_GROUP_get_order(group, order, ctx)
OpenSSL.BN_rand(order, OpenSSL.BN_num_bits(order), 0, 0)
return order
@staticmethod
def ec_invert(group, a, ctx):
"""
ECC inversion
"""
order = OpenSSL.BN_new()
OpenSSL.EC_GROUP_get_order(group, order, ctx)
inverse = OpenSSL.BN_mod_inverse(0, a, order, ctx)
return inverse
@staticmethod
def ec_gen_keypair(group, ctx):
"""
Generate an ECC keypair
"""
d = ECCBlind.ec_get_random(group, ctx)
Q = OpenSSL.EC_POINT_new(group)
OpenSSL.EC_POINT_mul(group, Q, d, 0, 0, 0)
return (d, Q)
@staticmethod
def ec_Ftor(F, group, ctx):
"""
x0 coordinate of F
"""
# F = (x0, y0)
x0 = OpenSSL.BN_new()
y0 = OpenSSL.BN_new()
OpenSSL.EC_POINT_get_affine_coordinates_GFp(group, F, x0, y0, ctx)
return x0
@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()
if pubkey:
self.group, self.G, self.n, self.Q = pubkey
else:
self.group = OpenSSL.EC_GROUP_new_by_curve_name(
OpenSSL.get_curve(curve))
# Order n
self.n = OpenSSL.BN_new()
OpenSSL.EC_GROUP_get_order(self.group, self.n, self.ctx)
# Generator G
self.G = OpenSSL.EC_GROUP_get0_generator(self.group)
# new keypair
self.keypair = ECCBlind.ec_gen_keypair(self.group, self.ctx)
self.Q = self.keypair[1]
self.pubkey = (self.group, self.G, self.n, self.Q)
# Identity O (infinity)
self.iO = OpenSSL.EC_POINT_new(self.group)
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):
"""
Init signer
"""
# Signer: Random integer k
self.k = ECCBlind.ec_get_random(self.group, self.ctx)
# R = kG
self.R = OpenSSL.EC_POINT_new(self.group)
OpenSSL.EC_POINT_mul(self.group, self.R, self.k, 0, 0, 0)
return self.R
def create_signing_request(self, R, msg):
"""
Requester creates a new signing request
"""
self.R = R
msghash = sha256(msg)
# Requester: 3 random blinding factors
self.F = OpenSSL.EC_POINT_new(self.group)
OpenSSL.EC_POINT_set_to_infinity(self.group, self.F)
temp = OpenSSL.EC_POINT_new(self.group)
abinv = OpenSSL.BN_new()
# F != O
while OpenSSL.EC_POINT_cmp(self.group, self.F, self.iO, self.ctx) == 0:
self.a = ECCBlind.ec_get_random(self.group, self.ctx)
self.b = ECCBlind.ec_get_random(self.group, self.ctx)
self.c = ECCBlind.ec_get_random(self.group, self.ctx)
# F = b^-1 * R...
self.binv = ECCBlind.ec_invert(self.group, self.b, self.ctx)
OpenSSL.EC_POINT_mul(self.group, temp, 0, self.R, self.binv, 0)
OpenSSL.EC_POINT_copy(self.F, temp)
# ... + a*b^-1 * Q...
OpenSSL.BN_mul(abinv, self.a, self.binv, self.ctx)
OpenSSL.EC_POINT_mul(self.group, temp, 0, self.Q, abinv, 0)
OpenSSL.EC_POINT_add(self.group, self.F, self.F, temp, 0)
# ... + c*G
OpenSSL.EC_POINT_mul(self.group, temp, 0, self.G, self.c, 0)
OpenSSL.EC_POINT_add(self.group, self.F, self.F, temp, 0)
# F = (x0, y0)
self.r = ECCBlind.ec_Ftor(self.F, self.group, self.ctx)
# Requester: Blinding (m' = br(m) + a)
self.m = OpenSSL.BN_new()
OpenSSL.BN_bin2bn(msghash, len(msghash), self.m)
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.m_, self.m, self.n, self.ctx)
OpenSSL.BN_mod_add(self.m_, self.m_, self.a, self.n, self.ctx)
return self.m_
def blind_sign(self, m_):
"""
Signer blind-signs the request
"""
self.m_ = m_
self.s_ = OpenSSL.BN_new()
OpenSSL.BN_mod_mul(self.s_, self.keypair[0], self.m_, self.n, self.ctx)
OpenSSL.BN_mod_add(self.s_, self.s_, self.k, self.n, self.ctx)
return self.s_
def unblind(self, s_):
"""
Requester unblinds the signature
"""
self.s_ = s_
s = OpenSSL.BN_new()
OpenSSL.BN_mod_mul(s, self.binv, self.s_, self.n, self.ctx)
OpenSSL.BN_mod_add(s, s, self.c, self.n, self.ctx)
self.signature = (s, self.F)
return self.signature
def verify(self, msg, signature, value=0):
"""
Verify signature with certifier's pubkey
"""
# convert msg to BIGNUM
self.m = OpenSSL.BN_new()
msghash = sha256(msg)
OpenSSL.BN_bin2bn(msghash, len(msghash), self.m)
# init
s, self.F = signature
if self.r is None:
self.r = ECCBlind.ec_Ftor(self.F, self.group, self.ctx)
lhs = OpenSSL.EC_POINT_new(self.group)
rhs = OpenSSL.EC_POINT_new(self.group)
OpenSSL.EC_POINT_mul(self.group, lhs, s, 0, 0, 0)
OpenSSL.EC_POINT_mul(self.group, rhs, 0, self.Q, self.m, 0)
OpenSSL.EC_POINT_mul(self.group, rhs, 0, rhs, self.r, 0)
OpenSSL.EC_POINT_add(self.group, rhs, rhs, self.F, self.ctx)
retval = OpenSSL.EC_POINT_cmp(self.group, lhs, rhs, self.ctx)
if retval == -1:
raise RuntimeError("EC_POINT_cmp returned an error")
elif retval != 0:
return False
elif self.metadata:
return self.metadata.verify(value)
return True