quick fix 2

This commit is contained in:
Bartłomiej Patyk
2025-10-22 19:05:25 +02:00
commit e5e64b6dc8
598 changed files with 300649 additions and 0 deletions

View 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))

View 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)

View 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)

View 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
)

View 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

View File

View 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

View 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)

View 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)