1# Copyright 2013 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
15from __future__ import absolute_import, division, print_function
16
17from nacl import exceptions as exc
18from nacl._sodium import ffi, lib
19from nacl.exceptions import ensure
20
21
22crypto_sign_BYTES = lib.crypto_sign_bytes()
23# crypto_sign_SEEDBYTES = lib.crypto_sign_seedbytes()
24crypto_sign_SEEDBYTES = lib.crypto_sign_secretkeybytes() // 2
25crypto_sign_PUBLICKEYBYTES = lib.crypto_sign_publickeybytes()
26crypto_sign_SECRETKEYBYTES = lib.crypto_sign_secretkeybytes()
27
28crypto_sign_curve25519_BYTES = lib.crypto_box_secretkeybytes()
29
30crypto_sign_ed25519ph_STATEBYTES = lib.crypto_sign_ed25519ph_statebytes()
31
32
33def crypto_sign_keypair():
34    """
35    Returns a randomly generated public key and secret key.
36
37    :rtype: (bytes(public_key), bytes(secret_key))
38    """
39    pk = ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES)
40    sk = ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)
41
42    rc = lib.crypto_sign_keypair(pk, sk)
43    ensure(rc == 0,
44           'Unexpected library error',
45           raising=exc.RuntimeError)
46
47    return (
48        ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:],
49        ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
50    )
51
52
53def crypto_sign_seed_keypair(seed):
54    """
55    Computes and returns the public key and secret key using the seed ``seed``.
56
57    :param seed: bytes
58    :rtype: (bytes(public_key), bytes(secret_key))
59    """
60    if len(seed) != crypto_sign_SEEDBYTES:
61        raise exc.ValueError("Invalid seed")
62
63    pk = ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES)
64    sk = ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)
65
66    rc = lib.crypto_sign_seed_keypair(pk, sk, seed)
67    ensure(rc == 0,
68           'Unexpected library error',
69           raising=exc.RuntimeError)
70
71    return (
72        ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:],
73        ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
74    )
75
76
77def crypto_sign(message, sk):
78    """
79    Signs the message ``message`` using the secret key ``sk`` and returns the
80    signed message.
81
82    :param message: bytes
83    :param sk: bytes
84    :rtype: bytes
85    """
86    signed = ffi.new("unsigned char[]", len(message) + crypto_sign_BYTES)
87    signed_len = ffi.new("unsigned long long *")
88
89    rc = lib.crypto_sign(signed, signed_len, message, len(message), sk)
90    ensure(rc == 0,
91           'Unexpected library error',
92           raising=exc.RuntimeError)
93
94    return ffi.buffer(signed, signed_len[0])[:]
95
96
97def crypto_sign_open(signed, pk):
98    """
99    Verifies the signature of the signed message ``signed`` using the public
100    key ``pk`` and returns the unsigned message.
101
102    :param signed: bytes
103    :param pk: bytes
104    :rtype: bytes
105    """
106    message = ffi.new("unsigned char[]", len(signed))
107    message_len = ffi.new("unsigned long long *")
108
109    if lib.crypto_sign_open(
110            message, message_len, signed, len(signed), pk) != 0:
111        raise exc.BadSignatureError("Signature was forged or corrupt")
112
113    return ffi.buffer(message, message_len[0])[:]
114
115
116def crypto_sign_ed25519_pk_to_curve25519(public_key_bytes):
117    """
118    Converts a public Ed25519 key (encoded as bytes ``public_key_bytes``) to
119    a public Curve25519 key as bytes.
120
121    Raises a ValueError if ``public_key_bytes`` is not of length
122    ``crypto_sign_PUBLICKEYBYTES``
123
124    :param public_key_bytes: bytes
125    :rtype: bytes
126    """
127    if len(public_key_bytes) != crypto_sign_PUBLICKEYBYTES:
128        raise exc.ValueError("Invalid curve public key")
129
130    curve_public_key_len = crypto_sign_curve25519_BYTES
131    curve_public_key = ffi.new("unsigned char[]", curve_public_key_len)
132
133    rc = lib.crypto_sign_ed25519_pk_to_curve25519(curve_public_key,
134                                                  public_key_bytes)
135    ensure(rc == 0,
136           'Unexpected library error',
137           raising=exc.RuntimeError)
138
139    return ffi.buffer(curve_public_key, curve_public_key_len)[:]
140
141
142def crypto_sign_ed25519_sk_to_curve25519(secret_key_bytes):
143    """
144    Converts a secret Ed25519 key (encoded as bytes ``secret_key_bytes``) to
145    a secret Curve25519 key as bytes.
146
147    Raises a ValueError if ``secret_key_bytes``is not of length
148    ``crypto_sign_SECRETKEYBYTES``
149
150    :param secret_key_bytes: bytes
151    :rtype: bytes
152    """
153    if len(secret_key_bytes) != crypto_sign_SECRETKEYBYTES:
154        raise exc.ValueError("Invalid curve secret key")
155
156    curve_secret_key_len = crypto_sign_curve25519_BYTES
157    curve_secret_key = ffi.new("unsigned char[]", curve_secret_key_len)
158
159    rc = lib.crypto_sign_ed25519_sk_to_curve25519(curve_secret_key,
160                                                  secret_key_bytes)
161    ensure(rc == 0,
162           'Unexpected library error',
163           raising=exc.RuntimeError)
164
165    return ffi.buffer(curve_secret_key, curve_secret_key_len)[:]
166
167
168def crypto_sign_ed25519_sk_to_pk(secret_key_bytes):
169    """
170    Extract the public Ed25519 key from a secret Ed25519 key (encoded
171    as bytes ``secret_key_bytes``).
172
173    Raises a ValueError if ``secret_key_bytes``is not of length
174    ``crypto_sign_SECRETKEYBYTES``
175
176    :param secret_key_bytes: bytes
177    :rtype: bytes
178    """
179    if len(secret_key_bytes) != crypto_sign_SECRETKEYBYTES:
180        raise exc.ValueError("Invalid secret key")
181
182    return secret_key_bytes[crypto_sign_SEEDBYTES:]
183
184
185def crypto_sign_ed25519_sk_to_seed(secret_key_bytes):
186    """
187    Extract the seed from a secret Ed25519 key (encoded
188    as bytes ``secret_key_bytes``).
189
190    Raises a ValueError if ``secret_key_bytes``is not of length
191    ``crypto_sign_SECRETKEYBYTES``
192
193    :param secret_key_bytes: bytes
194    :rtype: bytes
195    """
196    if len(secret_key_bytes) != crypto_sign_SECRETKEYBYTES:
197        raise exc.ValueError("Invalid secret key")
198
199    return secret_key_bytes[:crypto_sign_SEEDBYTES]
200
201
202class crypto_sign_ed25519ph_state(object):
203    """
204    State object wrapping the sha-512 state used in ed25519ph computation
205    """
206    __slots__ = ['state']
207
208    def __init__(self):
209        self.state = ffi.new('unsigned char[]',
210                             crypto_sign_ed25519ph_STATEBYTES)
211
212        rc = lib.crypto_sign_ed25519ph_init(self.state)
213
214        ensure(rc == 0,
215               'Unexpected library error',
216               raising=exc.RuntimeError)
217
218
219def crypto_sign_ed25519ph_update(edph, pmsg):
220    """
221    Update the hash state wrapped in edph
222
223    :param edph: the ed25519ph state being updated
224    :type edph: crypto_sign_ed25519ph_state
225    :param pmsg: the partial message
226    :type pmsg: bytes
227    :rtype: None
228    """
229    ensure(isinstance(edph, crypto_sign_ed25519ph_state),
230           'edph parameter must be a ed25519ph_state object',
231           raising=exc.TypeError)
232    ensure(isinstance(pmsg, bytes),
233           'pmsg parameter must be a bytes object',
234           raising=exc.TypeError)
235    rc = lib.crypto_sign_ed25519ph_update(edph.state,
236                                          pmsg,
237                                          len(pmsg))
238    ensure(rc == 0,
239           'Unexpected library error',
240           raising=exc.RuntimeError)
241
242
243def crypto_sign_ed25519ph_final_create(edph,
244                                       sk):
245    """
246    Create a signature for the data hashed in edph
247    using the secret key sk
248
249    :param edph: the ed25519ph state for the data
250                 being signed
251    :type edph: crypto_sign_ed25519ph_state
252    :param sk: the ed25519 secret part of the signing key
253    :type sk: bytes
254    :return: ed25519ph signature
255    :rtype: bytes
256    """
257    ensure(isinstance(edph, crypto_sign_ed25519ph_state),
258           'edph parameter must be a ed25519ph_state object',
259           raising=exc.TypeError)
260    ensure(isinstance(sk, bytes),
261           'secret key parameter must be a bytes object',
262           raising=exc.TypeError)
263    ensure(len(sk) == crypto_sign_SECRETKEYBYTES,
264           ('secret key must be {0} '
265            'bytes long').format(crypto_sign_SECRETKEYBYTES),
266           raising=exc.TypeError)
267    signature = ffi.new("unsigned char[]", crypto_sign_BYTES)
268    rc = lib.crypto_sign_ed25519ph_final_create(edph.state,
269                                                signature,
270                                                ffi.NULL,
271                                                sk)
272    ensure(rc == 0,
273           'Unexpected library error',
274           raising=exc.RuntimeError)
275
276    return ffi.buffer(signature, crypto_sign_BYTES)[:]
277
278
279def crypto_sign_ed25519ph_final_verify(edph,
280                                       signature,
281                                       pk):
282    """
283    Verify a prehashed signature using the public key pk
284
285    :param edph: the ed25519ph state for the data
286                 being verified
287    :type edph: crypto_sign_ed25519ph_state
288    :param signature: the signature being verified
289    :type signature: bytes
290    :param pk: the ed25519 public part of the signing key
291    :type pk: bytes
292    :return: True if the signature is valid
293    :rtype: boolean
294    :raises exc.BadSignatureError: if the signature is not valid
295    """
296    ensure(isinstance(edph, crypto_sign_ed25519ph_state),
297           'edph parameter must be a ed25519ph_state object',
298           raising=exc.TypeError)
299    ensure(isinstance(signature, bytes),
300           'signature parameter must be a bytes object',
301           raising=exc.TypeError)
302    ensure(len(signature) == crypto_sign_BYTES,
303           ('signature must be {0} '
304            'bytes long').format(crypto_sign_BYTES),
305           raising=exc.TypeError)
306    ensure(isinstance(pk, bytes),
307           'public key parameter must be a bytes object',
308           raising=exc.TypeError)
309    ensure(len(pk) == crypto_sign_PUBLICKEYBYTES,
310           ('public key must be {0} '
311            'bytes long').format(crypto_sign_PUBLICKEYBYTES),
312           raising=exc.TypeError)
313    rc = lib.crypto_sign_ed25519ph_final_verify(edph.state,
314                                                signature,
315                                                pk)
316    if rc != 0:
317        raise exc.BadSignatureError("Signature was forged or corrupt")
318
319    return True
320