1# Copyright 2018 Donald Stufft and individual contributors
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15
16from nacl import exceptions as exc
17from nacl._sodium import ffi, lib
18from nacl.exceptions import ensure
19
20
21has_crypto_core_ed25519 = bool(lib.PYNACL_HAS_CRYPTO_CORE_ED25519)
22
23crypto_core_ed25519_BYTES = 0
24crypto_core_ed25519_SCALARBYTES = 0
25crypto_core_ed25519_NONREDUCEDSCALARBYTES = 0
26
27if has_crypto_core_ed25519:
28 crypto_core_ed25519_BYTES = lib.crypto_core_ed25519_bytes()
29 crypto_core_ed25519_SCALARBYTES = lib.crypto_core_ed25519_scalarbytes()
30 crypto_core_ed25519_NONREDUCEDSCALARBYTES = (
31 lib.crypto_core_ed25519_nonreducedscalarbytes()
32 )
33
34
35def crypto_core_ed25519_is_valid_point(p: bytes) -> bool:
36 """
37 Check if ``p`` represents a point on the edwards25519 curve, in canonical
38 form, on the main subgroup, and that the point doesn't have a small order.
39
40 :param p: a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
41 representing a point on the edwards25519 curve
42 :type p: bytes
43 :return: point validity
44 :rtype: bool
45 :raises nacl.exceptions.UnavailableError: If called when using a
46 minimal build of libsodium.
47 """
48 ensure(
49 has_crypto_core_ed25519,
50 "Not available in minimal build",
51 raising=exc.UnavailableError,
52 )
53
54 ensure(
55 isinstance(p, bytes) and len(p) == crypto_core_ed25519_BYTES,
56 "Point must be a crypto_core_ed25519_BYTES long bytes sequence",
57 raising=exc.TypeError,
58 )
59
60 rc = lib.crypto_core_ed25519_is_valid_point(p)
61 return rc == 1
62
63
64def crypto_core_ed25519_add(p: bytes, q: bytes) -> bytes:
65 """
66 Add two points on the edwards25519 curve.
67
68 :param p: a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
69 representing a point on the edwards25519 curve
70 :type p: bytes
71 :param q: a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
72 representing a point on the edwards25519 curve
73 :type q: bytes
74 :return: a point on the edwards25519 curve represented as
75 a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
76 :rtype: bytes
77 :raises nacl.exceptions.UnavailableError: If called when using a
78 minimal build of libsodium.
79 """
80 ensure(
81 has_crypto_core_ed25519,
82 "Not available in minimal build",
83 raising=exc.UnavailableError,
84 )
85
86 ensure(
87 isinstance(p, bytes)
88 and isinstance(q, bytes)
89 and len(p) == crypto_core_ed25519_BYTES
90 and len(q) == crypto_core_ed25519_BYTES,
91 "Each point must be a {} long bytes sequence".format(
92 "crypto_core_ed25519_BYTES"
93 ),
94 raising=exc.TypeError,
95 )
96
97 r = ffi.new("unsigned char[]", crypto_core_ed25519_BYTES)
98
99 rc = lib.crypto_core_ed25519_add(r, p, q)
100 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
101
102 return ffi.buffer(r, crypto_core_ed25519_BYTES)[:]
103
104
105def crypto_core_ed25519_sub(p: bytes, q: bytes) -> bytes:
106 """
107 Subtract a point from another on the edwards25519 curve.
108
109 :param p: a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
110 representing a point on the edwards25519 curve
111 :type p: bytes
112 :param q: a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
113 representing a point on the edwards25519 curve
114 :type q: bytes
115 :return: a point on the edwards25519 curve represented as
116 a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
117 :rtype: bytes
118 :raises nacl.exceptions.UnavailableError: If called when using a
119 minimal build of libsodium.
120 """
121 ensure(
122 has_crypto_core_ed25519,
123 "Not available in minimal build",
124 raising=exc.UnavailableError,
125 )
126
127 ensure(
128 isinstance(p, bytes)
129 and isinstance(q, bytes)
130 and len(p) == crypto_core_ed25519_BYTES
131 and len(q) == crypto_core_ed25519_BYTES,
132 "Each point must be a {} long bytes sequence".format(
133 "crypto_core_ed25519_BYTES"
134 ),
135 raising=exc.TypeError,
136 )
137
138 r = ffi.new("unsigned char[]", crypto_core_ed25519_BYTES)
139
140 rc = lib.crypto_core_ed25519_sub(r, p, q)
141 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
142
143 return ffi.buffer(r, crypto_core_ed25519_BYTES)[:]
144
145
146def crypto_core_ed25519_scalar_invert(s: bytes) -> bytes:
147 """
148 Return the multiplicative inverse of integer ``s`` modulo ``L``,
149 i.e an integer ``i`` such that ``s * i = 1 (mod L)``, where ``L``
150 is the order of the main subgroup.
151
152 Raises a ``exc.RuntimeError`` if ``s`` is the integer zero.
153
154 :param s: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
155 long bytes sequence representing an integer
156 :type s: bytes
157 :return: an integer represented as a
158 :py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
159 :rtype: bytes
160 :raises nacl.exceptions.UnavailableError: If called when using a
161 minimal build of libsodium.
162 """
163 ensure(
164 has_crypto_core_ed25519,
165 "Not available in minimal build",
166 raising=exc.UnavailableError,
167 )
168
169 ensure(
170 isinstance(s, bytes) and len(s) == crypto_core_ed25519_SCALARBYTES,
171 "Integer s must be a {} long bytes sequence".format(
172 "crypto_core_ed25519_SCALARBYTES"
173 ),
174 raising=exc.TypeError,
175 )
176
177 r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
178
179 rc = lib.crypto_core_ed25519_scalar_invert(r, s)
180 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
181
182 return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]
183
184
185def crypto_core_ed25519_scalar_negate(s: bytes) -> bytes:
186 """
187 Return the integer ``n`` such that ``s + n = 0 (mod L)``, where ``L``
188 is the order of the main subgroup.
189
190 :param s: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
191 long bytes sequence representing an integer
192 :type s: bytes
193 :return: an integer represented as a
194 :py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
195 :rtype: bytes
196 :raises nacl.exceptions.UnavailableError: If called when using a
197 minimal build of libsodium.
198 """
199 ensure(
200 has_crypto_core_ed25519,
201 "Not available in minimal build",
202 raising=exc.UnavailableError,
203 )
204
205 ensure(
206 isinstance(s, bytes) and len(s) == crypto_core_ed25519_SCALARBYTES,
207 "Integer s must be a {} long bytes sequence".format(
208 "crypto_core_ed25519_SCALARBYTES"
209 ),
210 raising=exc.TypeError,
211 )
212
213 r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
214
215 lib.crypto_core_ed25519_scalar_negate(r, s)
216
217 return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]
218
219
220def crypto_core_ed25519_scalar_complement(s: bytes) -> bytes:
221 """
222 Return the complement of integer ``s`` modulo ``L``, i.e. an integer
223 ``c`` such that ``s + c = 1 (mod L)``, where ``L`` is the order of
224 the main subgroup.
225
226 :param s: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
227 long bytes sequence representing an integer
228 :type s: bytes
229 :return: an integer represented as a
230 :py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
231 :rtype: bytes
232 :raises nacl.exceptions.UnavailableError: If called when using a
233 minimal build of libsodium.
234 """
235 ensure(
236 has_crypto_core_ed25519,
237 "Not available in minimal build",
238 raising=exc.UnavailableError,
239 )
240
241 ensure(
242 isinstance(s, bytes) and len(s) == crypto_core_ed25519_SCALARBYTES,
243 "Integer s must be a {} long bytes sequence".format(
244 "crypto_core_ed25519_SCALARBYTES"
245 ),
246 raising=exc.TypeError,
247 )
248
249 r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
250
251 lib.crypto_core_ed25519_scalar_complement(r, s)
252
253 return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]
254
255
256def crypto_core_ed25519_scalar_add(p: bytes, q: bytes) -> bytes:
257 """
258 Add integers ``p`` and ``q`` modulo ``L``, where ``L`` is the order of
259 the main subgroup.
260
261 :param p: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
262 long bytes sequence representing an integer
263 :type p: bytes
264 :param q: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
265 long bytes sequence representing an integer
266 :type q: bytes
267 :return: an integer represented as a
268 :py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
269 :rtype: bytes
270 :raises nacl.exceptions.UnavailableError: If called when using a
271 minimal build of libsodium.
272 """
273 ensure(
274 has_crypto_core_ed25519,
275 "Not available in minimal build",
276 raising=exc.UnavailableError,
277 )
278
279 ensure(
280 isinstance(p, bytes)
281 and isinstance(q, bytes)
282 and len(p) == crypto_core_ed25519_SCALARBYTES
283 and len(q) == crypto_core_ed25519_SCALARBYTES,
284 "Each integer must be a {} long bytes sequence".format(
285 "crypto_core_ed25519_SCALARBYTES"
286 ),
287 raising=exc.TypeError,
288 )
289
290 r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
291
292 lib.crypto_core_ed25519_scalar_add(r, p, q)
293
294 return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]
295
296
297def crypto_core_ed25519_scalar_sub(p: bytes, q: bytes) -> bytes:
298 """
299 Subtract integers ``p`` and ``q`` modulo ``L``, where ``L`` is the
300 order of the main subgroup.
301
302 :param p: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
303 long bytes sequence representing an integer
304 :type p: bytes
305 :param q: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
306 long bytes sequence representing an integer
307 :type q: bytes
308 :return: an integer represented as a
309 :py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
310 :rtype: bytes
311 :raises nacl.exceptions.UnavailableError: If called when using a
312 minimal build of libsodium.
313 """
314 ensure(
315 has_crypto_core_ed25519,
316 "Not available in minimal build",
317 raising=exc.UnavailableError,
318 )
319
320 ensure(
321 isinstance(p, bytes)
322 and isinstance(q, bytes)
323 and len(p) == crypto_core_ed25519_SCALARBYTES
324 and len(q) == crypto_core_ed25519_SCALARBYTES,
325 "Each integer must be a {} long bytes sequence".format(
326 "crypto_core_ed25519_SCALARBYTES"
327 ),
328 raising=exc.TypeError,
329 )
330
331 r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
332
333 lib.crypto_core_ed25519_scalar_sub(r, p, q)
334
335 return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]
336
337
338def crypto_core_ed25519_scalar_mul(p: bytes, q: bytes) -> bytes:
339 """
340 Multiply integers ``p`` and ``q`` modulo ``L``, where ``L`` is the
341 order of the main subgroup.
342
343 :param p: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
344 long bytes sequence representing an integer
345 :type p: bytes
346 :param q: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
347 long bytes sequence representing an integer
348 :type q: bytes
349 :return: an integer represented as a
350 :py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
351 :rtype: bytes
352 :raises nacl.exceptions.UnavailableError: If called when using a
353 minimal build of libsodium.
354 """
355 ensure(
356 has_crypto_core_ed25519,
357 "Not available in minimal build",
358 raising=exc.UnavailableError,
359 )
360
361 ensure(
362 isinstance(p, bytes)
363 and isinstance(q, bytes)
364 and len(p) == crypto_core_ed25519_SCALARBYTES
365 and len(q) == crypto_core_ed25519_SCALARBYTES,
366 "Each integer must be a {} long bytes sequence".format(
367 "crypto_core_ed25519_SCALARBYTES"
368 ),
369 raising=exc.TypeError,
370 )
371
372 r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
373
374 lib.crypto_core_ed25519_scalar_mul(r, p, q)
375
376 return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]
377
378
379def crypto_core_ed25519_scalar_reduce(s: bytes) -> bytes:
380 """
381 Reduce integer ``s`` to ``s`` modulo ``L``, where ``L`` is the order
382 of the main subgroup.
383
384 :param s: a :py:data:`.crypto_core_ed25519_NONREDUCEDSCALARBYTES`
385 long bytes sequence representing an integer
386 :type s: bytes
387 :return: an integer represented as a
388 :py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
389 :rtype: bytes
390 :raises nacl.exceptions.UnavailableError: If called when using a
391 minimal build of libsodium.
392 """
393 ensure(
394 has_crypto_core_ed25519,
395 "Not available in minimal build",
396 raising=exc.UnavailableError,
397 )
398
399 ensure(
400 isinstance(s, bytes)
401 and len(s) == crypto_core_ed25519_NONREDUCEDSCALARBYTES,
402 "Integer s must be a {} long bytes sequence".format(
403 "crypto_core_ed25519_NONREDUCEDSCALARBYTES"
404 ),
405 raising=exc.TypeError,
406 )
407
408 r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
409
410 lib.crypto_core_ed25519_scalar_reduce(r, s)
411
412 return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]