This commit is contained in:
2025-04-06 03:14:47 +02:00
parent aaf9ab523b
commit b9c99befab
2263 changed files with 401112 additions and 20 deletions

View File

@ -0,0 +1,39 @@
# 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.
__all__ = [
"__title__",
"__summary__",
"__uri__",
"__version__",
"__author__",
"__email__",
"__license__",
"__copyright__",
]
__title__ = "PyNaCl"
__summary__ = (
"Python binding to the Networking and Cryptography (NaCl) library"
)
__uri__ = "https://github.com/pyca/pynacl/"
__version__ = "1.5.0"
__author__ = "The PyNaCl developers"
__email__ = "cryptography-dev@python.org"
__license__ = "Apache License 2.0"
__copyright__ = "Copyright 2013-2018 {}".format(__author__)

Binary file not shown.

View File

@ -0,0 +1,451 @@
# Copyright 2013-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.
from nacl.bindings.crypto_aead import (
crypto_aead_chacha20poly1305_ABYTES,
crypto_aead_chacha20poly1305_KEYBYTES,
crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX,
crypto_aead_chacha20poly1305_NPUBBYTES,
crypto_aead_chacha20poly1305_NSECBYTES,
crypto_aead_chacha20poly1305_decrypt,
crypto_aead_chacha20poly1305_encrypt,
crypto_aead_chacha20poly1305_ietf_ABYTES,
crypto_aead_chacha20poly1305_ietf_KEYBYTES,
crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX,
crypto_aead_chacha20poly1305_ietf_NPUBBYTES,
crypto_aead_chacha20poly1305_ietf_NSECBYTES,
crypto_aead_chacha20poly1305_ietf_decrypt,
crypto_aead_chacha20poly1305_ietf_encrypt,
crypto_aead_xchacha20poly1305_ietf_ABYTES,
crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX,
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES,
crypto_aead_xchacha20poly1305_ietf_NSECBYTES,
crypto_aead_xchacha20poly1305_ietf_decrypt,
crypto_aead_xchacha20poly1305_ietf_encrypt,
)
from nacl.bindings.crypto_box import (
crypto_box,
crypto_box_BEFORENMBYTES,
crypto_box_BOXZEROBYTES,
crypto_box_NONCEBYTES,
crypto_box_PUBLICKEYBYTES,
crypto_box_SEALBYTES,
crypto_box_SECRETKEYBYTES,
crypto_box_SEEDBYTES,
crypto_box_ZEROBYTES,
crypto_box_afternm,
crypto_box_beforenm,
crypto_box_keypair,
crypto_box_open,
crypto_box_open_afternm,
crypto_box_seal,
crypto_box_seal_open,
crypto_box_seed_keypair,
)
from nacl.bindings.crypto_core import (
crypto_core_ed25519_BYTES,
crypto_core_ed25519_NONREDUCEDSCALARBYTES,
crypto_core_ed25519_SCALARBYTES,
crypto_core_ed25519_add,
crypto_core_ed25519_is_valid_point,
crypto_core_ed25519_scalar_add,
crypto_core_ed25519_scalar_complement,
crypto_core_ed25519_scalar_invert,
crypto_core_ed25519_scalar_mul,
crypto_core_ed25519_scalar_negate,
crypto_core_ed25519_scalar_reduce,
crypto_core_ed25519_scalar_sub,
crypto_core_ed25519_sub,
has_crypto_core_ed25519,
)
from nacl.bindings.crypto_generichash import (
crypto_generichash_BYTES,
crypto_generichash_BYTES_MAX,
crypto_generichash_BYTES_MIN,
crypto_generichash_KEYBYTES,
crypto_generichash_KEYBYTES_MAX,
crypto_generichash_KEYBYTES_MIN,
crypto_generichash_PERSONALBYTES,
crypto_generichash_SALTBYTES,
crypto_generichash_STATEBYTES,
generichash_blake2b_final as crypto_generichash_blake2b_final,
generichash_blake2b_init as crypto_generichash_blake2b_init,
generichash_blake2b_salt_personal as crypto_generichash_blake2b_salt_personal,
generichash_blake2b_update as crypto_generichash_blake2b_update,
)
from nacl.bindings.crypto_hash import (
crypto_hash,
crypto_hash_BYTES,
crypto_hash_sha256,
crypto_hash_sha256_BYTES,
crypto_hash_sha512,
crypto_hash_sha512_BYTES,
)
from nacl.bindings.crypto_kx import (
crypto_kx_PUBLIC_KEY_BYTES,
crypto_kx_SECRET_KEY_BYTES,
crypto_kx_SEED_BYTES,
crypto_kx_SESSION_KEY_BYTES,
crypto_kx_client_session_keys,
crypto_kx_keypair,
crypto_kx_seed_keypair,
crypto_kx_server_session_keys,
)
from nacl.bindings.crypto_pwhash import (
crypto_pwhash_ALG_ARGON2I13,
crypto_pwhash_ALG_ARGON2ID13,
crypto_pwhash_ALG_DEFAULT,
crypto_pwhash_BYTES_MAX,
crypto_pwhash_BYTES_MIN,
crypto_pwhash_PASSWD_MAX,
crypto_pwhash_PASSWD_MIN,
crypto_pwhash_SALTBYTES,
crypto_pwhash_STRBYTES,
crypto_pwhash_alg,
crypto_pwhash_argon2i_MEMLIMIT_INTERACTIVE,
crypto_pwhash_argon2i_MEMLIMIT_MAX,
crypto_pwhash_argon2i_MEMLIMIT_MIN,
crypto_pwhash_argon2i_MEMLIMIT_MODERATE,
crypto_pwhash_argon2i_MEMLIMIT_SENSITIVE,
crypto_pwhash_argon2i_OPSLIMIT_INTERACTIVE,
crypto_pwhash_argon2i_OPSLIMIT_MAX,
crypto_pwhash_argon2i_OPSLIMIT_MIN,
crypto_pwhash_argon2i_OPSLIMIT_MODERATE,
crypto_pwhash_argon2i_OPSLIMIT_SENSITIVE,
crypto_pwhash_argon2i_STRPREFIX,
crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE,
crypto_pwhash_argon2id_MEMLIMIT_MAX,
crypto_pwhash_argon2id_MEMLIMIT_MIN,
crypto_pwhash_argon2id_MEMLIMIT_MODERATE,
crypto_pwhash_argon2id_MEMLIMIT_SENSITIVE,
crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE,
crypto_pwhash_argon2id_OPSLIMIT_MAX,
crypto_pwhash_argon2id_OPSLIMIT_MIN,
crypto_pwhash_argon2id_OPSLIMIT_MODERATE,
crypto_pwhash_argon2id_OPSLIMIT_SENSITIVE,
crypto_pwhash_argon2id_STRPREFIX,
crypto_pwhash_scryptsalsa208sha256_BYTES_MAX,
crypto_pwhash_scryptsalsa208sha256_BYTES_MIN,
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE,
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX,
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN,
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE,
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE,
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX,
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN,
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE,
crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX,
crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN,
crypto_pwhash_scryptsalsa208sha256_SALTBYTES,
crypto_pwhash_scryptsalsa208sha256_STRBYTES,
crypto_pwhash_scryptsalsa208sha256_STRPREFIX,
crypto_pwhash_scryptsalsa208sha256_ll,
crypto_pwhash_scryptsalsa208sha256_str,
crypto_pwhash_scryptsalsa208sha256_str_verify,
crypto_pwhash_str_alg,
crypto_pwhash_str_verify,
has_crypto_pwhash_scryptsalsa208sha256,
nacl_bindings_pick_scrypt_params,
)
from nacl.bindings.crypto_scalarmult import (
crypto_scalarmult,
crypto_scalarmult_BYTES,
crypto_scalarmult_SCALARBYTES,
crypto_scalarmult_base,
crypto_scalarmult_ed25519,
crypto_scalarmult_ed25519_BYTES,
crypto_scalarmult_ed25519_SCALARBYTES,
crypto_scalarmult_ed25519_base,
crypto_scalarmult_ed25519_base_noclamp,
crypto_scalarmult_ed25519_noclamp,
has_crypto_scalarmult_ed25519,
)
from nacl.bindings.crypto_secretbox import (
crypto_secretbox,
crypto_secretbox_BOXZEROBYTES,
crypto_secretbox_KEYBYTES,
crypto_secretbox_MACBYTES,
crypto_secretbox_MESSAGEBYTES_MAX,
crypto_secretbox_NONCEBYTES,
crypto_secretbox_ZEROBYTES,
crypto_secretbox_open,
)
from nacl.bindings.crypto_secretstream import (
crypto_secretstream_xchacha20poly1305_ABYTES,
crypto_secretstream_xchacha20poly1305_HEADERBYTES,
crypto_secretstream_xchacha20poly1305_KEYBYTES,
crypto_secretstream_xchacha20poly1305_STATEBYTES,
crypto_secretstream_xchacha20poly1305_TAG_FINAL,
crypto_secretstream_xchacha20poly1305_TAG_MESSAGE,
crypto_secretstream_xchacha20poly1305_TAG_PUSH,
crypto_secretstream_xchacha20poly1305_TAG_REKEY,
crypto_secretstream_xchacha20poly1305_init_pull,
crypto_secretstream_xchacha20poly1305_init_push,
crypto_secretstream_xchacha20poly1305_keygen,
crypto_secretstream_xchacha20poly1305_pull,
crypto_secretstream_xchacha20poly1305_push,
crypto_secretstream_xchacha20poly1305_rekey,
crypto_secretstream_xchacha20poly1305_state,
)
from nacl.bindings.crypto_shorthash import (
BYTES as crypto_shorthash_siphash24_BYTES,
KEYBYTES as crypto_shorthash_siphash24_KEYBYTES,
XBYTES as crypto_shorthash_siphashx24_BYTES,
XKEYBYTES as crypto_shorthash_siphashx24_KEYBYTES,
crypto_shorthash_siphash24,
crypto_shorthash_siphashx24,
has_crypto_shorthash_siphashx24,
)
from nacl.bindings.crypto_sign import (
crypto_sign,
crypto_sign_BYTES,
crypto_sign_PUBLICKEYBYTES,
crypto_sign_SECRETKEYBYTES,
crypto_sign_SEEDBYTES,
crypto_sign_ed25519_pk_to_curve25519,
crypto_sign_ed25519_sk_to_curve25519,
crypto_sign_ed25519_sk_to_pk,
crypto_sign_ed25519_sk_to_seed,
crypto_sign_ed25519ph_STATEBYTES,
crypto_sign_ed25519ph_final_create,
crypto_sign_ed25519ph_final_verify,
crypto_sign_ed25519ph_state,
crypto_sign_ed25519ph_update,
crypto_sign_keypair,
crypto_sign_open,
crypto_sign_seed_keypair,
)
from nacl.bindings.randombytes import (
randombytes,
randombytes_buf_deterministic,
)
from nacl.bindings.sodium_core import sodium_init
from nacl.bindings.utils import (
sodium_add,
sodium_increment,
sodium_memcmp,
sodium_pad,
sodium_unpad,
)
__all__ = [
"crypto_aead_chacha20poly1305_ABYTES",
"crypto_aead_chacha20poly1305_KEYBYTES",
"crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX",
"crypto_aead_chacha20poly1305_NPUBBYTES",
"crypto_aead_chacha20poly1305_NSECBYTES",
"crypto_aead_chacha20poly1305_decrypt",
"crypto_aead_chacha20poly1305_encrypt",
"crypto_aead_chacha20poly1305_ietf_ABYTES",
"crypto_aead_chacha20poly1305_ietf_KEYBYTES",
"crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX",
"crypto_aead_chacha20poly1305_ietf_NPUBBYTES",
"crypto_aead_chacha20poly1305_ietf_NSECBYTES",
"crypto_aead_chacha20poly1305_ietf_decrypt",
"crypto_aead_chacha20poly1305_ietf_encrypt",
"crypto_aead_xchacha20poly1305_ietf_ABYTES",
"crypto_aead_xchacha20poly1305_ietf_KEYBYTES",
"crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX",
"crypto_aead_xchacha20poly1305_ietf_NPUBBYTES",
"crypto_aead_xchacha20poly1305_ietf_NSECBYTES",
"crypto_aead_xchacha20poly1305_ietf_decrypt",
"crypto_aead_xchacha20poly1305_ietf_encrypt",
"crypto_box_SECRETKEYBYTES",
"crypto_box_PUBLICKEYBYTES",
"crypto_box_SEEDBYTES",
"crypto_box_NONCEBYTES",
"crypto_box_ZEROBYTES",
"crypto_box_BOXZEROBYTES",
"crypto_box_BEFORENMBYTES",
"crypto_box_SEALBYTES",
"crypto_box_keypair",
"crypto_box",
"crypto_box_open",
"crypto_box_beforenm",
"crypto_box_afternm",
"crypto_box_open_afternm",
"crypto_box_seal",
"crypto_box_seal_open",
"crypto_box_seed_keypair",
"has_crypto_core_ed25519",
"crypto_core_ed25519_BYTES",
"crypto_core_ed25519_UNIFORMBYTES",
"crypto_core_ed25519_SCALARBYTES",
"crypto_core_ed25519_NONREDUCEDSCALARBYTES",
"crypto_core_ed25519_add",
"crypto_core_ed25519_from_uniform",
"crypto_core_ed25519_is_valid_point",
"crypto_core_ed25519_sub",
"crypto_core_ed25519_scalar_invert",
"crypto_core_ed25519_scalar_negate",
"crypto_core_ed25519_scalar_complement",
"crypto_core_ed25519_scalar_add",
"crypto_core_ed25519_scalar_sub",
"crypto_core_ed25519_scalar_mul",
"crypto_core_ed25519_scalar_reduce",
"crypto_hash_BYTES",
"crypto_hash_sha256_BYTES",
"crypto_hash_sha512_BYTES",
"crypto_hash",
"crypto_hash_sha256",
"crypto_hash_sha512",
"crypto_generichash_BYTES",
"crypto_generichash_BYTES_MIN",
"crypto_generichash_BYTES_MAX",
"crypto_generichash_KEYBYTES",
"crypto_generichash_KEYBYTES_MIN",
"crypto_generichash_KEYBYTES_MAX",
"crypto_generichash_SALTBYTES",
"crypto_generichash_PERSONALBYTES",
"crypto_generichash_STATEBYTES",
"crypto_generichash_blake2b_salt_personal",
"crypto_generichash_blake2b_init",
"crypto_generichash_blake2b_update",
"crypto_generichash_blake2b_final",
"crypto_kx_keypair",
"crypto_kx_seed_keypair",
"crypto_kx_client_session_keys",
"crypto_kx_server_session_keys",
"crypto_kx_PUBLIC_KEY_BYTES",
"crypto_kx_SECRET_KEY_BYTES",
"crypto_kx_SEED_BYTES",
"crypto_kx_SESSION_KEY_BYTES",
"has_crypto_scalarmult_ed25519",
"crypto_scalarmult_BYTES",
"crypto_scalarmult_SCALARBYTES",
"crypto_scalarmult",
"crypto_scalarmult_base",
"crypto_scalarmult_ed25519_BYTES",
"crypto_scalarmult_ed25519_SCALARBYTES",
"crypto_scalarmult_ed25519",
"crypto_scalarmult_ed25519_base",
"crypto_scalarmult_ed25519_noclamp",
"crypto_scalarmult_ed25519_base_noclamp",
"crypto_secretbox_KEYBYTES",
"crypto_secretbox_NONCEBYTES",
"crypto_secretbox_ZEROBYTES",
"crypto_secretbox_BOXZEROBYTES",
"crypto_secretbox_MACBYTES",
"crypto_secretbox_MESSAGEBYTES_MAX",
"crypto_secretbox",
"crypto_secretbox_open",
"crypto_secretstream_xchacha20poly1305_ABYTES",
"crypto_secretstream_xchacha20poly1305_HEADERBYTES",
"crypto_secretstream_xchacha20poly1305_KEYBYTES",
"crypto_secretstream_xchacha20poly1305_STATEBYTES",
"crypto_secretstream_xchacha20poly1305_TAG_FINAL",
"crypto_secretstream_xchacha20poly1305_TAG_MESSAGE",
"crypto_secretstream_xchacha20poly1305_TAG_PUSH",
"crypto_secretstream_xchacha20poly1305_TAG_REKEY",
"crypto_secretstream_xchacha20poly1305_init_pull",
"crypto_secretstream_xchacha20poly1305_init_push",
"crypto_secretstream_xchacha20poly1305_keygen",
"crypto_secretstream_xchacha20poly1305_pull",
"crypto_secretstream_xchacha20poly1305_push",
"crypto_secretstream_xchacha20poly1305_rekey",
"crypto_secretstream_xchacha20poly1305_state",
"has_crypto_shorthash_siphashx24",
"crypto_shorthash_siphash24_BYTES",
"crypto_shorthash_siphash24_KEYBYTES",
"crypto_shorthash_siphash24",
"crypto_shorthash_siphashx24_BYTES",
"crypto_shorthash_siphashx24_KEYBYTES",
"crypto_shorthash_siphashx24",
"crypto_sign_BYTES",
"crypto_sign_SEEDBYTES",
"crypto_sign_PUBLICKEYBYTES",
"crypto_sign_SECRETKEYBYTES",
"crypto_sign_keypair",
"crypto_sign_seed_keypair",
"crypto_sign",
"crypto_sign_open",
"crypto_sign_ed25519_pk_to_curve25519",
"crypto_sign_ed25519_sk_to_curve25519",
"crypto_sign_ed25519_sk_to_pk",
"crypto_sign_ed25519_sk_to_seed",
"crypto_sign_ed25519ph_STATEBYTES",
"crypto_sign_ed25519ph_final_create",
"crypto_sign_ed25519ph_final_verify",
"crypto_sign_ed25519ph_state",
"crypto_sign_ed25519ph_update",
"crypto_pwhash_ALG_ARGON2I13",
"crypto_pwhash_ALG_ARGON2ID13",
"crypto_pwhash_ALG_DEFAULT",
"crypto_pwhash_BYTES_MAX",
"crypto_pwhash_BYTES_MIN",
"crypto_pwhash_PASSWD_MAX",
"crypto_pwhash_PASSWD_MIN",
"crypto_pwhash_SALTBYTES",
"crypto_pwhash_STRBYTES",
"crypto_pwhash_alg",
"crypto_pwhash_argon2i_MEMLIMIT_MIN",
"crypto_pwhash_argon2i_MEMLIMIT_MAX",
"crypto_pwhash_argon2i_MEMLIMIT_INTERACTIVE",
"crypto_pwhash_argon2i_MEMLIMIT_MODERATE",
"crypto_pwhash_argon2i_MEMLIMIT_SENSITIVE",
"crypto_pwhash_argon2i_OPSLIMIT_MIN",
"crypto_pwhash_argon2i_OPSLIMIT_MAX",
"crypto_pwhash_argon2i_OPSLIMIT_INTERACTIVE",
"crypto_pwhash_argon2i_OPSLIMIT_MODERATE",
"crypto_pwhash_argon2i_OPSLIMIT_SENSITIVE",
"crypto_pwhash_argon2i_STRPREFIX",
"crypto_pwhash_argon2id_MEMLIMIT_MIN",
"crypto_pwhash_argon2id_MEMLIMIT_MAX",
"crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE",
"crypto_pwhash_argon2id_MEMLIMIT_MODERATE",
"crypto_pwhash_argon2id_OPSLIMIT_MIN",
"crypto_pwhash_argon2id_OPSLIMIT_MAX",
"crypto_pwhash_argon2id_MEMLIMIT_SENSITIVE",
"crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE",
"crypto_pwhash_argon2id_OPSLIMIT_MODERATE",
"crypto_pwhash_argon2id_OPSLIMIT_SENSITIVE",
"crypto_pwhash_argon2id_STRPREFIX",
"crypto_pwhash_str_alg",
"crypto_pwhash_str_verify",
"has_crypto_pwhash_scryptsalsa208sha256",
"crypto_pwhash_scryptsalsa208sha256_BYTES_MAX",
"crypto_pwhash_scryptsalsa208sha256_BYTES_MIN",
"crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE",
"crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX",
"crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN",
"crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE",
"crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE",
"crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX",
"crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN",
"crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE",
"crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX",
"crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN",
"crypto_pwhash_scryptsalsa208sha256_SALTBYTES",
"crypto_pwhash_scryptsalsa208sha256_STRBYTES",
"crypto_pwhash_scryptsalsa208sha256_STRPREFIX",
"crypto_pwhash_scryptsalsa208sha256_ll",
"crypto_pwhash_scryptsalsa208sha256_str",
"crypto_pwhash_scryptsalsa208sha256_str_verify",
"nacl_bindings_pick_scrypt_params",
"randombytes",
"randombytes_buf_deterministic",
"sodium_init",
"sodium_add",
"sodium_increment",
"sodium_memcmp",
"sodium_pad",
"sodium_unpad",
]
# Initialize Sodium
sodium_init()

