From 66c5078ed0f3df1d6cbbda964e775053b3803c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Wed, 11 Jul 2018 09:51:01 +0200 Subject: [PATCH 1/9] Refactor get_field_order_size_in_bytes as a Curve method --- umbral/curve.py | 8 +++++++- umbral/curvebn.py | 3 +-- umbral/params.py | 3 +-- umbral/point.py | 12 ++++++------ umbral/signing.py | 3 +-- umbral/utils.py | 9 --------- 6 files changed, 16 insertions(+), 22 deletions(-) diff --git a/umbral/curve.py b/umbral/curve.py index e49463f..7014741 100644 --- a/umbral/curve.py +++ b/umbral/curve.py @@ -1,6 +1,6 @@ +from cryptography.hazmat.backends import default_backend from umbral import openssl - _AVAIL_CURVES = { 'secp256r1': 415, 'secp256k1': 714, @@ -40,6 +40,12 @@ class Curve: def __repr__(self): return "".format(self.curve_nid) + def get_field_order_size_in_bytes(self) -> int: + backend = default_backend() + size_in_bits = openssl._get_ec_group_degree(self.ec_group) + return (size_in_bits + 7) // 8 + + SECP256R1 = Curve(_AVAIL_CURVES['secp256r1']) SECP256K1 = Curve(_AVAIL_CURVES['secp256k1']) diff --git a/umbral/curvebn.py b/umbral/curvebn.py index b5c61f6..4bbdb2b 100644 --- a/umbral/curvebn.py +++ b/umbral/curvebn.py @@ -5,7 +5,6 @@ from umbral import openssl from umbral.config import default_curve from umbral.curve import Curve from umbral.params import UmbralParameters -from umbral.utils import get_field_order_size_in_bytes class CurveBN(object): @@ -31,7 +30,7 @@ class CurveBN(object): If no curve is provided, it uses the default. """ curve = curve if curve is not None else default_curve() - return get_field_order_size_in_bytes(curve) + return curve.get_field_order_size_in_bytes() @classmethod def gen_rand(cls, curve: Curve=None) -> 'CurveBN': diff --git a/umbral/params.py b/umbral/params.py index aca65cc..e2aac79 100644 --- a/umbral/params.py +++ b/umbral/params.py @@ -7,10 +7,9 @@ from umbral.curve import Curve class UmbralParameters(object): def __init__(self, curve: Curve) -> None: from umbral.point import Point, unsafe_hash_to_point - from umbral.utils import get_field_order_size_in_bytes self.curve = curve - self.CURVE_KEY_SIZE_BYTES = get_field_order_size_in_bytes(self.curve) + self.CURVE_KEY_SIZE_BYTES = self.curve.get_field_order_size_in_bytes() self.g = Point.get_generator_from_curve(curve=curve) g_bytes = self.g.to_bytes() diff --git a/umbral/point.py b/umbral/point.py index e07477e..55eebdc 100644 --- a/umbral/point.py +++ b/umbral/point.py @@ -9,7 +9,6 @@ from umbral.config import default_curve from umbral.curve import Curve from umbral.curvebn import CurveBN from umbral.params import UmbralParameters -from umbral.utils import get_field_order_size_in_bytes class Point(object): @@ -29,12 +28,13 @@ class Point(object): If no curve is provided, it uses the default curve. """ curve = curve if curve is not None else default_curve() - base_size = get_field_order_size_in_bytes(curve) - if not is_compressed: - base_size += get_field_order_size_in_bytes(curve) + coord_size = curve.get_field_order_size_in_bytes() - return base_size + 1 + if is_compressed: + return 1 + coord_size + else: + return 1 + 2 * coord_size @classmethod def gen_rand(cls, curve: Optional[Curve] = None) -> 'Point': @@ -127,7 +127,7 @@ class Point(object): if is_compressed is set to True. """ affine_x, affine_y = self.to_affine() - key_size = get_field_order_size_in_bytes(self.curve) + key_size = self.curve.get_field_order_size_in_bytes() if is_compressed: y_bit = (affine_y & 1) + 2 diff --git a/umbral/signing.py b/umbral/signing.py index dfa6ca0..2f583c0 100644 --- a/umbral/signing.py +++ b/umbral/signing.py @@ -10,7 +10,6 @@ from umbral.config import default_curve from umbral.curve import Curve from umbral.curvebn import CurveBN from umbral.keys import UmbralPublicKey, UmbralPrivateKey -from umbral.utils import get_field_order_size_in_bytes _BLAKE2B = hashes.BLAKE2b(64) @@ -32,7 +31,7 @@ class Signature: @classmethod def expected_bytes_length(cls, curve: Optional[Curve] = None) -> int: curve = curve if curve is not None else default_curve() - return get_field_order_size_in_bytes(curve) * 2 + return 2 * curve.get_field_order_size_in_bytes() def verify(self, message: bytes, verifying_key: UmbralPublicKey) -> bool: """ diff --git a/umbral/utils.py b/umbral/utils.py index ec43e50..97c257d 100644 --- a/umbral/utils.py +++ b/umbral/utils.py @@ -4,9 +4,6 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.hkdf import HKDF -from umbral import openssl -from umbral.curve import Curve - def lambda_coeff(id_i: 'CurveBN', selected_ids: List['CurveBN']) -> 'CurveBN': ids = [x for x in selected_ids if x != id_i] @@ -41,9 +38,3 @@ def kdf(ecpoint: 'Point', key_length: int) -> bytes: info=None, backend=default_backend() ).derive(data) - - -def get_field_order_size_in_bytes(curve: Curve) -> int: - backend = default_backend() - size_in_bits = openssl._get_ec_group_degree(curve.ec_group) - return (size_in_bits + 7) // 8 From d7c31475b806e01e6b65312aeacc3800adbbb6f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Wed, 11 Jul 2018 10:00:49 +0200 Subject: [PATCH 2/9] Remove forward references to 'CurveBN' --- umbral/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/umbral/utils.py b/umbral/utils.py index 97c257d..d9b70cf 100644 --- a/umbral/utils.py +++ b/umbral/utils.py @@ -4,8 +4,10 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.hkdf import HKDF +from umbral.curvebn import CurveBN -def lambda_coeff(id_i: 'CurveBN', selected_ids: List['CurveBN']) -> 'CurveBN': + +def lambda_coeff(id_i: CurveBN, selected_ids: List[CurveBN]) -> CurveBN: ids = [x for x in selected_ids if x != id_i] if not ids: @@ -20,7 +22,7 @@ def lambda_coeff(id_i: 'CurveBN', selected_ids: List['CurveBN']) -> 'CurveBN': return result -def poly_eval(coeff: List['CurveBN'], x: 'CurveBN') -> 'CurveBN': +def poly_eval(coeff: List[CurveBN], x: CurveBN) -> CurveBN: result = coeff[-1] for i in range(-2, -len(coeff) - 1, -1): result = ((result * x) + coeff[i]) From 2075082ef674daae7f249735695023a48f8cdc94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Wed, 11 Jul 2018 10:01:25 +0200 Subject: [PATCH 3/9] Update 'NuCypherKMS' to 'NuCypher' --- umbral/keys.py | 2 +- umbral/params.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/umbral/keys.py b/umbral/keys.py index ddb8390..69e6896 100644 --- a/umbral/keys.py +++ b/umbral/keys.py @@ -288,7 +288,7 @@ class UmbralKeyingMaterial(object): algorithm=hashes.BLAKE2b(64), length=64, salt=salt, - info=b"NuCypherKMS/KeyDerivation/"+label, + info=b"NuCypher/KeyDerivation/"+label, backend=default_backend() ).derive(self.keying_material) diff --git a/umbral/params.py b/umbral/params.py index e2aac79..7d748c5 100644 --- a/umbral/params.py +++ b/umbral/params.py @@ -14,7 +14,7 @@ class UmbralParameters(object): self.g = Point.get_generator_from_curve(curve=curve) g_bytes = self.g.to_bytes() - parameters_seed = b'NuCypherKMS/UmbralParameters/' + parameters_seed = b'NuCypher/UmbralParameters/' self.u = unsafe_hash_to_point(g_bytes, self, parameters_seed + b'u') def __eq__(self, other: 'UmbralParameters') -> bool: From d5bc44d01aa405566cbb38badf71c15cd5b5ff84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Wed, 11 Jul 2018 10:55:37 +0200 Subject: [PATCH 4/9] Modular opposite for CurveBN "I'm the opposite of every guy you've ever met" --- umbral/curvebn.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/umbral/curvebn.py b/umbral/curvebn.py index 4bbdb2b..05beeb4 100644 --- a/umbral/curvebn.py +++ b/umbral/curvebn.py @@ -190,6 +190,7 @@ class CurveBN(object): return CurveBN(product, self.curve) + def __add__(self, other) -> 'CurveBN': """ Performs a BN_mod_add on two BIGNUMs. @@ -232,6 +233,23 @@ class CurveBN(object): return CurveBN(inv, self.curve) + def __neg__(self) -> 'CurveBN': + """ + Computes the modular opposite (i.e., additive inverse) of a BIGNUM + + """ + zero = backend._int_to_bn(0) + zero = backend._ffi.gc(zero, backend._lib.BN_clear_free) + + the_opposite = openssl._get_new_BN() + with backend._tmp_bn_ctx() as bn_ctx: + res = backend._lib.BN_mod_sub( + the_opposite, zero, self.bignum, self.curve.order, bn_ctx + ) + backend.openssl_assert(res == 1) + + return CurveBN(the_opposite, self.curve) + def __mod__(self, other) -> 'CurveBN': """ Performs a BN_nnmod on two BIGNUMS. From 6183b65886f9bf6c514ce00687b5155ac188ca50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Wed, 11 Jul 2018 10:56:17 +0200 Subject: [PATCH 5/9] Testing correctness of CurveBN operations --- .../test_bignum/test_bignum_arithmetic.py | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/test_primitives/test_bignum/test_bignum_arithmetic.py b/tests/test_primitives/test_bignum/test_bignum_arithmetic.py index 92ef4f9..8d52f4b 100644 --- a/tests/test_primitives/test_bignum/test_bignum_arithmetic.py +++ b/tests/test_primitives/test_bignum/test_bignum_arithmetic.py @@ -1,3 +1,4 @@ +from cryptography.hazmat.backends.openssl import backend from umbral.curvebn import CurveBN @@ -9,9 +10,10 @@ def test_mocked_openssl_curvebn_arithmetic(mock_openssl, random_ec_curvebn1, ran random_ec_curvebn1 ** int(random_ec_curvebn2), # __pow__ (as int) random_ec_curvebn1 + random_ec_curvebn2, # __add__ random_ec_curvebn1 - random_ec_curvebn2, # __sub__ + -random_ec_curvebn1, # __neg__ random_ec_curvebn1 % random_ec_curvebn2, # __mod__ random_ec_curvebn1 % int(random_ec_curvebn2), # __mod__ (as int) - ~random_ec_curvebn1, # __invert__ + ~random_ec_curvebn1, # __invert__ random_ec_curvebn1 / random_ec_curvebn2 # __truediv__ ) @@ -21,3 +23,25 @@ def test_mocked_openssl_curvebn_arithmetic(mock_openssl, random_ec_curvebn1, ran assert operator_result assert isinstance(operator_result, CurveBN) + order = backend._bn_to_int(random_ec_curvebn1.curve.order) + random_ec_curvebn1 = int(random_ec_curvebn1) + random_ec_curvebn2 = int(random_ec_curvebn2) + + # For simplicity, we test these two cases separately + assert (int(operations_that_construct[-2]) * random_ec_curvebn1) % order == 1 + assert (int(operations_that_construct[-1]) * random_ec_curvebn2) % order == random_ec_curvebn1 + + # The remaining cases can be tested in bulk + expected_results = ( + (random_ec_curvebn1 * random_ec_curvebn2) % order, # __mul__ + pow(random_ec_curvebn1, random_ec_curvebn2, order), # __pow__ + pow(random_ec_curvebn1, random_ec_curvebn2, order), # __pow__ (as int) + (random_ec_curvebn1 + random_ec_curvebn2) % order, # __add__ + (random_ec_curvebn1 - random_ec_curvebn2) % order, # __sub__ + (-random_ec_curvebn1) % order, # __neg__ + random_ec_curvebn1 % random_ec_curvebn2, # __mod__ + random_ec_curvebn1 % int(random_ec_curvebn2), # __mod__ (as int) + ) + + for (result, expected) in zip(operations_that_construct[:-2], expected_results): + assert result == expected \ No newline at end of file From 3eb66de5079750400151110b5f24731a6736af31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Wed, 11 Jul 2018 11:32:33 +0200 Subject: [PATCH 6/9] Fix memleak in CurveBN.truediv --- umbral/curvebn.py | 1 + 1 file changed, 1 insertion(+) diff --git a/umbral/curvebn.py b/umbral/curvebn.py index 05beeb4..de1bf17 100644 --- a/umbral/curvebn.py +++ b/umbral/curvebn.py @@ -182,6 +182,7 @@ class CurveBN(object): backend._ffi.NULL, other.bignum, self.curve.order, bn_ctx ) backend.openssl_assert(inv_other != backend._ffi.NULL) + inv_other = backend._ffi.gc(inv_other, backend._lib.BN_clear_free) res = backend._lib.BN_mod_mul( product, self.bignum, inv_other, self.curve.order, bn_ctx From b5b3513707df5ef4a4f96a91cb80971d92665234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Wed, 11 Jul 2018 12:08:45 +0200 Subject: [PATCH 7/9] Support for int arguments in CurveBN.__add__ and __sub__ --- umbral/curvebn.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/umbral/curvebn.py b/umbral/curvebn.py index de1bf17..9eb8bbd 100644 --- a/umbral/curvebn.py +++ b/umbral/curvebn.py @@ -196,6 +196,10 @@ class CurveBN(object): """ Performs a BN_mod_add on two BIGNUMs. """ + if type(other) == int: + other = openssl._int_to_bn(other) + other = CurveBN(other, self.curve) + op_sum = openssl._get_new_BN() with backend._tmp_bn_ctx() as bn_ctx: res = backend._lib.BN_mod_add( @@ -209,6 +213,10 @@ class CurveBN(object): """ Performs a BN_mod_sub on two BIGNUMS. """ + if type(other) == int: + other = openssl._int_to_bn(other) + other = CurveBN(other, self.curve) + diff = openssl._get_new_BN() with backend._tmp_bn_ctx() as bn_ctx: res = backend._lib.BN_mod_sub( From f17b2419c44a0fc3f3afce216efdd334c0bbf635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Wed, 11 Jul 2018 12:09:47 +0200 Subject: [PATCH 8/9] Some type annotations for CurveBN --- umbral/curvebn.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/umbral/curvebn.py b/umbral/curvebn.py index 9eb8bbd..c6d2624 100644 --- a/umbral/curvebn.py +++ b/umbral/curvebn.py @@ -1,3 +1,5 @@ +from typing import Optional, Union + from cryptography.hazmat.backends.openssl import backend from cryptography.hazmat.primitives import hashes @@ -24,7 +26,7 @@ class CurveBN(object): self.curve = curve @classmethod - def expected_bytes_length(cls, curve: Curve=None) -> int: + def expected_bytes_length(cls, curve: Optional[Curve] = None) -> int: """ Returns the size (in bytes) of a CurveBN given the curve. If no curve is provided, it uses the default. @@ -33,7 +35,7 @@ class CurveBN(object): return curve.get_field_order_size_in_bytes() @classmethod - def gen_rand(cls, curve: Curve=None) -> 'CurveBN': + def gen_rand(cls, curve: Optional[Curve] = None) -> 'CurveBN': """ Returns a CurveBN object with a cryptographically secure OpenSSL BIGNUM based on the given curve. @@ -53,7 +55,7 @@ class CurveBN(object): return cls(new_rand_bn, curve) @classmethod - def from_int(cls, num: int, curve: Curve=None) -> 'CurveBN': + def from_int(cls, num: int, curve: Optional[Curve] = None) -> 'CurveBN': """ Returns a CurveBN object from a given integer on a curve. By default, the underlying OpenSSL BIGNUM has BN_FLG_CONSTTIME set for @@ -98,7 +100,7 @@ class CurveBN(object): return cls(bignum, params.curve) @classmethod - def from_bytes(cls, data: bytes, curve: Curve=None) -> 'CurveBN': + def from_bytes(cls, data: bytes, curve: Optional[Curve] = None) -> 'CurveBN': """ Returns a CurveBN object from the given byte data that's within the size of the provided curve's order. @@ -122,7 +124,7 @@ class CurveBN(object): """ return backend._bn_to_int(self.bignum) - def __eq__(self, other) -> bool: + def __eq__(self, other : Union[int, 'CurveBN']) -> bool: """ Compares the two BIGNUMS or int. """ @@ -134,7 +136,7 @@ class CurveBN(object): # -1 less than, 0 is equal to, 1 is greater than return not bool(backend._lib.BN_cmp(self.bignum, other.bignum)) - def __pow__(self, other) -> 'CurveBN': + def __pow__(self, other : Union[int, 'CurveBN']) -> 'CurveBN': """ Performs a BN_mod_exp on two BIGNUMS. @@ -170,7 +172,7 @@ class CurveBN(object): return CurveBN(product, self.curve) - def __truediv__(self, other) -> 'CurveBN': + def __truediv__(self, other : 'CurveBN') -> 'CurveBN': """ Performs a BN_div on two BIGNUMs (modulo the order of the curve). @@ -192,7 +194,7 @@ class CurveBN(object): return CurveBN(product, self.curve) - def __add__(self, other) -> 'CurveBN': + def __add__(self, other : Union[int, 'CurveBN']) -> 'CurveBN': """ Performs a BN_mod_add on two BIGNUMs. """ @@ -209,7 +211,7 @@ class CurveBN(object): return CurveBN(op_sum, self.curve) - def __sub__(self, other) -> 'CurveBN': + def __sub__(self, other : Union[int, 'CurveBN']) -> 'CurveBN': """ Performs a BN_mod_sub on two BIGNUMS. """ @@ -259,7 +261,7 @@ class CurveBN(object): return CurveBN(the_opposite, self.curve) - def __mod__(self, other) -> 'CurveBN': + def __mod__(self, other : Union[int, 'CurveBN']) -> 'CurveBN': """ Performs a BN_nnmod on two BIGNUMS. """ From f0180792516b57942d311a0576fbac8fdff7a612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Thu, 12 Jul 2018 11:20:22 +0200 Subject: [PATCH 9/9] Some refinement in lambda_coeff and poly_eval, from umbral.utils --- umbral/utils.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/umbral/utils.py b/umbral/utils.py index d9b70cf..f934d72 100644 --- a/umbral/utils.py +++ b/umbral/utils.py @@ -13,11 +13,9 @@ def lambda_coeff(id_i: CurveBN, selected_ids: List[CurveBN]) -> CurveBN: if not ids: return None - div_0 = ~(ids[0] - id_i) - result = ids[0] * div_0 + result = ids[0] / (ids[0] - id_i) for id_j in ids[1:]: - div_j = ~(id_j - id_i) - result = result * (id_j * div_j) + result = result * id_j / (id_j - id_i) return result @@ -25,7 +23,7 @@ def lambda_coeff(id_i: CurveBN, selected_ids: List[CurveBN]) -> CurveBN: def poly_eval(coeff: List[CurveBN], x: CurveBN) -> CurveBN: result = coeff[-1] for i in range(-2, -len(coeff) - 1, -1): - result = ((result * x) + coeff[i]) + result = (result * x) + coeff[i] return result