Coverage for tropicsquare / ports / micropython / x25519.py: 0%
50 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 21:24 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 21:24 +0000
2class X25519:
3 @classmethod
4 def exchange(cls, private_bytes, public_bytes):
5 if len(private_bytes) != 32 or len(public_bytes) != 32:
6 raise ValueError("Both private and public keys must be 32 bytes long")
8 # Clamp the private key per RFC 7748:
9 k = bytearray(private_bytes)
10 k[0] &= 248
11 k[31] &= 127
12 k[31] |= 64
13 scalar = int.from_bytes(k, "little")
14 u = int.from_bytes(public_bytes, "little")
16 # Curve25519 prime and constant:
17 p = 2**255 - 19
18 a24 = 121665 # (486662 - 2) // 4
20 # Set up ladder variables:
21 x1 = u
22 x2, z2 = 1, 0
23 x3, z3 = u, 1
24 swap = 0
26 # Loop over bits of the scalar, from bit 254 down to bit 0.
27 for t in range(254, -1, -1):
28 k_t = (scalar >> t) & 1
29 swap ^= k_t
30 # Conditional swap: if swap is 1, swap (x2,z2) with (x3,z3)
31 if swap:
32 x2, x3 = x3, x2
33 z2, z3 = z3, z2
34 swap = k_t
36 # Montgomery ladder step:
37 A = (x2 + z2) % p
38 AA = (A * A) % p
39 B = (x2 - z2) % p
40 BB = (B * B) % p
41 E = (AA - BB) % p
42 C = (x3 + z3) % p
43 D = (x3 - z3) % p
44 DA = (D * A) % p
45 CB = (C * B) % p
47 # Update x3 and z3:
48 x3 = (DA + CB) % p
49 x3 = (x3 * x3) % p
50 z3 = (DA - CB) % p
51 z3 = (z3 * z3) % p
52 z3 = (x1 * z3) % p
54 # Update x2 and z2:
55 x2 = (AA * BB) % p
56 z2 = (E * (AA + a24 * E)) % p
58 # Final conditional swap if needed:
59 if swap:
60 x2, x3 = x3, x2
61 z2, z3 = z3, z2
63 # Compute the shared secret as x2/z2 mod p:
64 z2_inv = pow(z2, p - 2, p)
65 shared_secret = (x2 * z2_inv) % p
67 # Return the result as 32-byte little-endian bytes:
68 return shared_secret.to_bytes(32, "little")
71 @classmethod
72 def pubkey(cls, private_bytes):
73 # The base point for X25519 is 9, represented as a 32-byte little-endian value.
74 basepoint = (9).to_bytes(32, "little")
75 return cls.exchange(private_bytes, basepoint)