-
Notifications
You must be signed in to change notification settings - Fork 80
/
x25519.py
136 lines (99 loc) · 3.18 KB
/
x25519.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# Authors:
# Hubert Kario (2017)
#
# See the LICENSE file for legal information regarding use of this file.
"""Handling X25519 and X448 curve based key agreement protocol."""
from .cryptomath import bytesToNumber, numberToByteArray, divceil
# the names of the variables come directly from RFC 7748 so changing them
# would make the code harder to audit/compare
# pylint: disable=invalid-name
def decodeUCoordinate(u, bits):
"""Function to decode the public U coordinate of X25519-family curves."""
if bits not in (255, 448):
raise ValueError("Invalid number of expected bits")
if bits % 8:
u[-1] &= (1 << (bits % 8)) - 1
return bytesToNumber(u, endian="little")
def decodeScalar22519(k):
"""Function to decode the private K parameter of the x25519 function."""
k[0] &= 248
k[31] &= 127
k[31] |= 64
return bytesToNumber(k, endian="little")
def decodeScalar448(k):
"""Function to decode the private K parameter of the X448 function."""
k[0] &= 252
k[55] |= 128
return bytesToNumber(k, endian="little")
def cswap(swap, x_2, x_3):
"""Conditional swap function."""
if swap:
return x_3, x_2
else:
return x_2, x_3
X25519_G = numberToByteArray(9, 32, endian="little")
X25519_ORDER_SIZE = 32
def x25519(k, u):
"""
Perform point multiplication on X25519 curve.
:type k: bytearray
:param k: random secret value (multiplier), should be 32 byte long
:type u: bytearray
:param u: curve generator or the other party key share
:rtype: bytearray
"""
bits = 255
k = decodeScalar22519(k)
u = decodeUCoordinate(u, bits)
a24 = 121665
p = 2**255 - 19
return _x25519_generic(k, u, bits, a24, p)
X448_G = numberToByteArray(5, 56, endian="little")
X448_ORDER_SIZE = 56
def x448(k, u):
"""
Perform point multiplication on X448 curve.
:type k: bytearray
:param k: random secret value (multiplier), should be 56 bytes long
:type u: bytearray
:param u: curve generator or the other party key share
:rtype: bytearray
"""
bits = 448
k = decodeScalar448(k)
u = decodeUCoordinate(u, bits)
a24 = 39081
p = 2**448 - 2**224 - 1
return _x25519_generic(k, u, bits, a24, p)
def _x25519_generic(k, u, bits, a24, p):
"""Generic Montgomery ladder implementation of the x25519 algorithm."""
x_1 = u
x_2 = 1
z_2 = 0
x_3 = u
z_3 = 1
swap = 0
for t in range(bits-1, -1, -1):
k_t = (k >> t) & 1
swap ^= k_t
x_2, x_3 = cswap(swap, x_2, x_3)
z_2, z_3 = cswap(swap, z_2, z_3)
swap = k_t
A = (x_2 + z_2) % p
AA = pow(A, 2, p)
B = (x_2 - z_2) % p
BB = pow(B, 2, p)
E = (AA - BB) % p
C = (x_3 + z_3) % p
D = (x_3 - z_3) % p
DA = (D * A) % p
CB = (C * B) % p
x_3 = pow(DA + CB, 2, p)
z_3 = (x_1 * pow(DA - CB, 2, p)) % p
x_2 = (AA * BB) % p
z_2 = (E * (AA + a24 * E)) % p
x_2, x_3 = cswap(swap, x_2, x_3)
z_2, z_3 = cswap(swap, z_2, z_3)
ret = (x_2 * pow(z_2, p - 2, p)) % p
return numberToByteArray(ret, divceil(bits, 8), endian="little")
# pylint: enable=invalid-name