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

1 

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") 

7 

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") 

15 

16 # Curve25519 prime and constant: 

17 p = 2**255 - 19 

18 a24 = 121665 # (486662 - 2) // 4 

19 

20 # Set up ladder variables: 

21 x1 = u 

22 x2, z2 = 1, 0 

23 x3, z3 = u, 1 

24 swap = 0 

25 

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 

35 

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 

46 

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 

53 

54 # Update x2 and z2: 

55 x2 = (AA * BB) % p 

56 z2 = (E * (AA + a24 * E)) % p 

57 

58 # Final conditional swap if needed: 

59 if swap: 

60 x2, x3 = x3, x2 

61 z2, z3 = z3, z2 

62 

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 

66 

67 # Return the result as 32-byte little-endian bytes: 

68 return shared_secret.to_bytes(32, "little") 

69 

70 

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)