def sodium_add(a, b): """ 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)[:]
def sodium_pad(s, blocksize): """ 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, integer_types), 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 crypto_aead_aes256gcm_decrypt( cipher, tag, nonce, key, additional_data, additional_data_len): if len(key) != crypto_aead_aes256gcm_KEYBYTES: raise ValueError("Invalid key") if len(nonce) != crypto_aead_aes256gcm_NPUBBYTES: raise ValueError("Invalid nonce") plaintext = ffi.new("unsigned char[]", len(cipher)) decrypted_len = ffi.new("unsigned long long *") ciphertext = cipher + tag if additional_data is None: additional_data = ffi.NULL if (lib.crypto_aead_aes256gcm_decrypt( plaintext, decrypted_len, ffi.NULL, ciphertext, len(ciphertext), additional_data, additional_data_len, nonce, key) != 0): raise CryptoError("Decryption failed. Ciphertext failed verification") plaintext = ffi.buffer(plaintext, len(cipher)) return plaintext
def generichash_blake2b_salt_personal(data, digest_size=crypto_generichash_BYTES, key=b'', salt=b'', person=b''): """One shot hash interface :param data: the input data to the hash function :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)[:]
def crypto_kx_keypair(): """ 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_sign_ed25519ph_final_create(edph, sk): """ 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 {0} ' '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_core_ed25519_sub(p, q): """ 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 """ 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_auth_hmacsha512256(message, k): a = ffi.new("unsigned char[]", crypto_auth_hmacsha512256_BYTES) rc = lib.crypto_auth_hmacsha512256(a, message, len(message), k) assert rc == 0 return ffi.buffer(a, crypto_auth_hmacsha512256_BYTES)[:]
def crypto_scalarmult_ed25519_base(n): """ 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 """ 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 generichash_blake2b_state_copy(statebuf): """Return a copy of the given blake2b hash state""" newstate = ffi.new("unsigned char[]", crypto_generichash_STATEBYTES) ffi.memmove(newstate, statebuf, crypto_generichash_STATEBYTES) return newstate
def crypto_pwhash_scryptsalsa208sha256_str( passwd, opslimit=SCRYPT_OPSLIMIT_INTERACTIVE, memlimit=SCRYPT_MEMLIMIT_INTERACTIVE): """ 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 """ 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_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 ValueError("Invalid nonce size") if len(pk) != crypto_box_PUBLICKEYBYTES: raise ValueError("Invalid public key") if len(sk) != crypto_box_SECRETKEYBYTES: raise 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) assert rc == 0 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 ValueError("Invalid nonce size") if len(pk) != crypto_box_PUBLICKEYBYTES: raise ValueError("Invalid public key") if len(sk) != crypto_box_SECRETKEYBYTES: raise ValueError("Invalid secret key") padded = (b"\x00" * crypto_box_BOXZEROBYTES) + ciphertext plaintext = ffi.new("unsigned char[]", len(padded)) if lib.crypto_box_open(plaintext, padded, len(padded), nonce, pk, sk) != 0: raise CryptoError("An error occurred trying to decrypt the message") return ffi.buffer(plaintext, len(padded))[crypto_box_ZEROBYTES:]
def crypto_sign(message, sk): """ 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) assert rc == 0 return ffi.buffer(signed, signed_len[0])[:]
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) assert rc == 0 return ( ffi.buffer(pk, crypto_box_PUBLICKEYBYTES)[:], ffi.buffer(sk, crypto_box_SECRETKEYBYTES)[:], )
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_pwhash_str_alg(passwd, opslimit, memlimit, alg): """ 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, integer_types), raising=TypeError) ensure(isinstance(memlimit, integer_types), 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_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_secretbox_open(ciphertext, nonce, key): """ 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:]
def crypto_sign_open(signed, pk): """ 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 generichash_blake2b_init(key=b'', salt=b'', person=b'', digest_size=crypto_generichash_BYTES): """ 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: an initizialized state buffer :rtype: bytes """ _checkparams(digest_size, key, salt, person) statebuf = ffi.new("unsigned char[]", crypto_generichash_STATEBYTES) # 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(statebuf, key, len(key), digest_size, _salt, _person) ensure(rc == 0, 'Unexpected failure', raising=exc.RuntimeError) return statebuf
def __init__(self): self.state = 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 __init__(self): """ Initialize a clean state object.""" self.statebuf = ffi.new( "unsigned char[]", crypto_secretstream_xchacha20poly1305_STATEBYTES, ) self.rawbuf = None self.tagbuf = None
def crypto_sign_keypair(): """ 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(message, sk): """ 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_kx_client_session_keys(client_public_key, client_secret_key, server_public_key): """ 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 {0} 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 {0} 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 {0} 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_sign_seed_keypair(seed): """ 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 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) assert rc == 0 return ( ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:], ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:], )
def randombytes(size): """ 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 crypto_hash_sha512(message): """ 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)) assert rc == 0 return ffi.buffer(digest, crypto_hash_sha512_BYTES)[:]
def crypto_pwhash_alg(outlen, passwd, salt, opslimit, memlimit, alg): """ 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 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, integer_types), raising=exc.TypeError) ensure(isinstance(opslimit, integer_types), raising=exc.TypeError) ensure(isinstance(memlimit, integer_types), raising=exc.TypeError) ensure(isinstance(alg, integer_types), raising=exc.TypeError) ensure(isinstance(passwd, bytes), raising=exc.TypeError) if len(salt) != crypto_pwhash_SALTBYTES: raise exc.ValueError("salt must be exactly {0} bytes long".format( crypto_pwhash_SALTBYTES)) if outlen < crypto_pwhash_BYTES_MIN: raise exc.ValueError( 'derived key must be at least {0} bytes long'.format( crypto_pwhash_BYTES_MIN)) elif outlen > crypto_pwhash_BYTES_MAX: raise exc.ValueError( 'derived key must be at most {0} 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_core_ed25519_add(p, q): """ 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_scalar_mul(p, q): """ 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_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_pwhash_scryptsalsa208sha256_str( passwd, opslimit=SCRYPT_OPSLIMIT_INTERACTIVE, memlimit=SCRYPT_MEMLIMIT_INTERACTIVE): """ 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_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 ValueError("Invalid nonce") if len(k) != crypto_box_BEFORENMBYTES: raise ValueError("Invalid shared key") padded = (b"\x00" * crypto_box_BOXZEROBYTES) + ciphertext plaintext = ffi.new("unsigned char[]", len(padded)) if lib.crypto_box_open_afternm( plaintext, padded, len(padded), nonce, k) != 0: raise CryptoError("An error occurred trying to decrypt the message") return ffi.buffer(plaintext, len(padded))[crypto_box_ZEROBYTES:]
def crypto_secretbox(message, nonce, key): """ 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_core_ed25519_scalar_invert(s): """ 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_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_sign_ed25519_sk_to_curve25519(secret_key_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 public_key_bytes: bytes :rtype: bytes """ if len(secret_key_bytes) != crypto_sign_SECRETKEYBYTES: raise exc.ValueError("Invalid curve public 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_scalarmult_ed25519_noclamp(n, p): """ 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 """ 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)[:]
def crypto_stream_chacha20_keygen(): outbuf = ffi.new("unsigned char[]", crypto_stream_chacha20_KEYBYTES) lib.crypto_stream_chacha20_keygen(outbuf) return ffi.buffer(outbuf, crypto_stream_chacha20_KEYBYTES)[:]
def generichash_blake2b_salt_personal(data, digest_size=crypto_generichash_BYTES, key=b'', salt=b'', person=b''): """One shot hash interface :param data: the input data to the hash function :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 """ ensure(isinstance(data, bytes), 'Input data must be a bytes sequence', raising=exc.TypeError) 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, integer_types), '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) 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)[:]
def crypto_secretstream_xchacha20poly1305_push( state, m, ad=None, tag=crypto_secretstream_xchacha20poly1305_TAG_MESSAGE, ): """ 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_aead_chacha20poly1305_ietf_encrypt(message, aad, nonce, key): """ Encrypt the given ``message`` using the IETF ratified chacha20poly1305 construction described in RFC7539. :param message: :type message: bytes :param aad: :type aad: 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 {0} 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 {0} 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 {0} 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_xchacha20poly1305_ietf_decrypt(ciphertext, aad, nonce, key): """ Decrypt the given ``ciphertext`` using the long-nonces xchacha20poly1305 construction. :param ciphertext: authenticated ciphertext :type ciphertext: bytes :param aad: :type aad: 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 {0} 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 {0} 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 {0} 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])[:]
def crypto_pwhash_argon2i(outlen, passwd, salt, opslimit, memlimit, alg): """ Derive a raw cryptographic key using the ``passwd`` and the ``salt`` given as input. :param outlen: the length of the derived key :type outlen: int :param passwd: The input password :type passwd: 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, integer_types), raising=exc.TypeError) ensure(isinstance(opslimit, integer_types), raising=exc.TypeError) ensure(isinstance(memlimit, integer_types), raising=exc.TypeError) ensure(isinstance(alg, integer_types), raising=exc.TypeError) ensure(isinstance(passwd, bytes), raising=exc.TypeError) if len(salt) != crypto_pwhash_argon2i_SALTBYTES: raise exc.ValueError( ("salt must be exactly {0} " "bytes long").format(crypto_pwhash_argon2i_SALTBYTES)) if outlen < crypto_pwhash_argon2i_BYTES_MIN: raise exc.ValueError(( 'derived key must be ' 'at least {0} bytes long').format(crypto_pwhash_argon2i_BYTES_MIN)) elif outlen > crypto_pwhash_argon2i_BYTES_MAX: raise exc.ValueError( ('derived key must be ' 'at most {0} bytes long').format(crypto_pwhash_argon2i_BYTES_MAX)) if memlimit < crypto_pwhash_argon2i_MEMLIMIT_MIN: raise exc.ValueError( ('memlimit must be ' 'at least {0} bytes').format(crypto_pwhash_argon2i_MEMLIMIT_MIN)) elif memlimit > crypto_pwhash_argon2i_MEMLIMIT_MAX: raise exc.ValueError( ('memlimit must be ' 'at most {0} bytes').format(crypto_pwhash_argon2i_MEMLIMIT_MAX)) if opslimit < crypto_pwhash_argon2i_OPSLIMIT_MIN: raise exc.ValueError( ('opslimit must be ' 'at least {0}').format(crypto_pwhash_argon2i_OPSLIMIT_MIN)) elif opslimit > crypto_pwhash_argon2i_OPSLIMIT_MAX: raise exc.ValueError( ('opslimit must be ' 'at most {0}').format(crypto_pwhash_argon2i_OPSLIMIT_MAX)) outbuf = ffi.new("unsigned char[]", outlen) ret = lib.crypto_pwhash_argon2i(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_secretstream_xchacha20poly1305_pull(state, c, ad=None): """ 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) return (ffi.buffer(state.rawbuf, mlen)[:], int(state.tagbuf[0]))
def __init__(self, digest_size): self._statebuf = ffi.new("unsigned char[]", crypto_generichash_STATEBYTES) self.digest_size = digest_size