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 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..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 @@ -5,7 +7,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): @@ -25,16 +26,16 @@ 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. """ 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': + def gen_rand(cls, curve: Optional[Curve] = None) -> 'CurveBN': """ Returns a CurveBN object with a cryptographically secure OpenSSL BIGNUM based on the given curve. @@ -54,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 @@ -99,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. @@ -123,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. """ @@ -135,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. @@ -171,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). @@ -183,6 +184,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 @@ -191,10 +193,15 @@ 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. """ + 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( @@ -204,10 +211,14 @@ 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. """ + 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( @@ -233,7 +244,24 @@ class CurveBN(object): return CurveBN(inv, self.curve) - def __mod__(self, other) -> 'CurveBN': + 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 : Union[int, 'CurveBN']) -> 'CurveBN': """ Performs a BN_nnmod on two BIGNUMS. """ 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 aca65cc..7d748c5 100644 --- a/umbral/params.py +++ b/umbral/params.py @@ -7,15 +7,14 @@ 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() - 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: diff --git a/umbral/point.py b/umbral/point.py index bdee309..01f0c53 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': @@ -129,7 +129,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..f934d72 100644 --- a/umbral/utils.py +++ b/umbral/utils.py @@ -4,29 +4,26 @@ 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 +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: 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 -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]) + result = (result * x) + coeff[i] return result @@ -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