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) 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 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)) 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_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)) 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_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 ValueError("Invalid key") if len(nonce) != crypto_secretbox_NONCEBYTES: raise 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_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 ValueError("Invalid nonce") if len(k) != crypto_box_BEFORENMBYTES: raise 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_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_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 ValueError("Invalid key") if len(nonce) != crypto_secretbox_NONCEBYTES: raise 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_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)) ensure(rc == 0, 'Unexpected library error', raising=exc.RuntimeError) return ffi.buffer(digest, crypto_hash_sha512_BYTES)[:]
def crypto_scalarmult_base(n): """ 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 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_shorthash_siphash24(data, key): """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 ValueError("Key length must be {0} bytes".format(KEYBYTES)) digest = ffi.new("unsigned char[]", BYTES) rc = lib.crypto_shorthash_siphash24(digest, data, len(data), key) utils.ensure(rc == 0, raising=exc.RuntimeError) return ffi.buffer(digest, BYTES)[:]
def generichash_blake2b_update(statebuf, data): """Update the blake2b hash state :param statebuf: an initialized blake2b state buffer as returned from :py:func:`.crypto_generichash_blake2b_init` :type name: bytes :param data: :type data: bytes """ ensure(isinstance(data, bytes), 'Input data must be a bytes sequence', raising=exc.TypeError) rc = lib.crypto_generichash_blake2b_update(statebuf, data, len(data)) ensure(rc == 0, 'Unexpected failure', raising=exc.RuntimeError)
def generichash_blake2b_final(statebuf, digest_size): """Finalize the blake2b hash state and return the digest. :param statebuf: :type statebuf: bytes :param digest_size: :type digest_size: int :return: the blake2 digest of the passed-in data stream :rtype: bytes """ _digest = ffi.new("unsigned char[]", crypto_generichash_BYTES_MAX) rc = lib.crypto_generichash_blake2b_final(statebuf, _digest, digest_size) ensure(rc == 0, 'Unexpected failure', raising=exc.RuntimeError) return ffi.buffer(_digest, digest_size)[:]
def verify_scryptsalsa208sha256(password_hash, password): """ 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 """ ensure(len(password_hash) == SCRYPT_PWHASH_SIZE, "The pw_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)
def kdf_scryptsalsa208sha256(size, password, salt, opslimit=SCRYPT_OPSLIMIT_SENSITIVE, memlimit=SCRYPT_MEMLIMIT_SENSITIVE, encoder=nacl.encoding.RawEncoder): """ Makes a key defined from ``password`` and ``salt`` that is ``size`` bytes long the enclosing module provides the constants - :py:const:`.SCRYPT_OPSLIMIT_INTERACTIVE` - :py:const:`.SCRYPT_MEMLIMIT_INTERACTIVE` - :py:const:`.SCRYPT_OPSLIMIT_SENSITIVE` - :py:const:`.SCRYPT_MEMLIMIT_SENSITIVE` as a guidance for correct settings respectively for the interactive login and the long term key protecting sensitive data use cases. :param int size: int :param bytes password: bytes :param bytes salt: bytes :param int opslimit: :param int memlimit: :rtype: bytes """ ensure(len(salt) == SCRYPT_SALTBYTES, "The salt must be exactly %s, not %s bytes long" % (SCRYPT_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, 2**n_log2, r, p, maxmem=maxmem, dklen=size))
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_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_pwhash_scryptsalsa208sha256_str_verify(passwd_hash, passwd): """ 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 """ 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 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 sodium_memcmp(inp1, inp2): """ 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 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) ensure(rc == 0, 'Unexpected library error', raising=exc.RuntimeError) return ( ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:], ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:], )
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 ValueError("Invalid key") if len(nonce) != crypto_secretbox_NONCEBYTES: raise 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_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 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_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 ValueError("Invalid public key") if len(sk) != crypto_box_SECRETKEYBYTES: raise 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 _sodium_init(): ensure(lib.sodium_init() != -1, "Could not initialize sodium", raising=exc.RuntimeError)
def _check_memory_occupation(n, r, p, maxmem=SCRYPT_MAX_MEM): 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 {0}'.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 crypto_pwhash_scryptsalsa208sha256_ll(passwd, salt, n, r, p, dklen=64, maxmem=SCRYPT_MAX_MEM): """ 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 """ 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 _checkparams(digest_size, key, salt, person): """Check hash paramters""" 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)