View File

@ -0,0 +1,559 @@
# Copyright 2017 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
from nacl import exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
"""
Implementations of authenticated encription with associated data (*AEAD*)
constructions building on the chacha20 stream cipher and the poly1305
authenticator
"""
crypto_aead_chacha20poly1305_ietf_KEYBYTES: int = (
lib.crypto_aead_chacha20poly1305_ietf_keybytes()
)
crypto_aead_chacha20poly1305_ietf_NSECBYTES: int = (
lib.crypto_aead_chacha20poly1305_ietf_nsecbytes()
)
crypto_aead_chacha20poly1305_ietf_NPUBBYTES: int = (
lib.crypto_aead_chacha20poly1305_ietf_npubbytes()
)
crypto_aead_chacha20poly1305_ietf_ABYTES: int = (
lib.crypto_aead_chacha20poly1305_ietf_abytes()
)
crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX: int = (
lib.crypto_aead_chacha20poly1305_ietf_messagebytes_max()
)
_aead_chacha20poly1305_ietf_CRYPTBYTES_MAX = (
crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX
+ crypto_aead_chacha20poly1305_ietf_ABYTES
)
crypto_aead_chacha20poly1305_KEYBYTES: int = (
lib.crypto_aead_chacha20poly1305_keybytes()
)
crypto_aead_chacha20poly1305_NSECBYTES: int = (
lib.crypto_aead_chacha20poly1305_nsecbytes()
)
crypto_aead_chacha20poly1305_NPUBBYTES: int = (
lib.crypto_aead_chacha20poly1305_npubbytes()
)
crypto_aead_chacha20poly1305_ABYTES: int = (
lib.crypto_aead_chacha20poly1305_abytes()
)
crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX: int = (
lib.crypto_aead_chacha20poly1305_messagebytes_max()
)
_aead_chacha20poly1305_CRYPTBYTES_MAX = (
crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX
+ crypto_aead_chacha20poly1305_ABYTES
)
crypto_aead_xchacha20poly1305_ietf_KEYBYTES: int = (
lib.crypto_aead_xchacha20poly1305_ietf_keybytes()
)
crypto_aead_xchacha20poly1305_ietf_NSECBYTES: int = (
lib.crypto_aead_xchacha20poly1305_ietf_nsecbytes()
)
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES: int = (
lib.crypto_aead_xchacha20poly1305_ietf_npubbytes()
)
crypto_aead_xchacha20poly1305_ietf_ABYTES: int = (
lib.crypto_aead_xchacha20poly1305_ietf_abytes()
)
crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX: int = (
lib.crypto_aead_xchacha20poly1305_ietf_messagebytes_max()
)
_aead_xchacha20poly1305_ietf_CRYPTBYTES_MAX = (
crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX
+ crypto_aead_xchacha20poly1305_ietf_ABYTES
)
def crypto_aead_chacha20poly1305_ietf_encrypt(
message: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
) -> bytes:
"""
Encrypt the given ``message`` using the IETF ratified chacha20poly1305
construction described in RFC7539.
:param message:
:type message: bytes
:param aad:
:type aad: Optional[bytes]
:param nonce:
:type nonce: bytes
:param key:
:type key: bytes
:return: authenticated ciphertext
:rtype: bytes
"""
ensure(
isinstance(message, bytes),
"Input message type must be bytes",
raising=exc.TypeError,
)
mlen = len(message)
ensure(
mlen <= crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX,
"Message must be at most {} bytes long".format(
crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX
),
raising=exc.ValueError,
)
ensure(
isinstance(aad, bytes) or (aad is None),
"Additional data must be bytes or None",
raising=exc.TypeError,
)
ensure(
isinstance(nonce, bytes)
and len(nonce) == crypto_aead_chacha20poly1305_ietf_NPUBBYTES,
"Nonce must be a {} bytes long bytes sequence".format(
crypto_aead_chacha20poly1305_ietf_NPUBBYTES
),
raising=exc.TypeError,
)
ensure(
isinstance(key, bytes)
and len(key) == crypto_aead_chacha20poly1305_ietf_KEYBYTES,
"Key must be a {} bytes long bytes sequence".format(
crypto_aead_chacha20poly1305_ietf_KEYBYTES
),
raising=exc.TypeError,
)
if aad:
_aad = aad
aalen = len(aad)
else:
_aad = ffi.NULL
aalen = 0
mxout = mlen + crypto_aead_chacha20poly1305_ietf_ABYTES
clen = ffi.new("unsigned long long *")
ciphertext = ffi.new("unsigned char[]", mxout)
res = lib.crypto_aead_chacha20poly1305_ietf_encrypt(
ciphertext, clen, message, mlen, _aad, aalen, ffi.NULL, nonce, key
)
ensure(res == 0, "Encryption failed.", raising=exc.CryptoError)
return ffi.buffer(ciphertext, clen[0])[:]
def crypto_aead_chacha20poly1305_ietf_decrypt(
ciphertext: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
) -> bytes:
"""
Decrypt the given ``ciphertext`` using the IETF ratified chacha20poly1305
construction described in RFC7539.
:param ciphertext:
:type ciphertext: bytes
:param aad:
:type aad: Optional[bytes]
:param nonce:
:type nonce: bytes
:param key:
:type key: bytes
:return: message
:rtype: bytes
"""
ensure(
isinstance(ciphertext, bytes),
"Input ciphertext type must be bytes",
raising=exc.TypeError,
)
clen = len(ciphertext)
ensure(
clen <= _aead_chacha20poly1305_ietf_CRYPTBYTES_MAX,
"Ciphertext must be at most {} bytes long".format(
_aead_chacha20poly1305_ietf_CRYPTBYTES_MAX
),
raising=exc.ValueError,
)
ensure(
isinstance(aad, bytes) or (aad is None),
"Additional data must be bytes or None",
raising=exc.TypeError,
)
ensure(
isinstance(nonce, bytes)
and len(nonce) == crypto_aead_chacha20poly1305_ietf_NPUBBYTES,
"Nonce must be a {} bytes long bytes sequence".format(
crypto_aead_chacha20poly1305_ietf_NPUBBYTES
),
raising=exc.TypeError,
)
ensure(
isinstance(key, bytes)
and len(key) == crypto_aead_chacha20poly1305_ietf_KEYBYTES,
"Key must be a {} bytes long bytes sequence".format(
crypto_aead_chacha20poly1305_ietf_KEYBYTES
),
raising=exc.TypeError,
)
mxout = clen - crypto_aead_chacha20poly1305_ietf_ABYTES
mlen = ffi.new("unsigned long long *")
message = ffi.new("unsigned char[]", mxout)
if aad:
_aad = aad
aalen = len(aad)
else:
_aad = ffi.NULL
aalen = 0
res = lib.crypto_aead_chacha20poly1305_ietf_decrypt(
message, mlen, ffi.NULL, ciphertext, clen, _aad, aalen, nonce, key
)
ensure(res == 0, "Decryption failed.", raising=exc.CryptoError)
return ffi.buffer(message, mlen[0])[:]
def crypto_aead_chacha20poly1305_encrypt(
message: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
) -> bytes:
"""
Encrypt the given ``message`` using the "legacy" construction
described in draft-agl-tls-chacha20poly1305.
:param message:
:type message: bytes
:param aad:
:type aad: Optional[bytes]
:param nonce:
:type nonce: bytes
:param key:
:type key: bytes
:return: authenticated ciphertext
:rtype: bytes
"""
ensure(
isinstance(message, bytes),
"Input message type must be bytes",
raising=exc.TypeError,
)
mlen = len(message)
ensure(
mlen <= crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX,
"Message must be at most {} bytes long".format(
crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX
),
raising=exc.ValueError,
)
ensure(
isinstance(aad, bytes) or (aad is None),
"Additional data must be bytes or None",
raising=exc.TypeError,
)
ensure(
isinstance(nonce, bytes)
and len(nonce) == crypto_aead_chacha20poly1305_NPUBBYTES,
"Nonce must be a {} bytes long bytes sequence".format(
crypto_aead_chacha20poly1305_NPUBBYTES
),
raising=exc.TypeError,
)
ensure(
isinstance(key, bytes)
and len(key) == crypto_aead_chacha20poly1305_KEYBYTES,
"Key must be a {} bytes long bytes sequence".format(
crypto_aead_chacha20poly1305_KEYBYTES
),
raising=exc.TypeError,
)
if aad:
_aad = aad
aalen = len(aad)
else:
_aad = ffi.NULL
aalen = 0
mlen = len(message)
mxout = mlen + crypto_aead_chacha20poly1305_ietf_ABYTES
clen = ffi.new("unsigned long long *")
ciphertext = ffi.new("unsigned char[]", mxout)
res = lib.crypto_aead_chacha20poly1305_encrypt(
ciphertext, clen, message, mlen, _aad, aalen, ffi.NULL, nonce, key
)
ensure(res == 0, "Encryption failed.", raising=exc.CryptoError)
return ffi.buffer(ciphertext, clen[0])[:]
def crypto_aead_chacha20poly1305_decrypt(
ciphertext: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
) -> bytes:
"""
Decrypt the given ``ciphertext`` using the "legacy" construction
described in draft-agl-tls-chacha20poly1305.
:param ciphertext: authenticated ciphertext
:type ciphertext: bytes
:param aad:
:type aad: Optional[bytes]
:param nonce:
:type nonce: bytes
:param key:
:type key: bytes
:return: message
:rtype: bytes
"""
ensure(
isinstance(ciphertext, bytes),
"Input ciphertext type must be bytes",
raising=exc.TypeError,
)
clen = len(ciphertext)
ensure(
clen <= _aead_chacha20poly1305_CRYPTBYTES_MAX,
"Ciphertext must be at most {} bytes long".format(
_aead_chacha20poly1305_CRYPTBYTES_MAX
),
raising=exc.ValueError,
)
ensure(
isinstance(aad, bytes) or (aad is None),
"Additional data must be bytes or None",
raising=exc.TypeError,
)
ensure(
isinstance(nonce, bytes)
and len(nonce) == crypto_aead_chacha20poly1305_NPUBBYTES,
"Nonce must be a {} bytes long bytes sequence".format(
crypto_aead_chacha20poly1305_NPUBBYTES
),
raising=exc.TypeError,
)
ensure(
isinstance(key, bytes)
and len(key) == crypto_aead_chacha20poly1305_KEYBYTES,
"Key must be a {} bytes long bytes sequence".format(
crypto_aead_chacha20poly1305_KEYBYTES
),
raising=exc.TypeError,
)
mxout = clen - crypto_aead_chacha20poly1305_ABYTES
mlen = ffi.new("unsigned long long *")
message = ffi.new("unsigned char[]", mxout)
if aad:
_aad = aad
aalen = len(aad)
else:
_aad = ffi.NULL
aalen = 0
res = lib.crypto_aead_chacha20poly1305_decrypt(
message, mlen, ffi.NULL, ciphertext, clen, _aad, aalen, nonce, key
)
ensure(res == 0, "Decryption failed.", raising=exc.CryptoError)
return ffi.buffer(message, mlen[0])[:]
def crypto_aead_xchacha20poly1305_ietf_encrypt(
message: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
) -> bytes:
"""
Encrypt the given ``message`` using the long-nonces xchacha20poly1305
construction.
:param message:
:type message: bytes
:param aad:
:type aad: Optional[bytes]
:param nonce:
:type nonce: bytes
:param key:
:type key: bytes
:return: authenticated ciphertext
:rtype: bytes
"""
ensure(
isinstance(message, bytes),
"Input message type must be bytes",
raising=exc.TypeError,
)
mlen = len(message)
ensure(
mlen <= crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX,
"Message must be at most {} bytes long".format(
crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX
),
raising=exc.ValueError,
)
ensure(
isinstance(aad, bytes) or (aad is None),
"Additional data must be bytes or None",
raising=exc.TypeError,
)
ensure(
isinstance(nonce, bytes)
and len(nonce) == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES,
"Nonce must be a {} bytes long bytes sequence".format(
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
),
raising=exc.TypeError,
)
ensure(
isinstance(key, bytes)
and len(key) == crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
"Key must be a {} bytes long bytes sequence".format(
crypto_aead_xchacha20poly1305_ietf_KEYBYTES
),
raising=exc.TypeError,
)
if aad:
_aad = aad
aalen = len(aad)
else:
_aad = ffi.NULL
aalen = 0
mlen = len(message)
mxout = mlen + crypto_aead_xchacha20poly1305_ietf_ABYTES
clen = ffi.new("unsigned long long *")
ciphertext = ffi.new("unsigned char[]", mxout)
res = lib.crypto_aead_xchacha20poly1305_ietf_encrypt(
ciphertext, clen, message, mlen, _aad, aalen, ffi.NULL, nonce, key
)
ensure(res == 0, "Encryption failed.", raising=exc.CryptoError)
return ffi.buffer(ciphertext, clen[0])[:]
def crypto_aead_xchacha20poly1305_ietf_decrypt(
ciphertext: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
) -> bytes:
"""
Decrypt the given ``ciphertext`` using the long-nonces xchacha20poly1305
construction.
:param ciphertext: authenticated ciphertext
:type ciphertext: bytes
:param aad:
:type aad: Optional[bytes]
:param nonce:
:type nonce: bytes
:param key:
:type key: bytes
:return: message
:rtype: bytes
"""
ensure(
isinstance(ciphertext, bytes),
"Input ciphertext type must be bytes",
raising=exc.TypeError,
)
clen = len(ciphertext)
ensure(
clen <= _aead_xchacha20poly1305_ietf_CRYPTBYTES_MAX,
"Ciphertext must be at most {} bytes long".format(
_aead_xchacha20poly1305_ietf_CRYPTBYTES_MAX
),
raising=exc.ValueError,
)
ensure(
isinstance(aad, bytes) or (aad is None),
"Additional data must be bytes or None",
raising=exc.TypeError,
)
ensure(
isinstance(nonce, bytes)
and len(nonce) == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES,
"Nonce must be a {} bytes long bytes sequence".format(
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
),
raising=exc.TypeError,
)
ensure(
isinstance(key, bytes)
and len(key) == crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
"Key must be a {} bytes long bytes sequence".format(
crypto_aead_xchacha20poly1305_ietf_KEYBYTES
),
raising=exc.TypeError,
)
mxout = clen - crypto_aead_xchacha20poly1305_ietf_ABYTES
mlen = ffi.new("unsigned long long *")
message = ffi.new("unsigned char[]", mxout)
if aad:
_aad = aad
aalen = len(aad)
else:
_aad = ffi.NULL
aalen = 0
res = lib.crypto_aead_xchacha20poly1305_ietf_decrypt(
message, mlen, ffi.NULL, ciphertext, clen, _aad, aalen, nonce, key
)
ensure(res == 0, "Decryption failed.", raising=exc.CryptoError)
return ffi.buffer(message, mlen[0])[:]

View File

