|
|
|
@ -1,17 +1,23 @@
|
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
"""
|
|
|
|
|
src/pyelliptic/ecc.py
|
|
|
|
|
=====================
|
|
|
|
|
"""
|
|
|
|
|
# pylint: disable=protected-access
|
|
|
|
|
|
|
|
|
|
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
|
|
|
|
|
# See LICENSE for details.
|
|
|
|
|
|
|
|
|
|
from hashlib import sha512
|
|
|
|
|
from pyelliptic.openssl import OpenSSL
|
|
|
|
|
from pyelliptic.cipher import Cipher
|
|
|
|
|
from pyelliptic.hash import hmac_sha256, equals
|
|
|
|
|
from struct import pack, unpack
|
|
|
|
|
|
|
|
|
|
from pyelliptic.cipher import Cipher
|
|
|
|
|
from pyelliptic.hash import equals, hmac_sha256
|
|
|
|
|
from pyelliptic.openssl import OpenSSL
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ECC:
|
|
|
|
|
class ECC(object):
|
|
|
|
|
"""
|
|
|
|
|
Asymmetric encryption with Elliptic Curve Cryptography (ECC)
|
|
|
|
|
ECDH, ECDSA and ECIES
|
|
|
|
@ -40,13 +46,21 @@ class ECC:
|
|
|
|
|
>>> print bob.get_ecdh_key(alice.get_pubkey()).encode('hex')
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
def __init__(self, pubkey=None, privkey=None, pubkey_x=None,
|
|
|
|
|
pubkey_y=None, raw_privkey=None, curve='sect283r1'):
|
|
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
|
self,
|
|
|
|
|
pubkey=None,
|
|
|
|
|
privkey=None,
|
|
|
|
|
pubkey_x=None,
|
|
|
|
|
pubkey_y=None,
|
|
|
|
|
raw_privkey=None,
|
|
|
|
|
curve='sect283r1',
|
|
|
|
|
): # pylint: disable=too-many-arguments
|
|
|
|
|
"""
|
|
|
|
|
For a normal and High level use, specifie pubkey,
|
|
|
|
|
privkey (if you need) and the curve
|
|
|
|
|
"""
|
|
|
|
|
if type(curve) == str:
|
|
|
|
|
if isinstance(curve, str):
|
|
|
|
|
self.curve = OpenSSL.get_curve(curve)
|
|
|
|
|
else:
|
|
|
|
|
self.curve = curve
|
|
|
|
@ -54,9 +68,9 @@ class ECC:
|
|
|
|
|
if pubkey_x is not None and pubkey_y is not None:
|
|
|
|
|
self._set_keys(pubkey_x, pubkey_y, raw_privkey)
|
|
|
|
|
elif pubkey is not None:
|
|
|
|
|
curve, pubkey_x, pubkey_y, i = ECC._decode_pubkey(pubkey)
|
|
|
|
|
curve, pubkey_x, pubkey_y, _ = ECC._decode_pubkey(pubkey)
|
|
|
|
|
if privkey is not None:
|
|
|
|
|
curve2, raw_privkey, i = ECC._decode_privkey(privkey)
|
|
|
|
|
curve2, raw_privkey, _ = ECC._decode_privkey(privkey)
|
|
|
|
|
if curve != curve2:
|
|
|
|
|
raise Exception("Bad ECC keys ...")
|
|
|
|
|
self.curve = curve
|
|
|
|
@ -83,9 +97,11 @@ class ECC:
|
|
|
|
|
return OpenSSL.curves.keys()
|
|
|
|
|
|
|
|
|
|
def get_curve(self):
|
|
|
|
|
"""Encryption object from curve name"""
|
|
|
|
|
return OpenSSL.get_curve_by_id(self.curve)
|
|
|
|
|
|
|
|
|
|
def get_curve_id(self):
|
|
|
|
|
"""Currently used curve"""
|
|
|
|
|
return self.curve
|
|
|
|
|
|
|
|
|
|
def get_pubkey(self):
|
|
|
|
@ -93,22 +109,24 @@ class ECC:
|
|
|
|
|
High level function which returns :
|
|
|
|
|
curve(2) + len_of_pubkeyX(2) + pubkeyX + len_of_pubkeyY + pubkeyY
|
|
|
|
|
"""
|
|
|
|
|
return b''.join((pack('!H', self.curve),
|
|
|
|
|
pack('!H', len(self.pubkey_x)),
|
|
|
|
|
self.pubkey_x,
|
|
|
|
|
pack('!H', len(self.pubkey_y)),
|
|
|
|
|
self.pubkey_y
|
|
|
|
|
))
|
|
|
|
|
return b''.join((
|
|
|
|
|
pack('!H', self.curve),
|
|
|
|
|
pack('!H', len(self.pubkey_x)),
|
|
|
|
|
self.pubkey_x,
|
|
|
|
|
pack('!H', len(self.pubkey_y)),
|
|
|
|
|
self.pubkey_y,
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
def get_privkey(self):
|
|
|
|
|
"""
|
|
|
|
|
High level function which returns
|
|
|
|
|
curve(2) + len_of_privkey(2) + privkey
|
|
|
|
|
"""
|
|
|
|
|
return b''.join((pack('!H', self.curve),
|
|
|
|
|
pack('!H', len(self.privkey)),
|
|
|
|
|
self.privkey
|
|
|
|
|
))
|
|
|
|
|
return b''.join((
|
|
|
|
|
pack('!H', self.curve),
|
|
|
|
|
pack('!H', len(self.privkey)),
|
|
|
|
|
self.privkey,
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _decode_pubkey(pubkey):
|
|
|
|
@ -153,12 +171,9 @@ class ECC:
|
|
|
|
|
group = OpenSSL.EC_KEY_get0_group(key)
|
|
|
|
|
pub_key = OpenSSL.EC_KEY_get0_public_key(key)
|
|
|
|
|
|
|
|
|
|
if (OpenSSL.EC_POINT_get_affine_coordinates_GFp(group, pub_key,
|
|
|
|
|
pub_key_x,
|
|
|
|
|
pub_key_y, 0
|
|
|
|
|
)) == 0:
|
|
|
|
|
raise Exception(
|
|
|
|
|
"[OpenSSL] EC_POINT_get_affine_coordinates_GFp FAIL ...")
|
|
|
|
|
if OpenSSL.EC_POINT_get_affine_coordinates_GFp(
|
|
|
|
|
group, pub_key, pub_key_x, pub_key_y, 0) == 0:
|
|
|
|
|
raise Exception("[OpenSSL] EC_POINT_get_affine_coordinates_GFp FAIL ...")
|
|
|
|
|
|
|
|
|
|
privkey = OpenSSL.malloc(0, OpenSSL.BN_num_bytes(priv_key))
|
|
|
|
|
pubkeyx = OpenSSL.malloc(0, OpenSSL.BN_num_bytes(pub_key_x))
|
|
|
|
@ -183,12 +198,13 @@ class ECC:
|
|
|
|
|
High level function. Compute public key with the local private key
|
|
|
|
|
and returns a 512bits shared key
|
|
|
|
|
"""
|
|
|
|
|
curve, pubkey_x, pubkey_y, i = ECC._decode_pubkey(pubkey)
|
|
|
|
|
curve, pubkey_x, pubkey_y, _ = ECC._decode_pubkey(pubkey)
|
|
|
|
|
if curve != self.curve:
|
|
|
|
|
raise Exception("ECC keys must be from the same curve !")
|
|
|
|
|
return sha512(self.raw_get_ecdh_key(pubkey_x, pubkey_y)).digest()
|
|
|
|
|
|
|
|
|
|
def raw_get_ecdh_key(self, pubkey_x, pubkey_y):
|
|
|
|
|
"""ECDH key as binary data"""
|
|
|
|
|
try:
|
|
|
|
|
ecdh_keybuffer = OpenSSL.malloc(0, 32)
|
|
|
|
|
|
|
|
|
@ -248,20 +264,22 @@ class ECC:
|
|
|
|
|
Check the public key and the private key.
|
|
|
|
|
The private key is optional (replace by None)
|
|
|
|
|
"""
|
|
|
|
|
curve, pubkey_x, pubkey_y, i = ECC._decode_pubkey(pubkey)
|
|
|
|
|
curve, pubkey_x, pubkey_y, _ = ECC._decode_pubkey(pubkey)
|
|
|
|
|
if privkey is None:
|
|
|
|
|
raw_privkey = None
|
|
|
|
|
curve2 = curve
|
|
|
|
|
else:
|
|
|
|
|
curve2, raw_privkey, i = ECC._decode_privkey(privkey)
|
|
|
|
|
curve2, raw_privkey, _ = ECC._decode_privkey(privkey)
|
|
|
|
|
if curve != curve2:
|
|
|
|
|
raise Exception("Bad public and private key")
|
|
|
|
|
return self.raw_check_key(raw_privkey, pubkey_x, pubkey_y, curve)
|
|
|
|
|
|
|
|
|
|
def raw_check_key(self, privkey, pubkey_x, pubkey_y, curve=None):
|
|
|
|
|
"""Check key validity, key is supplied as binary data"""
|
|
|
|
|
# pylint: disable=too-many-branches
|
|
|
|
|
if curve is None:
|
|
|
|
|
curve = self.curve
|
|
|
|
|
elif type(curve) == str:
|
|
|
|
|
elif isinstance(curve, str):
|
|
|
|
|
curve = OpenSSL.get_curve(curve)
|
|
|
|
|
else:
|
|
|
|
|
curve = curve
|
|
|
|
@ -306,6 +324,7 @@ class ECC:
|
|
|
|
|
"""
|
|
|
|
|
Sign the input with ECDSA method and returns the signature
|
|
|
|
|
"""
|
|
|
|
|
# pylint: disable=too-many-branches,too-many-locals
|
|
|
|
|
try:
|
|
|
|
|
size = len(inputb)
|
|
|
|
|
buff = OpenSSL.malloc(inputb, size)
|
|
|
|
@ -369,13 +388,13 @@ class ECC:
|
|
|
|
|
OpenSSL.EVP_MD_CTX_free(md_ctx)
|
|
|
|
|
else:
|
|
|
|
|
OpenSSL.EVP_MD_CTX_destroy(md_ctx)
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def verify(self, sig, inputb, digest_alg=OpenSSL.digest_ecdsa_sha1):
|
|
|
|
|
"""
|
|
|
|
|
Verify the signature with the input and the local public key.
|
|
|
|
|
Returns a boolean
|
|
|
|
|
"""
|
|
|
|
|
# pylint: disable=too-many-branches
|
|
|
|
|
try:
|
|
|
|
|
bsig = OpenSSL.malloc(sig, len(sig))
|
|
|
|
|
binputb = OpenSSL.malloc(inputb, len(inputb))
|
|
|
|
@ -419,12 +438,9 @@ class ECC:
|
|
|
|
|
|
|
|
|
|
if ret == -1:
|
|
|
|
|
return False # Fail to Check
|
|
|
|
|
else:
|
|
|
|
|
if ret == 0:
|
|
|
|
|
return False # Bad signature !
|
|
|
|
|
else:
|
|
|
|
|
return True # Good
|
|
|
|
|
return False
|
|
|
|
|
if ret == 0:
|
|
|
|
|
return False # Bad signature !
|
|
|
|
|
return True # Good
|
|
|
|
|
|
|
|
|
|
finally:
|
|
|
|
|
OpenSSL.EC_KEY_free(key)
|
|
|
|
@ -441,13 +457,21 @@ class ECC:
|
|
|
|
|
"""
|
|
|
|
|
Encrypt data with ECIES method using the public key of the recipient.
|
|
|
|
|
"""
|
|
|
|
|
curve, pubkey_x, pubkey_y, i = ECC._decode_pubkey(pubkey)
|
|
|
|
|
curve, pubkey_x, pubkey_y, _ = ECC._decode_pubkey(pubkey)
|
|
|
|
|
return ECC.raw_encrypt(data, pubkey_x, pubkey_y, curve=curve,
|
|
|
|
|
ephemcurve=ephemcurve, ciphername=ciphername)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def raw_encrypt(data, pubkey_x, pubkey_y, curve='sect283r1',
|
|
|
|
|
ephemcurve=None, ciphername='aes-256-cbc'):
|
|
|
|
|
def raw_encrypt(
|
|
|
|
|
data,
|
|
|
|
|
pubkey_x,
|
|
|
|
|
pubkey_y,
|
|
|
|
|
curve='sect283r1',
|
|
|
|
|
ephemcurve=None,
|
|
|
|
|
ciphername='aes-256-cbc',
|
|
|
|
|
): # pylint: disable=too-many-arguments
|
|
|
|
|
"""ECHD encryption, keys supplied in binary data format"""
|
|
|
|
|
|
|
|
|
|
if ephemcurve is None:
|
|
|
|
|
ephemcurve = curve
|
|
|
|
|
ephem = ECC(curve=ephemcurve)
|
|
|
|
@ -464,12 +488,13 @@ class ECC:
|
|
|
|
|
"""
|
|
|
|
|
Decrypt data with ECIES method using the local private key
|
|
|
|
|
"""
|
|
|
|
|
# pylint: disable=too-many-locals
|
|
|
|
|
blocksize = OpenSSL.get_cipher(ciphername).get_blocksize()
|
|
|
|
|
iv = data[:blocksize]
|
|
|
|
|
i = blocksize
|
|
|
|
|
curve, pubkey_x, pubkey_y, i2 = ECC._decode_pubkey(data[i:])
|
|
|
|
|
_, pubkey_x, pubkey_y, i2 = ECC._decode_pubkey(data[i:])
|
|
|
|
|
i += i2
|
|
|
|
|
ciphertext = data[i:len(data)-32]
|
|
|
|
|
ciphertext = data[i:len(data) - 32]
|
|
|
|
|
i += len(ciphertext)
|
|
|
|
|
mac = data[i:]
|
|
|
|
|
key = sha512(self.raw_get_ecdh_key(pubkey_x, pubkey_y)).digest()
|
|
|
|
|