#!/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 and Ali Zakerolhosseini , 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 # pylint: disable=invalid-name from .openssl import OpenSSL 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 def __init__(self, curve="secp256k1", pubkey=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) 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 # 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(msg, len(msg), 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): """ Verify signature with certifier's pubkey """ # convert msg to BIGNUM self.m = OpenSSL.BN_new() OpenSSL.BN_bin2bn(msg, len(msg), 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") else: return retval == 0