@ -0,0 +1,324 @@
# 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 Tuple
from nacl import exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
__all__ = ["crypto_box_keypair", "crypto_box"]
crypto_box_SECRETKEYBYTES: int = lib.crypto_box_secretkeybytes()
crypto_box_PUBLICKEYBYTES: int = lib.crypto_box_publickeybytes()
crypto_box_SEEDBYTES: int = lib.crypto_box_seedbytes()
crypto_box_NONCEBYTES: int = lib.crypto_box_noncebytes()
crypto_box_ZEROBYTES: int = lib.crypto_box_zerobytes()
crypto_box_BOXZEROBYTES: int = lib.crypto_box_boxzerobytes()
crypto_box_BEFORENMBYTES: int = lib.crypto_box_beforenmbytes()
crypto_box_SEALBYTES: int = lib.crypto_box_sealbytes()
def crypto_box_keypair() -> Tuple[bytes, bytes]:
"""
Returns a randomly generated public and secret key.
:rtype: (bytes(public_key), bytes(secret_key))
"""
pk = ffi.new("unsigned char[]", crypto_box_PUBLICKEYBYTES)
sk = ffi.new("unsigned char[]", crypto_box_SECRETKEYBYTES)
rc = lib.crypto_box_keypair(pk, sk)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return (
ffi.buffer(pk, crypto_box_PUBLICKEYBYTES)[:],
ffi.buffer(sk, crypto_box_SECRETKEYBYTES)[:],
)
def crypto_box_seed_keypair(seed: bytes) -> Tuple[bytes, bytes]:
"""
Returns a (public, secret) keypair deterministically generated
from an input ``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: bytes
:rtype: (bytes(public_key), bytes(secret_key))
"""
ensure(isinstance(seed, bytes), "seed must be bytes", raising=TypeError)
if len(seed) != crypto_box_SEEDBYTES:
raise exc.ValueError("Invalid seed")
pk = ffi.new("unsigned char[]", crypto_box_PUBLICKEYBYTES)
sk = ffi.new("unsigned char[]", crypto_box_SECRETKEYBYTES)
rc = lib.crypto_box_seed_keypair(pk, sk, seed)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return (
ffi.buffer(pk, crypto_box_PUBLICKEYBYTES)[:],
ffi.buffer(sk, crypto_box_SECRETKEYBYTES)[:],
)
def crypto_box(message: bytes, nonce: bytes, pk: bytes, sk: bytes) -> bytes:
"""
Encrypts and returns a message ``message`` using the secret key ``sk``,
public key ``pk``, and the nonce ``nonce``.
:param message: bytes
:param nonce: bytes
:param pk: bytes
:param sk: bytes
:rtype: bytes
"""
if len(nonce) != crypto_box_NONCEBYTES:
raise exc.ValueError("Invalid nonce size")
if len(pk) != crypto_box_PUBLICKEYBYTES:
raise exc.ValueError("Invalid public key")
if len(sk) != crypto_box_SECRETKEYBYTES:
raise exc.ValueError("Invalid secret key")
padded = (b"\x00" * crypto_box_ZEROBYTES) + message
ciphertext = ffi.new("unsigned char[]", len(padded))
rc = lib.crypto_box(ciphertext, padded, len(padded), nonce, pk, sk)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(ciphertext, len(padded))[crypto_box_BOXZEROBYTES:]
def crypto_box_open(
ciphertext: bytes, nonce: bytes, pk: bytes, sk: bytes
) -> bytes:
"""
Decrypts and returns an encrypted message ``ciphertext``, using the secret
key ``sk``, public key ``pk``, and the nonce ``nonce``.
:param ciphertext: bytes
:param nonce: bytes
:param pk: bytes
:param sk: bytes
:rtype: bytes
"""
if len(nonce) != crypto_box_NONCEBYTES:
raise exc.ValueError("Invalid nonce size")
if len(pk) != crypto_box_PUBLICKEYBYTES:
raise exc.ValueError("Invalid public key")
if len(sk) != crypto_box_SECRETKEYBYTES:
raise exc.ValueError("Invalid secret key")
padded = (b"\x00" * crypto_box_BOXZEROBYTES) + ciphertext
plaintext = ffi.new("unsigned char[]", len(padded))
res = lib.crypto_box_open(plaintext, padded, len(padded), nonce, pk, sk)
ensure(
res == 0,
"An error occurred trying to decrypt the message",
raising=exc.CryptoError,
)
return ffi.buffer(plaintext, len(padded))[crypto_box_ZEROBYTES:]
def crypto_box_beforenm(pk: bytes, sk: bytes) -> bytes:
"""
Computes and returns the shared key for the public key ``pk`` and the
secret key ``sk``. This can be used to speed up operations where the same
set of keys is going to be used multiple times.
:param pk: bytes
:param sk: bytes
:rtype: bytes
"""
if len(pk) != crypto_box_PUBLICKEYBYTES:
raise exc.ValueError("Invalid public key")
if len(sk) != crypto_box_SECRETKEYBYTES:
raise exc.ValueError("Invalid secret key")
k = ffi.new("unsigned char[]", crypto_box_BEFORENMBYTES)
rc = lib.crypto_box_beforenm(k, pk, sk)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(k, crypto_box_BEFORENMBYTES)[:]
def crypto_box_afternm(message: bytes, nonce: bytes, k: bytes) -> bytes:
"""
Encrypts and returns the message ``message`` using the shared key ``k`` and
the nonce ``nonce``.
:param message: bytes
:param nonce: bytes
:param k: bytes
:rtype: bytes
"""
if len(nonce) != crypto_box_NONCEBYTES:
raise exc.ValueError("Invalid nonce")
if len(k) != crypto_box_BEFORENMBYTES:
raise exc.ValueError("Invalid shared key")
padded = b"\x00" * crypto_box_ZEROBYTES + message
ciphertext = ffi.new("unsigned char[]", len(padded))
rc = lib.crypto_box_afternm(ciphertext, padded, len(padded), nonce, k)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(ciphertext, len(padded))[crypto_box_BOXZEROBYTES:]
def crypto_box_open_afternm(
ciphertext: bytes, nonce: bytes, k: bytes
) -> bytes:
"""
Decrypts and returns the encrypted message ``ciphertext``, using the shared
key ``k`` and the nonce ``nonce``.
:param ciphertext: bytes
:param nonce: bytes
:param k: bytes
:rtype: bytes
"""
if len(nonce) != crypto_box_NONCEBYTES:
raise exc.ValueError("Invalid nonce")
if len(k) != crypto_box_BEFORENMBYTES:
raise exc.ValueError("Invalid shared key")
padded = (b"\x00" * crypto_box_BOXZEROBYTES) + ciphertext
plaintext = ffi.new("unsigned char[]", len(padded))
res = lib.crypto_box_open_afternm(plaintext, padded, len(padded), nonce, k)
ensure(
res == 0,
"An error occurred trying to decrypt the message",
raising=exc.CryptoError,
)
return ffi.buffer(plaintext, len(padded))[crypto_box_ZEROBYTES:]
def crypto_box_seal(message: bytes, pk: bytes) -> bytes:
"""
Encrypts and returns a message ``message`` using an ephemeral secret key
and the public key ``pk``.
The ephemeral public key, which is embedded in the sealed box, is also
used, in combination with ``pk``, to derive the nonce needed for the
underlying box construct.
:param message: bytes
:param pk: bytes
:rtype: bytes
.. versionadded:: 1.2
"""
ensure(
isinstance(message, bytes),
"input message must be bytes",
raising=TypeError,
)
ensure(
isinstance(pk, bytes), "public key must be bytes", raising=TypeError
)
if len(pk) != crypto_box_PUBLICKEYBYTES:
raise exc.ValueError("Invalid public key")
_mlen = len(message)
_clen = crypto_box_SEALBYTES + _mlen
ciphertext = ffi.new("unsigned char[]", _clen)
rc = lib.crypto_box_seal(ciphertext, message, _mlen, pk)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(ciphertext, _clen)[:]
def crypto_box_seal_open(ciphertext: bytes, pk: bytes, sk: bytes) -> bytes:
"""
Decrypts and returns an encrypted message ``ciphertext``, using the
recipent's secret key ``sk`` and the sender's ephemeral public key
embedded in the sealed box. The box contruct nonce is derived from
the recipient's public key ``pk`` and the sender's public key.
:param ciphertext: bytes
:param pk: bytes
:param sk: bytes
:rtype: bytes
.. versionadded:: 1.2
"""
ensure(
isinstance(ciphertext, bytes),
"input ciphertext must be bytes",
raising=TypeError,
)
ensure(
isinstance(pk, bytes), "public key must be bytes", raising=TypeError
)
ensure(
isinstance(sk, bytes), "secret key must be bytes", raising=TypeError
)
if len(pk) != crypto_box_PUBLICKEYBYTES:
raise exc.ValueError("Invalid public key")
if len(sk) != crypto_box_SECRETKEYBYTES:
raise exc.ValueError("Invalid secret key")
_clen = len(ciphertext)
ensure(
_clen >= crypto_box_SEALBYTES,
("Input cyphertext must be at least {} long").format(
crypto_box_SEALBYTES
),
raising=exc.TypeError,
)
_mlen = _clen - crypto_box_SEALBYTES
# zero-length malloc results are implementation.dependent
plaintext = ffi.new("unsigned char[]", max(1, _mlen))
res = lib.crypto_box_seal_open(plaintext, ciphertext, _clen, pk, sk)
ensure(
res == 0,
"An error occurred trying to decrypt the message",
raising=exc.CryptoError,
)
return ffi.buffer(plaintext, _mlen)[:]

View File

@ -0,0 +1,412 @@
# Copyright 2018 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 nacl import exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
has_crypto_core_ed25519 = bool(lib.PYNACL_HAS_CRYPTO_CORE_ED25519)
crypto_core_ed25519_BYTES = 0
crypto_core_ed25519_SCALARBYTES = 0
crypto_core_ed25519_NONREDUCEDSCALARBYTES = 0
if has_crypto_core_ed25519:
crypto_core_ed25519_BYTES = lib.crypto_core_ed25519_bytes()
crypto_core_ed25519_SCALARBYTES = lib.crypto_core_ed25519_scalarbytes()
crypto_core_ed25519_NONREDUCEDSCALARBYTES = (
lib.crypto_core_ed25519_nonreducedscalarbytes()
)
def crypto_core_ed25519_is_valid_point(p: bytes) -> bool:
"""
Check if ``p`` represents a point on the edwards25519 curve, in canonical
form, on the main subgroup, and that the point doesn't have a small order.
:param p: a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
representing a point on the edwards25519 curve
:type p: bytes
:return: point validity
:rtype: bool
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_core_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(p, bytes) and len(p) == crypto_core_ed25519_BYTES,
"Point must be a crypto_core_ed25519_BYTES long bytes sequence",
raising=exc.TypeError,
)
rc = lib.crypto_core_ed25519_is_valid_point(p)
return rc == 1
def crypto_core_ed25519_add(p: bytes, q: bytes) -> bytes:
"""
Add two points on the edwards25519 curve.
:param p: a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
representing a point on the edwards25519 curve
:type p: bytes
:param q: a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
representing a point on the edwards25519 curve
:type q: bytes
:return: a point on the edwards25519 curve represented as
a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_core_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(p, bytes)
and isinstance(q, bytes)
and len(p) == crypto_core_ed25519_BYTES
and len(q) == crypto_core_ed25519_BYTES,
"Each point must be a {} long bytes sequence".format(
"crypto_core_ed25519_BYTES"
),
raising=exc.TypeError,
)
r = ffi.new("unsigned char[]", crypto_core_ed25519_BYTES)
rc = lib.crypto_core_ed25519_add(r, p, q)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(r, crypto_core_ed25519_BYTES)[:]
def crypto_core_ed25519_sub(p: bytes, q: bytes) -> bytes:
"""
Subtract a point from another on the edwards25519 curve.
:param p: a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
representing a point on the edwards25519 curve
:type p: bytes
:param q: a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
representing a point on the edwards25519 curve
:type q: bytes
:return: a point on the edwards25519 curve represented as
a :py:data:`.crypto_core_ed25519_BYTES` long bytes sequence
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_core_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(p, bytes)
and isinstance(q, bytes)
and len(p) == crypto_core_ed25519_BYTES
and len(q) == crypto_core_ed25519_BYTES,
"Each point must be a {} long bytes sequence".format(
"crypto_core_ed25519_BYTES"
),
raising=exc.TypeError,
)
r = ffi.new("unsigned char[]", crypto_core_ed25519_BYTES)
rc = lib.crypto_core_ed25519_sub(r, p, q)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(r, crypto_core_ed25519_BYTES)[:]
def crypto_core_ed25519_scalar_invert(s: bytes) -> bytes:
"""
Return the multiplicative inverse of integer ``s`` modulo ``L``,
i.e an integer ``i`` such that ``s * i = 1 (mod L)``, where ``L``
is the order of the main subgroup.
Raises a ``exc.RuntimeError`` if ``s`` is the integer zero.
:param s: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
long bytes sequence representing an integer
:type s: bytes
:return: an integer represented as a
:py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_core_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(s, bytes) and len(s) == crypto_core_ed25519_SCALARBYTES,
"Integer s must be a {} long bytes sequence".format(
"crypto_core_ed25519_SCALARBYTES"
),
raising=exc.TypeError,
)
r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
rc = lib.crypto_core_ed25519_scalar_invert(r, s)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]
def crypto_core_ed25519_scalar_negate(s: bytes) -> bytes:
"""
Return the integer ``n`` such that ``s + n = 0 (mod L)``, where ``L``
is the order of the main subgroup.
:param s: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
long bytes sequence representing an integer
:type s: bytes
:return: an integer represented as a
:py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_core_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(s, bytes) and len(s) == crypto_core_ed25519_SCALARBYTES,
"Integer s must be a {} long bytes sequence".format(
"crypto_core_ed25519_SCALARBYTES"
),
raising=exc.TypeError,
)
r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
lib.crypto_core_ed25519_scalar_negate(r, s)
return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]
def crypto_core_ed25519_scalar_complement(s: bytes) -> bytes:
"""
Return the complement of integer ``s`` modulo ``L``, i.e. an integer
``c`` such that ``s + c = 1 (mod L)``, where ``L`` is the order of
the main subgroup.
:param s: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
long bytes sequence representing an integer
:type s: bytes
:return: an integer represented as a
:py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_core_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(s, bytes) and len(s) == crypto_core_ed25519_SCALARBYTES,
"Integer s must be a {} long bytes sequence".format(
"crypto_core_ed25519_SCALARBYTES"
),
raising=exc.TypeError,
)
r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
lib.crypto_core_ed25519_scalar_complement(r, s)
return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]
def crypto_core_ed25519_scalar_add(p: bytes, q: bytes) -> bytes:
"""
Add integers ``p`` and ``q`` modulo ``L``, where ``L`` is the order of
the main subgroup.
:param p: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
long bytes sequence representing an integer
:type p: bytes
:param q: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
long bytes sequence representing an integer
:type q: bytes
:return: an integer represented as a
:py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_core_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(p, bytes)
and isinstance(q, bytes)
and len(p) == crypto_core_ed25519_SCALARBYTES
and len(q) == crypto_core_ed25519_SCALARBYTES,
"Each integer must be a {} long bytes sequence".format(
"crypto_core_ed25519_SCALARBYTES"
),
raising=exc.TypeError,
)
r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
lib.crypto_core_ed25519_scalar_add(r, p, q)
return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]
def crypto_core_ed25519_scalar_sub(p: bytes, q: bytes) -> bytes:
"""
Subtract integers ``p`` and ``q`` modulo ``L``, where ``L`` is the
order of the main subgroup.
:param p: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
long bytes sequence representing an integer
:type p: bytes
:param q: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
long bytes sequence representing an integer
:type q: bytes
:return: an integer represented as a
:py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_core_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(p, bytes)
and isinstance(q, bytes)
and len(p) == crypto_core_ed25519_SCALARBYTES
and len(q) == crypto_core_ed25519_SCALARBYTES,
"Each integer must be a {} long bytes sequence".format(
"crypto_core_ed25519_SCALARBYTES"
),
raising=exc.TypeError,
)
r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
lib.crypto_core_ed25519_scalar_sub(r, p, q)
return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]
def crypto_core_ed25519_scalar_mul(p: bytes, q: bytes) -> bytes:
"""
Multiply integers ``p`` and ``q`` modulo ``L``, where ``L`` is the
order of the main subgroup.
:param p: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
long bytes sequence representing an integer
:type p: bytes
:param q: a :py:data:`.crypto_core_ed25519_SCALARBYTES`
long bytes sequence representing an integer
:type q: bytes
:return: an integer represented as a
:py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_core_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(p, bytes)
and isinstance(q, bytes)
and len(p) == crypto_core_ed25519_SCALARBYTES
and len(q) == crypto_core_ed25519_SCALARBYTES,
"Each integer must be a {} long bytes sequence".format(
"crypto_core_ed25519_SCALARBYTES"
),
raising=exc.TypeError,
)
r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
lib.crypto_core_ed25519_scalar_mul(r, p, q)
return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]
def crypto_core_ed25519_scalar_reduce(s: bytes) -> bytes:
"""
Reduce integer ``s`` to ``s`` modulo ``L``, where ``L`` is the order
of the main subgroup.
:param s: a :py:data:`.crypto_core_ed25519_NONREDUCEDSCALARBYTES`
long bytes sequence representing an integer
:type s: bytes
:return: an integer represented as a
:py:data:`.crypto_core_ed25519_SCALARBYTES` long bytes sequence
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_core_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(s, bytes)
and len(s) == crypto_core_ed25519_NONREDUCEDSCALARBYTES,
"Integer s must be a {} long bytes sequence".format(
"crypto_core_ed25519_NONREDUCEDSCALARBYTES"
),
raising=exc.TypeError,
)
r = ffi.new("unsigned char[]", crypto_core_ed25519_SCALARBYTES)
lib.crypto_core_ed25519_scalar_reduce(r, s)
return ffi.buffer(r, crypto_core_ed25519_SCALARBYTES)[:]

