quick fix 2
This commit is contained in:
105
Lib/site-packages/nacl/encoding.py
Normal file
105
Lib/site-packages/nacl/encoding.py
Normal file
@ -0,0 +1,105 @@
|
||||
# Copyright 2013 Donald Stufft and individual contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import base64
|
||||
import binascii
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from typing import SupportsBytes, Type
|
||||
|
||||
|
||||
# TODO: when the minimum supported version of Python is 3.8, we can import
|
||||
# Protocol from typing, and replace Encoder with a Protocol instead.
|
||||
class _Encoder(metaclass=ABCMeta):
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def encode(data: bytes) -> bytes:
|
||||
"""Transform raw data to encoded data."""
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def decode(data: bytes) -> bytes:
|
||||
"""Transform encoded data back to raw data.
|
||||
|
||||
Decoding after encoding should be a no-op, i.e. `decode(encode(x)) == x`.
|
||||
"""
|
||||
|
||||
|
||||
# Functions that use encoders are passed a subclass of _Encoder, not an instance
|
||||
# (because the methods are all static). Let's gloss over that detail by defining
|
||||
# an alias for Type[_Encoder].
|
||||
Encoder = Type[_Encoder]
|
||||
|
||||
|
||||
class RawEncoder(_Encoder):
|
||||
@staticmethod
|
||||
def encode(data: bytes) -> bytes:
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def decode(data: bytes) -> bytes:
|
||||
return data
|
||||
|
||||
|
||||
class HexEncoder(_Encoder):
|
||||
@staticmethod
|
||||
def encode(data: bytes) -> bytes:
|
||||
return binascii.hexlify(data)
|
||||
|
||||
@staticmethod
|
||||
def decode(data: bytes) -> bytes:
|
||||
return binascii.unhexlify(data)
|
||||
|
||||
|
||||
class Base16Encoder(_Encoder):
|
||||
@staticmethod
|
||||
def encode(data: bytes) -> bytes:
|
||||
return base64.b16encode(data)
|
||||
|
||||
@staticmethod
|
||||
def decode(data: bytes) -> bytes:
|
||||
return base64.b16decode(data)
|
||||
|
||||
|
||||
class Base32Encoder(_Encoder):
|
||||
@staticmethod
|
||||
def encode(data: bytes) -> bytes:
|
||||
return base64.b32encode(data)
|
||||
|
||||
@staticmethod
|
||||
def decode(data: bytes) -> bytes:
|
||||
return base64.b32decode(data)
|
||||
|
||||
|
||||
class Base64Encoder(_Encoder):
|
||||
@staticmethod
|
||||
def encode(data: bytes) -> bytes:
|
||||
return base64.b64encode(data)
|
||||
|
||||
@staticmethod
|
||||
def decode(data: bytes) -> bytes:
|
||||
return base64.b64decode(data)
|
||||
|
||||
|
||||
class URLSafeBase64Encoder(_Encoder):
|
||||
@staticmethod
|
||||
def encode(data: bytes) -> bytes:
|
||||
return base64.urlsafe_b64encode(data)
|
||||
|
||||
@staticmethod
|
||||
def decode(data: bytes) -> bytes:
|
||||
return base64.urlsafe_b64decode(data)
|
||||
|
||||
|
||||
class Encodable:
|
||||
def encode(self: SupportsBytes, encoder: Encoder = RawEncoder) -> bytes:
|
||||
return encoder.encode(bytes(self))
|
||||
88
Lib/site-packages/nacl/exceptions.py
Normal file
88
Lib/site-packages/nacl/exceptions.py
Normal file
@ -0,0 +1,88 @@
|
||||
# Copyright 2013 Donald Stufft and individual contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
# We create a clone of various builtin Exception types which additionally
|
||||
# inherit from CryptoError. Below, we refer to the parent types via the
|
||||
# `builtins` namespace, so mypy can distinguish between (e.g.)
|
||||
# `nacl.exceptions.RuntimeError` and `builtins.RuntimeError`.
|
||||
import builtins
|
||||
from typing import Type
|
||||
|
||||
|
||||
class CryptoError(Exception):
|
||||
"""
|
||||
Base exception for all nacl related errors
|
||||
"""
|
||||
|
||||
|
||||
class BadSignatureError(CryptoError):
|
||||
"""
|
||||
Raised when the signature was forged or otherwise corrupt.
|
||||
"""
|
||||
|
||||
|
||||
class RuntimeError(builtins.RuntimeError, CryptoError):
|
||||
pass
|
||||
|
||||
|
||||
class AssertionError(builtins.AssertionError, CryptoError):
|
||||
pass
|
||||
|
||||
|
||||
class TypeError(builtins.TypeError, CryptoError):
|
||||
pass
|
||||
|
||||
|
||||
class ValueError(builtins.ValueError, CryptoError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidkeyError(CryptoError):
|
||||
pass
|
||||
|
||||
|
||||
class CryptPrefixError(InvalidkeyError):
|
||||
pass
|
||||
|
||||
|
||||
class UnavailableError(RuntimeError):
|
||||
"""
|
||||
is a subclass of :class:`~nacl.exceptions.RuntimeError`, raised when
|
||||
trying to call functions not available in a minimal build of
|
||||
libsodium.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def ensure(cond: bool, *args: object, **kwds: Type[Exception]) -> None:
|
||||
"""
|
||||
Return if a condition is true, otherwise raise a caller-configurable
|
||||
:py:class:`Exception`
|
||||
:param bool cond: the condition to be checked
|
||||
:param sequence args: the arguments to be passed to the exception's
|
||||
constructor
|
||||
The only accepted named parameter is `raising` used to configure the
|
||||
exception to be raised if `cond` is not `True`
|
||||
"""
|
||||
_CHK_UNEXP = "check_condition() got an unexpected keyword argument {0}"
|
||||
|
||||
raising = kwds.pop("raising", AssertionError)
|
||||
if kwds:
|
||||
raise TypeError(_CHK_UNEXP.format(repr(kwds.popitem()[0])))
|
||||
|
||||
if cond is True:
|
||||
return
|
||||
raise raising(*args)
|
||||
182
Lib/site-packages/nacl/hash.py
Normal file
182
Lib/site-packages/nacl/hash.py
Normal file
@ -0,0 +1,182 @@
|
||||
# Copyright 2013 Donald Stufft and individual contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
The :mod:`nacl.hash` module exposes one-shot interfaces
|
||||
for libsodium selected hash primitives and the constants needed
|
||||
for their usage.
|
||||
"""
|
||||
|
||||
|
||||
import nacl.bindings
|
||||
import nacl.encoding
|
||||
|
||||
|
||||
BLAKE2B_BYTES = nacl.bindings.crypto_generichash_BYTES
|
||||
"""Default digest size for :func:`blake2b` hash"""
|
||||
BLAKE2B_BYTES_MIN = nacl.bindings.crypto_generichash_BYTES_MIN
|
||||
"""Minimum allowed digest size for :func:`blake2b` hash"""
|
||||
BLAKE2B_BYTES_MAX = nacl.bindings.crypto_generichash_BYTES_MAX
|
||||
"""Maximum allowed digest size for :func:`blake2b` hash"""
|
||||
BLAKE2B_KEYBYTES = nacl.bindings.crypto_generichash_KEYBYTES
|
||||
"""Default size of the ``key`` byte array for :func:`blake2b` hash"""
|
||||
BLAKE2B_KEYBYTES_MIN = nacl.bindings.crypto_generichash_KEYBYTES_MIN
|
||||
"""Minimum allowed size of the ``key`` byte array for :func:`blake2b` hash"""
|
||||
BLAKE2B_KEYBYTES_MAX = nacl.bindings.crypto_generichash_KEYBYTES_MAX
|
||||
"""Maximum allowed size of the ``key`` byte array for :func:`blake2b` hash"""
|
||||
BLAKE2B_SALTBYTES = nacl.bindings.crypto_generichash_SALTBYTES
|
||||
"""Maximum allowed length of the ``salt`` byte array for
|
||||
:func:`blake2b` hash"""
|
||||
BLAKE2B_PERSONALBYTES = nacl.bindings.crypto_generichash_PERSONALBYTES
|
||||
"""Maximum allowed length of the ``personalization``
|
||||
byte array for :func:`blake2b` hash"""
|
||||
|
||||
SIPHASH_BYTES = nacl.bindings.crypto_shorthash_siphash24_BYTES
|
||||
"""Size of the :func:`siphash24` digest"""
|
||||
SIPHASH_KEYBYTES = nacl.bindings.crypto_shorthash_siphash24_KEYBYTES
|
||||
"""Size of the secret ``key`` used by the :func:`siphash24` MAC"""
|
||||
|
||||
SIPHASHX_AVAILABLE = nacl.bindings.has_crypto_shorthash_siphashx24
|
||||
"""``True`` if :func:`siphashx24` is available to be called"""
|
||||
|
||||
SIPHASHX_BYTES = nacl.bindings.crypto_shorthash_siphashx24_BYTES
|
||||
"""Size of the :func:`siphashx24` digest"""
|
||||
SIPHASHX_KEYBYTES = nacl.bindings.crypto_shorthash_siphashx24_KEYBYTES
|
||||
"""Size of the secret ``key`` used by the :func:`siphashx24` MAC"""
|
||||
|
||||
_b2b_hash = nacl.bindings.crypto_generichash_blake2b_salt_personal
|
||||
_sip_hash = nacl.bindings.crypto_shorthash_siphash24
|
||||
_sip_hashx = nacl.bindings.crypto_shorthash_siphashx24
|
||||
|
||||
|
||||
def sha256(
|
||||
message: bytes, encoder: nacl.encoding.Encoder = nacl.encoding.HexEncoder
|
||||
) -> bytes:
|
||||
"""
|
||||
Hashes ``message`` with SHA256.
|
||||
|
||||
:param message: The message to hash.
|
||||
:type message: bytes
|
||||
:param encoder: A class that is able to encode the hashed message.
|
||||
:returns: The hashed message.
|
||||
:rtype: bytes
|
||||
"""
|
||||
return encoder.encode(nacl.bindings.crypto_hash_sha256(message))
|
||||
|
||||
|
||||
def sha512(
|
||||
message: bytes, encoder: nacl.encoding.Encoder = nacl.encoding.HexEncoder
|
||||
) -> bytes:
|
||||
"""
|
||||
Hashes ``message`` with SHA512.
|
||||
|
||||
:param message: The message to hash.
|
||||
:type message: bytes
|
||||
:param encoder: A class that is able to encode the hashed message.
|
||||
:returns: The hashed message.
|
||||
:rtype: bytes
|
||||
"""
|
||||
return encoder.encode(nacl.bindings.crypto_hash_sha512(message))
|
||||
|
||||
|
||||
def blake2b(
|
||||
data: bytes,
|
||||
digest_size: int = BLAKE2B_BYTES,
|
||||
key: bytes = b"",
|
||||
salt: bytes = b"",
|
||||
person: bytes = b"",
|
||||
encoder: nacl.encoding.Encoder = nacl.encoding.HexEncoder,
|
||||
) -> bytes:
|
||||
"""
|
||||
Hashes ``data`` with blake2b.
|
||||
|
||||
:param data: the digest input byte sequence
|
||||
:type data: bytes
|
||||
:param digest_size: the requested digest size; must be at most
|
||||
:const:`BLAKE2B_BYTES_MAX`;
|
||||
the default digest size is
|
||||
:const:`BLAKE2B_BYTES`
|
||||
:type digest_size: int
|
||||
:param key: the key to be set for keyed MAC/PRF usage; if set, the key
|
||||
must be at most :data:`~nacl.hash.BLAKE2B_KEYBYTES_MAX` long
|
||||
:type key: bytes
|
||||
:param salt: an initialization salt at most
|
||||
:const:`BLAKE2B_SALTBYTES` long;
|
||||
it will be zero-padded if needed
|
||||
:type salt: bytes
|
||||
:param person: a personalization string at most
|
||||
:const:`BLAKE2B_PERSONALBYTES` long;
|
||||
it will be zero-padded if needed
|
||||
:type person: bytes
|
||||
:param encoder: the encoder to use on returned digest
|
||||
:type encoder: class
|
||||
:returns: The hashed message.
|
||||
:rtype: bytes
|
||||
"""
|
||||
|
||||
digest = _b2b_hash(
|
||||
data, digest_size=digest_size, key=key, salt=salt, person=person
|
||||
)
|
||||
return encoder.encode(digest)
|
||||
|
||||
|
||||
generichash = blake2b
|
||||
|
||||
|
||||
def siphash24(
|
||||
message: bytes,
|
||||
key: bytes = b"",
|
||||
encoder: nacl.encoding.Encoder = nacl.encoding.HexEncoder,
|
||||
) -> bytes:
|
||||
"""
|
||||
Computes a keyed MAC of ``message`` using the short-input-optimized
|
||||
siphash-2-4 construction.
|
||||
|
||||
:param message: The message to hash.
|
||||
:type message: bytes
|
||||
:param key: the message authentication key for the siphash MAC construct
|
||||
:type key: bytes(:const:`SIPHASH_KEYBYTES`)
|
||||
:param encoder: A class that is able to encode the hashed message.
|
||||
:returns: The hashed message.
|
||||
:rtype: bytes(:const:`SIPHASH_BYTES`)
|
||||
"""
|
||||
digest = _sip_hash(message, key)
|
||||
return encoder.encode(digest)
|
||||
|
||||
|
||||
shorthash = siphash24
|
||||
|
||||
|
||||
def siphashx24(
|
||||
message: bytes,
|
||||
key: bytes = b"",
|
||||
encoder: nacl.encoding.Encoder = nacl.encoding.HexEncoder,
|
||||
) -> bytes:
|
||||
"""
|
||||
Computes a keyed MAC of ``message`` using the 128 bit variant of the
|
||||
siphash-2-4 construction.
|
||||
|
||||
:param message: The message to hash.
|
||||
:type message: bytes
|
||||
:param key: the message authentication key for the siphash MAC construct
|
||||
:type key: bytes(:const:`SIPHASHX_KEYBYTES`)
|
||||
:param encoder: A class that is able to encode the hashed message.
|
||||
:returns: The hashed message.
|
||||
:rtype: bytes(:const:`SIPHASHX_BYTES`)
|
||||
:raises nacl.exceptions.UnavailableError: If called when using a
|
||||
minimal build of libsodium.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
"""
|
||||
digest = _sip_hashx(message, key)
|
||||
return encoder.encode(digest)
|
||||
143
Lib/site-packages/nacl/hashlib.py
Normal file
143
Lib/site-packages/nacl/hashlib.py
Normal file
@ -0,0 +1,143 @@
|
||||
# Copyright 2016-2019 Donald Stufft and individual contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import binascii
|
||||
from typing import NoReturn
|
||||
|
||||
import nacl.bindings
|
||||
from nacl.utils import bytes_as_string
|
||||
|
||||
BYTES = nacl.bindings.crypto_generichash_BYTES
|
||||
BYTES_MIN = nacl.bindings.crypto_generichash_BYTES_MIN
|
||||
BYTES_MAX = nacl.bindings.crypto_generichash_BYTES_MAX
|
||||
KEYBYTES = nacl.bindings.crypto_generichash_KEYBYTES
|
||||
KEYBYTES_MIN = nacl.bindings.crypto_generichash_KEYBYTES_MIN
|
||||
KEYBYTES_MAX = nacl.bindings.crypto_generichash_KEYBYTES_MAX
|
||||
SALTBYTES = nacl.bindings.crypto_generichash_SALTBYTES
|
||||
PERSONALBYTES = nacl.bindings.crypto_generichash_PERSONALBYTES
|
||||
|
||||
SCRYPT_AVAILABLE = nacl.bindings.has_crypto_pwhash_scryptsalsa208sha256
|
||||
|
||||
_b2b_init = nacl.bindings.crypto_generichash_blake2b_init
|
||||
_b2b_final = nacl.bindings.crypto_generichash_blake2b_final
|
||||
_b2b_update = nacl.bindings.crypto_generichash_blake2b_update
|
||||
|
||||
|
||||
class blake2b:
|
||||
"""
|
||||
:py:mod:`hashlib` API compatible blake2b algorithm implementation
|
||||
"""
|
||||
|
||||
MAX_DIGEST_SIZE = BYTES
|
||||
MAX_KEY_SIZE = KEYBYTES_MAX
|
||||
PERSON_SIZE = PERSONALBYTES
|
||||
SALT_SIZE = SALTBYTES
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: bytes = b"",
|
||||
digest_size: int = BYTES,
|
||||
key: bytes = b"",
|
||||
salt: bytes = b"",
|
||||
person: bytes = b"",
|
||||
):
|
||||
"""
|
||||
:py:class:`.blake2b` algorithm initializer
|
||||
|
||||
:param data:
|
||||
:type data: bytes
|
||||
:param int digest_size: the requested digest size; must be
|
||||
at most :py:attr:`.MAX_DIGEST_SIZE`;
|
||||
the default digest size is :py:data:`.BYTES`
|
||||
:param key: the key to be set for keyed MAC/PRF usage; if set,
|
||||
the key must be at most :py:data:`.KEYBYTES_MAX` long
|
||||
:type key: bytes
|
||||
:param salt: a initialization salt at most
|
||||
:py:attr:`.SALT_SIZE` long; it will be zero-padded
|
||||
if needed
|
||||
:type salt: bytes
|
||||
:param person: a personalization string at most
|
||||
:py:attr:`.PERSONAL_SIZE` long; it will be zero-padded
|
||||
if needed
|
||||
:type person: bytes
|
||||
"""
|
||||
|
||||
self._state = _b2b_init(
|
||||
key=key, salt=salt, person=person, digest_size=digest_size
|
||||
)
|
||||
self._digest_size = digest_size
|
||||
|
||||
if data:
|
||||
self.update(data)
|
||||
|
||||
@property
|
||||
def digest_size(self) -> int:
|
||||
return self._digest_size
|
||||
|
||||
@property
|
||||
def block_size(self) -> int:
|
||||
return 128
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "blake2b"
|
||||
|
||||
def update(self, data: bytes) -> None:
|
||||
_b2b_update(self._state, data)
|
||||
|
||||
def digest(self) -> bytes:
|
||||
_st = self._state.copy()
|
||||
return _b2b_final(_st)
|
||||
|
||||
def hexdigest(self) -> str:
|
||||
return bytes_as_string(binascii.hexlify(self.digest()))
|
||||
|
||||
def copy(self) -> "blake2b":
|
||||
_cp = type(self)(digest_size=self.digest_size)
|
||||
_st = self._state.copy()
|
||||
_cp._state = _st
|
||||
return _cp
|
||||
|
||||
def __reduce__(self) -> NoReturn:
|
||||
"""
|
||||
Raise the same exception as hashlib's blake implementation
|
||||
on copy.copy()
|
||||
"""
|
||||
raise TypeError(
|
||||
"can't pickle {} objects".format(self.__class__.__name__)
|
||||
)
|
||||
|
||||
|
||||
def scrypt(
|
||||
password: bytes,
|
||||
salt: bytes = b"",
|
||||
n: int = 2 ** 20,
|
||||
r: int = 8,
|
||||
p: int = 1,
|
||||
maxmem: int = 2 ** 25,
|
||||
dklen: int = 64,
|
||||
) -> bytes:
|
||||
"""
|
||||
Derive a cryptographic key using the scrypt KDF.
|
||||
|
||||
:raises nacl.exceptions.UnavailableError: If called when using a
|
||||
minimal build of libsodium.
|
||||
|
||||
Implements the same signature as the ``hashlib.scrypt`` implemented
|
||||
in cpython version 3.6
|
||||
"""
|
||||
return nacl.bindings.crypto_pwhash_scryptsalsa208sha256_ll(
|
||||
password, salt, n, r, p, maxmem=maxmem, dklen=dklen
|
||||
)
|
||||
423
Lib/site-packages/nacl/public.py
Normal file
423
Lib/site-packages/nacl/public.py
Normal file
@ -0,0 +1,423 @@
|
||||
# Copyright 2013 Donald Stufft and individual contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from typing import ClassVar, Generic, Optional, Type, TypeVar
|
||||
|
||||
import nacl.bindings
|
||||
from nacl import encoding
|
||||
from nacl import exceptions as exc
|
||||
from nacl.encoding import Encoder
|
||||
from nacl.utils import EncryptedMessage, StringFixer, random
|
||||
|
||||
|
||||
class PublicKey(encoding.Encodable, StringFixer):
|
||||
"""
|
||||
The public key counterpart to an Curve25519 :class:`nacl.public.PrivateKey`
|
||||
for encrypting messages.
|
||||
|
||||
:param public_key: [:class:`bytes`] Encoded Curve25519 public key
|
||||
:param encoder: A class that is able to decode the `public_key`
|
||||
|
||||
:cvar SIZE: The size that the public key is required to be
|
||||
"""
|
||||
|
||||
SIZE: ClassVar[int] = nacl.bindings.crypto_box_PUBLICKEYBYTES
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
public_key: bytes,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
):
|
||||
self._public_key = encoder.decode(public_key)
|
||||
if not isinstance(self._public_key, bytes):
|
||||
raise exc.TypeError("PublicKey must be created from 32 bytes")
|
||||
|
||||
if len(self._public_key) != self.SIZE:
|
||||
raise exc.ValueError(
|
||||
"The public key must be exactly {} bytes long".format(
|
||||
self.SIZE
|
||||
)
|
||||
)
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return self._public_key
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(bytes(self))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return nacl.bindings.sodium_memcmp(bytes(self), bytes(other))
|
||||
|
||||
def __ne__(self, other: object) -> bool:
|
||||
return not (self == other)
|
||||
|
||||
|
||||
class PrivateKey(encoding.Encodable, StringFixer):
|
||||
"""
|
||||
Private key for decrypting messages using the Curve25519 algorithm.
|
||||
|
||||
.. warning:: This **must** be protected and remain secret. Anyone who
|
||||
knows the value of your :class:`~nacl.public.PrivateKey` can decrypt
|
||||
any message encrypted by the corresponding
|
||||
:class:`~nacl.public.PublicKey`
|
||||
|
||||
:param private_key: The private key used to decrypt messages
|
||||
:param encoder: The encoder class used to decode the given keys
|
||||
|
||||
:cvar SIZE: The size that the private key is required to be
|
||||
:cvar SEED_SIZE: The size that the seed used to generate the
|
||||
private key is required to be
|
||||
"""
|
||||
|
||||
SIZE: ClassVar[int] = nacl.bindings.crypto_box_SECRETKEYBYTES
|
||||
SEED_SIZE: ClassVar[int] = nacl.bindings.crypto_box_SEEDBYTES
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
private_key: bytes,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
):
|
||||
# Decode the secret_key
|
||||
private_key = encoder.decode(private_key)
|
||||
# verify the given secret key type and size are correct
|
||||
if not (
|
||||
isinstance(private_key, bytes) and len(private_key) == self.SIZE
|
||||
):
|
||||
raise exc.TypeError(
|
||||
(
|
||||
"PrivateKey must be created from a {} "
|
||||
"bytes long raw secret key"
|
||||
).format(self.SIZE)
|
||||
)
|
||||
|
||||
raw_public_key = nacl.bindings.crypto_scalarmult_base(private_key)
|
||||
|
||||
self._private_key = private_key
|
||||
self.public_key = PublicKey(raw_public_key)
|
||||
|
||||
@classmethod
|
||||
def from_seed(
|
||||
cls,
|
||||
seed: bytes,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
) -> "PrivateKey":
|
||||
"""
|
||||
Generate a PrivateKey using a deterministic construction
|
||||
starting from a caller-provided seed
|
||||
|
||||
.. warning:: The seed **must** be high-entropy; therefore,
|
||||
its generator **must** be a cryptographic quality
|
||||
random function like, for example, :func:`~nacl.utils.random`.
|
||||
|
||||
.. warning:: The seed **must** be protected and remain secret.
|
||||
Anyone who knows the seed is really in possession of
|
||||
the corresponding PrivateKey.
|
||||
|
||||
:param seed: The seed used to generate the private key
|
||||
:rtype: :class:`~nacl.public.PrivateKey`
|
||||
"""
|
||||
# decode the seed
|
||||
seed = encoder.decode(seed)
|
||||
# Verify the given seed type and size are correct
|
||||
if not (isinstance(seed, bytes) and len(seed) == cls.SEED_SIZE):
|
||||
raise exc.TypeError(
|
||||
(
|
||||
"PrivateKey seed must be a {} bytes long "
|
||||
"binary sequence"
|
||||
).format(cls.SEED_SIZE)
|
||||
)
|
||||
# generate a raw keypair from the given seed
|
||||
raw_pk, raw_sk = nacl.bindings.crypto_box_seed_keypair(seed)
|
||||
# construct a instance from the raw secret key
|
||||
return cls(raw_sk)
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return self._private_key
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((type(self), bytes(self.public_key)))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return self.public_key == other.public_key
|
||||
|
||||
def __ne__(self, other: object) -> bool:
|
||||
return not (self == other)
|
||||
|
||||
@classmethod
|
||||
def generate(cls) -> "PrivateKey":
|
||||
"""
|
||||
Generates a random :class:`~nacl.public.PrivateKey` object
|
||||
|
||||
:rtype: :class:`~nacl.public.PrivateKey`
|
||||
"""
|
||||
return cls(random(PrivateKey.SIZE), encoder=encoding.RawEncoder)
|
||||
|
||||
|
||||
_Box = TypeVar("_Box", bound="Box")
|
||||
|
||||
|
||||
class Box(encoding.Encodable, StringFixer):
|
||||
"""
|
||||
The Box class boxes and unboxes messages between a pair of keys
|
||||
|
||||
The ciphertexts generated by :class:`~nacl.public.Box` include a 16
|
||||
byte authenticator which is checked as part of the decryption. An invalid
|
||||
authenticator will cause the decrypt function to raise an exception. The
|
||||
authenticator is not a signature. Once you've decrypted the message you've
|
||||
demonstrated the ability to create arbitrary valid message, so messages you
|
||||
send are repudiable. For non-repudiable messages, sign them after
|
||||
encryption.
|
||||
|
||||
:param private_key: :class:`~nacl.public.PrivateKey` used to encrypt and
|
||||
decrypt messages
|
||||
:param public_key: :class:`~nacl.public.PublicKey` used to encrypt and
|
||||
decrypt messages
|
||||
|
||||
:cvar NONCE_SIZE: The size that the nonce is required to be.
|
||||
"""
|
||||
|
||||
NONCE_SIZE: ClassVar[int] = nacl.bindings.crypto_box_NONCEBYTES
|
||||
_shared_key: bytes
|
||||
|
||||
def __init__(self, private_key: PrivateKey, public_key: PublicKey):
|
||||
if not isinstance(private_key, PrivateKey) or not isinstance(
|
||||
public_key, PublicKey
|
||||
):
|
||||
raise exc.TypeError(
|
||||
"Box must be created from a PrivateKey and a PublicKey"
|
||||
)
|
||||
self._shared_key = nacl.bindings.crypto_box_beforenm(
|
||||
public_key.encode(encoder=encoding.RawEncoder),
|
||||
private_key.encode(encoder=encoding.RawEncoder),
|
||||
)
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return self._shared_key
|
||||
|
||||
@classmethod
|
||||
def decode(
|
||||
cls: Type[_Box], encoded: bytes, encoder: Encoder = encoding.RawEncoder
|
||||
) -> _Box:
|
||||
"""
|
||||
Alternative constructor. Creates a Box from an existing Box's shared key.
|
||||
"""
|
||||
# Create an empty box
|
||||
box: _Box = cls.__new__(cls)
|
||||
|
||||
# Assign our decoded value to the shared key of the box
|
||||
box._shared_key = encoder.decode(encoded)
|
||||
|
||||
return box
|
||||
|
||||
def encrypt(
|
||||
self,
|
||||
plaintext: bytes,
|
||||
nonce: Optional[bytes] = None,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
) -> EncryptedMessage:
|
||||
"""
|
||||
Encrypts the plaintext message using the given `nonce` (or generates
|
||||
one randomly if omitted) and returns the ciphertext encoded with the
|
||||
encoder.
|
||||
|
||||
.. warning:: It is **VITALLY** important that the nonce is a nonce,
|
||||
i.e. it is a number used only once for any given key. If you fail
|
||||
to do this, you compromise the privacy of the messages encrypted.
|
||||
|
||||
:param plaintext: [:class:`bytes`] The plaintext message to encrypt
|
||||
:param nonce: [:class:`bytes`] The nonce to use in the encryption
|
||||
:param encoder: The encoder to use to encode the ciphertext
|
||||
:rtype: [:class:`nacl.utils.EncryptedMessage`]
|
||||
"""
|
||||
if nonce is None:
|
||||
nonce = random(self.NONCE_SIZE)
|
||||
|
||||
if len(nonce) != self.NONCE_SIZE:
|
||||
raise exc.ValueError(
|
||||
"The nonce must be exactly %s bytes long" % self.NONCE_SIZE
|
||||
)
|
||||
|
||||
ciphertext = nacl.bindings.crypto_box_afternm(
|
||||
plaintext,
|
||||
nonce,
|
||||
self._shared_key,
|
||||
)
|
||||
|
||||
encoded_nonce = encoder.encode(nonce)
|
||||
encoded_ciphertext = encoder.encode(ciphertext)
|
||||
|
||||
return EncryptedMessage._from_parts(
|
||||
encoded_nonce,
|
||||
encoded_ciphertext,
|
||||
encoder.encode(nonce + ciphertext),
|
||||
)
|
||||
|
||||
def decrypt(
|
||||
self,
|
||||
ciphertext: bytes,
|
||||
nonce: Optional[bytes] = None,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
) -> bytes:
|
||||
"""
|
||||
Decrypts the ciphertext using the `nonce` (explicitly, when passed as a
|
||||
parameter or implicitly, when omitted, as part of the ciphertext) and
|
||||
returns the plaintext message.
|
||||
|
||||
:param ciphertext: [:class:`bytes`] The encrypted message to decrypt
|
||||
:param nonce: [:class:`bytes`] The nonce used when encrypting the
|
||||
ciphertext
|
||||
:param encoder: The encoder used to decode the ciphertext.
|
||||
:rtype: [:class:`bytes`]
|
||||
"""
|
||||
# Decode our ciphertext
|
||||
ciphertext = encoder.decode(ciphertext)
|
||||
|
||||
if nonce is None:
|
||||
# If we were given the nonce and ciphertext combined, split them.
|
||||
nonce = ciphertext[: self.NONCE_SIZE]
|
||||
ciphertext = ciphertext[self.NONCE_SIZE :]
|
||||
|
||||
if len(nonce) != self.NONCE_SIZE:
|
||||
raise exc.ValueError(
|
||||
"The nonce must be exactly %s bytes long" % self.NONCE_SIZE
|
||||
)
|
||||
|
||||
plaintext = nacl.bindings.crypto_box_open_afternm(
|
||||
ciphertext,
|
||||
nonce,
|
||||
self._shared_key,
|
||||
)
|
||||
|
||||
return plaintext
|
||||
|
||||
def shared_key(self) -> bytes:
|
||||
"""
|
||||
Returns the Curve25519 shared secret, that can then be used as a key in
|
||||
other symmetric ciphers.
|
||||
|
||||
.. warning:: It is **VITALLY** important that you use a nonce with your
|
||||
symmetric cipher. If you fail to do this, you compromise the
|
||||
privacy of the messages encrypted. Ensure that the key length of
|
||||
your cipher is 32 bytes.
|
||||
:rtype: [:class:`bytes`]
|
||||
"""
|
||||
|
||||
return self._shared_key
|
||||
|
||||
|
||||
_Key = TypeVar("_Key", PublicKey, PrivateKey)
|
||||
|
||||
|
||||
class SealedBox(Generic[_Key], encoding.Encodable, StringFixer):
|
||||
"""
|
||||
The SealedBox class boxes and unboxes messages addressed to
|
||||
a specified key-pair by using ephemeral sender's keypairs,
|
||||
whose private part will be discarded just after encrypting
|
||||
a single plaintext message.
|
||||
|
||||
The ciphertexts generated by :class:`~nacl.public.SecretBox` include
|
||||
the public part of the ephemeral key before the :class:`~nacl.public.Box`
|
||||
ciphertext.
|
||||
|
||||
:param recipient_key: a :class:`~nacl.public.PublicKey` used to encrypt
|
||||
messages and derive nonces, or a :class:`~nacl.public.PrivateKey` used
|
||||
to decrypt messages.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
"""
|
||||
|
||||
_public_key: bytes
|
||||
_private_key: Optional[bytes]
|
||||
|
||||
def __init__(self, recipient_key: _Key):
|
||||
if isinstance(recipient_key, PublicKey):
|
||||
self._public_key = recipient_key.encode(
|
||||
encoder=encoding.RawEncoder
|
||||
)
|
||||
self._private_key = None
|
||||
elif isinstance(recipient_key, PrivateKey):
|
||||
self._private_key = recipient_key.encode(
|
||||
encoder=encoding.RawEncoder
|
||||
)
|
||||
self._public_key = recipient_key.public_key.encode(
|
||||
encoder=encoding.RawEncoder
|
||||
)
|
||||
else:
|
||||
raise exc.TypeError(
|
||||
"SealedBox must be created from a PublicKey or a PrivateKey"
|
||||
)
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return self._public_key
|
||||
|
||||
def encrypt(
|
||||
self,
|
||||
plaintext: bytes,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
) -> bytes:
|
||||
"""
|
||||
Encrypts the plaintext message using a random-generated ephemeral
|
||||
keypair and returns a "composed ciphertext", containing both
|
||||
the public part of the keypair and the ciphertext proper,
|
||||
encoded with the encoder.
|
||||
|
||||
The private part of the ephemeral key-pair will be scrubbed before
|
||||
returning the ciphertext, therefore, the sender will not be able to
|
||||
decrypt the generated ciphertext.
|
||||
|
||||
:param plaintext: [:class:`bytes`] The plaintext message to encrypt
|
||||
:param encoder: The encoder to use to encode the ciphertext
|
||||
:return bytes: encoded ciphertext
|
||||
"""
|
||||
|
||||
ciphertext = nacl.bindings.crypto_box_seal(plaintext, self._public_key)
|
||||
|
||||
encoded_ciphertext = encoder.encode(ciphertext)
|
||||
|
||||
return encoded_ciphertext
|
||||
|
||||
def decrypt(
|
||||
self: "SealedBox[PrivateKey]",
|
||||
ciphertext: bytes,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
) -> bytes:
|
||||
"""
|
||||
Decrypts the ciphertext using the ephemeral public key enclosed
|
||||
in the ciphertext and the SealedBox private key, returning
|
||||
the plaintext message.
|
||||
|
||||
:param ciphertext: [:class:`bytes`] The encrypted message to decrypt
|
||||
:param encoder: The encoder used to decode the ciphertext.
|
||||
:return bytes: The original plaintext
|
||||
:raises TypeError: if this SealedBox was created with a
|
||||
:class:`~nacl.public.PublicKey` rather than a
|
||||
:class:`~nacl.public.PrivateKey`.
|
||||
"""
|
||||
# Decode our ciphertext
|
||||
ciphertext = encoder.decode(ciphertext)
|
||||
|
||||
if self._private_key is None:
|
||||
raise TypeError(
|
||||
"SealedBoxes created with a public key cannot decrypt"
|
||||
)
|
||||
plaintext = nacl.bindings.crypto_box_seal_open(
|
||||
ciphertext,
|
||||
self._public_key,
|
||||
self._private_key,
|
||||
)
|
||||
|
||||
return plaintext
|
||||
0
Lib/site-packages/nacl/py.typed
Normal file
0
Lib/site-packages/nacl/py.typed
Normal file
305
Lib/site-packages/nacl/secret.py
Normal file
305
Lib/site-packages/nacl/secret.py
Normal file
@ -0,0 +1,305 @@
|
||||
# Copyright 2013 Donald Stufft and individual contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from typing import ClassVar, Optional
|
||||
|
||||
import nacl.bindings
|
||||
from nacl import encoding
|
||||
from nacl import exceptions as exc
|
||||
from nacl.utils import EncryptedMessage, StringFixer, random
|
||||
|
||||
|
||||
class SecretBox(encoding.Encodable, StringFixer):
|
||||
"""
|
||||
The SecretBox class encrypts and decrypts messages using the given secret
|
||||
key.
|
||||
|
||||
The ciphertexts generated by :class:`~nacl.secret.Secretbox` include a 16
|
||||
byte authenticator which is checked as part of the decryption. An invalid
|
||||
authenticator will cause the decrypt function to raise an exception. The
|
||||
authenticator is not a signature. Once you've decrypted the message you've
|
||||
demonstrated the ability to create arbitrary valid message, so messages you
|
||||
send are repudiable. For non-repudiable messages, sign them after
|
||||
encryption.
|
||||
|
||||
Encryption is done using `XSalsa20-Poly1305`_, and there are no practical
|
||||
limits on the number or size of messages (up to 2⁶⁴ messages, each up to 2⁶⁴
|
||||
bytes).
|
||||
|
||||
.. _XSalsa20-Poly1305: https://doc.libsodium.org/secret-key_cryptography/secretbox#algorithm-details
|
||||
|
||||
:param key: The secret key used to encrypt and decrypt messages
|
||||
:param encoder: The encoder class used to decode the given key
|
||||
|
||||
:cvar KEY_SIZE: The size that the key is required to be.
|
||||
:cvar NONCE_SIZE: The size that the nonce is required to be.
|
||||
:cvar MACBYTES: The size of the authentication MAC tag in bytes.
|
||||
:cvar MESSAGEBYTES_MAX: The maximum size of a message which can be
|
||||
safely encrypted with a single key/nonce
|
||||
pair.
|
||||
"""
|
||||
|
||||
KEY_SIZE: ClassVar[int] = nacl.bindings.crypto_secretbox_KEYBYTES
|
||||
NONCE_SIZE: ClassVar[int] = nacl.bindings.crypto_secretbox_NONCEBYTES
|
||||
MACBYTES: ClassVar[int] = nacl.bindings.crypto_secretbox_MACBYTES
|
||||
MESSAGEBYTES_MAX: ClassVar[
|
||||
int
|
||||
] = nacl.bindings.crypto_secretbox_MESSAGEBYTES_MAX
|
||||
|
||||
def __init__(
|
||||
self, key: bytes, encoder: encoding.Encoder = encoding.RawEncoder
|
||||
):
|
||||
key = encoder.decode(key)
|
||||
if not isinstance(key, bytes):
|
||||
raise exc.TypeError("SecretBox must be created from 32 bytes")
|
||||
|
||||
if len(key) != self.KEY_SIZE:
|
||||
raise exc.ValueError(
|
||||
"The key must be exactly %s bytes long" % self.KEY_SIZE,
|
||||
)
|
||||
|
||||
self._key = key
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return self._key
|
||||
|
||||
def encrypt(
|
||||
self,
|
||||
plaintext: bytes,
|
||||
nonce: Optional[bytes] = None,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
) -> EncryptedMessage:
|
||||
"""
|
||||
Encrypts the plaintext message using the given `nonce` (or generates
|
||||
one randomly if omitted) and returns the ciphertext encoded with the
|
||||
encoder.
|
||||
|
||||
.. warning:: It is **VITALLY** important that the nonce is a nonce,
|
||||
i.e. it is a number used only once for any given key. If you fail
|
||||
to do this, you compromise the privacy of the messages encrypted.
|
||||
Give your nonces a different prefix, or have one side use an odd
|
||||
counter and one an even counter. Just make sure they are different.
|
||||
|
||||
:param plaintext: [:class:`bytes`] The plaintext message to encrypt
|
||||
:param nonce: [:class:`bytes`] The nonce to use in the encryption
|
||||
:param encoder: The encoder to use to encode the ciphertext
|
||||
:rtype: [:class:`nacl.utils.EncryptedMessage`]
|
||||
"""
|
||||
if nonce is None:
|
||||
nonce = random(self.NONCE_SIZE)
|
||||
|
||||
if len(nonce) != self.NONCE_SIZE:
|
||||
raise exc.ValueError(
|
||||
"The nonce must be exactly %s bytes long" % self.NONCE_SIZE,
|
||||
)
|
||||
|
||||
ciphertext = nacl.bindings.crypto_secretbox(
|
||||
plaintext, nonce, self._key
|
||||
)
|
||||
|
||||
encoded_nonce = encoder.encode(nonce)
|
||||
encoded_ciphertext = encoder.encode(ciphertext)
|
||||
|
||||
return EncryptedMessage._from_parts(
|
||||
encoded_nonce,
|
||||
encoded_ciphertext,
|
||||
encoder.encode(nonce + ciphertext),
|
||||
)
|
||||
|
||||
def decrypt(
|
||||
self,
|
||||
ciphertext: bytes,
|
||||
nonce: Optional[bytes] = None,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
) -> bytes:
|
||||
"""
|
||||
Decrypts the ciphertext using the `nonce` (explicitly, when passed as a
|
||||
parameter or implicitly, when omitted, as part of the ciphertext) and
|
||||
returns the plaintext message.
|
||||
|
||||
:param ciphertext: [:class:`bytes`] The encrypted message to decrypt
|
||||
:param nonce: [:class:`bytes`] The nonce used when encrypting the
|
||||
ciphertext
|
||||
:param encoder: The encoder used to decode the ciphertext.
|
||||
:rtype: [:class:`bytes`]
|
||||
"""
|
||||
# Decode our ciphertext
|
||||
ciphertext = encoder.decode(ciphertext)
|
||||
|
||||
if nonce is None:
|
||||
# If we were given the nonce and ciphertext combined, split them.
|
||||
nonce = ciphertext[: self.NONCE_SIZE]
|
||||
ciphertext = ciphertext[self.NONCE_SIZE :]
|
||||
|
||||
if len(nonce) != self.NONCE_SIZE:
|
||||
raise exc.ValueError(
|
||||
"The nonce must be exactly %s bytes long" % self.NONCE_SIZE,
|
||||
)
|
||||
|
||||
plaintext = nacl.bindings.crypto_secretbox_open(
|
||||
ciphertext, nonce, self._key
|
||||
)
|
||||
|
||||
return plaintext
|
||||
|
||||
|
||||
class Aead(encoding.Encodable, StringFixer):
|
||||
"""
|
||||
The AEAD class encrypts and decrypts messages using the given secret key.
|
||||
|
||||
Unlike :class:`~nacl.secret.SecretBox`, AEAD supports authenticating
|
||||
non-confidential data received alongside the message, such as a length
|
||||
or type tag.
|
||||
|
||||
Like :class:`~nacl.secret.Secretbox`, this class provides authenticated
|
||||
encryption. An inauthentic message will cause the decrypt function to raise
|
||||
an exception.
|
||||
|
||||
Likewise, the authenticator should not be mistaken for a (public-key)
|
||||
signature: recipients (with the ability to decrypt messages) are capable of
|
||||
creating arbitrary valid message; in particular, this means AEAD messages
|
||||
are repudiable. For non-repudiable messages, sign them after encryption.
|
||||
|
||||
The cryptosystem used is `XChacha20-Poly1305`_ as specified for
|
||||
`standardization`_. There are `no practical limits`_ to how much can safely
|
||||
be encrypted under a given key (up to 2⁶⁴ messages each containing up
|
||||
to 2⁶⁴ bytes).
|
||||
|
||||
.. _standardization: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
||||
.. _XChacha20-Poly1305: https://doc.libsodium.org/secret-key_cryptography/aead#xchacha-20-poly1305
|
||||
.. _no practical limits: https://doc.libsodium.org/secret-key_cryptography/aead#limitations
|
||||
|
||||
:param key: The secret key used to encrypt and decrypt messages
|
||||
:param encoder: The encoder class used to decode the given key
|
||||
|
||||
:cvar KEY_SIZE: The size that the key is required to be.
|
||||
:cvar NONCE_SIZE: The size that the nonce is required to be.
|
||||
:cvar MACBYTES: The size of the authentication MAC tag in bytes.
|
||||
:cvar MESSAGEBYTES_MAX: The maximum size of a message which can be
|
||||
safely encrypted with a single key/nonce
|
||||
pair.
|
||||
"""
|
||||
|
||||
KEY_SIZE = nacl.bindings.crypto_aead_xchacha20poly1305_ietf_KEYBYTES
|
||||
NONCE_SIZE = nacl.bindings.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
|
||||
MACBYTES = nacl.bindings.crypto_aead_xchacha20poly1305_ietf_ABYTES
|
||||
MESSAGEBYTES_MAX = (
|
||||
nacl.bindings.crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
key: bytes,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
):
|
||||
key = encoder.decode(key)
|
||||
if not isinstance(key, bytes):
|
||||
raise exc.TypeError("AEAD must be created from 32 bytes")
|
||||
|
||||
if len(key) != self.KEY_SIZE:
|
||||
raise exc.ValueError(
|
||||
"The key must be exactly %s bytes long" % self.KEY_SIZE,
|
||||
)
|
||||
|
||||
self._key = key
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return self._key
|
||||
|
||||
def encrypt(
|
||||
self,
|
||||
plaintext: bytes,
|
||||
aad: bytes = b"",
|
||||
nonce: Optional[bytes] = None,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
) -> EncryptedMessage:
|
||||
"""
|
||||
Encrypts the plaintext message using the given `nonce` (or generates
|
||||
one randomly if omitted) and returns the ciphertext encoded with the
|
||||
encoder.
|
||||
|
||||
.. warning:: It is vitally important for :param nonce: to be unique.
|
||||
By default, it is generated randomly; [:class:`Aead`] uses XChacha20
|
||||
for extended (192b) nonce size, so the risk of reusing random nonces
|
||||
is negligible. It is *strongly recommended* to keep this behaviour,
|
||||
as nonce reuse will compromise the privacy of encrypted messages.
|
||||
Should implicit nonces be inadequate for your application, the
|
||||
second best option is using split counters; e.g. if sending messages
|
||||
encrypted under a shared key between 2 users, each user can use the
|
||||
number of messages it sent so far, prefixed or suffixed with a 1bit
|
||||
user id. Note that the counter must **never** be rolled back (due
|
||||
to overflow, on-disk state being rolled back to an earlier backup,
|
||||
...)
|
||||
|
||||
:param plaintext: [:class:`bytes`] The plaintext message to encrypt
|
||||
:param nonce: [:class:`bytes`] The nonce to use in the encryption
|
||||
:param encoder: The encoder to use to encode the ciphertext
|
||||
:rtype: [:class:`nacl.utils.EncryptedMessage`]
|
||||
"""
|
||||
if nonce is None:
|
||||
nonce = random(self.NONCE_SIZE)
|
||||
|
||||
if len(nonce) != self.NONCE_SIZE:
|
||||
raise exc.ValueError(
|
||||
"The nonce must be exactly %s bytes long" % self.NONCE_SIZE,
|
||||
)
|
||||
|
||||
ciphertext = nacl.bindings.crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||
plaintext, aad, nonce, self._key
|
||||
)
|
||||
|
||||
encoded_nonce = encoder.encode(nonce)
|
||||
encoded_ciphertext = encoder.encode(ciphertext)
|
||||
|
||||
return EncryptedMessage._from_parts(
|
||||
encoded_nonce,
|
||||
encoded_ciphertext,
|
||||
encoder.encode(nonce + ciphertext),
|
||||
)
|
||||
|
||||
def decrypt(
|
||||
self,
|
||||
ciphertext: bytes,
|
||||
aad: bytes = b"",
|
||||
nonce: Optional[bytes] = None,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
) -> bytes:
|
||||
"""
|
||||
Decrypts the ciphertext using the `nonce` (explicitly, when passed as a
|
||||
parameter or implicitly, when omitted, as part of the ciphertext) and
|
||||
returns the plaintext message.
|
||||
|
||||
:param ciphertext: [:class:`bytes`] The encrypted message to decrypt
|
||||
:param nonce: [:class:`bytes`] The nonce used when encrypting the
|
||||
ciphertext
|
||||
:param encoder: The encoder used to decode the ciphertext.
|
||||
:rtype: [:class:`bytes`]
|
||||
"""
|
||||
# Decode our ciphertext
|
||||
ciphertext = encoder.decode(ciphertext)
|
||||
|
||||
if nonce is None:
|
||||
# If we were given the nonce and ciphertext combined, split them.
|
||||
nonce = ciphertext[: self.NONCE_SIZE]
|
||||
ciphertext = ciphertext[self.NONCE_SIZE :]
|
||||
|
||||
if len(nonce) != self.NONCE_SIZE:
|
||||
raise exc.ValueError(
|
||||
"The nonce must be exactly %s bytes long" % self.NONCE_SIZE,
|
||||
)
|
||||
|
||||
plaintext = nacl.bindings.crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
ciphertext, aad, nonce, self._key
|
||||
)
|
||||
|
||||
return plaintext
|
||||
250
Lib/site-packages/nacl/signing.py
Normal file
250
Lib/site-packages/nacl/signing.py
Normal file
@ -0,0 +1,250 @@
|
||||
# Copyright 2013 Donald Stufft and individual contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from typing import Optional
|
||||
|
||||
import nacl.bindings
|
||||
from nacl import encoding
|
||||
from nacl import exceptions as exc
|
||||
from nacl.public import (
|
||||
PrivateKey as _Curve25519_PrivateKey,
|
||||
PublicKey as _Curve25519_PublicKey,
|
||||
)
|
||||
from nacl.utils import StringFixer, random
|
||||
|
||||
|
||||
class SignedMessage(bytes):
|
||||
"""
|
||||
A bytes subclass that holds a messaged that has been signed by a
|
||||
:class:`SigningKey`.
|
||||
"""
|
||||
|
||||
_signature: bytes
|
||||
_message: bytes
|
||||
|
||||
@classmethod
|
||||
def _from_parts(
|
||||
cls, signature: bytes, message: bytes, combined: bytes
|
||||
) -> "SignedMessage":
|
||||
obj = cls(combined)
|
||||
obj._signature = signature
|
||||
obj._message = message
|
||||
return obj
|
||||
|
||||
@property
|
||||
def signature(self) -> bytes:
|
||||
"""
|
||||
The signature contained within the :class:`SignedMessage`.
|
||||
"""
|
||||
return self._signature
|
||||
|
||||
@property
|
||||
def message(self) -> bytes:
|
||||
"""
|
||||
The message contained within the :class:`SignedMessage`.
|
||||
"""
|
||||
return self._message
|
||||
|
||||
|
||||
class VerifyKey(encoding.Encodable, StringFixer):
|
||||
"""
|
||||
The public key counterpart to an Ed25519 SigningKey for producing digital
|
||||
signatures.
|
||||
|
||||
:param key: [:class:`bytes`] Serialized Ed25519 public key
|
||||
:param encoder: A class that is able to decode the `key`
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, key: bytes, encoder: encoding.Encoder = encoding.RawEncoder
|
||||
):
|
||||
# Decode the key
|
||||
key = encoder.decode(key)
|
||||
if not isinstance(key, bytes):
|
||||
raise exc.TypeError("VerifyKey must be created from 32 bytes")
|
||||
|
||||
if len(key) != nacl.bindings.crypto_sign_PUBLICKEYBYTES:
|
||||
raise exc.ValueError(
|
||||
"The key must be exactly %s bytes long"
|
||||
% nacl.bindings.crypto_sign_PUBLICKEYBYTES,
|
||||
)
|
||||
|
||||
self._key = key
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return self._key
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(bytes(self))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return nacl.bindings.sodium_memcmp(bytes(self), bytes(other))
|
||||
|
||||
def __ne__(self, other: object) -> bool:
|
||||
return not (self == other)
|
||||
|
||||
def verify(
|
||||
self,
|
||||
smessage: bytes,
|
||||
signature: Optional[bytes] = None,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
) -> bytes:
|
||||
"""
|
||||
Verifies the signature of a signed message, returning the message
|
||||
if it has not been tampered with else raising
|
||||
:class:`~nacl.signing.BadSignatureError`.
|
||||
|
||||
:param smessage: [:class:`bytes`] Either the original messaged or a
|
||||
signature and message concated together.
|
||||
:param signature: [:class:`bytes`] If an unsigned message is given for
|
||||
smessage then the detached signature must be provided.
|
||||
:param encoder: A class that is able to decode the secret message and
|
||||
signature.
|
||||
:rtype: :class:`bytes`
|
||||
"""
|
||||
if signature is not None:
|
||||
# If we were given the message and signature separately, validate
|
||||
# signature size and combine them.
|
||||
if not isinstance(signature, bytes):
|
||||
raise exc.TypeError(
|
||||
"Verification signature must be created from %d bytes"
|
||||
% nacl.bindings.crypto_sign_BYTES,
|
||||
)
|
||||
|
||||
if len(signature) != nacl.bindings.crypto_sign_BYTES:
|
||||
raise exc.ValueError(
|
||||
"The signature must be exactly %d bytes long"
|
||||
% nacl.bindings.crypto_sign_BYTES,
|
||||
)
|
||||
|
||||
smessage = signature + encoder.decode(smessage)
|
||||
else:
|
||||
# Decode the signed message
|
||||
smessage = encoder.decode(smessage)
|
||||
|
||||
return nacl.bindings.crypto_sign_open(smessage, self._key)
|
||||
|
||||
def to_curve25519_public_key(self) -> _Curve25519_PublicKey:
|
||||
"""
|
||||
Converts a :class:`~nacl.signing.VerifyKey` to a
|
||||
:class:`~nacl.public.PublicKey`
|
||||
|
||||
:rtype: :class:`~nacl.public.PublicKey`
|
||||
"""
|
||||
raw_pk = nacl.bindings.crypto_sign_ed25519_pk_to_curve25519(self._key)
|
||||
return _Curve25519_PublicKey(raw_pk)
|
||||
|
||||
|
||||
class SigningKey(encoding.Encodable, StringFixer):
|
||||
"""
|
||||
Private key for producing digital signatures using the Ed25519 algorithm.
|
||||
|
||||
Signing keys are produced from a 32-byte (256-bit) random seed value. This
|
||||
value can be passed into the :class:`~nacl.signing.SigningKey` as a
|
||||
:func:`bytes` whose length is 32.
|
||||
|
||||
.. warning:: This **must** be protected and remain secret. Anyone who knows
|
||||
the value of your :class:`~nacl.signing.SigningKey` or it's seed can
|
||||
masquerade as you.
|
||||
|
||||
:param seed: [:class:`bytes`] Random 32-byte value (i.e. private key)
|
||||
:param encoder: A class that is able to decode the seed
|
||||
|
||||
:ivar: verify_key: [:class:`~nacl.signing.VerifyKey`] The verify
|
||||
(i.e. public) key that corresponds with this signing key.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
seed: bytes,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
):
|
||||
# Decode the seed
|
||||
seed = encoder.decode(seed)
|
||||
if not isinstance(seed, bytes):
|
||||
raise exc.TypeError(
|
||||
"SigningKey must be created from a 32 byte seed"
|
||||
)
|
||||
|
||||
# Verify that our seed is the proper size
|
||||
if len(seed) != nacl.bindings.crypto_sign_SEEDBYTES:
|
||||
raise exc.ValueError(
|
||||
"The seed must be exactly %d bytes long"
|
||||
% nacl.bindings.crypto_sign_SEEDBYTES
|
||||
)
|
||||
|
||||
public_key, secret_key = nacl.bindings.crypto_sign_seed_keypair(seed)
|
||||
|
||||
self._seed = seed
|
||||
self._signing_key = secret_key
|
||||
self.verify_key = VerifyKey(public_key)
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return self._seed
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(bytes(self))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return nacl.bindings.sodium_memcmp(bytes(self), bytes(other))
|
||||
|
||||
def __ne__(self, other: object) -> bool:
|
||||
return not (self == other)
|
||||
|
||||
@classmethod
|
||||
def generate(cls) -> "SigningKey":
|
||||
"""
|
||||
Generates a random :class:`~nacl.signing.SigningKey` object.
|
||||
|
||||
:rtype: :class:`~nacl.signing.SigningKey`
|
||||
"""
|
||||
return cls(
|
||||
random(nacl.bindings.crypto_sign_SEEDBYTES),
|
||||
encoder=encoding.RawEncoder,
|
||||
)
|
||||
|
||||
def sign(
|
||||
self,
|
||||
message: bytes,
|
||||
encoder: encoding.Encoder = encoding.RawEncoder,
|
||||
) -> SignedMessage:
|
||||
"""
|
||||
Sign a message using this key.
|
||||
|
||||
:param message: [:class:`bytes`] The data to be signed.
|
||||
:param encoder: A class that is used to encode the signed message.
|
||||
:rtype: :class:`~nacl.signing.SignedMessage`
|
||||
"""
|
||||
raw_signed = nacl.bindings.crypto_sign(message, self._signing_key)
|
||||
|
||||
crypto_sign_BYTES = nacl.bindings.crypto_sign_BYTES
|
||||
signature = encoder.encode(raw_signed[:crypto_sign_BYTES])
|
||||
message = encoder.encode(raw_signed[crypto_sign_BYTES:])
|
||||
signed = encoder.encode(raw_signed)
|
||||
|
||||
return SignedMessage._from_parts(signature, message, signed)
|
||||
|
||||
def to_curve25519_private_key(self) -> _Curve25519_PrivateKey:
|
||||
"""
|
||||
Converts a :class:`~nacl.signing.SigningKey` to a
|
||||
:class:`~nacl.public.PrivateKey`
|
||||
|
||||
:rtype: :class:`~nacl.public.PrivateKey`
|
||||
"""
|
||||
sk = self._signing_key
|
||||
raw_private = nacl.bindings.crypto_sign_ed25519_sk_to_curve25519(sk)
|
||||
return _Curve25519_PrivateKey(raw_private)
|
||||
88
Lib/site-packages/nacl/utils.py
Normal file
88
Lib/site-packages/nacl/utils.py
Normal file
@ -0,0 +1,88 @@
|
||||
# Copyright 2013 Donald Stufft and individual contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import os
|
||||
from typing import SupportsBytes, Type, TypeVar
|
||||
|
||||
import nacl.bindings
|
||||
from nacl import encoding
|
||||
|
||||
_EncryptedMessage = TypeVar("_EncryptedMessage", bound="EncryptedMessage")
|
||||
|
||||
|
||||
class EncryptedMessage(bytes):
|
||||
"""
|
||||
A bytes subclass that holds a messaged that has been encrypted by a
|
||||
:class:`SecretBox`.
|
||||
"""
|
||||
|
||||
_nonce: bytes
|
||||
_ciphertext: bytes
|
||||
|
||||
@classmethod
|
||||
def _from_parts(
|
||||
cls: Type[_EncryptedMessage],
|
||||
nonce: bytes,
|
||||
ciphertext: bytes,
|
||||
combined: bytes,
|
||||
) -> _EncryptedMessage:
|
||||
obj = cls(combined)
|
||||
obj._nonce = nonce
|
||||
obj._ciphertext = ciphertext
|
||||
return obj
|
||||
|
||||
@property
|
||||
def nonce(self) -> bytes:
|
||||
"""
|
||||
The nonce used during the encryption of the :class:`EncryptedMessage`.
|
||||
"""
|
||||
return self._nonce
|
||||
|
||||
@property
|
||||
def ciphertext(self) -> bytes:
|
||||
"""
|
||||
The ciphertext contained within the :class:`EncryptedMessage`.
|
||||
"""
|
||||
return self._ciphertext
|
||||
|
||||
|
||||
class StringFixer:
|
||||
def __str__(self: SupportsBytes) -> str:
|
||||
return str(self.__bytes__())
|
||||
|
||||
|
||||
def bytes_as_string(bytes_in: bytes) -> str:
|
||||
return bytes_in.decode("ascii")
|
||||
|
||||
|
||||
def random(size: int = 32) -> bytes:
|
||||
return os.urandom(size)
|
||||
|
||||
|
||||
def randombytes_deterministic(
|
||||
size: int, seed: bytes, encoder: encoding.Encoder = encoding.RawEncoder
|
||||
) -> bytes:
|
||||
"""
|
||||
Returns ``size`` number of deterministically generated pseudorandom bytes
|
||||
from a seed
|
||||
|
||||
:param size: int
|
||||
:param seed: bytes
|
||||
:param encoder: The encoder class used to encode the produced bytes
|
||||
:rtype: bytes
|
||||
"""
|
||||
raw_data = nacl.bindings.randombytes_buf_deterministic(size, seed)
|
||||
|
||||
return encoder.encode(raw_data)
|
||||
Reference in New Issue
Block a user