python有两个实现
https://gist.github.com/nickovs/cc3c22d15f239a2640c185035c06f8a3
保存为 curve25519.py
"""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 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 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 _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))
调用
from binascii import hexlify, unhexlify from curve25519 import X25519PrivateKey private_key = unhexlify('') peer_public_key = unhexlify('') x25519x = X25519PrivateKey.from_private_bytes(private_key) shared_secret = x25519x.exchange(peer_public_key) print(hexlify(shared_secret))
另外一个实现
https://github.com/1ocalhost/x25519
调用方法
from binascii import hexlify, unhexlify import x25519 private_key = unhexlify('') public_key = x25519.scalar_base_mult(private_key) print(hexlify(public_key)) peer_public_key = unhexlify('') shared_secret = x25519.scalar_mult(private_key, peer_public_key) print(hexlify(shared_secret))
生成key
aaa = x25519.djbec.genkey() bbb = x25519.djbec.encodeint(aaa) print('priv=', hexlify(bbb))
rust
use rand_core::{OsRng, RngCore}; use x25519_dalek::{EphemeralSecret, StaticSecret, PublicKey, x25519}; use hex::FromHex; fn main() { //let alice_secret = StaticSecret::new(OsRng); //let alice_secret = StaticSecret::random_from_rng(&mut OsRng); let mut x4 = [0u8; 32]; let _ = hex::decode_to_slice("", &mut x4 as &mut [u8]).unwrap(); let alice_secret = StaticSecret::from(x4); let xxx = alice_secret.to_bytes(); println!("alice secret={}", hex::encode_upper(&xxx)); let alice_public = PublicKey::from(&alice_secret); // let bob_secret = StaticSecret::new(OsRng); // let bob_secret = StaticSecret::random_from_rng(&mut OsRng); //let tmp = hex::decode("").unwrap(); //let x4: [u8; 32] = (&tmp[..]).try_into().unwrap(); let x4 = <[u8; 32]>::from_hex("").unwrap(); let bob_secret = StaticSecret::from(x4); let bob_public = PublicKey::from(&bob_secret); let yyy = bob_secret.to_bytes(); println!("bob secret={}", hex::encode_upper(&yyy)); let zzz = bob_public.to_bytes(); println!("bob public={}", hex::encode_upper(&zzz)); //let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); //let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); let alice_shared = x25519(xxx, zzz); let bob_shared = x25519(yyy, alice_public.to_bytes()); assert_eq!(alice_shared, bob_shared); println!("shared={}", hex::encode_upper(&alice_shared)); }