View File

@ -0,0 +1,281 @@
# Copyright 2013-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.
from typing import NoReturn, TypeVar
from nacl import exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
crypto_generichash_BYTES: int = lib.crypto_generichash_blake2b_bytes()
crypto_generichash_BYTES_MIN: int = lib.crypto_generichash_blake2b_bytes_min()
crypto_generichash_BYTES_MAX: int = lib.crypto_generichash_blake2b_bytes_max()
crypto_generichash_KEYBYTES: int = lib.crypto_generichash_blake2b_keybytes()
crypto_generichash_KEYBYTES_MIN: int = (
lib.crypto_generichash_blake2b_keybytes_min()
)
crypto_generichash_KEYBYTES_MAX: int = (
lib.crypto_generichash_blake2b_keybytes_max()
)
crypto_generichash_SALTBYTES: int = lib.crypto_generichash_blake2b_saltbytes()
crypto_generichash_PERSONALBYTES: int = (
lib.crypto_generichash_blake2b_personalbytes()
)
crypto_generichash_STATEBYTES: int = lib.crypto_generichash_statebytes()
_OVERLONG = "{0} length greater than {1} bytes"
_TOOBIG = "{0} greater than {1}"
def _checkparams(
digest_size: int, key: bytes, salt: bytes, person: bytes
) -> None:
"""Check hash parameters"""
ensure(
isinstance(key, bytes),
"Key must be a bytes sequence",
raising=exc.TypeError,
)
ensure(
isinstance(salt, bytes),
"Salt must be a bytes sequence",
raising=exc.TypeError,
)
ensure(
isinstance(person, bytes),
"Person must be a bytes sequence",
raising=exc.TypeError,
)
ensure(
isinstance(digest_size, int),
"Digest size must be an integer number",
raising=exc.TypeError,
)
ensure(
digest_size <= crypto_generichash_BYTES_MAX,
_TOOBIG.format("Digest_size", crypto_generichash_BYTES_MAX),
raising=exc.ValueError,
)
ensure(
len(key) <= crypto_generichash_KEYBYTES_MAX,
_OVERLONG.format("Key", crypto_generichash_KEYBYTES_MAX),
raising=exc.ValueError,
)
ensure(
len(salt) <= crypto_generichash_SALTBYTES,
_OVERLONG.format("Salt", crypto_generichash_SALTBYTES),
raising=exc.ValueError,
)
ensure(
len(person) <= crypto_generichash_PERSONALBYTES,
_OVERLONG.format("Person", crypto_generichash_PERSONALBYTES),
raising=exc.ValueError,
)
def generichash_blake2b_salt_personal(
data: bytes,
digest_size: int = crypto_generichash_BYTES,
key: bytes = b"",
salt: bytes = b"",
person: bytes = b"",
) -> bytes:
"""One shot hash interface
:param data: the input data to the hash function
:type data: bytes
:param digest_size: must be at most
:py:data:`.crypto_generichash_BYTES_MAX`;
the default digest size is
:py:data:`.crypto_generichash_BYTES`
:type digest_size: int
:param key: must be at most
:py:data:`.crypto_generichash_KEYBYTES_MAX` long
:type key: bytes
:param salt: must be at most
:py:data:`.crypto_generichash_SALTBYTES` long;
will be zero-padded if needed
:type salt: bytes
:param person: must be at most
:py:data:`.crypto_generichash_PERSONALBYTES` long:
will be zero-padded if needed
:type person: bytes
:return: digest_size long digest
:rtype: bytes
"""
_checkparams(digest_size, key, salt, person)
ensure(
isinstance(data, bytes),
"Input data must be a bytes sequence",
raising=exc.TypeError,
)
digest = ffi.new("unsigned char[]", digest_size)
# both _salt and _personal must be zero-padded to the correct length
_salt = ffi.new("unsigned char []", crypto_generichash_SALTBYTES)
_person = ffi.new("unsigned char []", crypto_generichash_PERSONALBYTES)
ffi.memmove(_salt, salt, len(salt))
ffi.memmove(_person, person, len(person))
rc = lib.crypto_generichash_blake2b_salt_personal(
digest, digest_size, data, len(data), key, len(key), _salt, _person
)
ensure(rc == 0, "Unexpected failure", raising=exc.RuntimeError)
return ffi.buffer(digest, digest_size)[:]
_Blake2State = TypeVar("_Blake2State", bound="Blake2State")
class Blake2State:
"""
Python-level wrapper for the crypto_generichash_blake2b state buffer
"""
__slots__ = ["_statebuf", "digest_size"]
def __init__(self, digest_size: int):
self._statebuf = ffi.new(
"unsigned char[]", crypto_generichash_STATEBYTES
)
self.digest_size = digest_size
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 copy(self: _Blake2State) -> _Blake2State:
_st = self.__class__(self.digest_size)
ffi.memmove(
_st._statebuf, self._statebuf, crypto_generichash_STATEBYTES
)
return _st
def generichash_blake2b_init(
key: bytes = b"",
salt: bytes = b"",
person: bytes = b"",
digest_size: int = crypto_generichash_BYTES,
) -> Blake2State:
"""
Create a new initialized blake2b hash state
:param key: must be at most
:py:data:`.crypto_generichash_KEYBYTES_MAX` long
:type key: bytes
:param salt: must be at most
:py:data:`.crypto_generichash_SALTBYTES` long;
will be zero-padded if needed
:type salt: bytes
:param person: must be at most
:py:data:`.crypto_generichash_PERSONALBYTES` long:
will be zero-padded if needed
:type person: bytes
:param digest_size: must be at most
:py:data:`.crypto_generichash_BYTES_MAX`;
the default digest size is
:py:data:`.crypto_generichash_BYTES`
:type digest_size: int
:return: a initialized :py:class:`.Blake2State`
:rtype: object
"""
_checkparams(digest_size, key, salt, person)
state = Blake2State(digest_size)
# both _salt and _personal must be zero-padded to the correct length
_salt = ffi.new("unsigned char []", crypto_generichash_SALTBYTES)
_person = ffi.new("unsigned char []", crypto_generichash_PERSONALBYTES)
ffi.memmove(_salt, salt, len(salt))
ffi.memmove(_person, person, len(person))
rc = lib.crypto_generichash_blake2b_init_salt_personal(
state._statebuf, key, len(key), digest_size, _salt, _person
)
ensure(rc == 0, "Unexpected failure", raising=exc.RuntimeError)
return state
def generichash_blake2b_update(state: Blake2State, data: bytes) -> None:
"""Update the blake2b hash state
:param state: a initialized Blake2bState object as returned from
:py:func:`.crypto_generichash_blake2b_init`
:type state: :py:class:`.Blake2State`
:param data:
:type data: bytes
"""
ensure(
isinstance(state, Blake2State),
"State must be a Blake2State object",
raising=exc.TypeError,
)
ensure(
isinstance(data, bytes),
"Input data must be a bytes sequence",
raising=exc.TypeError,
)
rc = lib.crypto_generichash_blake2b_update(
state._statebuf, data, len(data)
)
ensure(rc == 0, "Unexpected failure", raising=exc.RuntimeError)
def generichash_blake2b_final(state: Blake2State) -> bytes:
"""Finalize the blake2b hash state and return the digest.
:param state: a initialized Blake2bState object as returned from
:py:func:`.crypto_generichash_blake2b_init`
:type state: :py:class:`.Blake2State`
:return: the blake2 digest of the passed-in data stream
:rtype: bytes
"""
ensure(
isinstance(state, Blake2State),
"State must be a Blake2State object",
raising=exc.TypeError,
)
_digest = ffi.new("unsigned char[]", crypto_generichash_BYTES_MAX)
rc = lib.crypto_generichash_blake2b_final(
state._statebuf, _digest, state.digest_size
)
ensure(rc == 0, "Unexpected failure", raising=exc.RuntimeError)
return ffi.buffer(_digest, state.digest_size)[:]

View File

@ -0,0 +1,63 @@
# 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 nacl import exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
# crypto_hash_BYTES = lib.crypto_hash_bytes()
crypto_hash_BYTES: int = lib.crypto_hash_sha512_bytes()
crypto_hash_sha256_BYTES: int = lib.crypto_hash_sha256_bytes()
crypto_hash_sha512_BYTES: int = lib.crypto_hash_sha512_bytes()
def crypto_hash(message: bytes) -> bytes:
"""
Hashes and returns the message ``message``.
:param message: bytes
:rtype: bytes
"""
digest = ffi.new("unsigned char[]", crypto_hash_BYTES)
rc = lib.crypto_hash(digest, message, len(message))
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(digest, crypto_hash_BYTES)[:]
def crypto_hash_sha256(message: bytes) -> bytes:
"""
Hashes and returns the message ``message``.
:param message: bytes
:rtype: bytes
"""
digest = ffi.new("unsigned char[]", crypto_hash_sha256_BYTES)
rc = lib.crypto_hash_sha256(digest, message, len(message))
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(digest, crypto_hash_sha256_BYTES)[:]
def crypto_hash_sha512(message: bytes) -> bytes:
"""
Hashes and returns the message ``message``.
:param message: bytes
:rtype: bytes
"""
digest = ffi.new("unsigned char[]", crypto_hash_sha512_BYTES)
rc = lib.crypto_hash_sha512(digest, message, len(message))
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(digest, crypto_hash_sha512_BYTES)[:]

View File

@ -0,0 +1,200 @@
# Copyright 2018 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 Tuple
from nacl import exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
__all__ = [
"crypto_kx_keypair",
"crypto_kx_client_session_keys",
"crypto_kx_server_session_keys",
"crypto_kx_PUBLIC_KEY_BYTES",
"crypto_kx_SECRET_KEY_BYTES",
"crypto_kx_SEED_BYTES",
"crypto_kx_SESSION_KEY_BYTES",
]
"""
Implementations of client, server key exchange
"""
crypto_kx_PUBLIC_KEY_BYTES: int = lib.crypto_kx_publickeybytes()
crypto_kx_SECRET_KEY_BYTES: int = lib.crypto_kx_secretkeybytes()
crypto_kx_SEED_BYTES: int = lib.crypto_kx_seedbytes()
crypto_kx_SESSION_KEY_BYTES: int = lib.crypto_kx_sessionkeybytes()
def crypto_kx_keypair() -> Tuple[bytes, bytes]:
"""
Generate a keypair.
This is a duplicate crypto_box_keypair, but
is included for api consistency.
:return: (public_key, secret_key)
:rtype: (bytes, bytes)
"""
public_key = ffi.new("unsigned char[]", crypto_kx_PUBLIC_KEY_BYTES)
secret_key = ffi.new("unsigned char[]", crypto_kx_SECRET_KEY_BYTES)
res = lib.crypto_kx_keypair(public_key, secret_key)
ensure(res == 0, "Key generation failed.", raising=exc.CryptoError)
return (
ffi.buffer(public_key, crypto_kx_PUBLIC_KEY_BYTES)[:],
ffi.buffer(secret_key, crypto_kx_SECRET_KEY_BYTES)[:],
)
def crypto_kx_seed_keypair(seed: bytes) -> Tuple[bytes, bytes]:
"""
Generate a keypair with a given seed.
This is functionally the same as crypto_box_seed_keypair, however
it uses the blake2b hash primitive instead of sha512.
It is included mainly for api consistency when using crypto_kx.
:param seed: random seed
:type seed: bytes
:return: (public_key, secret_key)
:rtype: (bytes, bytes)
"""
public_key = ffi.new("unsigned char[]", crypto_kx_PUBLIC_KEY_BYTES)
secret_key = ffi.new("unsigned char[]", crypto_kx_SECRET_KEY_BYTES)
ensure(
isinstance(seed, bytes) and len(seed) == crypto_kx_SEED_BYTES,
"Seed must be a {} byte long bytes sequence".format(
crypto_kx_SEED_BYTES
),
raising=exc.TypeError,
)
res = lib.crypto_kx_seed_keypair(public_key, secret_key, seed)
ensure(res == 0, "Key generation failed.", raising=exc.CryptoError)
return (
ffi.buffer(public_key, crypto_kx_PUBLIC_KEY_BYTES)[:],
ffi.buffer(secret_key, crypto_kx_SECRET_KEY_BYTES)[:],
)
def crypto_kx_client_session_keys(
client_public_key: bytes,
client_secret_key: bytes,
server_public_key: bytes,
) -> Tuple[bytes, bytes]:
"""
Generate session keys for the client.
:param client_public_key:
:type client_public_key: bytes
:param client_secret_key:
:type client_secret_key: bytes
:param server_public_key:
:type server_public_key: bytes
:return: (rx_key, tx_key)
:rtype: (bytes, bytes)
"""
ensure(
isinstance(client_public_key, bytes)
and len(client_public_key) == crypto_kx_PUBLIC_KEY_BYTES,
"Client public key must be a {} bytes long bytes sequence".format(
crypto_kx_PUBLIC_KEY_BYTES
),
raising=exc.TypeError,
)
ensure(
isinstance(client_secret_key, bytes)
and len(client_secret_key) == crypto_kx_SECRET_KEY_BYTES,
"Client secret key must be a {} bytes long bytes sequence".format(
crypto_kx_PUBLIC_KEY_BYTES
),
raising=exc.TypeError,
)
ensure(
isinstance(server_public_key, bytes)
and len(server_public_key) == crypto_kx_PUBLIC_KEY_BYTES,
"Server public key must be a {} bytes long bytes sequence".format(
crypto_kx_PUBLIC_KEY_BYTES
),
raising=exc.TypeError,
)
rx_key = ffi.new("unsigned char[]", crypto_kx_SESSION_KEY_BYTES)
tx_key = ffi.new("unsigned char[]", crypto_kx_SESSION_KEY_BYTES)
res = lib.crypto_kx_client_session_keys(
rx_key, tx_key, client_public_key, client_secret_key, server_public_key
)
ensure(
res == 0,
"Client session key generation failed.",
raising=exc.CryptoError,
)
return (
ffi.buffer(rx_key, crypto_kx_SESSION_KEY_BYTES)[:],
ffi.buffer(tx_key, crypto_kx_SESSION_KEY_BYTES)[:],
)
def crypto_kx_server_session_keys(
server_public_key: bytes,
server_secret_key: bytes,
client_public_key: bytes,
) -> Tuple[bytes, bytes]:
"""
Generate session keys for the server.
:param server_public_key:
:type server_public_key: bytes
:param server_secret_key:
:type server_secret_key: bytes
:param client_public_key:
:type client_public_key: bytes
:return: (rx_key, tx_key)
:rtype: (bytes, bytes)
"""
ensure(
isinstance(server_public_key, bytes)
and len(server_public_key) == crypto_kx_PUBLIC_KEY_BYTES,
"Server public key must be a {} bytes long bytes sequence".format(
crypto_kx_PUBLIC_KEY_BYTES
),
raising=exc.TypeError,
)
ensure(
isinstance(server_secret_key, bytes)
and len(server_secret_key) == crypto_kx_SECRET_KEY_BYTES,
"Server secret key must be a {} bytes long bytes sequence".format(
crypto_kx_PUBLIC_KEY_BYTES
),
raising=exc.TypeError,
)
ensure(
isinstance(client_public_key, bytes)
and len(client_public_key) == crypto_kx_PUBLIC_KEY_BYTES,
"Client public key must be a {} bytes long bytes sequence".format(
crypto_kx_PUBLIC_KEY_BYTES
),
raising=exc.TypeError,
)
rx_key = ffi.new("unsigned char[]", crypto_kx_SESSION_KEY_BYTES)
tx_key = ffi.new("unsigned char[]", crypto_kx_SESSION_KEY_BYTES)
res = lib.crypto_kx_server_session_keys(
rx_key, tx_key, server_public_key, server_secret_key, client_public_key
)
ensure(
res == 0,
"Server session key generation failed.",
raising=exc.CryptoError,
)
return (
ffi.buffer(rx_key, crypto_kx_SESSION_KEY_BYTES)[:],
ffi.buffer(tx_key, crypto_kx_SESSION_KEY_BYTES)[:],
)

View File

