Blind signature support in pyelliptic

- add blind signature functionality to pyelliptic as described in #1409
- add tests for blind signatures
- PEP8 fixes for pyelliptic
- some minor refactoring is necessary for further integration, this is just a
  minimal implementation to pass a test
This commit is contained in:
Peter Šurda 2019-08-26 17:46:25 +02:00
parent f180b1a5b0
commit 2e0f7755c6
Signed by: PeterSurda
GPG Key ID: 0C5F50C0B5F37D87
7 changed files with 394 additions and 51 deletions

View File

@ -2,18 +2,20 @@
# Author: Yann GUIBET # Author: Yann GUIBET
# Contact: <yannguibet@gmail.com> # Contact: <yannguibet@gmail.com>
from .openssl import OpenSSL
from .ecc import ECC
from .eccblind import ECCBlind
from .cipher import Cipher
from .hash import hmac_sha256, hmac_sha512, pbkdf2
__version__ = '1.3' __version__ = '1.3'
__all__ = [ __all__ = [
'OpenSSL', 'OpenSSL',
'ECC', 'ECC',
'ECCBlind',
'Cipher', 'Cipher',
'hmac_sha256', 'hmac_sha256',
'hmac_sha512', 'hmac_sha512',
'pbkdf2' 'pbkdf2'
] ]
from .openssl import OpenSSL
from .ecc import ECC
from .cipher import Cipher
from .hash import hmac_sha256, hmac_sha512, pbkdf2

View File

@ -4,7 +4,7 @@
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com> # Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
# See LICENSE for details. # See LICENSE for details.
from pyelliptic.openssl import OpenSSL from openssl import OpenSSL
class Cipher: class Cipher:

View File

@ -12,9 +12,9 @@ src/pyelliptic/ecc.py
from hashlib import sha512 from hashlib import sha512
from struct import pack, unpack from struct import pack, unpack
from pyelliptic.cipher import Cipher from cipher import Cipher
from pyelliptic.hash import equals, hmac_sha256 from hash import equals, hmac_sha256
from pyelliptic.openssl import OpenSSL from openssl import OpenSSL
class ECC(object): class ECC(object):

179
src/pyelliptic/eccblind.py Normal file
View File

