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
17import nacl.bindings
18from nacl import encoding
19from nacl import exceptions as exc
20from nacl.utils import EncryptedMessage, StringFixer, random
21
22
23class PublicKey(encoding.Encodable, StringFixer, object):
24    """
25    The public key counterpart to an Curve25519 :class:`nacl.public.PrivateKey`
26    for encrypting messages.
27
28    :param public_key: [:class:`bytes`] Encoded Curve25519 public key
29    :param encoder: A class that is able to decode the `public_key`
30
31    :cvar SIZE: The size that the public key is required to be
32    """
33
34    SIZE = nacl.bindings.crypto_box_PUBLICKEYBYTES
35
36    def __init__(self, public_key, encoder=encoding.RawEncoder):
37        self._public_key = encoder.decode(public_key)
38        if not isinstance(self._public_key, bytes):
39            raise exc.TypeError("PublicKey must be created from 32 bytes")
40
41        if len(self._public_key) != self.SIZE:
42            raise exc.ValueError(
43                "The public key must be exactly {0} bytes long".format(
44                    self.SIZE
45                )
46            )
47
48    def __bytes__(self):
49        return self._public_key
50
51    def __hash__(self):
52        return hash(bytes(self))
53
54    def __eq__(self, other):
55        if not isinstance(other, self.__class__):
56            return False
57        return nacl.bindings.sodium_memcmp(bytes(self), bytes(other))
58
59    def __ne__(self, other):
60        return not (self == other)
61
62
63class PrivateKey(encoding.Encodable, StringFixer, object):
64    """
65    Private key for decrypting messages using the Curve25519 algorithm.
66
67    .. warning:: This **must** be protected and remain secret. Anyone who
68        knows the value of your :class:`~nacl.public.PrivateKey` can decrypt
69        any message encrypted by the corresponding
70        :class:`~nacl.public.PublicKey`
71
72    :param private_key: The private key used to decrypt messages
73    :param encoder: The encoder class used to decode the given keys
74
75    :cvar SIZE: The size that the private key is required to be
76    :cvar SEED_SIZE: The size that the seed used to generate the
77                     private key is required to be
78    """
79
80    SIZE = nacl.bindings.crypto_box_SECRETKEYBYTES
81    SEED_SIZE = nacl.bindings.crypto_box_SEEDBYTES
82
83    def __init__(self, private_key, encoder=encoding.RawEncoder):
84        # Decode the secret_key
85        private_key = encoder.decode(private_key)
86        # verify the given secret key type and size are correct
87        if not (isinstance(private_key, bytes) and
88                len(private_key) == self.SIZE):
89            raise exc.TypeError(("PrivateKey must be created from a {0} "
90                                 "bytes long raw secret key").format(self.SIZE)
91                                )
92
93        raw_public_key = nacl.bindings.crypto_scalarmult_base(private_key)
94
95        self._private_key = private_key
96        self.public_key = PublicKey(raw_public_key)
97
98    @classmethod
99    def from_seed(cls, seed, encoder=encoding.RawEncoder):
100        """
101        Generate a PrivateKey using a deterministic construction
102        starting from a caller-provided seed
103
104        .. warning:: The seed **must** be high-entropy; therefore,
105            its generator **must** be a cryptographic quality
106            random function like, for example, :func:`~nacl.utils.random`.
107
108        .. warning:: The seed **must** be protected and remain secret.
109            Anyone who knows the seed is really in possession of
110            the corresponding PrivateKey.
111
112        :param seed: The seed used to generate the private key
113        :rtype: :class:`~nacl.public.PrivateKey`
114        """
115        # decode the seed
116        seed = encoder.decode(seed)
117        # Verify the given seed type and size are correct
118        if not (isinstance(seed, bytes) and len(seed) == cls.SEED_SIZE):
119            raise exc.TypeError(("PrivateKey seed must be a {0} bytes long "
120                                 "binary sequence").format(cls.SEED_SIZE)
121                                )
122        # generate a raw keypair from the given seed
123        raw_pk, raw_sk = nacl.bindings.crypto_box_seed_keypair(seed)
124        # construct a instance from the raw secret key
125        return cls(raw_sk)
126
127    def __bytes__(self):
128        return self._private_key
129
130    def __hash__(self):
131        return hash((type(self), bytes(self.public_key)))
132
133    def __eq__(self, other):
134        if not isinstance(other, self.__class__):
135            return False
136        return self.public_key == other.public_key
137
138    def __ne__(self, other):
139        return not (self == other)
140
141    @classmethod
142    def generate(cls):
143        """
144        Generates a random :class:`~nacl.public.PrivateKey` object
145
146        :rtype: :class:`~nacl.public.PrivateKey`
147        """
148        return cls(random(PrivateKey.SIZE), encoder=encoding.RawEncoder)
149
150
151class Box(encoding.Encodable, StringFixer, object):
152    """
153    The Box class boxes and unboxes messages between a pair of keys
154
155    The ciphertexts generated by :class:`~nacl.public.Box` include a 16
156    byte authenticator which is checked as part of the decryption. An invalid
157    authenticator will cause the decrypt function to raise an exception. The
158    authenticator is not a signature. Once you've decrypted the message you've
159    demonstrated the ability to create arbitrary valid message, so messages you
160    send are repudiable. For non-repudiable messages, sign them after
161    encryption.
162
163    :param private_key: :class:`~nacl.public.PrivateKey` used to encrypt and
164        decrypt messages
165    :param public_key: :class:`~nacl.public.PublicKey` used to encrypt and
166        decrypt messages
167
168    :cvar NONCE_SIZE: The size that the nonce is required to be.
169    """
170
171    NONCE_SIZE = nacl.bindings.crypto_box_NONCEBYTES
172
173    def __init__(self, private_key, public_key):
174        if private_key and public_key:
175            if ((not isinstance(private_key, PrivateKey) or
176                 not isinstance(public_key, PublicKey))):
177                raise exc.TypeError("Box must be created from "
178                                    "a PrivateKey and a PublicKey")
179            self._shared_key = nacl.bindings.crypto_box_beforenm(
180                public_key.encode(encoder=encoding.RawEncoder),
181                private_key.encode(encoder=encoding.RawEncoder),
182            )
183        else:
184            self._shared_key = None
185
186    def __bytes__(self):
187        return self._shared_key
188
189    @classmethod
190    def decode(cls, encoded, encoder=encoding.RawEncoder):
191        # Create an empty box
192        box = cls(None, None)
193
194        # Assign our decoded value to the shared key of the box
195        box._shared_key = encoder.decode(encoded)
196
197        return box
198
199    def encrypt(self, plaintext, nonce=None, encoder=encoding.RawEncoder):
200        """
201        Encrypts the plaintext message using the given `nonce` (or generates
202        one randomly if omitted) and returns the ciphertext encoded with the
203        encoder.
204
205        .. warning:: It is **VITALLY** important that the nonce is a nonce,
206            i.e. it is a number used only once for any given key. If you fail
207            to do this, you compromise the privacy of the messages encrypted.
208
209        :param plaintext: [:class:`bytes`] The plaintext message to encrypt
210        :param nonce: [:class:`bytes`] The nonce to use in the encryption
211        :param encoder: The encoder to use to encode the ciphertext
212        :rtype: [:class:`nacl.utils.EncryptedMessage`]
213        """
214        if nonce is None:
215            nonce = random(self.NONCE_SIZE)
216
217        if len(nonce) != self.NONCE_SIZE:
218            raise exc.ValueError("The nonce must be exactly %s bytes long" %
219                                 self.NONCE_SIZE)
220
221        ciphertext = nacl.bindings.crypto_box_afternm(
222            plaintext,
223            nonce,
224            self._shared_key,
225        )
226
227        encoded_nonce = encoder.encode(nonce)
228        encoded_ciphertext = encoder.encode(ciphertext)
229
230        return EncryptedMessage._from_parts(
231            encoded_nonce,
232            encoded_ciphertext,
233            encoder.encode(nonce + ciphertext),
234        )
235
236    def decrypt(self, ciphertext, nonce=None, encoder=encoding.RawEncoder):
237        """
238        Decrypts the ciphertext using the `nonce` (explicitly, when passed as a
239        parameter or implicitly, when omitted, as part of the ciphertext) and
240        returns the plaintext message.
241
242        :param ciphertext: [:class:`bytes`] The encrypted message to decrypt
243        :param nonce: [:class:`bytes`] The nonce used when encrypting the
244            ciphertext
245        :param encoder: The encoder used to decode the ciphertext.
246        :rtype: [:class:`bytes`]
247        """
248        # Decode our ciphertext
249        ciphertext = encoder.decode(ciphertext)
250
251        if nonce is None:
252            # If we were given the nonce and ciphertext combined, split them.
253            nonce = ciphertext[:self.NONCE_SIZE]
254            ciphertext = ciphertext[self.NONCE_SIZE:]
255
256        if len(nonce) != self.NONCE_SIZE:
257            raise exc.ValueError("The nonce must be exactly %s bytes long" %
258                                 self.NONCE_SIZE)
259
260        plaintext = nacl.bindings.crypto_box_open_afternm(
261            ciphertext,
262            nonce,
263            self._shared_key,
264        )
265
266        return plaintext
267
268    def shared_key(self):
269        """
270        Returns the Curve25519 shared secret, that can then be used as a key in
271        other symmetric ciphers.
272
273        .. warning:: It is **VITALLY** important that you use a nonce with your
274            symmetric cipher. If you fail to do this, you compromise the
275            privacy of the messages encrypted. Ensure that the key length of
276            your cipher is 32 bytes.
277        :rtype: [:class:`bytes`]
278        """
279
280        return self._shared_key
281
282
283class SealedBox(encoding.Encodable, StringFixer, object):
284    """
285    The SealedBox class boxes and unboxes messages addressed to
286    a specified key-pair by using ephemeral sender's keypairs,
287    whose private part will be discarded just after encrypting
288    a single plaintext message.
289
290    The ciphertexts generated by :class:`~nacl.public.SecretBox` include
291    the public part of the ephemeral key before the :class:`~nacl.public.Box`
292    ciphertext.
293
294    :param public_key: :class:`~nacl.public.PublicKey` used to encrypt
295        messages and derive nonces
296    :param private_key: :class:`~nacl.public.PrivateKey` used to decrypt
297        messages
298
299    .. versionadded:: 1.2
300    """
301
302    def __init__(self, recipient_key):
303
304        if isinstance(recipient_key, PublicKey):
305            self._public_key = recipient_key.encode(
306                encoder=encoding.RawEncoder)
307            self._private_key = None
308        elif isinstance(recipient_key, PrivateKey):
309            self._private_key = recipient_key.encode(
310                encoder=encoding.RawEncoder)
311            self._public_key = recipient_key.public_key.encode(
312                encoder=encoding.RawEncoder)
313        else:
314            raise exc.TypeError("SealedBox must be created from "
315                                "a PublicKey or a PrivateKey")
316
317    def __bytes__(self):
318        return self._public_key
319
320    def encrypt(self, plaintext, encoder=encoding.RawEncoder):
321        """
322        Encrypts the plaintext message using a random-generated ephemeral
323        keypair and returns a "composed ciphertext", containing both
324        the public part of the keypair and the ciphertext proper,
325        encoded with the encoder.
326
327        The private part of the ephemeral key-pair will be scrubbed before
328        returning the ciphertext, therefore, the sender will not be able to
329        decrypt the generated ciphertext.
330
331        :param plaintext: [:class:`bytes`] The plaintext message to encrypt
332        :param encoder: The encoder to use to encode the ciphertext
333        :return bytes: encoded ciphertext
334        """
335
336        ciphertext = nacl.bindings.crypto_box_seal(
337            plaintext,
338            self._public_key
339        )
340
341        encoded_ciphertext = encoder.encode(ciphertext)
342
343        return encoded_ciphertext
344
345    def decrypt(self, ciphertext, encoder=encoding.RawEncoder):
346        """
347        Decrypts the ciphertext using the ephemeral public key enclosed
348        in the ciphertext and the SealedBox private key, returning
349        the plaintext message.
350
351        :param ciphertext: [:class:`bytes`] The encrypted message to decrypt
352        :param encoder: The encoder used to decode the ciphertext.
353        :return bytes: The original plaintext
354        """
355        # Decode our ciphertext
356        ciphertext = encoder.decode(ciphertext)
357
358        plaintext = nacl.bindings.crypto_box_seal_open(
359            ciphertext,
360            self._public_key,
361            self._private_key,
362        )
363
364        return plaintext
365