1# Copyright 2013-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
20has_crypto_scalarmult_ed25519 = bool(lib.PYNACL_HAS_CRYPTO_SCALARMULT_ED25519)
21
22crypto_scalarmult_BYTES: int = lib.crypto_scalarmult_bytes()
23crypto_scalarmult_SCALARBYTES: int = lib.crypto_scalarmult_scalarbytes()
24
25crypto_scalarmult_ed25519_BYTES = 0
26crypto_scalarmult_ed25519_SCALARBYTES = 0
27
28if has_crypto_scalarmult_ed25519:
29 crypto_scalarmult_ed25519_BYTES = lib.crypto_scalarmult_ed25519_bytes()
30 crypto_scalarmult_ed25519_SCALARBYTES = (
31 lib.crypto_scalarmult_ed25519_scalarbytes()
32 )
33
34
35def crypto_scalarmult_base(n: bytes) -> bytes:
36 """
37 Computes and returns the scalar product of a standard group element and an
38 integer ``n``.
39
40 :param n: bytes
41 :rtype: bytes
42 """
43 q = ffi.new("unsigned char[]", crypto_scalarmult_BYTES)
44
45 rc = lib.crypto_scalarmult_base(q, n)
46 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
47
48 return ffi.buffer(q, crypto_scalarmult_SCALARBYTES)[:]
49
50
51def crypto_scalarmult(n: bytes, p: bytes) -> bytes:
52 """
53 Computes and returns the scalar product of the given group element and an
54 integer ``n``.
55
56 :param p: bytes
57 :param n: bytes
58 :rtype: bytes
59 """
60 q = ffi.new("unsigned char[]", crypto_scalarmult_BYTES)
61
62 rc = lib.crypto_scalarmult(q, n, p)
63 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
64
65 return ffi.buffer(q, crypto_scalarmult_SCALARBYTES)[:]
66
67
68def crypto_scalarmult_ed25519_base(n: bytes) -> bytes:
69 """
70 Computes and returns the scalar product of a standard group element and an
71 integer ``n`` on the edwards25519 curve.
72
73 :param n: a :py:data:`.crypto_scalarmult_ed25519_SCALARBYTES` long bytes
74 sequence representing a scalar
75 :type n: bytes
76 :return: a point on the edwards25519 curve, represented as a
77 :py:data:`.crypto_scalarmult_ed25519_BYTES` long bytes sequence
78 :rtype: bytes
79 :raises nacl.exceptions.UnavailableError: If called when using a
80 minimal build of libsodium.
81 """
82 ensure(
83 has_crypto_scalarmult_ed25519,
84 "Not available in minimal build",
85 raising=exc.UnavailableError,
86 )
87
88 ensure(
89 isinstance(n, bytes)
90 and len(n) == crypto_scalarmult_ed25519_SCALARBYTES,
91 "Input must be a {} long bytes sequence".format(
92 "crypto_scalarmult_ed25519_SCALARBYTES"
93 ),
94 raising=exc.TypeError,
95 )
96
97 q = ffi.new("unsigned char[]", crypto_scalarmult_ed25519_BYTES)
98
99 rc = lib.crypto_scalarmult_ed25519_base(q, n)
100 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
101
102 return ffi.buffer(q, crypto_scalarmult_ed25519_BYTES)[:]
103
104
105def crypto_scalarmult_ed25519_base_noclamp(n: bytes) -> bytes:
106 """
107 Computes and returns the scalar product of a standard group element and an
108 integer ``n`` on the edwards25519 curve. The integer ``n`` is not clamped.
109
110 :param n: a :py:data:`.crypto_scalarmult_ed25519_SCALARBYTES` long bytes
111 sequence representing a scalar
112 :type n: bytes
113 :return: a point on the edwards25519 curve, represented as a
114 :py:data:`.crypto_scalarmult_ed25519_BYTES` long bytes sequence
115 :rtype: bytes
116 :raises nacl.exceptions.UnavailableError: If called when using a
117 minimal build of libsodium.
118 """
119 ensure(
120 has_crypto_scalarmult_ed25519,
121 "Not available in minimal build",
122 raising=exc.UnavailableError,
123 )
124
125 ensure(
126 isinstance(n, bytes)
127 and len(n) == crypto_scalarmult_ed25519_SCALARBYTES,
128 "Input must be a {} long bytes sequence".format(
129 "crypto_scalarmult_ed25519_SCALARBYTES"
130 ),
131 raising=exc.TypeError,
132 )
133
134 q = ffi.new("unsigned char[]", crypto_scalarmult_ed25519_BYTES)
135
136 rc = lib.crypto_scalarmult_ed25519_base_noclamp(q, n)
137 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
138
139 return ffi.buffer(q, crypto_scalarmult_ed25519_BYTES)[:]
140
141
142def crypto_scalarmult_ed25519(n: bytes, p: bytes) -> bytes:
143 """
144 Computes and returns the scalar product of a *clamped* integer ``n``
145 and the given group element on the edwards25519 curve.
146 The scalar is clamped, as done in the public key generation case,
147 by setting to zero the bits in position [0, 1, 2, 255] and setting
148 to one the bit in position 254.
149
150 :param n: a :py:data:`.crypto_scalarmult_ed25519_SCALARBYTES` long bytes
151 sequence representing a scalar
152 :type n: bytes
153 :param p: a :py:data:`.crypto_scalarmult_ed25519_BYTES` long bytes sequence
154 representing a point on the edwards25519 curve
155 :type p: bytes
156 :return: a point on the edwards25519 curve, represented as a
157 :py:data:`.crypto_scalarmult_ed25519_BYTES` long bytes sequence
158 :rtype: bytes
159 :raises nacl.exceptions.UnavailableError: If called when using a
160 minimal build of libsodium.
161 """
162 ensure(
163 has_crypto_scalarmult_ed25519,
164 "Not available in minimal build",
165 raising=exc.UnavailableError,
166 )
167
168 ensure(
169 isinstance(n, bytes)
170 and len(n) == crypto_scalarmult_ed25519_SCALARBYTES,
171 "Input must be a {} long bytes sequence".format(
172 "crypto_scalarmult_ed25519_SCALARBYTES"
173 ),
174 raising=exc.TypeError,
175 )
176
177 ensure(
178 isinstance(p, bytes) and len(p) == crypto_scalarmult_ed25519_BYTES,
179 "Input must be a {} long bytes sequence".format(
180 "crypto_scalarmult_ed25519_BYTES"
181 ),
182 raising=exc.TypeError,
183 )
184
185 q = ffi.new("unsigned char[]", crypto_scalarmult_ed25519_BYTES)
186
187 rc = lib.crypto_scalarmult_ed25519(q, n, p)
188 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
189
190 return ffi.buffer(q, crypto_scalarmult_ed25519_BYTES)[:]
191
192
193def crypto_scalarmult_ed25519_noclamp(n: bytes, p: bytes) -> bytes:
194 """
195 Computes and returns the scalar product of an integer ``n``
196 and the given group element on the edwards25519 curve. The integer
197 ``n`` is not clamped.
198
199 :param n: a :py:data:`.crypto_scalarmult_ed25519_SCALARBYTES` long bytes
200 sequence representing a scalar
201 :type n: bytes
202 :param p: a :py:data:`.crypto_scalarmult_ed25519_BYTES` long bytes sequence
203 representing a point on the edwards25519 curve
204 :type p: bytes
205 :return: a point on the edwards25519 curve, represented as a
206 :py:data:`.crypto_scalarmult_ed25519_BYTES` long bytes sequence
207 :rtype: bytes
208 :raises nacl.exceptions.UnavailableError: If called when using a
209 minimal build of libsodium.
210 """
211 ensure(
212 has_crypto_scalarmult_ed25519,
213 "Not available in minimal build",
214 raising=exc.UnavailableError,
215 )
216
217 ensure(
218 isinstance(n, bytes)
219 and len(n) == crypto_scalarmult_ed25519_SCALARBYTES,
220 "Input must be a {} long bytes sequence".format(
221 "crypto_scalarmult_ed25519_SCALARBYTES"
222 ),
223 raising=exc.TypeError,
224 )
225
226 ensure(
227 isinstance(p, bytes) and len(p) == crypto_scalarmult_ed25519_BYTES,
228 "Input must be a {} long bytes sequence".format(
229 "crypto_scalarmult_ed25519_BYTES"
230 ),
231 raising=exc.TypeError,
232 )
233
234 q = ffi.new("unsigned char[]", crypto_scalarmult_ed25519_BYTES)
235
236 rc = lib.crypto_scalarmult_ed25519_noclamp(q, n, p)
237 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
238
239 return ffi.buffer(q, crypto_scalarmult_ed25519_BYTES)[:]