Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/nacl/public.py: 39%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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.
14from typing import ClassVar, Generic, Optional, Type, TypeVar
16import nacl.bindings
17from nacl import encoding
18from nacl import exceptions as exc
19from nacl.encoding import Encoder
20from nacl.utils import EncryptedMessage, StringFixer, random
23class PublicKey(encoding.Encodable, StringFixer):
24 """
25 The public key counterpart to an Curve25519 :class:`nacl.public.PrivateKey`
26 for encrypting messages.
28 :param public_key: [:class:`bytes`] Encoded Curve25519 public key
29 :param encoder: A class that is able to decode the `public_key`
31 :cvar SIZE: The size that the public key is required to be
32 """
34 SIZE: ClassVar[int] = nacl.bindings.crypto_box_PUBLICKEYBYTES
36 def __init__(
37 self,
38 public_key: bytes,
39 encoder: encoding.Encoder = encoding.RawEncoder,
40 ):
41 self._public_key = encoder.decode(public_key)
42 if not isinstance(self._public_key, bytes):
43 raise exc.TypeError("PublicKey must be created from 32 bytes")
45 if len(self._public_key) != self.SIZE:
46 raise exc.ValueError(
47 "The public key must be exactly {} bytes long".format(
48 self.SIZE
49 )
50 )
52 def __bytes__(self) -> bytes:
53 return self._public_key
55 def __hash__(self) -> int:
56 return hash(bytes(self))
58 def __eq__(self, other: object) -> bool:
59 if not isinstance(other, self.__class__):
60 return False
61 return nacl.bindings.sodium_memcmp(bytes(self), bytes(other))
63 def __ne__(self, other: object) -> bool:
64 return not (self == other)
67class PrivateKey(encoding.Encodable, StringFixer):
68 """
69 Private key for decrypting messages using the Curve25519 algorithm.
71 .. warning:: This **must** be protected and remain secret. Anyone who
72 knows the value of your :class:`~nacl.public.PrivateKey` can decrypt
73 any message encrypted by the corresponding
74 :class:`~nacl.public.PublicKey`
76 :param private_key: The private key used to decrypt messages
77 :param encoder: The encoder class used to decode the given keys
79 :cvar SIZE: The size that the private key is required to be
80 :cvar SEED_SIZE: The size that the seed used to generate the
81 private key is required to be
82 """
84 SIZE: ClassVar[int] = nacl.bindings.crypto_box_SECRETKEYBYTES
85 SEED_SIZE: ClassVar[int] = nacl.bindings.crypto_box_SEEDBYTES
87 def __init__(
88 self,
89 private_key: bytes,
90 encoder: encoding.Encoder = encoding.RawEncoder,
91 ):
92 # Decode the secret_key
93 private_key = encoder.decode(private_key)
94 # verify the given secret key type and size are correct
95 if not (
96 isinstance(private_key, bytes) and len(private_key) == self.SIZE
97 ):
98 raise exc.TypeError(
99 (
100 "PrivateKey must be created from a {} "
101 "bytes long raw secret key"
102 ).format(self.SIZE)
103 )
105 raw_public_key = nacl.bindings.crypto_scalarmult_base(private_key)
107 self._private_key = private_key
108 self.public_key = PublicKey(raw_public_key)
110 @classmethod
111 def from_seed(
112 cls,
113 seed: bytes,
114 encoder: encoding.Encoder = encoding.RawEncoder,
115 ) -> "PrivateKey":
116 """
117 Generate a PrivateKey using a deterministic construction
118 starting from a caller-provided seed
120 .. warning:: The seed **must** be high-entropy; therefore,
121 its generator **must** be a cryptographic quality
122 random function like, for example, :func:`~nacl.utils.random`.
124 .. warning:: The seed **must** be protected and remain secret.
125 Anyone who knows the seed is really in possession of
126 the corresponding PrivateKey.
128 :param seed: The seed used to generate the private key
129 :rtype: :class:`~nacl.public.PrivateKey`
130 """
131 # decode the seed
132 seed = encoder.decode(seed)
133 # Verify the given seed type and size are correct
134 if not (isinstance(seed, bytes) and len(seed) == cls.SEED_SIZE):
135 raise exc.TypeError(
136 (
137 "PrivateKey seed must be a {} bytes long "
138 "binary sequence"
139 ).format(cls.SEED_SIZE)
140 )
141 # generate a raw keypair from the given seed
142 raw_pk, raw_sk = nacl.bindings.crypto_box_seed_keypair(seed)
143 # construct a instance from the raw secret key
144 return cls(raw_sk)
146 def __bytes__(self) -> bytes:
147 return self._private_key
149 def __hash__(self) -> int:
150 return hash((type(self), bytes(self.public_key)))
152 def __eq__(self, other: object) -> bool:
153 if not isinstance(other, self.__class__):
154 return False
155 return self.public_key == other.public_key
157 def __ne__(self, other: object) -> bool:
158 return not (self == other)
160 @classmethod
161 def generate(cls) -> "PrivateKey":
162 """
163 Generates a random :class:`~nacl.public.PrivateKey` object
165 :rtype: :class:`~nacl.public.PrivateKey`
166 """
167 return cls(random(PrivateKey.SIZE), encoder=encoding.RawEncoder)
170_Box = TypeVar("_Box", bound="Box")
173class Box(encoding.Encodable, StringFixer):
174 """
175 The Box class boxes and unboxes messages between a pair of keys
177 The ciphertexts generated by :class:`~nacl.public.Box` include a 16
178 byte authenticator which is checked as part of the decryption. An invalid
179 authenticator will cause the decrypt function to raise an exception. The
180 authenticator is not a signature. Once you've decrypted the message you've
181 demonstrated the ability to create arbitrary valid message, so messages you
182 send are repudiable. For non-repudiable messages, sign them after
183 encryption.
185 :param private_key: :class:`~nacl.public.PrivateKey` used to encrypt and
186 decrypt messages
187 :param public_key: :class:`~nacl.public.PublicKey` used to encrypt and
188 decrypt messages
190 :cvar NONCE_SIZE: The size that the nonce is required to be.
191 """
193 NONCE_SIZE: ClassVar[int] = nacl.bindings.crypto_box_NONCEBYTES
194 _shared_key: bytes
196 def __init__(self, private_key: PrivateKey, public_key: PublicKey):
197 if not isinstance(private_key, PrivateKey) or not isinstance(
198 public_key, PublicKey
199 ):
200 raise exc.TypeError(
201 "Box must be created from a PrivateKey and a PublicKey"
202 )
203 self._shared_key = nacl.bindings.crypto_box_beforenm(
204 public_key.encode(encoder=encoding.RawEncoder),
205 private_key.encode(encoder=encoding.RawEncoder),
206 )
208 def __bytes__(self) -> bytes:
209 return self._shared_key
211 @classmethod
212 def decode(
213 cls: Type[_Box], encoded: bytes, encoder: Encoder = encoding.RawEncoder
214 ) -> _Box:
215 """
216 Alternative constructor. Creates a Box from an existing Box's shared key.
217 """
218 # Create an empty box
219 box: _Box = cls.__new__(cls)
221 # Assign our decoded value to the shared key of the box
222 box._shared_key = encoder.decode(encoded)
224 return box
226 def encrypt(
227 self,
228 plaintext: bytes,
229 nonce: Optional[bytes] = None,
230 encoder: encoding.Encoder = encoding.RawEncoder,
231 ) -> EncryptedMessage:
232 """
233 Encrypts the plaintext message using the given `nonce` (or generates
234 one randomly if omitted) and returns the ciphertext encoded with the
235 encoder.
237 .. warning:: It is **VITALLY** important that the nonce is a nonce,
238 i.e. it is a number used only once for any given key. If you fail
239 to do this, you compromise the privacy of the messages encrypted.
241 :param plaintext: [:class:`bytes`] The plaintext message to encrypt
242 :param nonce: [:class:`bytes`] The nonce to use in the encryption
243 :param encoder: The encoder to use to encode the ciphertext
244 :rtype: [:class:`nacl.utils.EncryptedMessage`]
245 """
246 if nonce is None:
247 nonce = random(self.NONCE_SIZE)
249 if len(nonce) != self.NONCE_SIZE:
250 raise exc.ValueError(
251 "The nonce must be exactly %s bytes long" % self.NONCE_SIZE
252 )
254 ciphertext = nacl.bindings.crypto_box_afternm(
255 plaintext,
256 nonce,
257 self._shared_key,
258 )
260 encoded_nonce = encoder.encode(nonce)
261 encoded_ciphertext = encoder.encode(ciphertext)
263 return EncryptedMessage._from_parts(
264 encoded_nonce,
265 encoded_ciphertext,
266 encoder.encode(nonce + ciphertext),
267 )
269 def decrypt(
270 self,
271 ciphertext: bytes,
272 nonce: Optional[bytes] = None,
273 encoder: encoding.Encoder = encoding.RawEncoder,
274 ) -> bytes:
275 """
276 Decrypts the ciphertext using the `nonce` (explicitly, when passed as a
277 parameter or implicitly, when omitted, as part of the ciphertext) and
278 returns the plaintext message.
280 :param ciphertext: [:class:`bytes`] The encrypted message to decrypt
281 :param nonce: [:class:`bytes`] The nonce used when encrypting the
282 ciphertext
283 :param encoder: The encoder used to decode the ciphertext.
284 :rtype: [:class:`bytes`]
285 """
286 # Decode our ciphertext
287 ciphertext = encoder.decode(ciphertext)
289 if nonce is None:
290 # If we were given the nonce and ciphertext combined, split them.
291 nonce = ciphertext[: self.NONCE_SIZE]
292 ciphertext = ciphertext[self.NONCE_SIZE :]
294 if len(nonce) != self.NONCE_SIZE:
295 raise exc.ValueError(
296 "The nonce must be exactly %s bytes long" % self.NONCE_SIZE
297 )
299 plaintext = nacl.bindings.crypto_box_open_afternm(
300 ciphertext,
301 nonce,
302 self._shared_key,
303 )
305 return plaintext
307 def shared_key(self) -> bytes:
308 """
309 Returns the Curve25519 shared secret, that can then be used as a key in
310 other symmetric ciphers.
312 .. warning:: It is **VITALLY** important that you use a nonce with your
313 symmetric cipher. If you fail to do this, you compromise the
314 privacy of the messages encrypted. Ensure that the key length of
315 your cipher is 32 bytes.
316 :rtype: [:class:`bytes`]
317 """
319 return self._shared_key
322_Key = TypeVar("_Key", PublicKey, PrivateKey)
325class SealedBox(Generic[_Key], encoding.Encodable, StringFixer):
326 """
327 The SealedBox class boxes and unboxes messages addressed to
328 a specified key-pair by using ephemeral sender's keypairs,
329 whose private part will be discarded just after encrypting
330 a single plaintext message.
332 The ciphertexts generated by :class:`~nacl.public.SecretBox` include
333 the public part of the ephemeral key before the :class:`~nacl.public.Box`
334 ciphertext.
336 :param recipient_key: a :class:`~nacl.public.PublicKey` used to encrypt
337 messages and derive nonces, or a :class:`~nacl.public.PrivateKey` used
338 to decrypt messages.
340 .. versionadded:: 1.2
341 """
343 _public_key: bytes
344 _private_key: Optional[bytes]
346 def __init__(self, recipient_key: _Key):
347 if isinstance(recipient_key, PublicKey):
348 self._public_key = recipient_key.encode(
349 encoder=encoding.RawEncoder
350 )
351 self._private_key = None
352 elif isinstance(recipient_key, PrivateKey):
353 self._private_key = recipient_key.encode(
354 encoder=encoding.RawEncoder
355 )
356 self._public_key = recipient_key.public_key.encode(
357 encoder=encoding.RawEncoder
358 )
359 else:
360 raise exc.TypeError(
361 "SealedBox must be created from a PublicKey or a PrivateKey"
362 )
364 def __bytes__(self) -> bytes:
365 return self._public_key
367 def encrypt(
368 self,
369 plaintext: bytes,
370 encoder: encoding.Encoder = encoding.RawEncoder,
371 ) -> bytes:
372 """
373 Encrypts the plaintext message using a random-generated ephemeral
374 keypair and returns a "composed ciphertext", containing both
375 the public part of the keypair and the ciphertext proper,
376 encoded with the encoder.
378 The private part of the ephemeral key-pair will be scrubbed before
379 returning the ciphertext, therefore, the sender will not be able to
380 decrypt the generated ciphertext.
382 :param plaintext: [:class:`bytes`] The plaintext message to encrypt
383 :param encoder: The encoder to use to encode the ciphertext
384 :return bytes: encoded ciphertext
385 """
387 ciphertext = nacl.bindings.crypto_box_seal(plaintext, self._public_key)
389 encoded_ciphertext = encoder.encode(ciphertext)
391 return encoded_ciphertext
393 def decrypt(
394 self: "SealedBox[PrivateKey]",
395 ciphertext: bytes,
396 encoder: encoding.Encoder = encoding.RawEncoder,
397 ) -> bytes:
398 """
399 Decrypts the ciphertext using the ephemeral public key enclosed
400 in the ciphertext and the SealedBox private key, returning
401 the plaintext message.
403 :param ciphertext: [:class:`bytes`] The encrypted message to decrypt
404 :param encoder: The encoder used to decode the ciphertext.
405 :return bytes: The original plaintext
406 :raises TypeError: if this SealedBox was created with a
407 :class:`~nacl.public.PublicKey` rather than a
408 :class:`~nacl.public.PrivateKey`.
409 """
410 # Decode our ciphertext
411 ciphertext = encoder.decode(ciphertext)
413 if self._private_key is None:
414 raise TypeError(
415 "SealedBoxes created with a public key cannot decrypt"
416 )
417 plaintext = nacl.bindings.crypto_box_seal_open(
418 ciphertext,
419 self._public_key,
420 self._private_key,
421 )
423 return plaintext