Use internal implementation for X25519 key exchanges
This commit is contained in:
		
							parent
							
								
									7a596882a8
								
							
						
					
					
						commit
						06fffe5a94
					
				
							
								
								
									
										148
									
								
								RNS/Cryptography/Curve25519.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								RNS/Cryptography/Curve25519.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,148 @@ | ||||
| """A pure Python implementation of Curve25519 | ||||
| This module supports both a low-level interface through curve25519(base_point, secret) | ||||
| and curve25519_base(secret) that take 32-byte blocks of data as inputs and a higher | ||||
| level interface using the X25519PrivateKey and X25519PublicKey classes that are | ||||
| compatible with the classes in cryptography.hazmat.primitives.asymmetric.x25519 with | ||||
| the same names. | ||||
| """ | ||||
| 
 | ||||
| # By Nicko van Someren, 2021. This code is released into the public domain. | ||||
| 
 | ||||
| #                                  #### WARNING #### | ||||
| 
 | ||||
| # Since this code makes use of Python's built-in large integer types, it is NOT EXPECTED | ||||
| # to run in constant time. While some effort is made to minimise the time variations, | ||||
| # the underlying math functions are likely to have running times that are highly | ||||
| # value-dependent, leaving this code potentially vulnerable to timing attacks. If this | ||||
| # code is to be used to provide cryptographic security in an environment where the start | ||||
| # and end times of the execution can be guessed, inferred or measured then it is critical | ||||
| # that steps are taken to hide the execution time, for instance by adding a delay so that | ||||
| # encrypted packets are not sent until a fixed time after the _start_ of execution. | ||||
| 
 | ||||
| 
 | ||||
| # Implements ladder multiplication as described in "Montgomery curves and the Montgomery | ||||
| # ladder" by Daniel J. Bernstein and Tanja Lange. https://eprint.iacr.org/2017/293.pdf | ||||
| 
 | ||||
| # Curve25519 is a Montgomery curve defined by: | ||||
| # y**2 = x**3 + A * x**2 + x  mod P | ||||
| # where P = 2**255-19 and A = 486662 | ||||
| 
 | ||||
| import os | ||||
| 
 | ||||
| P = 2 ** 255 - 19 | ||||
| _A = 486662 | ||||
| 
 | ||||
| 
 | ||||
| def _point_add(point_n, point_m, point_diff): | ||||
|     """Given the projection of two points and their difference, return their sum""" | ||||
|     (xn, zn) = point_n | ||||
|     (xm, zm) = point_m | ||||
|     (x_diff, z_diff) = point_diff | ||||
|     x = (z_diff << 2) * (xm * xn - zm * zn) ** 2 | ||||
|     z = (x_diff << 2) * (xm * zn - zm * xn) ** 2 | ||||
|     return x % P, z % P | ||||
| 
 | ||||
| 
 | ||||
| def _point_double(point_n): | ||||
|     """Double a point provided in projective coordinates""" | ||||
|     (xn, zn) = point_n | ||||
|     xn2 = xn ** 2 | ||||
|     zn2 = zn ** 2 | ||||
|     x = (xn2 - zn2) ** 2 | ||||
|     xzn = xn * zn | ||||
|     z = 4 * xzn * (xn2 + _A * xzn + zn2) | ||||
|     return x % P, z % P | ||||
| 
 | ||||
| 
 | ||||
| def _const_time_swap(a, b, swap): | ||||
|     """Swap two values in constant time""" | ||||
|     index = int(swap) * 2 | ||||
|     temp = (a, b, b, a) | ||||
|     return temp[index:index+2] | ||||
| 
 | ||||
| 
 | ||||
| def _raw_curve25519(base, n): | ||||
|     """Raise the point base to the power n""" | ||||
|     zero = (1, 0) | ||||
|     one = (base, 1) | ||||
|     mP, m1P = zero, one | ||||
| 
 | ||||
|     for i in reversed(range(256)): | ||||
|         bit = bool(n & (1 << i)) | ||||
|         mP, m1P = _const_time_swap(mP, m1P, bit) | ||||
|         mP, m1P = _point_double(mP), _point_add(mP, m1P, one) | ||||
|         mP, m1P = _const_time_swap(mP, m1P, bit) | ||||
| 
 | ||||