@ -0,0 +1,179 @@
#!/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
# 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)
def __init__(self, curve="secp256k1"):
self.group = OpenSSL.EC_GROUP_new_by_curve_name(OpenSSL.get_curve(curve))
self.ctx = OpenSSL.BN_CTX_new()
# Order n
self.n = OpenSSL.BN_new()
OpenSSL.EC_GROUP_get_order(self.group, self.n, self.ctx)
# Identity O (infinity)
self.iO = OpenSSL.EC_POINT_new(self.group)
OpenSSL.EC_POINT_set_to_infinity(self.group, self.iO)
# Generator G
self.G = OpenSSL.EC_GROUP_get0_generator(self.group)
# Certifier's pubkey
self.pubkey = (self.group, self.G, self.n)
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)
def create_signing_request(self, msg):
"""
Requester creates a new signing request
"""
# new keypair
self.keypair = ECCBlind.ec_gen_keypair(self.group, self.ctx)
# 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)
self.Q = self.keypair[1]
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)
x0 = OpenSSL.BN_new()
y0 = OpenSSL.BN_new()
OpenSSL.EC_POINT_get_affine_coordinates_GFp(self.group, self.F, x0, y0,
self.ctx)
self.r = x0
# 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)
def blind_sign(self):
"""
Signer blind-signs the request
"""
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)
def unblind(self):
"""
Requester unblinds the signature
"""
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)
def verify(self):
"""
Verify signature with certifier's pubkey
"""
lhs = OpenSSL.EC_POINT_new(self.group)
rhs = OpenSSL.EC_POINT_new(self.group)
OpenSSL.EC_POINT_mul(self.group, lhs, self.signature[0], 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

View File

@ -4,7 +4,7 @@
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com> # Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
# See LICENSE for details. # See LICENSE for details.
from pyelliptic.openssl import OpenSSL from openssl import OpenSSL
# For python3 # For python3

View File

@ -19,7 +19,9 @@ class CipherName:
self._blocksize = blocksize self._blocksize = blocksize
def __str__(self): def __str__(self):
return "Cipher : " + self._name + " | Blocksize : " + str(self._blocksize) + " | Fonction pointer : " + str(self._pointer) return "Cipher : " + self._name + \
" | Blocksize : " + str(self._blocksize) + \
" | Function pointer : " + str(self._pointer)
def get_pointer(self): def get_pointer(self):
return self._pointer() return self._pointer()
@ -130,7 +132,11 @@ class _OpenSSL:
self.EC_POINT_get_affine_coordinates_GFp = self._lib.EC_POINT_get_affine_coordinates_GFp self.EC_POINT_get_affine_coordinates_GFp = self._lib.EC_POINT_get_affine_coordinates_GFp
self.EC_POINT_get_affine_coordinates_GFp.restype = ctypes.c_int self.EC_POINT_get_affine_coordinates_GFp.restype = ctypes.c_int
self.EC_POINT_get_affine_coordinates_GFp.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] self.EC_POINT_get_affine_coordinates_GFp.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.EC_KEY_set_private_key = self._lib.EC_KEY_set_private_key self.EC_KEY_set_private_key = self._lib.EC_KEY_set_private_key
self.EC_KEY_set_private_key.restype = ctypes.c_int self.EC_KEY_set_private_key.restype = ctypes.c_int
@ -144,11 +150,16 @@ class _OpenSSL:
self.EC_KEY_set_group = self._lib.EC_KEY_set_group self.EC_KEY_set_group = self._lib.EC_KEY_set_group
self.EC_KEY_set_group.restype = ctypes.c_int self.EC_KEY_set_group.restype = ctypes.c_int
self.EC_KEY_set_group.argtypes = [ctypes.c_void_p, ctypes.c_void_p] self.EC_KEY_set_group.argtypes = [ctypes.c_void_p,
ctypes.c_void_p]
self.EC_POINT_set_affine_coordinates_GFp = self._lib.EC_POINT_set_affine_coordinates_GFp self.EC_POINT_set_affine_coordinates_GFp = self._lib.EC_POINT_set_affine_coordinates_GFp
self.EC_POINT_set_affine_coordinates_GFp.restype = ctypes.c_int self.EC_POINT_set_affine_coordinates_GFp.restype = ctypes.c_int
self.EC_POINT_set_affine_coordinates_GFp.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] self.EC_POINT_set_affine_coordinates_GFp.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.EC_POINT_new = self._lib.EC_POINT_new self.EC_POINT_new = self._lib.EC_POINT_new
self.EC_POINT_new.restype = ctypes.c_void_p self.EC_POINT_new.restype = ctypes.c_void_p
@ -164,7 +175,11 @@ class _OpenSSL:
self.EC_POINT_mul = self._lib.EC_POINT_mul self.EC_POINT_mul = self._lib.EC_POINT_mul
self.EC_POINT_mul.restype = None self.EC_POINT_mul.restype = None
self.EC_POINT_mul.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] self.EC_POINT_mul.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.EC_KEY_set_private_key = self._lib.EC_KEY_set_private_key self.EC_KEY_set_private_key = self._lib.EC_KEY_set_private_key
self.EC_KEY_set_private_key.restype = ctypes.c_int self.EC_KEY_set_private_key.restype = ctypes.c_int
@ -178,7 +193,8 @@ class _OpenSSL:
self.EC_KEY_set_method = self._lib.EC_KEY_set_method self.EC_KEY_set_method = self._lib.EC_KEY_set_method
self._lib.EC_KEY_set_method.restype = ctypes.c_int self._lib.EC_KEY_set_method.restype = ctypes.c_int
self._lib.EC_KEY_set_method.argtypes = [ctypes.c_void_p, ctypes.c_void_p] self._lib.EC_KEY_set_method.argtypes = [ctypes.c_void_p,
ctypes.c_void_p]
else: else:
self.ECDH_OpenSSL = self._lib.ECDH_OpenSSL self.ECDH_OpenSSL = self._lib.ECDH_OpenSSL
self._lib.ECDH_OpenSSL.restype = ctypes.c_void_p self._lib.ECDH_OpenSSL.restype = ctypes.c_void_p
@ -186,7 +202,8 @@ class _OpenSSL:
self.ECDH_set_method = self._lib.ECDH_set_method self.ECDH_set_method = self._lib.ECDH_set_method
self._lib.ECDH_set_method.restype = ctypes.c_int self._lib.ECDH_set_method.restype = ctypes.c_int
self._lib.ECDH_set_method.argtypes = [ctypes.c_void_p, ctypes.c_void_p] self._lib.ECDH_set_method.argtypes = [ctypes.c_void_p,
ctypes.c_void_p]
self.BN_CTX_new = self._lib.BN_CTX_new self.BN_CTX_new = self._lib.BN_CTX_new
self._lib.BN_CTX_new.restype = ctypes.c_void_p self._lib.BN_CTX_new.restype = ctypes.c_void_p
@ -195,12 +212,15 @@ class _OpenSSL:
self.ECDH_compute_key = self._lib.ECDH_compute_key self.ECDH_compute_key = self._lib.ECDH_compute_key
self.ECDH_compute_key.restype = ctypes.c_int self.ECDH_compute_key.restype = ctypes.c_int
self.ECDH_compute_key.argtypes = [ctypes.c_void_p, self.ECDH_compute_key.argtypes = [ctypes.c_void_p,
ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p] ctypes.c_int,
ctypes.c_void_p,
ctypes.c_void_p]
self.EVP_CipherInit_ex = self._lib.EVP_CipherInit_ex self.EVP_CipherInit_ex = self._lib.EVP_CipherInit_ex
self.EVP_CipherInit_ex.restype = ctypes.c_int self.EVP_CipherInit_ex.restype = ctypes.c_int
self.EVP_CipherInit_ex.argtypes = [ctypes.c_void_p, self.EVP_CipherInit_ex.argtypes = [ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_void_p] ctypes.c_void_p,
ctypes.c_void_p]
self.EVP_CIPHER_CTX_new = self._lib.EVP_CIPHER_CTX_new self.EVP_CIPHER_CTX_new = self._lib.EVP_CIPHER_CTX_new
self.EVP_CIPHER_CTX_new.restype = ctypes.c_void_p self.EVP_CIPHER_CTX_new.restype = ctypes.c_void_p
@ -377,6 +397,124 @@ class _OpenSSL:
ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p,
ctypes.c_int, ctypes.c_void_p] ctypes.c_int, ctypes.c_void_p]
# Blind signature requirements
self.BN_CTX_new = self._lib.BN_CTX_new
self.BN_CTX_new.restype = ctypes.c_void_p
self.BN_CTX_new.argtypes = []
self.BN_dup = self._lib.BN_dup
self.BN_dup.restype = ctypes.c_void_p
self.BN_dup.argtypes = [ctypes.c_void_p]
self.BN_rand = self._lib.BN_rand
self.BN_rand.restype = ctypes.c_int
self.BN_rand.argtypes = [ctypes.c_void_p,
ctypes.c_int,
ctypes.c_int]
self.BN_set_word = self._lib.BN_set_word
self.BN_set_word.restype = ctypes.c_int
self.BN_set_word.argtypes = [ctypes.c_void_p,
ctypes.c_ulong]
self.BN_mul = self._lib.BN_mul
self.BN_mul.restype = ctypes.c_int
self.BN_mul.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.BN_mod_add = self._lib.BN_mod_add
self.BN_mod_add.restype = ctypes.c_int
self.BN_mod_add.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.BN_mod_inverse = self._lib.BN_mod_inverse
self.BN_mod_inverse.restype = ctypes.c_void_p
self.BN_mod_inverse.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.BN_mod_mul = self._lib.BN_mod_mul
self.BN_mod_mul.restype = ctypes.c_int
self.BN_mod_mul.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.BN_lshift = self._lib.BN_lshift
self.BN_lshift.restype = ctypes.c_int
self.BN_lshift.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_int]
self.BN_sub_word = self._lib.BN_sub_word
self.BN_sub_word.restype = ctypes.c_int
self.BN_sub_word.argtypes = [ctypes.c_void_p,
ctypes.c_ulong]
self.BN_cmp = self._lib.BN_cmp
self.BN_cmp.restype = ctypes.c_int
self.BN_cmp.argtypes = [ctypes.c_void_p,
ctypes.c_void_p]
self.BN_bn2dec = self._lib.BN_bn2dec
self.BN_bn2dec.restype = ctypes.c_char_p
self.BN_bn2dec.argtypes = [ctypes.c_void_p]
self.BN_CTX_free = self._lib.BN_CTX_free
self.BN_CTX_free.argtypes = [ctypes.c_void_p]
self.EC_GROUP_new_by_curve_name = self._lib.EC_GROUP_new_by_curve_name
self.EC_GROUP_new_by_curve_name.restype = ctypes.c_void_p
self.EC_GROUP_new_by_curve_name.argtypes = [ctypes.c_int]
self.EC_GROUP_get_order = self._lib.EC_GROUP_get_order
self.EC_GROUP_get_order.restype = ctypes.c_int
self.EC_GROUP_get_order.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.EC_GROUP_get_cofactor = self._lib.EC_GROUP_get_cofactor
self.EC_GROUP_get_cofactor.restype = ctypes.c_int
self.EC_GROUP_get_cofactor.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.EC_GROUP_get0_generator = self._lib.EC_GROUP_get0_generator
self.EC_GROUP_get0_generator.restype = ctypes.c_void_p
self.EC_GROUP_get0_generator.argtypes = [ctypes.c_void_p]
self.EC_POINT_copy = self._lib.EC_POINT_copy
self.EC_POINT_copy.restype = ctypes.c_int
self.EC_POINT_copy.argtypes = [ctypes.c_void_p,
ctypes.c_void_p]
self.EC_POINT_add = self._lib.EC_POINT_add
self.EC_POINT_add.restype = ctypes.c_int
self.EC_POINT_add.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.EC_POINT_cmp = self._lib.EC_POINT_cmp
self.EC_POINT_cmp.restype = ctypes.c_int
self.EC_POINT_cmp.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.EC_POINT_set_to_infinity = self._lib.EC_POINT_set_to_infinity
self.EC_POINT_set_to_infinity.restype = ctypes.c_int
self.EC_POINT_set_to_infinity.argtypes = [ctypes.c_void_p,
ctypes.c_void_p]
self._set_ciphers() self._set_ciphers()
self._set_curves() self._set_curves()
@ -494,6 +632,7 @@ class _OpenSSL:
buffer = self.create_string_buffer(size) buffer = self.create_string_buffer(size)
return buffer return buffer
def loadOpenSSL(): def loadOpenSSL():
global OpenSSL global OpenSSL
from os import path, environ from os import path, environ
@ -548,4 +687,5 @@ def loadOpenSSL():
pass pass
raise Exception("Couldn't find and load the OpenSSL library. You must install it.") raise Exception("Couldn't find and load the OpenSSL library. You must install it.")
loadOpenSSL() loadOpenSSL()

View File

@ -0,0 +1,22 @@
"""
Test for ECC blind signatures
"""
import os
import unittest
from src.pyelliptic.eccblind import ECCBlind
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"""
blind_sig = ECCBlind()
blind_sig.signer_init()
msg = os.urandom(64)
blind_sig.create_signing_request(msg)
blind_sig.blind_sign()
blind_sig.unblind()
self.assertTrue(blind_sig.verify())