Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interfacing with Rust #263

Merged
merged 25 commits into from
May 27, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bbf168e
Clean slate
fjarri Mar 16, 2021
439b675
Drop Py3.5 support
fjarri Mar 20, 2021
2050e3c
Remove unused dependencies
fjarri Mar 20, 2021
54ba9bb
Exclude abstract methods during coverage tests
fjarri Mar 20, 2021
f030fd3
Use a composable approach for serialization
fjarri Mar 16, 2021
f33431d
Working secret & public keys
fjarri Mar 16, 2021
2c28ae8
Add Capsule class and encrypt()/decrypt_original()
fjarri Mar 16, 2021
d6626ba
Add generate_kfrags()
fjarri Mar 17, 2021
b96888c
Add reencryption functionality
fjarri Mar 18, 2021
d532ef1
Move all OpenSSL stuff into one module, move around some low-level de…
fjarri Mar 19, 2021
c419705
Add SecretKeyFactory
fjarri Mar 20, 2021
6af41b0
Remove repeated casting to bytes from hashing calls
fjarri Mar 19, 2021
9e87006
Add back performance tests
fjarri Mar 19, 2021
d659697
Skip rust-umbral tests if the library is not available
fjarri Mar 20, 2021
f58a258
curve_scalar: don't check range in __init__, only in publicly used co…
fjarri Mar 20, 2021
c401c52
Add tests
fjarri Mar 20, 2021
bcb0071
Update docs
fjarri Mar 21, 2021
0f82580
Add some API docs
fjarri Mar 21, 2021
fe6e32b
Update vector generating script and regenerate vectors
fjarri Mar 21, 2021
ee7d31b
RFC for docs
fjarri Mar 26, 2021
a08a552
Replace `dem.ErrorInvalidTag` and `Capsule.NotValid` with `GenericErr…
fjarri Mar 27, 2021
7d0f2fe
Cache public key in the secret key to speed up `generate_kfrags()`
fjarri Mar 27, 2021
a7f4a7a
Fix logic in bn_from_bytes(..., apply_modulus=True)
fjarri Apr 13, 2021
fd9e1d4
Remove a TODO
fjarri Apr 13, 2021
503a1c6
Always set constant time operations for OpenSSL bignums
fjarri Apr 13, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions umbral/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
from umbral.__about__ import (
from .__about__ import (
__author__, __license__, __summary__, __title__, __version__, __copyright__, __email__, __url__
)

from .keys import SecretKey, PublicKey

__all__ = [
"__title__", "__summary__", "__version__", "__author__", "__license__", "__copyright__", "__email__", "__url__"
"__title__",
"__summary__",
"__version__",
"__author__",
"__license__",
"__copyright__",
"__email__",
"__url__",
"SecretKey",
"PublicKey",
]
120 changes: 120 additions & 0 deletions umbral/curve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
from cryptography.hazmat.backends import default_backend

from . import openssl


class Curve:
"""
Acts as a container to store constant variables such as the OpenSSL
curve_nid, the EC_GROUP struct, and the order of the curve.

Contains a whitelist of supported elliptic curves used in pyUmbral.

"""

_supported_curves = {
415: 'secp256r1',
714: 'secp256k1',
715: 'secp384r1'
}

def __init__(self, nid: int) -> None:
"""
Instantiates an OpenSSL curve with the provided curve_nid and derives
the proper EC_GROUP struct and order. You can _only_ instantiate curves
with supported nids (see `Curve.supported_curves`).
"""

try:
self.__curve_name = self._supported_curves[nid]
except KeyError:
raise NotImplementedError("Curve NID {} is not supported.".format(nid))

# set only once
self.__curve_nid = nid
self.__ec_group = openssl._get_ec_group_by_curve_nid(self.__curve_nid)
self.__order = openssl._get_ec_order_by_group(self.ec_group)
self.__generator = openssl._get_ec_generator_by_group(self.ec_group)

# Init cache
self.__field_order_size_in_bytes = 0
self.__group_order_size_in_bytes = 0

@classmethod
def from_name(cls, name: str) -> 'Curve':
"""
Alternate constructor to generate a curve instance by its name.

Raises NotImplementedError if the name cannot be mapped to a known
supported curve NID.

"""

name = name.casefold() # normalize

for supported_nid, supported_name in cls._supported_curves.items():
if name == supported_name:
instance = cls(nid=supported_nid)
break
else:
message = "{} is not supported curve name.".format(name)
raise NotImplementedError(message)

return instance

def __eq__(self, other):
return self.__curve_nid == other.curve_nid

def __str__(self):
return "<OpenSSL Curve(nid={}, name={})>".format(self.__curve_nid, self.__curve_name)

#
# Immutable Curve Data
#

@property
def field_order_size_in_bytes(self) -> int:
if not self.__field_order_size_in_bytes:
size_in_bits = openssl._get_ec_group_degree(self.__ec_group)
self.__field_order_size_in_bytes = (size_in_bits + 7) // 8
return self.__field_order_size_in_bytes

@property
def group_order_size_in_bytes(self) -> int:
if not self.__group_order_size_in_bytes:
BN_num_bytes = default_backend()._lib.BN_num_bytes
self.__group_order_size_in_bytes = BN_num_bytes(self.order)
return self.__group_order_size_in_bytes

@property
def curve_nid(self) -> int:
return self.__curve_nid

@property
def name(self) -> str:
return self.__curve_name

@property
def ec_group(self):
return self.__ec_group

@property
def order(self):
return self.__order

@property
def generator(self):
return self.__generator


#
# Global Curve Instances
#

SECP256R1 = Curve.from_name('secp256r1')
SECP256K1 = Curve.from_name('secp256k1')
SECP384R1 = Curve.from_name('secp384r1')

CURVES = (SECP256K1, SECP256R1, SECP384R1)

CURVE = SECP256K1
148 changes: 148 additions & 0 deletions umbral/curve_point.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
from typing import Optional, Tuple

from cryptography.hazmat.backends.openssl import backend

from . import openssl
from .curve import CURVE
from .curve_scalar import CurveScalar
from .serializable import Serializable


class CurvePoint(Serializable):
"""
Represents an OpenSSL EC_POINT except more Pythonic.
"""

def __init__(self, backend_point) -> None:
self._backend_point = backend_point

@classmethod
def generator(cls) -> 'CurvePoint':
return cls(CURVE.generator)

@classmethod
def random(cls) -> 'CurvePoint':
"""
Returns a CurvePoint object with a cryptographically secure EC_POINT based
on the provided curve.
"""
return cls.generator() * CurveScalar.random_nonzero()

@classmethod
def from_affine(cls, coords: Tuple[int, int]) -> 'CurvePoint':
"""
Returns a CurvePoint object from the given affine coordinates in a tuple in
the format of (x, y) and a given curve.
"""
affine_x, affine_y = coords
if type(affine_x) == int:
affine_x = openssl._int_to_bn(affine_x, curve=None)

if type(affine_y) == int:
affine_y = openssl._int_to_bn(affine_y, curve=None)

backend_point = openssl._get_EC_POINT_via_affine(affine_x, affine_y, CURVE)
return cls(backend_point)

def to_affine(self):
"""
Returns a tuple of Python ints in the format of (x, y) that represents
the point in the curve.
"""
affine_x, affine_y = openssl._get_affine_coords_via_EC_POINT(self._backend_point, CURVE)
return (backend._bn_to_int(affine_x), backend._bn_to_int(affine_y))

@classmethod
def __take__(cls, data: bytes) -> Tuple['CurvePoint', bytes]:
"""
Returns a CurvePoint object from the given byte data on the curve provided.
"""
size = CURVE.field_order_size_in_bytes + 1 # compressed point size
point_data, data = cls.__take_bytes__(data, size)

point = openssl._get_new_EC_POINT(CURVE)
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.EC_POINT_oct2point(
CURVE.ec_group, point, point_data, len(point_data), bn_ctx);
backend.openssl_assert(res == 1)

return cls(point), data

def __bytes__(self) -> bytes:
"""
Returns the CurvePoint serialized as bytes in the compressed form.
"""
point_conversion_form = backend._lib.POINT_CONVERSION_COMPRESSED
size = CURVE.field_order_size_in_bytes + 1 # compressed point size

bin_ptr = backend._ffi.new("unsigned char[]", size)
with backend._tmp_bn_ctx() as bn_ctx:
bin_len = backend._lib.EC_POINT_point2oct(
CURVE.ec_group, self._backend_point, point_conversion_form,
bin_ptr, size, bn_ctx
)
backend.openssl_assert(bin_len != 0)

return bytes(backend._ffi.buffer(bin_ptr, bin_len)[:])

def __eq__(self, other):
"""
Compares two EC_POINTS for equality.
"""
with backend._tmp_bn_ctx() as bn_ctx:
is_equal = backend._lib.EC_POINT_cmp(
CURVE.ec_group, self._backend_point, other._backend_point, bn_ctx
)
backend.openssl_assert(is_equal != -1)

# 1 is not-equal, 0 is equal, -1 is error
return not bool(is_equal)

def __mul__(self, other: CurveScalar) -> 'CurvePoint':
"""
Performs an EC_POINT_mul on an EC_POINT and a BIGNUM.
"""
# TODO: Check that both points use the same curve.
prod = openssl._get_new_EC_POINT(CURVE)
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.EC_POINT_mul(
CURVE.ec_group, prod, backend._ffi.NULL,
self._backend_point, other._backend_bignum, bn_ctx
)
backend.openssl_assert(res == 1)

return CurvePoint(prod)

def __add__(self, other: 'CurvePoint') -> 'CurvePoint':
"""
Performs an EC_POINT_add on two EC_POINTS.
"""
op_sum = openssl._get_new_EC_POINT(CURVE)
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.EC_POINT_add(
CURVE.ec_group, op_sum, self._backend_point, other._backend_point, bn_ctx
)
backend.openssl_assert(res == 1)
return CurvePoint(op_sum)

def __sub__(self, other: 'CurvePoint') -> 'CurvePoint':
"""
Performs subtraction by adding the inverse of the `other` to the point.
"""
return (self + (-other))

def __neg__(self) -> 'CurvePoint':
"""
Computes the additive inverse of a CurvePoint, by performing an
EC_POINT_invert on itself.
"""
inv = backend._lib.EC_POINT_dup(self._backend_point, CURVE.ec_group)
backend.openssl_assert(inv != backend._ffi.NULL)
inv = backend._ffi.gc(inv, backend._lib.EC_POINT_clear_free)

with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.EC_POINT_invert(
CURVE.ec_group, inv, bn_ctx
)
backend.openssl_assert(res == 1)
return CurvePoint(inv)
Loading