@ -0,0 +1,600 @@
# 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 sys
from typing import Tuple
import nacl.exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
has_crypto_pwhash_scryptsalsa208sha256 = bool(
lib.PYNACL_HAS_CRYPTO_PWHASH_SCRYPTSALSA208SHA256
)
crypto_pwhash_scryptsalsa208sha256_STRPREFIX = b""
crypto_pwhash_scryptsalsa208sha256_SALTBYTES = 0
crypto_pwhash_scryptsalsa208sha256_STRBYTES = 0
crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN = 0
crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX = 0
crypto_pwhash_scryptsalsa208sha256_BYTES_MIN = 0
crypto_pwhash_scryptsalsa208sha256_BYTES_MAX = 0
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN = 0
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX = 0
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN = 0
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX = 0
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE = 0
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE = 0
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE = 0
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE = 0
if has_crypto_pwhash_scryptsalsa208sha256:
crypto_pwhash_scryptsalsa208sha256_STRPREFIX = ffi.string(
ffi.cast("char *", lib.crypto_pwhash_scryptsalsa208sha256_strprefix())
)[:]
crypto_pwhash_scryptsalsa208sha256_SALTBYTES = (
lib.crypto_pwhash_scryptsalsa208sha256_saltbytes()
)
crypto_pwhash_scryptsalsa208sha256_STRBYTES = (
lib.crypto_pwhash_scryptsalsa208sha256_strbytes()
)
crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN = (
lib.crypto_pwhash_scryptsalsa208sha256_passwd_min()
)
crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX = (
lib.crypto_pwhash_scryptsalsa208sha256_passwd_max()
)
crypto_pwhash_scryptsalsa208sha256_BYTES_MIN = (
lib.crypto_pwhash_scryptsalsa208sha256_bytes_min()
)
crypto_pwhash_scryptsalsa208sha256_BYTES_MAX = (
lib.crypto_pwhash_scryptsalsa208sha256_bytes_max()
)
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN = (
lib.crypto_pwhash_scryptsalsa208sha256_memlimit_min()
)
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX = (
lib.crypto_pwhash_scryptsalsa208sha256_memlimit_max()
)
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN = (
lib.crypto_pwhash_scryptsalsa208sha256_opslimit_min()
)
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX = (
lib.crypto_pwhash_scryptsalsa208sha256_opslimit_max()
)
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE = (
lib.crypto_pwhash_scryptsalsa208sha256_opslimit_interactive()
)
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE = (
lib.crypto_pwhash_scryptsalsa208sha256_memlimit_interactive()
)
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE = (
lib.crypto_pwhash_scryptsalsa208sha256_opslimit_sensitive()
)
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE = (
lib.crypto_pwhash_scryptsalsa208sha256_memlimit_sensitive()
)
crypto_pwhash_ALG_ARGON2I13: int = lib.crypto_pwhash_alg_argon2i13()
crypto_pwhash_ALG_ARGON2ID13: int = lib.crypto_pwhash_alg_argon2id13()
crypto_pwhash_ALG_DEFAULT: int = lib.crypto_pwhash_alg_default()
crypto_pwhash_SALTBYTES: int = lib.crypto_pwhash_saltbytes()
crypto_pwhash_STRBYTES: int = lib.crypto_pwhash_strbytes()
crypto_pwhash_PASSWD_MIN: int = lib.crypto_pwhash_passwd_min()
crypto_pwhash_PASSWD_MAX: int = lib.crypto_pwhash_passwd_max()
crypto_pwhash_BYTES_MIN: int = lib.crypto_pwhash_bytes_min()
crypto_pwhash_BYTES_MAX: int = lib.crypto_pwhash_bytes_max()
crypto_pwhash_argon2i_STRPREFIX: bytes = ffi.string(
ffi.cast("char *", lib.crypto_pwhash_argon2i_strprefix())
)[:]
crypto_pwhash_argon2i_MEMLIMIT_MIN: int = (
lib.crypto_pwhash_argon2i_memlimit_min()
)
crypto_pwhash_argon2i_MEMLIMIT_MAX: int = (
lib.crypto_pwhash_argon2i_memlimit_max()
)
crypto_pwhash_argon2i_OPSLIMIT_MIN: int = (
lib.crypto_pwhash_argon2i_opslimit_min()
)
crypto_pwhash_argon2i_OPSLIMIT_MAX: int = (
lib.crypto_pwhash_argon2i_opslimit_max()
)
crypto_pwhash_argon2i_OPSLIMIT_INTERACTIVE: int = (
lib.crypto_pwhash_argon2i_opslimit_interactive()
)
crypto_pwhash_argon2i_MEMLIMIT_INTERACTIVE: int = (
lib.crypto_pwhash_argon2i_memlimit_interactive()
)
crypto_pwhash_argon2i_OPSLIMIT_MODERATE: int = (
lib.crypto_pwhash_argon2i_opslimit_moderate()
)
crypto_pwhash_argon2i_MEMLIMIT_MODERATE: int = (
lib.crypto_pwhash_argon2i_memlimit_moderate()
)
crypto_pwhash_argon2i_OPSLIMIT_SENSITIVE: int = (
lib.crypto_pwhash_argon2i_opslimit_sensitive()
)
crypto_pwhash_argon2i_MEMLIMIT_SENSITIVE: int = (
lib.crypto_pwhash_argon2i_memlimit_sensitive()
)
crypto_pwhash_argon2id_STRPREFIX: bytes = ffi.string(
ffi.cast("char *", lib.crypto_pwhash_argon2id_strprefix())
)[:]
crypto_pwhash_argon2id_MEMLIMIT_MIN: int = (
lib.crypto_pwhash_argon2id_memlimit_min()
)
crypto_pwhash_argon2id_MEMLIMIT_MAX: int = (
lib.crypto_pwhash_argon2id_memlimit_max()
)
crypto_pwhash_argon2id_OPSLIMIT_MIN: int = (
lib.crypto_pwhash_argon2id_opslimit_min()
)
crypto_pwhash_argon2id_OPSLIMIT_MAX: int = (
lib.crypto_pwhash_argon2id_opslimit_max()
)
crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE: int = (
lib.crypto_pwhash_argon2id_opslimit_interactive()
)
crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE: int = (
lib.crypto_pwhash_argon2id_memlimit_interactive()
)
crypto_pwhash_argon2id_OPSLIMIT_MODERATE: int = (
lib.crypto_pwhash_argon2id_opslimit_moderate()
)
crypto_pwhash_argon2id_MEMLIMIT_MODERATE: int = (
lib.crypto_pwhash_argon2id_memlimit_moderate()
)
crypto_pwhash_argon2id_OPSLIMIT_SENSITIVE: int = (
lib.crypto_pwhash_argon2id_opslimit_sensitive()
)
crypto_pwhash_argon2id_MEMLIMIT_SENSITIVE: int = (
lib.crypto_pwhash_argon2id_memlimit_sensitive()
)
SCRYPT_OPSLIMIT_INTERACTIVE = (
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE
)
SCRYPT_MEMLIMIT_INTERACTIVE = (
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE
)
SCRYPT_OPSLIMIT_SENSITIVE = (
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE
)
SCRYPT_MEMLIMIT_SENSITIVE = (
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE
)
SCRYPT_SALTBYTES = crypto_pwhash_scryptsalsa208sha256_SALTBYTES
SCRYPT_STRBYTES = crypto_pwhash_scryptsalsa208sha256_STRBYTES
SCRYPT_PR_MAX = (1 << 30) - 1
LOG2_UINT64_MAX = 63
UINT64_MAX = (1 << 64) - 1
SCRYPT_MAX_MEM = 32 * (1024 * 1024)
def _check_memory_occupation(
n: int, r: int, p: int, maxmem: int = SCRYPT_MAX_MEM
) -> None:
ensure(r != 0, "Invalid block size", raising=exc.ValueError)
ensure(p != 0, "Invalid parallelization factor", raising=exc.ValueError)
ensure(
(n & (n - 1)) == 0,
"Cost factor must be a power of 2",
raising=exc.ValueError,
)
ensure(n > 1, "Cost factor must be at least 2", raising=exc.ValueError)
ensure(
p <= SCRYPT_PR_MAX / r,
"p*r is greater than {}".format(SCRYPT_PR_MAX),
raising=exc.ValueError,
)
ensure(n < (1 << (16 * r)), raising=exc.ValueError)
Blen = p * 128 * r
i = UINT64_MAX / 128
ensure(n + 2 <= i / r, raising=exc.ValueError)
Vlen = 32 * r * (n + 2) * 4
ensure(Blen <= UINT64_MAX - Vlen, raising=exc.ValueError)
ensure(Blen <= sys.maxsize - Vlen, raising=exc.ValueError)
ensure(
Blen + Vlen <= maxmem,
"Memory limit would be exceeded with the choosen n, r, p",
raising=exc.ValueError,
)
def nacl_bindings_pick_scrypt_params(
opslimit: int, memlimit: int
) -> Tuple[int, int, int]:
"""Python implementation of libsodium's pickparams"""
if opslimit < 32768:
opslimit = 32768
r = 8
if opslimit < (memlimit // 32):
p = 1
maxn = opslimit // (4 * r)
for n_log2 in range(1, 63): # pragma: no branch
if (2 ** n_log2) > (maxn // 2):
break
else:
maxn = memlimit // (r * 128)
for n_log2 in range(1, 63): # pragma: no branch
if (2 ** n_log2) > maxn // 2:
break
maxrp = (opslimit // 4) // (2 ** n_log2)
if maxrp > 0x3FFFFFFF: # pragma: no cover
maxrp = 0x3FFFFFFF
p = maxrp // r
return n_log2, r, p
def crypto_pwhash_scryptsalsa208sha256_ll(
passwd: bytes,
salt: bytes,
n: int,
r: int,
p: int,
dklen: int = 64,
maxmem: int = SCRYPT_MAX_MEM,
) -> bytes:
"""
Derive a cryptographic key using the ``passwd`` and ``salt``
given as input.
The work factor can be tuned by by picking different
values for the parameters
:param bytes passwd:
:param bytes salt:
:param bytes salt: *must* be *exactly* :py:const:`.SALTBYTES` long
:param int dklen:
:param int opslimit:
:param int n:
:param int r: block size,
:param int p: the parallelism factor
:param int maxmem: the maximum available memory available for scrypt's
operations
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_pwhash_scryptsalsa208sha256,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(isinstance(n, int), raising=TypeError)
ensure(isinstance(r, int), raising=TypeError)
ensure(isinstance(p, int), raising=TypeError)
ensure(isinstance(passwd, bytes), raising=TypeError)
ensure(isinstance(salt, bytes), raising=TypeError)
_check_memory_occupation(n, r, p, maxmem)
buf = ffi.new("uint8_t[]", dklen)
ret = lib.crypto_pwhash_scryptsalsa208sha256_ll(
passwd, len(passwd), salt, len(salt), n, r, p, buf, dklen
)
ensure(
ret == 0,
"Unexpected failure in key derivation",
raising=exc.RuntimeError,
)
return ffi.buffer(ffi.cast("char *", buf), dklen)[:]
def crypto_pwhash_scryptsalsa208sha256_str(
passwd: bytes,
opslimit: int = SCRYPT_OPSLIMIT_INTERACTIVE,
memlimit: int = SCRYPT_MEMLIMIT_INTERACTIVE,
) -> bytes:
"""
Derive a cryptographic key using the ``passwd`` and ``salt``
given as input, returning a string representation which includes
the salt and the tuning parameters.
The returned string can be directly stored as a password hash.
See :py:func:`.crypto_pwhash_scryptsalsa208sha256` for a short
discussion about ``opslimit`` and ``memlimit`` values.
:param bytes passwd:
:param int opslimit:
:param int memlimit:
:return: serialized key hash, including salt and tuning parameters
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_pwhash_scryptsalsa208sha256,
"Not available in minimal build",
raising=exc.UnavailableError,
)
buf = ffi.new("char[]", SCRYPT_STRBYTES)
ret = lib.crypto_pwhash_scryptsalsa208sha256_str(
buf, passwd, len(passwd), opslimit, memlimit
)
ensure(
ret == 0,
"Unexpected failure in password hashing",
raising=exc.RuntimeError,
)
return ffi.string(buf)
def crypto_pwhash_scryptsalsa208sha256_str_verify(
passwd_hash: bytes, passwd: bytes
) -> bool:
"""
Verifies the ``passwd`` against the ``passwd_hash`` that was generated.
Returns True or False depending on the success
:param passwd_hash: bytes
:param passwd: bytes
:rtype: boolean
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_pwhash_scryptsalsa208sha256,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
len(passwd_hash) == SCRYPT_STRBYTES - 1,
"Invalid password hash",
raising=exc.ValueError,
)
ret = lib.crypto_pwhash_scryptsalsa208sha256_str_verify(
passwd_hash, passwd, len(passwd)
)
ensure(ret == 0, "Wrong password", raising=exc.InvalidkeyError)
# all went well, therefore:
return True
def _check_argon2_limits_alg(opslimit: int, memlimit: int, alg: int) -> None:
if alg == crypto_pwhash_ALG_ARGON2I13:
if memlimit < crypto_pwhash_argon2i_MEMLIMIT_MIN:
raise exc.ValueError(
"memlimit must be at least {} bytes".format(
crypto_pwhash_argon2i_MEMLIMIT_MIN
)
)
elif memlimit > crypto_pwhash_argon2i_MEMLIMIT_MAX:
raise exc.ValueError(
"memlimit must be at most {} bytes".format(
crypto_pwhash_argon2i_MEMLIMIT_MAX
)
)
if opslimit < crypto_pwhash_argon2i_OPSLIMIT_MIN:
raise exc.ValueError(
"opslimit must be at least {}".format(
crypto_pwhash_argon2i_OPSLIMIT_MIN
)
)
elif opslimit > crypto_pwhash_argon2i_OPSLIMIT_MAX:
raise exc.ValueError(
"opslimit must be at most {}".format(
crypto_pwhash_argon2i_OPSLIMIT_MAX
)
)
elif alg == crypto_pwhash_ALG_ARGON2ID13:
if memlimit < crypto_pwhash_argon2id_MEMLIMIT_MIN:
raise exc.ValueError(
"memlimit must be at least {} bytes".format(
crypto_pwhash_argon2id_MEMLIMIT_MIN
)
)
elif memlimit > crypto_pwhash_argon2id_MEMLIMIT_MAX:
raise exc.ValueError(
"memlimit must be at most {} bytes".format(
crypto_pwhash_argon2id_MEMLIMIT_MAX
)
)
if opslimit < crypto_pwhash_argon2id_OPSLIMIT_MIN:
raise exc.ValueError(
"opslimit must be at least {}".format(
crypto_pwhash_argon2id_OPSLIMIT_MIN
)
)
elif opslimit > crypto_pwhash_argon2id_OPSLIMIT_MAX:
raise exc.ValueError(
"opslimit must be at most {}".format(
crypto_pwhash_argon2id_OPSLIMIT_MAX
)
)
else:
raise exc.TypeError("Unsupported algorithm")
def crypto_pwhash_alg(
outlen: int,
passwd: bytes,
salt: bytes,
opslimit: int,
memlimit: int,
alg: int,
) -> bytes:
"""
Derive a raw cryptographic key using the ``passwd`` and the ``salt``
given as input to the ``alg`` algorithm.
:param outlen: the length of the derived key
:type outlen: int
:param passwd: The input password
:type passwd: bytes
:param salt:
:type salt: bytes
:param opslimit: computational cost
:type opslimit: int
:param memlimit: memory cost
:type memlimit: int
:param alg: algorithm identifier
:type alg: int
:return: derived key
:rtype: bytes
"""
ensure(isinstance(outlen, int), raising=exc.TypeError)
ensure(isinstance(opslimit, int), raising=exc.TypeError)
ensure(isinstance(memlimit, int), raising=exc.TypeError)
ensure(isinstance(alg, int), raising=exc.TypeError)
ensure(isinstance(passwd, bytes), raising=exc.TypeError)
if len(salt) != crypto_pwhash_SALTBYTES:
raise exc.ValueError(
"salt must be exactly {} bytes long".format(
crypto_pwhash_SALTBYTES
)
)
if outlen < crypto_pwhash_BYTES_MIN:
raise exc.ValueError(
"derived key must be at least {} bytes long".format(
crypto_pwhash_BYTES_MIN
)
)
elif outlen > crypto_pwhash_BYTES_MAX:
raise exc.ValueError(
"derived key must be at most {} bytes long".format(
crypto_pwhash_BYTES_MAX
)
)
_check_argon2_limits_alg(opslimit, memlimit, alg)
outbuf = ffi.new("unsigned char[]", outlen)
ret = lib.crypto_pwhash(
outbuf, outlen, passwd, len(passwd), salt, opslimit, memlimit, alg
)
ensure(
ret == 0,
"Unexpected failure in key derivation",
raising=exc.RuntimeError,
)
return ffi.buffer(outbuf, outlen)[:]
def crypto_pwhash_str_alg(
passwd: bytes,
opslimit: int,
memlimit: int,
alg: int,
) -> bytes:
"""
Derive a cryptographic key using the ``passwd`` given as input
and a random salt, returning a string representation which
includes the salt, the tuning parameters and the used algorithm.
:param passwd: The input password
:type passwd: bytes
:param opslimit: computational cost
:type opslimit: int
:param memlimit: memory cost
:type memlimit: int
:param alg: The algorithm to use
:type alg: int
:return: serialized derived key and parameters
:rtype: bytes
"""
ensure(isinstance(opslimit, int), raising=TypeError)
ensure(isinstance(memlimit, int), raising=TypeError)
ensure(isinstance(passwd, bytes), raising=TypeError)
_check_argon2_limits_alg(opslimit, memlimit, alg)
outbuf = ffi.new("char[]", 128)
ret = lib.crypto_pwhash_str_alg(
outbuf, passwd, len(passwd), opslimit, memlimit, alg
)
ensure(
ret == 0,
"Unexpected failure in key derivation",
raising=exc.RuntimeError,
)
return ffi.string(outbuf)
def crypto_pwhash_str_verify(passwd_hash: bytes, passwd: bytes) -> bool:
"""
Verifies the ``passwd`` against a given password hash.
Returns True on success, raises InvalidkeyError on failure
:param passwd_hash: saved password hash
:type passwd_hash: bytes
:param passwd: password to be checked
:type passwd: bytes
:return: success
:rtype: boolean
"""
ensure(isinstance(passwd_hash, bytes), raising=TypeError)
ensure(isinstance(passwd, bytes), raising=TypeError)
ensure(
len(passwd_hash) <= 127,
"Hash must be at most 127 bytes long",
raising=exc.ValueError,
)
ret = lib.crypto_pwhash_str_verify(passwd_hash, passwd, len(passwd))
ensure(ret == 0, "Wrong password", raising=exc.InvalidkeyError)
# all went well, therefore:
return True
crypto_pwhash_argon2i_str_verify = crypto_pwhash_str_verify

View File

@ -0,0 +1,240 @@
# Copyright 2013-2018 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 nacl import exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
has_crypto_scalarmult_ed25519 = bool(lib.PYNACL_HAS_CRYPTO_SCALARMULT_ED25519)
crypto_scalarmult_BYTES: int = lib.crypto_scalarmult_bytes()
crypto_scalarmult_SCALARBYTES: int = lib.crypto_scalarmult_scalarbytes()
crypto_scalarmult_ed25519_BYTES = 0
crypto_scalarmult_ed25519_SCALARBYTES = 0
if has_crypto_scalarmult_ed25519:
crypto_scalarmult_ed25519_BYTES = lib.crypto_scalarmult_ed25519_bytes()
crypto_scalarmult_ed25519_SCALARBYTES = (
lib.crypto_scalarmult_ed25519_scalarbytes()
)
def crypto_scalarmult_base(n: bytes) -> bytes:
"""
Computes and returns the scalar product of a standard group element and an
integer ``n``.
:param n: bytes
:rtype: bytes
"""
q = ffi.new("unsigned char[]", crypto_scalarmult_BYTES)
rc = lib.crypto_scalarmult_base(q, n)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(q, crypto_scalarmult_SCALARBYTES)[:]
def crypto_scalarmult(n: bytes, p: bytes) -> bytes:
"""
Computes and returns the scalar product of the given group element and an
integer ``n``.
:param p: bytes
:param n: bytes
:rtype: bytes
"""
q = ffi.new("unsigned char[]", crypto_scalarmult_BYTES)
rc = lib.crypto_scalarmult(q, n, p)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(q, crypto_scalarmult_SCALARBYTES)[:]
def crypto_scalarmult_ed25519_base(n: bytes) -> bytes:
"""
Computes and returns the scalar product of a standard group element and an
integer ``n`` on the edwards25519 curve.
:param n: a :py:data:`.crypto_scalarmult_ed25519_SCALARBYTES` long bytes
sequence representing a scalar
:type n: bytes
:return: a point on the edwards25519 curve, represented as a
:py:data:`.crypto_scalarmult_ed25519_BYTES` long bytes sequence
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_scalarmult_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(n, bytes)
and len(n) == crypto_scalarmult_ed25519_SCALARBYTES,
"Input must be a {} long bytes sequence".format(
"crypto_scalarmult_ed25519_SCALARBYTES"
),
raising=exc.TypeError,
)
q = ffi.new("unsigned char[]", crypto_scalarmult_ed25519_BYTES)
rc = lib.crypto_scalarmult_ed25519_base(q, n)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(q, crypto_scalarmult_ed25519_BYTES)[:]
def crypto_scalarmult_ed25519_base_noclamp(n: bytes) -> bytes:
"""
Computes and returns the scalar product of a standard group element and an
integer ``n`` on the edwards25519 curve. The integer ``n`` is not clamped.
:param n: a :py:data:`.crypto_scalarmult_ed25519_SCALARBYTES` long bytes
sequence representing a scalar
:type n: bytes
:return: a point on the edwards25519 curve, represented as a
:py:data:`.crypto_scalarmult_ed25519_BYTES` long bytes sequence
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_scalarmult_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(n, bytes)
and len(n) == crypto_scalarmult_ed25519_SCALARBYTES,
"Input must be a {} long bytes sequence".format(
"crypto_scalarmult_ed25519_SCALARBYTES"
),
raising=exc.TypeError,
)
q = ffi.new("unsigned char[]", crypto_scalarmult_ed25519_BYTES)
rc = lib.crypto_scalarmult_ed25519_base_noclamp(q, n)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(q, crypto_scalarmult_ed25519_BYTES)[:]
def crypto_scalarmult_ed25519(n: bytes, p: bytes) -> bytes:
"""
Computes and returns the scalar product of a *clamped* integer ``n``
and the given group element on the edwards25519 curve.
The scalar is clamped, as done in the public key generation case,
by setting to zero the bits in position [0, 1, 2, 255] and setting
to one the bit in position 254.
:param n: a :py:data:`.crypto_scalarmult_ed25519_SCALARBYTES` long bytes
sequence representing a scalar
:type n: bytes
:param p: a :py:data:`.crypto_scalarmult_ed25519_BYTES` long bytes sequence
representing a point on the edwards25519 curve
:type p: bytes
:return: a point on the edwards25519 curve, represented as a
:py:data:`.crypto_scalarmult_ed25519_BYTES` long bytes sequence
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_scalarmult_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(n, bytes)
and len(n) == crypto_scalarmult_ed25519_SCALARBYTES,
"Input must be a {} long bytes sequence".format(
"crypto_scalarmult_ed25519_SCALARBYTES"
),
raising=exc.TypeError,
)
ensure(
isinstance(p, bytes) and len(p) == crypto_scalarmult_ed25519_BYTES,
"Input must be a {} long bytes sequence".format(
"crypto_scalarmult_ed25519_BYTES"
),
raising=exc.TypeError,
)
q = ffi.new("unsigned char[]", crypto_scalarmult_ed25519_BYTES)
rc = lib.crypto_scalarmult_ed25519(q, n, p)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(q, crypto_scalarmult_ed25519_BYTES)[:]
def crypto_scalarmult_ed25519_noclamp(n: bytes, p: bytes) -> bytes:
"""
Computes and returns the scalar product of an integer ``n``
and the given group element on the edwards25519 curve. The integer
``n`` is not clamped.
:param n: a :py:data:`.crypto_scalarmult_ed25519_SCALARBYTES` long bytes
sequence representing a scalar
:type n: bytes
:param p: a :py:data:`.crypto_scalarmult_ed25519_BYTES` long bytes sequence
representing a point on the edwards25519 curve
:type p: bytes
:return: a point on the edwards25519 curve, represented as a
:py:data:`.crypto_scalarmult_ed25519_BYTES` long bytes sequence
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_scalarmult_ed25519,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
isinstance(n, bytes)
and len(n) == crypto_scalarmult_ed25519_SCALARBYTES,
"Input must be a {} long bytes sequence".format(
"crypto_scalarmult_ed25519_SCALARBYTES"
),
raising=exc.TypeError,
)
ensure(
isinstance(p, bytes) and len(p) == crypto_scalarmult_ed25519_BYTES,
"Input must be a {} long bytes sequence".format(
"crypto_scalarmult_ed25519_BYTES"
),
raising=exc.TypeError,
)
q = ffi.new("unsigned char[]", crypto_scalarmult_ed25519_BYTES)
rc = lib.crypto_scalarmult_ed25519_noclamp(q, n, p)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(q, crypto_scalarmult_ed25519_BYTES)[:]

View File

@ -0,0 +1,86 @@
# 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 nacl import exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
crypto_secretbox_KEYBYTES: int = lib.crypto_secretbox_keybytes()
crypto_secretbox_NONCEBYTES: int = lib.crypto_secretbox_noncebytes()
crypto_secretbox_ZEROBYTES: int = lib.crypto_secretbox_zerobytes()
crypto_secretbox_BOXZEROBYTES: int = lib.crypto_secretbox_boxzerobytes()
crypto_secretbox_MACBYTES: int = lib.crypto_secretbox_macbytes()
crypto_secretbox_MESSAGEBYTES_MAX: int = (
lib.crypto_secretbox_messagebytes_max()
)
def crypto_secretbox(message: bytes, nonce: bytes, key: bytes) -> bytes:
"""
Encrypts and returns the message ``message`` with the secret ``key`` and
the nonce ``nonce``.
:param message: bytes
:param nonce: bytes
:param key: bytes
:rtype: bytes
"""
if len(key) != crypto_secretbox_KEYBYTES:
raise exc.ValueError("Invalid key")
if len(nonce) != crypto_secretbox_NONCEBYTES:
raise exc.ValueError("Invalid nonce")
padded = b"\x00" * crypto_secretbox_ZEROBYTES + message
ciphertext = ffi.new("unsigned char[]", len(padded))
res = lib.crypto_secretbox(ciphertext, padded, len(padded), nonce, key)
ensure(res == 0, "Encryption failed", raising=exc.CryptoError)
ciphertext = ffi.buffer(ciphertext, len(padded))
return ciphertext[crypto_secretbox_BOXZEROBYTES:]
def crypto_secretbox_open(
ciphertext: bytes, nonce: bytes, key: bytes
) -> bytes:
"""
Decrypt and returns the encrypted message ``ciphertext`` with the secret
``key`` and the nonce ``nonce``.
:param ciphertext: bytes
:param nonce: bytes
:param key: bytes
:rtype: bytes
"""
if len(key) != crypto_secretbox_KEYBYTES:
raise exc.ValueError("Invalid key")
if len(nonce) != crypto_secretbox_NONCEBYTES:
raise exc.ValueError("Invalid nonce")
padded = b"\x00" * crypto_secretbox_BOXZEROBYTES + ciphertext
plaintext = ffi.new("unsigned char[]", len(padded))
res = lib.crypto_secretbox_open(plaintext, padded, len(padded), nonce, key)
ensure(
res == 0,
"Decryption failed. Ciphertext failed verification",
raising=exc.CryptoError,
)
plaintext = ffi.buffer(plaintext, len(padded))
return plaintext[crypto_secretbox_ZEROBYTES:]

View File

@ -0,0 +1,357 @@
# Copyright 2013-2018 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 ByteString, Optional, Tuple, cast
from nacl import exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
crypto_secretstream_xchacha20poly1305_ABYTES: int = (
lib.crypto_secretstream_xchacha20poly1305_abytes()
)
crypto_secretstream_xchacha20poly1305_HEADERBYTES: int = (
lib.crypto_secretstream_xchacha20poly1305_headerbytes()
)
crypto_secretstream_xchacha20poly1305_KEYBYTES: int = (
lib.crypto_secretstream_xchacha20poly1305_keybytes()
)
crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX: int = (
lib.crypto_secretstream_xchacha20poly1305_messagebytes_max()
)
crypto_secretstream_xchacha20poly1305_STATEBYTES: int = (
lib.crypto_secretstream_xchacha20poly1305_statebytes()
)
crypto_secretstream_xchacha20poly1305_TAG_MESSAGE: int = (
lib.crypto_secretstream_xchacha20poly1305_tag_message()
)
crypto_secretstream_xchacha20poly1305_TAG_PUSH: int = (
lib.crypto_secretstream_xchacha20poly1305_tag_push()
)
crypto_secretstream_xchacha20poly1305_TAG_REKEY: int = (
lib.crypto_secretstream_xchacha20poly1305_tag_rekey()
)
crypto_secretstream_xchacha20poly1305_TAG_FINAL: int = (
lib.crypto_secretstream_xchacha20poly1305_tag_final()
)
def crypto_secretstream_xchacha20poly1305_keygen() -> bytes:
"""
Generate a key for use with
:func:`.crypto_secretstream_xchacha20poly1305_init_push`.
"""
keybuf = ffi.new(
"unsigned char[]",
crypto_secretstream_xchacha20poly1305_KEYBYTES,
)
lib.crypto_secretstream_xchacha20poly1305_keygen(keybuf)
return ffi.buffer(keybuf)[:]
class crypto_secretstream_xchacha20poly1305_state:
"""
An object wrapping the crypto_secretstream_xchacha20poly1305 state.
"""
__slots__ = ["statebuf", "rawbuf", "tagbuf"]
def __init__(self) -> None:
"""Initialize a clean state object."""
self.statebuf: ByteString = ffi.new(
"unsigned char[]",
crypto_secretstream_xchacha20poly1305_STATEBYTES,
)
self.rawbuf: Optional[ByteString] = None
self.tagbuf: Optional[ByteString] = None
def crypto_secretstream_xchacha20poly1305_init_push(
state: crypto_secretstream_xchacha20poly1305_state, key: bytes
) -> bytes:
"""
Initialize a crypto_secretstream_xchacha20poly1305 encryption buffer.
:param state: a secretstream state object
:type state: crypto_secretstream_xchacha20poly1305_state
:param key: must be
:data:`.crypto_secretstream_xchacha20poly1305_KEYBYTES` long
:type key: bytes
:return: header
:rtype: bytes
"""
ensure(
isinstance(state, crypto_secretstream_xchacha20poly1305_state),
"State must be a crypto_secretstream_xchacha20poly1305_state object",
raising=exc.TypeError,
)
ensure(
isinstance(key, bytes),
"Key must be a bytes sequence",
raising=exc.TypeError,
)
ensure(
len(key) == crypto_secretstream_xchacha20poly1305_KEYBYTES,
"Invalid key length",
raising=exc.ValueError,
)
headerbuf = ffi.new(
"unsigned char []",
crypto_secretstream_xchacha20poly1305_HEADERBYTES,
)
rc = lib.crypto_secretstream_xchacha20poly1305_init_push(
state.statebuf, headerbuf, key
)
ensure(rc == 0, "Unexpected failure", raising=exc.RuntimeError)
return ffi.buffer(headerbuf)[:]
def crypto_secretstream_xchacha20poly1305_push(
state: crypto_secretstream_xchacha20poly1305_state,
m: bytes,
ad: Optional[bytes] = None,
tag: int = crypto_secretstream_xchacha20poly1305_TAG_MESSAGE,
) -> bytes:
"""
Add an encrypted message to the secret stream.
:param state: a secretstream state object
:type state: crypto_secretstream_xchacha20poly1305_state
:param m: the message to encrypt, the maximum length of an individual
message is
:data:`.crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX`.
:type m: bytes
:param ad: additional data to include in the authentication tag
:type ad: bytes or None
:param tag: the message tag, usually
:data:`.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE` or
:data:`.crypto_secretstream_xchacha20poly1305_TAG_FINAL`.
:type tag: int
:return: ciphertext
:rtype: bytes
"""
ensure(
isinstance(state, crypto_secretstream_xchacha20poly1305_state),
"State must be a crypto_secretstream_xchacha20poly1305_state object",
raising=exc.TypeError,
)
ensure(isinstance(m, bytes), "Message is not bytes", raising=exc.TypeError)
ensure(
len(m) <= crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX,
"Message is too long",
raising=exc.ValueError,
)
ensure(
ad is None or isinstance(ad, bytes),
"Additional data must be bytes or None",
raising=exc.TypeError,
)
clen = len(m) + crypto_secretstream_xchacha20poly1305_ABYTES
if state.rawbuf is None or len(state.rawbuf) < clen:
state.rawbuf = ffi.new("unsigned char[]", clen)
if ad is None:
ad = ffi.NULL
adlen = 0
else:
adlen = len(ad)
rc = lib.crypto_secretstream_xchacha20poly1305_push(
state.statebuf,
state.rawbuf,
ffi.NULL,
m,
len(m),
ad,
adlen,
tag,
)
ensure(rc == 0, "Unexpected failure", raising=exc.RuntimeError)
return ffi.buffer(state.rawbuf, clen)[:]
def crypto_secretstream_xchacha20poly1305_init_pull(
state: crypto_secretstream_xchacha20poly1305_state,
header: bytes,
key: bytes,
) -> None:
"""
Initialize a crypto_secretstream_xchacha20poly1305 decryption buffer.
:param state: a secretstream state object
:type state: crypto_secretstream_xchacha20poly1305_state
:param header: must be
:data:`.crypto_secretstream_xchacha20poly1305_HEADERBYTES` long
:type header: bytes
:param key: must be
:data:`.crypto_secretstream_xchacha20poly1305_KEYBYTES` long
:type key: bytes
"""
ensure(
isinstance(state, crypto_secretstream_xchacha20poly1305_state),
"State must be a crypto_secretstream_xchacha20poly1305_state object",
raising=exc.TypeError,
)
ensure(
isinstance(header, bytes),
"Header must be a bytes sequence",
raising=exc.TypeError,
)
ensure(
len(header) == crypto_secretstream_xchacha20poly1305_HEADERBYTES,
"Invalid header length",
raising=exc.ValueError,
)
ensure(
isinstance(key, bytes),
"Key must be a bytes sequence",
raising=exc.TypeError,
)
ensure(
len(key) == crypto_secretstream_xchacha20poly1305_KEYBYTES,
"Invalid key length",
raising=exc.ValueError,
)
if state.tagbuf is None:
state.tagbuf = ffi.new("unsigned char *")
rc = lib.crypto_secretstream_xchacha20poly1305_init_pull(
state.statebuf, header, key
)
ensure(rc == 0, "Unexpected failure", raising=exc.RuntimeError)
def crypto_secretstream_xchacha20poly1305_pull(
state: crypto_secretstream_xchacha20poly1305_state,
c: bytes,
ad: Optional[bytes] = None,
) -> Tuple[bytes, int]:
"""
Read a decrypted message from the secret stream.
:param state: a secretstream state object
:type state: crypto_secretstream_xchacha20poly1305_state
:param c: the ciphertext to decrypt, the maximum length of an individual
ciphertext is
:data:`.crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX` +
:data:`.crypto_secretstream_xchacha20poly1305_ABYTES`.
:type c: bytes
:param ad: additional data to include in the authentication tag
:type ad: bytes or None
:return: (message, tag)
:rtype: (bytes, int)
"""
ensure(
isinstance(state, crypto_secretstream_xchacha20poly1305_state),
"State must be a crypto_secretstream_xchacha20poly1305_state object",
raising=exc.TypeError,
)
ensure(
state.tagbuf is not None,
(
"State must be initialized using "
"crypto_secretstream_xchacha20poly1305_init_pull"
),
raising=exc.ValueError,
)
ensure(
isinstance(c, bytes),
"Ciphertext is not bytes",
raising=exc.TypeError,
)
ensure(
len(c) >= crypto_secretstream_xchacha20poly1305_ABYTES,
"Ciphertext is too short",
raising=exc.ValueError,
)
ensure(
len(c)
<= (
crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX
+ crypto_secretstream_xchacha20poly1305_ABYTES
),
"Ciphertext is too long",
raising=exc.ValueError,
)
ensure(
ad is None or isinstance(ad, bytes),
"Additional data must be bytes or None",
raising=exc.TypeError,
)
mlen = len(c) - crypto_secretstream_xchacha20poly1305_ABYTES
if state.rawbuf is None or len(state.rawbuf) < mlen:
state.rawbuf = ffi.new("unsigned char[]", mlen)
if ad is None:
ad = ffi.NULL
adlen = 0
else:
adlen = len(ad)
rc = lib.crypto_secretstream_xchacha20poly1305_pull(
state.statebuf,
state.rawbuf,
ffi.NULL,
state.tagbuf,
c,
len(c),
ad,
adlen,
)
ensure(rc == 0, "Unexpected failure", raising=exc.RuntimeError)
# Cast safety: we `ensure` above that `state.tagbuf is not None`.
return (
ffi.buffer(state.rawbuf, mlen)[:],
int(cast(bytes, state.tagbuf)[0]),
)
def crypto_secretstream_xchacha20poly1305_rekey(
state: crypto_secretstream_xchacha20poly1305_state,
) -> None:
"""
Explicitly change the encryption key in the stream.
Normally the stream is re-keyed as needed or an explicit ``tag`` of
:data:`.crypto_secretstream_xchacha20poly1305_TAG_REKEY` is added to a
message to ensure forward secrecy, but this method can be used instead
if the re-keying is controlled without adding the tag.
:param state: a secretstream state object
:type state: crypto_secretstream_xchacha20poly1305_state
"""
ensure(
isinstance(state, crypto_secretstream_xchacha20poly1305_state),
"State must be a crypto_secretstream_xchacha20poly1305_state object",
raising=exc.TypeError,
)
lib.crypto_secretstream_xchacha20poly1305_rekey(state.statebuf)

View File

@ -0,0 +1,81 @@
# Copyright 2016 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 nacl.exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
has_crypto_shorthash_siphashx24 = bool(
lib.PYNACL_HAS_CRYPTO_SHORTHASH_SIPHASHX24
)
BYTES: int = lib.crypto_shorthash_siphash24_bytes()
KEYBYTES: int = lib.crypto_shorthash_siphash24_keybytes()
XBYTES = 0
XKEYBYTES = 0
if has_crypto_shorthash_siphashx24:
XBYTES = lib.crypto_shorthash_siphashx24_bytes()
XKEYBYTES = lib.crypto_shorthash_siphashx24_keybytes()
def crypto_shorthash_siphash24(data: bytes, key: bytes) -> bytes:
"""Compute a fast, cryptographic quality, keyed hash of the input data
:param data:
:type data: bytes
:param key: len(key) must be equal to
:py:data:`.KEYBYTES` (16)
:type key: bytes
"""
if len(key) != KEYBYTES:
raise exc.ValueError(
"Key length must be exactly {} bytes".format(KEYBYTES)
)
digest = ffi.new("unsigned char[]", BYTES)
rc = lib.crypto_shorthash_siphash24(digest, data, len(data), key)
ensure(rc == 0, raising=exc.RuntimeError)
return ffi.buffer(digest, BYTES)[:]
def crypto_shorthash_siphashx24(data: bytes, key: bytes) -> bytes:
"""Compute a fast, cryptographic quality, keyed hash of the input data
:param data:
:type data: bytes
:param key: len(key) must be equal to
:py:data:`.XKEYBYTES` (16)
:type key: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
"""
ensure(
has_crypto_shorthash_siphashx24,
"Not available in minimal build",
raising=exc.UnavailableError,
)
if len(key) != XKEYBYTES:
raise exc.ValueError(
"Key length must be exactly {} bytes".format(XKEYBYTES)
)
digest = ffi.new("unsigned char[]", XBYTES)
rc = lib.crypto_shorthash_siphashx24(digest, data, len(data), key)
ensure(rc == 0, raising=exc.RuntimeError)
return ffi.buffer(digest, XBYTES)[:]

View File

@ -0,0 +1,327 @@
# 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 Tuple
from nacl import exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
crypto_sign_BYTES: int = lib.crypto_sign_bytes()
# crypto_sign_SEEDBYTES = lib.crypto_sign_seedbytes()
crypto_sign_SEEDBYTES: int = lib.crypto_sign_secretkeybytes() // 2
crypto_sign_PUBLICKEYBYTES: int = lib.crypto_sign_publickeybytes()
crypto_sign_SECRETKEYBYTES: int = lib.crypto_sign_secretkeybytes()
crypto_sign_curve25519_BYTES: int = lib.crypto_box_secretkeybytes()
crypto_sign_ed25519ph_STATEBYTES: int = lib.crypto_sign_ed25519ph_statebytes()
def crypto_sign_keypair() -> Tuple[bytes, bytes]:
"""
Returns a randomly generated public key and secret key.
:rtype: (bytes(public_key), bytes(secret_key))
"""
pk = ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES)
sk = ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)
rc = lib.crypto_sign_keypair(pk, sk)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return (
ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:],
ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
)
def crypto_sign_seed_keypair(seed: bytes) -> Tuple[bytes, bytes]:
"""
Computes and returns the public key and secret key using the seed ``seed``.
:param seed: bytes
:rtype: (bytes(public_key), bytes(secret_key))
"""
if len(seed) != crypto_sign_SEEDBYTES:
raise exc.ValueError("Invalid seed")
pk = ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES)
sk = ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)
rc = lib.crypto_sign_seed_keypair(pk, sk, seed)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return (
ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:],
ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
)
def crypto_sign(message: bytes, sk: bytes) -> bytes:
"""
Signs the message ``message`` using the secret key ``sk`` and returns the
signed message.
:param message: bytes
:param sk: bytes
:rtype: bytes
"""
signed = ffi.new("unsigned char[]", len(message) + crypto_sign_BYTES)
signed_len = ffi.new("unsigned long long *")
rc = lib.crypto_sign(signed, signed_len, message, len(message), sk)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(signed, signed_len[0])[:]
def crypto_sign_open(signed: bytes, pk: bytes) -> bytes:
"""
Verifies the signature of the signed message ``signed`` using the public
key ``pk`` and returns the unsigned message.
:param signed: bytes
:param pk: bytes
:rtype: bytes
"""
message = ffi.new("unsigned char[]", len(signed))
message_len = ffi.new("unsigned long long *")
if (
lib.crypto_sign_open(message, message_len, signed, len(signed), pk)
!= 0
):
raise exc.BadSignatureError("Signature was forged or corrupt")
return ffi.buffer(message, message_len[0])[:]
def crypto_sign_ed25519_pk_to_curve25519(public_key_bytes: bytes) -> bytes:
"""
Converts a public Ed25519 key (encoded as bytes ``public_key_bytes``) to
a public Curve25519 key as bytes.
Raises a ValueError if ``public_key_bytes`` is not of length
``crypto_sign_PUBLICKEYBYTES``
:param public_key_bytes: bytes
:rtype: bytes
"""
if len(public_key_bytes) != crypto_sign_PUBLICKEYBYTES:
raise exc.ValueError("Invalid curve public key")
curve_public_key_len = crypto_sign_curve25519_BYTES
curve_public_key = ffi.new("unsigned char[]", curve_public_key_len)
rc = lib.crypto_sign_ed25519_pk_to_curve25519(
curve_public_key, public_key_bytes
)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(curve_public_key, curve_public_key_len)[:]
def crypto_sign_ed25519_sk_to_curve25519(secret_key_bytes: bytes) -> bytes:
"""
Converts a secret Ed25519 key (encoded as bytes ``secret_key_bytes``) to
a secret Curve25519 key as bytes.
Raises a ValueError if ``secret_key_bytes``is not of length
``crypto_sign_SECRETKEYBYTES``
:param secret_key_bytes: bytes
:rtype: bytes
"""
if len(secret_key_bytes) != crypto_sign_SECRETKEYBYTES:
raise exc.ValueError("Invalid curve secret key")
curve_secret_key_len = crypto_sign_curve25519_BYTES
curve_secret_key = ffi.new("unsigned char[]", curve_secret_key_len)
rc = lib.crypto_sign_ed25519_sk_to_curve25519(
curve_secret_key, secret_key_bytes
)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(curve_secret_key, curve_secret_key_len)[:]
def crypto_sign_ed25519_sk_to_pk(secret_key_bytes: bytes) -> bytes:
"""
Extract the public Ed25519 key from a secret Ed25519 key (encoded
as bytes ``secret_key_bytes``).
Raises a ValueError if ``secret_key_bytes``is not of length
``crypto_sign_SECRETKEYBYTES``
:param secret_key_bytes: bytes
:rtype: bytes
"""
if len(secret_key_bytes) != crypto_sign_SECRETKEYBYTES:
raise exc.ValueError("Invalid secret key")
return secret_key_bytes[crypto_sign_SEEDBYTES:]
def crypto_sign_ed25519_sk_to_seed(secret_key_bytes: bytes) -> bytes:
"""
Extract the seed from a secret Ed25519 key (encoded
as bytes ``secret_key_bytes``).
Raises a ValueError if ``secret_key_bytes``is not of length
``crypto_sign_SECRETKEYBYTES``
:param secret_key_bytes: bytes
:rtype: bytes
"""
if len(secret_key_bytes) != crypto_sign_SECRETKEYBYTES:
raise exc.ValueError("Invalid secret key")
return secret_key_bytes[:crypto_sign_SEEDBYTES]
class crypto_sign_ed25519ph_state:
"""
State object wrapping the sha-512 state used in ed25519ph computation
"""
__slots__ = ["state"]
def __init__(self) -> None:
self.state: bytes = ffi.new(
"unsigned char[]", crypto_sign_ed25519ph_STATEBYTES
)
rc = lib.crypto_sign_ed25519ph_init(self.state)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
def crypto_sign_ed25519ph_update(
edph: crypto_sign_ed25519ph_state, pmsg: bytes
) -> None:
"""
Update the hash state wrapped in edph
:param edph: the ed25519ph state being updated
:type edph: crypto_sign_ed25519ph_state
:param pmsg: the partial message
:type pmsg: bytes
:rtype: None
"""
ensure(
isinstance(edph, crypto_sign_ed25519ph_state),
"edph parameter must be a ed25519ph_state object",
raising=exc.TypeError,
)
ensure(
isinstance(pmsg, bytes),
"pmsg parameter must be a bytes object",
raising=exc.TypeError,
)
rc = lib.crypto_sign_ed25519ph_update(edph.state, pmsg, len(pmsg))
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
def crypto_sign_ed25519ph_final_create(
edph: crypto_sign_ed25519ph_state, sk: bytes
) -> bytes:
"""
Create a signature for the data hashed in edph
using the secret key sk
:param edph: the ed25519ph state for the data
being signed
:type edph: crypto_sign_ed25519ph_state
:param sk: the ed25519 secret part of the signing key
:type sk: bytes
:return: ed25519ph signature
:rtype: bytes
"""
ensure(
isinstance(edph, crypto_sign_ed25519ph_state),
"edph parameter must be a ed25519ph_state object",
raising=exc.TypeError,
)
ensure(
isinstance(sk, bytes),
"secret key parameter must be a bytes object",
raising=exc.TypeError,
)
ensure(
len(sk) == crypto_sign_SECRETKEYBYTES,
("secret key must be {} bytes long").format(
crypto_sign_SECRETKEYBYTES
),
raising=exc.TypeError,
)
signature = ffi.new("unsigned char[]", crypto_sign_BYTES)
rc = lib.crypto_sign_ed25519ph_final_create(
edph.state, signature, ffi.NULL, sk
)
ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
return ffi.buffer(signature, crypto_sign_BYTES)[:]
def crypto_sign_ed25519ph_final_verify(
edph: crypto_sign_ed25519ph_state, signature: bytes, pk: bytes
) -> bool:
"""
Verify a prehashed signature using the public key pk
:param edph: the ed25519ph state for the data
being verified
:type edph: crypto_sign_ed25519ph_state
:param signature: the signature being verified
:type signature: bytes
:param pk: the ed25519 public part of the signing key
:type pk: bytes
:return: True if the signature is valid
:rtype: boolean
:raises exc.BadSignatureError: if the signature is not valid
"""
ensure(
isinstance(edph, crypto_sign_ed25519ph_state),
"edph parameter must be a ed25519ph_state object",
raising=exc.TypeError,
)
ensure(
isinstance(signature, bytes),
"signature parameter must be a bytes object",
raising=exc.TypeError,
)
ensure(
len(signature) == crypto_sign_BYTES,
("signature must be {} bytes long").format(crypto_sign_BYTES),
raising=exc.TypeError,
)
ensure(
isinstance(pk, bytes),
"public key parameter must be a bytes object",
raising=exc.TypeError,
)
ensure(
len(pk) == crypto_sign_PUBLICKEYBYTES,
("public key must be {} bytes long").format(
crypto_sign_PUBLICKEYBYTES
),
raising=exc.TypeError,
)
rc = lib.crypto_sign_ed25519ph_final_verify(edph.state, signature, pk)
if rc != 0:
raise exc.BadSignatureError("Signature was forged or corrupt")
return True

View File

@ -0,0 +1,51 @@
# 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 nacl import exceptions as exc
from nacl._sodium import ffi, lib
randombytes_SEEDBYTES: int = lib.randombytes_seedbytes()
def randombytes(size: int) -> bytes:
"""
Returns ``size`` number of random bytes from a cryptographically secure
random source.
:param size: int
:rtype: bytes
"""
buf = ffi.new("unsigned char[]", size)
lib.randombytes(buf, size)
return ffi.buffer(buf, size)[:]
def randombytes_buf_deterministic(size: int, seed: bytes) -> bytes:
"""
Returns ``size`` number of deterministically generated pseudorandom bytes
from a seed
:param size: int
:param seed: bytes
:rtype: bytes
"""
if len(seed) != randombytes_SEEDBYTES:
raise exc.TypeError(
"Deterministic random bytes must be generated from 32 bytes"
)
buf = ffi.new("unsigned char[]", size)
lib.randombytes_buf_deterministic(buf, size, seed)
return ffi.buffer(buf, size)[:]

View File

@ -0,0 +1,33 @@
# 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 nacl import exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
def _sodium_init() -> None:
ensure(
lib.sodium_init() != -1,
"Could not initialize sodium",
raising=exc.RuntimeError,
)
def sodium_init() -> None:
"""
Initializes sodium, picking the best implementations available for this
machine.
"""
ffi.init_once(_sodium_init, "libsodium")

View File

@ -0,0 +1,141 @@
# Copyright 2013-2017 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 nacl.exceptions as exc
from nacl._sodium import ffi, lib
from nacl.exceptions import ensure
def sodium_memcmp(inp1: bytes, inp2: bytes) -> bool:
"""
Compare contents of two memory regions in constant time
"""
ensure(isinstance(inp1, bytes), raising=exc.TypeError)
ensure(isinstance(inp2, bytes), raising=exc.TypeError)
ln = max(len(inp1), len(inp2))
buf1 = ffi.new("char []", ln)
buf2 = ffi.new("char []", ln)
ffi.memmove(buf1, inp1, len(inp1))
ffi.memmove(buf2, inp2, len(inp2))
eqL = len(inp1) == len(inp2)
eqC = lib.sodium_memcmp(buf1, buf2, ln) == 0
return eqL and eqC
def sodium_pad(s: bytes, blocksize: int) -> bytes:
"""
Pad the input bytearray ``s`` to a multiple of ``blocksize``
using the ISO/IEC 7816-4 algorithm
:param s: input bytes string
:type s: bytes
:param blocksize:
:type blocksize: int
:return: padded string
:rtype: bytes
"""
ensure(isinstance(s, bytes), raising=exc.TypeError)
ensure(isinstance(blocksize, int), raising=exc.TypeError)
if blocksize <= 0:
raise exc.ValueError
s_len = len(s)
m_len = s_len + blocksize
buf = ffi.new("unsigned char []", m_len)
p_len = ffi.new("size_t []", 1)
ffi.memmove(buf, s, s_len)
rc = lib.sodium_pad(p_len, buf, s_len, blocksize, m_len)
ensure(rc == 0, "Padding failure", raising=exc.CryptoError)
return ffi.buffer(buf, p_len[0])[:]
def sodium_unpad(s: bytes, blocksize: int) -> bytes:
"""
Remove ISO/IEC 7816-4 padding from the input byte array ``s``
:param s: input bytes string
:type s: bytes
:param blocksize:
:type blocksize: int
:return: unpadded string
:rtype: bytes
"""
ensure(isinstance(s, bytes), raising=exc.TypeError)
ensure(isinstance(blocksize, int), raising=exc.TypeError)
s_len = len(s)
u_len = ffi.new("size_t []", 1)
rc = lib.sodium_unpad(u_len, s, s_len, blocksize)
if rc != 0:
raise exc.CryptoError("Unpadding failure")
return s[: u_len[0]]
def sodium_increment(inp: bytes) -> bytes:
"""
Increment the value of a byte-sequence interpreted
as the little-endian representation of a unsigned big integer.
:param inp: input bytes buffer
:type inp: bytes
:return: a byte-sequence representing, as a little-endian
unsigned big integer, the value ``to_int(inp)``
incremented by one.
:rtype: bytes
"""
ensure(isinstance(inp, bytes), raising=exc.TypeError)
ln = len(inp)
buf = ffi.new("unsigned char []", ln)
ffi.memmove(buf, inp, ln)
lib.sodium_increment(buf, ln)
return ffi.buffer(buf, ln)[:]
def sodium_add(a: bytes, b: bytes) -> bytes:
"""
Given a couple of *same-sized* byte sequences, interpreted as the
little-endian representation of two unsigned integers, compute
the modular addition of the represented values, in constant time for
a given common length of the byte sequences.
:param a: input bytes buffer
:type a: bytes
:param b: input bytes buffer
:type b: bytes
:return: a byte-sequence representing, as a little-endian big integer,
the integer value of ``(to_int(a) + to_int(b)) mod 2^(8*len(a))``
:rtype: bytes
"""
ensure(isinstance(a, bytes), raising=exc.TypeError)
ensure(isinstance(b, bytes), raising=exc.TypeError)
ln = len(a)
ensure(len(b) == ln, raising=exc.TypeError)
buf_a = ffi.new("unsigned char []", ln)
buf_b = ffi.new("unsigned char []", ln)
ffi.memmove(buf_a, a, ln)
ffi.memmove(buf_b, b, ln)
lib.sodium_add(buf_a, buf_b, ln)
return ffi.buffer(buf_a, ln)[:]

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

@ -0,0 +1,75 @@
# Copyright 2017 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 nacl.exceptions import CryptPrefixError
from . import _argon2, argon2i, argon2id, scrypt
STRPREFIX = argon2id.STRPREFIX
PWHASH_SIZE = argon2id.PWHASH_SIZE
assert _argon2.ALG_ARGON2_DEFAULT == _argon2.ALG_ARGON2ID13
# since version 1.0.15 of libsodium
PASSWD_MIN = argon2id.PASSWD_MIN
PASSWD_MAX = argon2id.PASSWD_MAX
MEMLIMIT_MAX = argon2id.MEMLIMIT_MAX
MEMLIMIT_MIN = argon2id.MEMLIMIT_MIN
OPSLIMIT_MAX = argon2id.OPSLIMIT_MAX
OPSLIMIT_MIN = argon2id.OPSLIMIT_MIN
OPSLIMIT_INTERACTIVE = argon2id.OPSLIMIT_INTERACTIVE
MEMLIMIT_INTERACTIVE = argon2id.MEMLIMIT_INTERACTIVE
OPSLIMIT_MODERATE = argon2id.OPSLIMIT_MODERATE
MEMLIMIT_MODERATE = argon2id.MEMLIMIT_MODERATE
OPSLIMIT_SENSITIVE = argon2id.OPSLIMIT_SENSITIVE
MEMLIMIT_SENSITIVE = argon2id.MEMLIMIT_SENSITIVE
str = argon2id.str
assert argon2i.ALG != argon2id.ALG
SCRYPT_SALTBYTES = scrypt.SALTBYTES
SCRYPT_PWHASH_SIZE = scrypt.PWHASH_SIZE
SCRYPT_OPSLIMIT_INTERACTIVE = scrypt.OPSLIMIT_INTERACTIVE
SCRYPT_MEMLIMIT_INTERACTIVE = scrypt.MEMLIMIT_INTERACTIVE
SCRYPT_OPSLIMIT_SENSITIVE = scrypt.OPSLIMIT_SENSITIVE
SCRYPT_MEMLIMIT_SENSITIVE = scrypt.MEMLIMIT_SENSITIVE
kdf_scryptsalsa208sha256 = scrypt.kdf
scryptsalsa208sha256_str = scrypt.str
verify_scryptsalsa208sha256 = scrypt.verify
def verify(password_hash: bytes, password: bytes) -> bool:
"""
Takes a modular crypt encoded stored password hash derived using one
of the algorithms supported by `libsodium` and checks if the user provided
password will hash to the same string when using the parameters saved
in the stored hash
"""
if password_hash.startswith(argon2id.STRPREFIX):
return argon2id.verify(password_hash, password)
elif password_hash.startswith(argon2i.STRPREFIX):
return argon2id.verify(password_hash, password)
elif scrypt.AVAILABLE and password_hash.startswith(scrypt.STRPREFIX):
return scrypt.verify(password_hash, password)
else:
raise (
CryptPrefixError(
"given password_hash is not in a supported format"
)
)

View File

@ -0,0 +1,49 @@
# 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 nacl.bindings
_argon2_strbytes_plus_one = nacl.bindings.crypto_pwhash_STRBYTES
PWHASH_SIZE = _argon2_strbytes_plus_one - 1
SALTBYTES = nacl.bindings.crypto_pwhash_SALTBYTES
PASSWD_MIN = nacl.bindings.crypto_pwhash_PASSWD_MIN
PASSWD_MAX = nacl.bindings.crypto_pwhash_PASSWD_MAX
PWHASH_SIZE = _argon2_strbytes_plus_one - 1
BYTES_MAX = nacl.bindings.crypto_pwhash_BYTES_MAX
BYTES_MIN = nacl.bindings.crypto_pwhash_BYTES_MIN
ALG_ARGON2I13 = nacl.bindings.crypto_pwhash_ALG_ARGON2I13
ALG_ARGON2ID13 = nacl.bindings.crypto_pwhash_ALG_ARGON2ID13
ALG_ARGON2_DEFAULT = nacl.bindings.crypto_pwhash_ALG_DEFAULT
def verify(password_hash: bytes, password: bytes) -> bool:
"""
Takes a modular crypt encoded argon2i or argon2id stored password hash
and checks if the user provided password will hash to the same string
when using the stored parameters
:param password_hash: password hash serialized in modular crypt() format
:type password_hash: bytes
:param password: user provided password
:type password: bytes
:rtype: boolean
.. versionadded:: 1.2
"""
return nacl.bindings.crypto_pwhash_str_verify(password_hash, password)

View File

@ -0,0 +1,132 @@
# 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 nacl.bindings
import nacl.encoding
from . import _argon2
ALG = _argon2.ALG_ARGON2I13
STRPREFIX = nacl.bindings.crypto_pwhash_argon2i_STRPREFIX
SALTBYTES = _argon2.SALTBYTES
PASSWD_MIN = _argon2.PASSWD_MIN
PASSWD_MAX = _argon2.PASSWD_MAX
PWHASH_SIZE = _argon2.PWHASH_SIZE
BYTES_MIN = _argon2.BYTES_MIN
BYTES_MAX = _argon2.BYTES_MAX
verify = _argon2.verify
MEMLIMIT_MAX = nacl.bindings.crypto_pwhash_argon2i_MEMLIMIT_MAX
MEMLIMIT_MIN = nacl.bindings.crypto_pwhash_argon2i_MEMLIMIT_MIN
OPSLIMIT_MAX = nacl.bindings.crypto_pwhash_argon2i_OPSLIMIT_MAX
OPSLIMIT_MIN = nacl.bindings.crypto_pwhash_argon2i_OPSLIMIT_MIN
OPSLIMIT_INTERACTIVE = nacl.bindings.crypto_pwhash_argon2i_OPSLIMIT_INTERACTIVE
MEMLIMIT_INTERACTIVE = nacl.bindings.crypto_pwhash_argon2i_MEMLIMIT_INTERACTIVE
OPSLIMIT_SENSITIVE = nacl.bindings.crypto_pwhash_argon2i_OPSLIMIT_SENSITIVE
MEMLIMIT_SENSITIVE = nacl.bindings.crypto_pwhash_argon2i_MEMLIMIT_SENSITIVE
OPSLIMIT_MODERATE = nacl.bindings.crypto_pwhash_argon2i_OPSLIMIT_MODERATE
MEMLIMIT_MODERATE = nacl.bindings.crypto_pwhash_argon2i_MEMLIMIT_MODERATE
def kdf(
size: int,
password: bytes,
salt: bytes,
opslimit: int = OPSLIMIT_SENSITIVE,
memlimit: int = MEMLIMIT_SENSITIVE,
encoder: nacl.encoding.Encoder = nacl.encoding.RawEncoder,
) -> bytes:
"""
Derive a ``size`` bytes long key from a caller-supplied
``password`` and ``salt`` pair using the argon2i
memory-hard construct.
the enclosing module provides the constants
- :py:const:`.OPSLIMIT_INTERACTIVE`
- :py:const:`.MEMLIMIT_INTERACTIVE`
- :py:const:`.OPSLIMIT_MODERATE`
- :py:const:`.MEMLIMIT_MODERATE`
- :py:const:`.OPSLIMIT_SENSITIVE`
- :py:const:`.MEMLIMIT_SENSITIVE`
as a guidance for correct settings.
:param size: derived key size, must be between
:py:const:`.BYTES_MIN` and
:py:const:`.BYTES_MAX`
:type size: int
:param password: password used to seed the key derivation procedure;
it length must be between
:py:const:`.PASSWD_MIN` and
:py:const:`.PASSWD_MAX`
:type password: bytes
:param salt: **RANDOM** salt used in the key derivation procedure;
its length must be exactly :py:const:`.SALTBYTES`
:type salt: bytes
:param opslimit: the time component (operation count)
of the key derivation procedure's computational cost;
it must be between
:py:const:`.OPSLIMIT_MIN` and
:py:const:`.OPSLIMIT_MAX`
:type opslimit: int
:param memlimit: the memory occupation component
of the key derivation procedure's computational cost;
it must be between
:py:const:`.MEMLIMIT_MIN` and
:py:const:`.MEMLIMIT_MAX`
:type memlimit: int
:rtype: bytes
.. versionadded:: 1.2
"""
return encoder.encode(
nacl.bindings.crypto_pwhash_alg(
size, password, salt, opslimit, memlimit, ALG
)
)
def str(
password: bytes,
opslimit: int = OPSLIMIT_INTERACTIVE,
memlimit: int = MEMLIMIT_INTERACTIVE,
) -> bytes:
"""
Hashes a password with a random salt, using the memory-hard
argon2i construct and returning an ascii string that has all
the needed info to check against a future password
The default settings for opslimit and memlimit are those deemed
correct for the interactive user login case.
:param bytes password:
:param int opslimit:
:param int memlimit:
:rtype: bytes
.. versionadded:: 1.2
"""
return nacl.bindings.crypto_pwhash_str_alg(
password, opslimit, memlimit, ALG
)

View File

@ -0,0 +1,135 @@
# 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 nacl.bindings
import nacl.encoding
from . import _argon2
ALG = _argon2.ALG_ARGON2ID13
STRPREFIX = nacl.bindings.crypto_pwhash_argon2id_STRPREFIX
SALTBYTES = _argon2.SALTBYTES
PASSWD_MIN = _argon2.PASSWD_MIN
PASSWD_MAX = _argon2.PASSWD_MAX
PWHASH_SIZE = _argon2.PWHASH_SIZE
BYTES_MIN = _argon2.BYTES_MIN
BYTES_MAX = _argon2.BYTES_MAX
verify = _argon2.verify
MEMLIMIT_MIN = nacl.bindings.crypto_pwhash_argon2id_MEMLIMIT_MIN
MEMLIMIT_MAX = nacl.bindings.crypto_pwhash_argon2id_MEMLIMIT_MAX
OPSLIMIT_MIN = nacl.bindings.crypto_pwhash_argon2id_OPSLIMIT_MIN
OPSLIMIT_MAX = nacl.bindings.crypto_pwhash_argon2id_OPSLIMIT_MAX
OPSLIMIT_INTERACTIVE = (
nacl.bindings.crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE
)
MEMLIMIT_INTERACTIVE = (
nacl.bindings.crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE
)
OPSLIMIT_SENSITIVE = nacl.bindings.crypto_pwhash_argon2id_OPSLIMIT_SENSITIVE
MEMLIMIT_SENSITIVE = nacl.bindings.crypto_pwhash_argon2id_MEMLIMIT_SENSITIVE
OPSLIMIT_MODERATE = nacl.bindings.crypto_pwhash_argon2id_OPSLIMIT_MODERATE
MEMLIMIT_MODERATE = nacl.bindings.crypto_pwhash_argon2id_MEMLIMIT_MODERATE
def kdf(
size: int,
password: bytes,
salt: bytes,
opslimit: int = OPSLIMIT_SENSITIVE,
memlimit: int = MEMLIMIT_SENSITIVE,
encoder: nacl.encoding.Encoder = nacl.encoding.RawEncoder,
) -> bytes:
"""
Derive a ``size`` bytes long key from a caller-supplied
``password`` and ``salt`` pair using the argon2i
memory-hard construct.
the enclosing module provides the constants
- :py:const:`.OPSLIMIT_INTERACTIVE`
- :py:const:`.MEMLIMIT_INTERACTIVE`
- :py:const:`.OPSLIMIT_MODERATE`
- :py:const:`.MEMLIMIT_MODERATE`
- :py:const:`.OPSLIMIT_SENSITIVE`
- :py:const:`.MEMLIMIT_SENSITIVE`
as a guidance for correct settings.
:param size: derived key size, must be between
:py:const:`.BYTES_MIN` and
:py:const:`.BYTES_MAX`
:type size: int
:param password: password used to seed the key derivation procedure;
it length must be between
:py:const:`.PASSWD_MIN` and
:py:const:`.PASSWD_MAX`
:type password: bytes
:param salt: **RANDOM** salt used in the key derivation procedure;
its length must be exactly :py:const:`.SALTBYTES`
:type salt: bytes
:param opslimit: the time component (operation count)
of the key derivation procedure's computational cost;
it must be between
:py:const:`.OPSLIMIT_MIN` and
:py:const:`.OPSLIMIT_MAX`
:type opslimit: int
:param memlimit: the memory occupation component
of the key derivation procedure's computational cost;
it must be between
:py:const:`.MEMLIMIT_MIN` and
:py:const:`.MEMLIMIT_MAX`
:type memlimit: int
:rtype: bytes
.. versionadded:: 1.2
"""
return encoder.encode(
nacl.bindings.crypto_pwhash_alg(
size, password, salt, opslimit, memlimit, ALG
)
)
def str(
password: bytes,
opslimit: int = OPSLIMIT_INTERACTIVE,
memlimit: int = MEMLIMIT_INTERACTIVE,
) -> bytes:
"""
Hashes a password with a random salt, using the memory-hard
argon2id construct and returning an ascii string that has all
the needed info to check against a future password
The default settings for opslimit and memlimit are those deemed
correct for the interactive user login case.
:param bytes password:
:param int opslimit:
:param int memlimit:
:rtype: bytes
.. versionadded:: 1.2
"""
return nacl.bindings.crypto_pwhash_str_alg(
password, opslimit, memlimit, ALG
)

View File

@ -0,0 +1,211 @@
# 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 cast
import nacl.bindings
import nacl.encoding
from nacl import exceptions as exc
from nacl.exceptions import ensure
_strbytes_plus_one = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_STRBYTES
AVAILABLE = nacl.bindings.has_crypto_pwhash_scryptsalsa208sha256
STRPREFIX = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_STRPREFIX
SALTBYTES = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_SALTBYTES
PASSWD_MIN = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN
PASSWD_MAX = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX
PWHASH_SIZE = _strbytes_plus_one - 1
BYTES_MIN = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN
BYTES_MAX = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX
MEMLIMIT_MIN = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN
MEMLIMIT_MAX = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX
OPSLIMIT_MIN = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN
OPSLIMIT_MAX = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX
OPSLIMIT_INTERACTIVE = (
nacl.bindings.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE
)
MEMLIMIT_INTERACTIVE = (
nacl.bindings.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE
)
OPSLIMIT_SENSITIVE = (
nacl.bindings.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE
)
MEMLIMIT_SENSITIVE = (
nacl.bindings.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE
)
OPSLIMIT_MODERATE = 8 * OPSLIMIT_INTERACTIVE
MEMLIMIT_MODERATE = 8 * MEMLIMIT_INTERACTIVE
def kdf(
size: int,
password: bytes,
salt: bytes,
opslimit: int = OPSLIMIT_SENSITIVE,
memlimit: int = MEMLIMIT_SENSITIVE,
encoder: nacl.encoding.Encoder = nacl.encoding.RawEncoder,
) -> bytes:
"""
Derive a ``size`` bytes long key from a caller-supplied
``password`` and ``salt`` pair using the scryptsalsa208sha256
memory-hard construct.
the enclosing module provides the constants
- :py:const:`.OPSLIMIT_INTERACTIVE`
- :py:const:`.MEMLIMIT_INTERACTIVE`
- :py:const:`.OPSLIMIT_SENSITIVE`
- :py:const:`.MEMLIMIT_SENSITIVE`
- :py:const:`.OPSLIMIT_MODERATE`
- :py:const:`.MEMLIMIT_MODERATE`
as a guidance for correct settings respectively for the
interactive login and the long term key protecting sensitive data
use cases.
:param size: derived key size, must be between
:py:const:`.BYTES_MIN` and
:py:const:`.BYTES_MAX`
:type size: int
:param password: password used to seed the key derivation procedure;
it length must be between
:py:const:`.PASSWD_MIN` and
:py:const:`.PASSWD_MAX`
:type password: bytes
:param salt: **RANDOM** salt used in the key derivation procedure;
its length must be exactly :py:const:`.SALTBYTES`
:type salt: bytes
:param opslimit: the time component (operation count)
of the key derivation procedure's computational cost;
it must be between
:py:const:`.OPSLIMIT_MIN` and
:py:const:`.OPSLIMIT_MAX`
:type opslimit: int
:param memlimit: the memory occupation component
of the key derivation procedure's computational cost;
it must be between
:py:const:`.MEMLIMIT_MIN` and
:py:const:`.MEMLIMIT_MAX`
:type memlimit: int
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
.. versionadded:: 1.2
"""
ensure(
AVAILABLE,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
len(salt) == SALTBYTES,
"The salt must be exactly %s, not %s bytes long"
% (SALTBYTES, len(salt)),
raising=exc.ValueError,
)
n_log2, r, p = nacl.bindings.nacl_bindings_pick_scrypt_params(
opslimit, memlimit
)
maxmem = memlimit + (2 ** 16)
return encoder.encode(
nacl.bindings.crypto_pwhash_scryptsalsa208sha256_ll(
password,
salt,
# Cast safety: n_log2 is a positive integer, and so 2 ** n_log2 is also
# a positive integer. Mypy+typeshed can't deduce this, because there's no
# way to for them to know that n_log2: int is positive.
cast(int, 2 ** n_log2),
r,
p,
maxmem=maxmem,
dklen=size,
)
)
def str(
password: bytes,
opslimit: int = OPSLIMIT_INTERACTIVE,
memlimit: int = MEMLIMIT_INTERACTIVE,
) -> bytes:
"""
Hashes a password with a random salt, using the memory-hard
scryptsalsa208sha256 construct and returning an ascii string
that has all the needed info to check against a future password
The default settings for opslimit and memlimit are those deemed
correct for the interactive user login case.
:param bytes password:
:param int opslimit:
:param int memlimit:
:rtype: bytes
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
.. versionadded:: 1.2
"""
ensure(
AVAILABLE,
"Not available in minimal build",
raising=exc.UnavailableError,
)
return nacl.bindings.crypto_pwhash_scryptsalsa208sha256_str(
password, opslimit, memlimit
)
def verify(password_hash: bytes, password: bytes) -> bool:
"""
Takes the output of scryptsalsa208sha256 and compares it against
a user provided password to see if they are the same
:param password_hash: bytes
:param password: bytes
:rtype: boolean
:raises nacl.exceptions.UnavailableError: If called when using a
minimal build of libsodium.
.. versionadded:: 1.2
"""
ensure(
AVAILABLE,
"Not available in minimal build",
raising=exc.UnavailableError,
)
ensure(
len(password_hash) == PWHASH_SIZE,
"The password hash must be exactly %s bytes long"
% nacl.bindings.crypto_pwhash_scryptsalsa208sha256_STRBYTES,
raising=exc.ValueError,
)
return nacl.bindings.crypto_pwhash_scryptsalsa208sha256_str_verify(
password_hash, password
)

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)