|     x, z = mP | ||||
|     inv_z = pow(z, P - 2, P) | ||||
|     return (x * inv_z) % P | ||||
| 
 | ||||
| 
 | ||||
| def _unpack_number(s): | ||||
|     """Unpack 32 bytes to a 256 bit value""" | ||||
|     if len(s) != 32: | ||||
|         raise ValueError('Curve25519 values must be 32 bytes') | ||||
|     return int.from_bytes(s, "little") | ||||
| 
 | ||||
| 
 | ||||
| def _pack_number(n): | ||||
|     """Pack a value into 32 bytes""" | ||||
|     return n.to_bytes(32, "little") | ||||
| 
 | ||||
| 
 | ||||
| def _fix_secret(n): | ||||
|     """Mask a value to be an acceptable exponent""" | ||||
|     n &= ~7 | ||||
|     n &= ~(128 << 8 * 31) | ||||
|     n |= 64 << 8 * 31 | ||||
|     return n | ||||
| 
 | ||||
| 
 | ||||
| def curve25519(base_point_raw, secret_raw): | ||||
|     """Raise the base point to a given power""" | ||||
|     base_point = _unpack_number(base_point_raw) | ||||
|     secret = _fix_secret(_unpack_number(secret_raw)) | ||||
|     return _pack_number(_raw_curve25519(base_point, secret)) | ||||
| 
 | ||||
| 
 | ||||
| def curve25519_base(secret_raw): | ||||
|     """Raise the generator point to a given power""" | ||||
|     secret = _fix_secret(_unpack_number(secret_raw)) | ||||
|     return _pack_number(_raw_curve25519(9, secret)) | ||||
| 
 | ||||
| 
 | ||||
| class X25519PublicKey: | ||||
|     def __init__(self, x): | ||||
|         self.x = x | ||||
| 
 | ||||
|     @classmethod | ||||
|     def from_public_bytes(cls, data): | ||||
|         return cls(_unpack_number(data)) | ||||
| 
 | ||||
|     def public_bytes(self): | ||||
|         return _pack_number(self.x) | ||||
| 
 | ||||
| 
 | ||||
| class X25519PrivateKey: | ||||
|     def __init__(self, a): | ||||
|         self.a = a | ||||
| 
 | ||||
|     @classmethod | ||||
|     def generate(cls): | ||||
|         return cls.from_private_bytes(os.urandom(32)) | ||||
| 
 | ||||
|     @classmethod | ||||
|     def from_private_bytes(cls, data): | ||||
|         return cls(_fix_secret(_unpack_number(data))) | ||||
| 
 | ||||
|     def private_bytes(self): | ||||
|         return _pack_number(self.a) | ||||
| 
 | ||||
|     def public_key(self): | ||||
|         return X25519PublicKey.from_public_bytes(_pack_number(_raw_curve25519(9, self.a))) | ||||
| 
 | ||||
|     def exchange(self, peer_public_key): | ||||
|         if isinstance(peer_public_key, bytes): | ||||
|             peer_public_key = X25519PublicKey.from_public_bytes(peer_public_key) | ||||
| 
 | ||||
|         return _pack_number(_raw_curve25519(peer_public_key.x, self.a)) | ||||
							
								
								
									
										10
									
								
								RNS/Cryptography/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								RNS/Cryptography/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| import os | ||||
| import glob | ||||
| 
 | ||||
| from .Hashes import sha256 | ||||
| from .HKDF import hkdf | ||||
| from .PKCS7 import PKCS7 | ||||
| from .Fernet import Fernet | ||||
| 
 | ||||
| modules = glob.glob(os.path.dirname(__file__)+"/*.py") | ||||
| __all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')] | ||||
| @ -28,11 +28,10 @@ import atexit | ||||
| import hashlib | ||||
| 
 | ||||
| from .vendor import umsgpack as umsgpack | ||||
| from cryptography.hazmat.backends import default_backend | ||||
| from cryptography.hazmat.primitives import serialization | ||||
| from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey | ||||
| from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey | ||||
| 
 | ||||
