tuxbot-bot/venv/lib/python3.7/site-packages/nacl/bindings/crypto_box.py
2019-12-16 18:12:10 +01:00

313 lines
9.5 KiB
Python

# 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 __future__ import absolute_import, division, print_function
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 = lib.crypto_box_secretkeybytes()
crypto_box_PUBLICKEYBYTES = lib.crypto_box_publickeybytes()
crypto_box_SEEDBYTES = lib.crypto_box_seedbytes()
crypto_box_NONCEBYTES = lib.crypto_box_noncebytes()
crypto_box_ZEROBYTES = lib.crypto_box_zerobytes()
crypto_box_BOXZEROBYTES = lib.crypto_box_boxzerobytes()
crypto_box_BEFORENMBYTES = lib.crypto_box_beforenmbytes()
crypto_box_SEALBYTES = lib.crypto_box_sealbytes()
def crypto_box_keypair():
"""
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):
"""
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, nonce, pk, sk):
"""
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, nonce, pk, sk):
"""
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, sk):
"""
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, nonce, k):
"""
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, nonce, k):
"""
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, pk):
"""
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, pk, sk):
"""
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)
_mlen = _clen - crypto_box_SEALBYTES
plaintext = ffi.new("unsigned char[]", _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)[:]