| from RNS.Cryptography.Curve25519 import X25519PrivateKey, X25519PublicKey | ||||
| from RNS.Cryptography import Fernet | ||||
| 
 | ||||
| 
 | ||||
| @ -292,11 +291,7 @@ class Identity: | ||||
| 
 | ||||
|     def create_keys(self): | ||||
|         self.prv           = X25519PrivateKey.generate() | ||||
|         self.prv_bytes     = self.prv.private_bytes( | ||||
|             encoding=serialization.Encoding.Raw, | ||||
|             format=serialization.PrivateFormat.Raw, | ||||
|             encryption_algorithm=serialization.NoEncryption() | ||||
|         ) | ||||
|         self.prv_bytes     = self.prv.private_bytes() | ||||
| 
 | ||||
|         self.sig_prv       = Ed25519PrivateKey.generate() | ||||
|         self.sig_prv_bytes = self.sig_prv.private_bytes( | ||||
| @ -306,10 +301,7 @@ class Identity: | ||||
|         ) | ||||
| 
 | ||||
|         self.pub           = self.prv.public_key() | ||||
|         self.pub_bytes     = self.pub.public_bytes( | ||||
|             encoding=serialization.Encoding.Raw, | ||||
|             format=serialization.PublicFormat.Raw | ||||
|         ) | ||||
|         self.pub_bytes     = self.pub.public_bytes() | ||||
| 
 | ||||
|         self.sig_pub       = self.sig_prv.public_key() | ||||
|         self.sig_pub_bytes = self.sig_pub.public_bytes( | ||||
| @ -347,10 +339,7 @@ class Identity: | ||||
|             self.sig_prv       = Ed25519PrivateKey.from_private_bytes(self.sig_prv_bytes) | ||||
|              | ||||
|             self.pub           = self.prv.public_key() | ||||
|             self.pub_bytes     = self.pub.public_bytes( | ||||
|                 encoding=serialization.Encoding.Raw, | ||||
|                 format=serialization.PublicFormat.Raw | ||||
|             ) | ||||
|             self.pub_bytes     = self.pub.public_bytes() | ||||
| 
 | ||||
|             self.sig_pub       = self.sig_prv.public_key() | ||||
|             self.sig_pub_bytes = self.sig_pub.public_bytes( | ||||
| @ -416,10 +405,7 @@ class Identity: | ||||
|         """ | ||||
|         if self.pub != None: | ||||
|             ephemeral_key = X25519PrivateKey.generate() | ||||
|             ephemeral_pub_bytes = ephemeral_key.public_key().public_bytes( | ||||
|                 encoding=serialization.Encoding.Raw, | ||||
|                 format=serialization.PublicFormat.Raw | ||||
|             ) | ||||
|             ephemeral_pub_bytes = ephemeral_key.public_key().public_bytes() | ||||
| 
 | ||||
|             shared_key = ephemeral_key.exchange(self.pub) | ||||
|              | ||||
|  | ||||
| @ -20,12 +20,11 @@ | ||||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| # SOFTWARE. | ||||
| 
 | ||||
| from cryptography.hazmat.backends import default_backend | ||||
| from cryptography.hazmat.primitives import hashes | ||||
| from cryptography.hazmat.primitives import serialization | ||||
| from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey | ||||
| from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey | ||||
| 
 | ||||
| from RNS.Cryptography.Curve25519 import X25519PrivateKey, X25519PublicKey | ||||
| from RNS.Cryptography import Fernet | ||||
| 
 | ||||
| from time import sleep | ||||
| @ -178,10 +177,7 @@ class Link: | ||||
|         self.fernet  = None | ||||
|          | ||||
|         self.pub = self.prv.public_key() | ||||
|         self.pub_bytes = self.pub.public_bytes( | ||||
|             encoding=serialization.Encoding.Raw, | ||||
|             format=serialization.PublicFormat.Raw | ||||
|         ) | ||||
|         self.pub_bytes = self.pub.public_bytes() | ||||
| 
 | ||||
|         self.sig_pub = self.sig_prv.public_key() | ||||
|         self.sig_pub_bytes = self.sig_pub.public_bytes( | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user