def crypto_lock(key, nonce, msg, ad=b''): """ :returns: (bytes(mac), bytes(ciphertext)) """ ensure_length('key', key, 32) ensure_length('nonce', nonce, 24) key = ffi.from_buffer('uint8_t[32]', key) nonce = ffi.from_buffer('uint8_t[24]', nonce) mac = ffi.new('uint8_t[16]') ct = ffi.new('uint8_t[]', len(msg)) msg = ffi.from_buffer('uint8_t[]', msg) ad = ffi.from_buffer('uint8_t[]', ad) lib.crypto_lock_aead( mac, ct, key, nonce, ad, len(ad), msg, len(msg), ) return bytes(mac), bytes(ct)
def crypto_sign(secret_key, msg): ensure_length('secret_key', secret_key, 32) sk = ffi.from_buffer('uint8_t[32]', secret_key) msg = ffi.from_buffer('uint8_t[]', msg) sig = ffi.new('uint8_t[64]') pk = ffi.new('uint8_t[32]') lib.crypto_sign_public_key(pk, secret_key) lib.crypto_sign(sig, sk, pk, msg, len(msg)) lib.crypto_wipe(pk, 32) return bytes(sig)
def crypto_from_eddsa_public(eddsa): ensure_length('eddsa', eddsa, 32) eddsa = ffi.from_buffer('uint8_t[32]', eddsa) x25519 = ffi.new('uint8_t[32]') lib.crypto_from_eddsa_public(x25519, eddsa) return bytes(x25519)
def crypto_unlock(key, mac, nonce, ciphertext, ad=b''): """ :returns: None or bytes(msg) """ ensure_length('key', key, 32) ensure_length('mac', mac, 16) ensure_length('nonce', nonce, 24) key = ffi.from_buffer('uint8_t[32]', key) mac = ffi.from_buffer('uint8_t[16]', mac) nonce = ffi.from_buffer('uint8_t[24]', nonce) ct = ffi.from_buffer('uint8_t[]', ciphertext) ad = ffi.from_buffer('uint8_t[]', ad) pt = ffi.new('uint8_t[]', len(ciphertext)) rv = lib.crypto_unlock_aead( pt, key, nonce, mac, ad, len(ad), ct, len(ct), ) if rv != 0: return None return bytes(pt)
def crypto_sign_public_key(secret_key): ensure_length('secret_key', secret_key, 32) sk = ffi.from_buffer('uint8_t[32]', secret_key) pk = ffi.new('uint8_t[32]') lib.crypto_sign_public_key(pk, sk) return bytes(pk)
def crypto_x25519_public_key(your_secret_key): ensure_length('your_secret_key', your_secret_key, 32) sk = ffi.from_buffer('uint8_t[32]', your_secret_key) pk = ffi.new('uint8_t[32]') lib.crypto_x25519_public_key(pk, sk) return bytes(pk)
def crypto_blake2b_init(key=b'', hash_size=64): ensure_range('len(key)', len(key), BLAKE2B_KEY_MIN, BLAKE2B_KEY_MAX) ensure_range('hash_size', hash_size, BLAKE2B_HASH_MIN, BLAKE2B_HASH_MAX) ctx = ffi.new('crypto_blake2b_ctx *') key = ffi.from_buffer('uint8_t[]', key) lib.crypto_blake2b_general_init(ctx, hash_size, key, len(key)) return ctx
def crypto_key_exchange(your_secret_key, their_public_key): ensure_length('your_secret_key', your_secret_key, 32) ensure_length('their_public_key', their_public_key, 32) sk = ffi.from_buffer('uint8_t[32]', your_secret_key) pk = ffi.from_buffer('uint8_t[32]', their_public_key) shared = ffi.new('uint8_t[32]') lib.crypto_key_exchange(shared, sk, pk) return bytes(shared)
def test_crypto_lock_equivalent(key, nonce, msg): # we expose the more general crypto_lock_aead() # function, so check if we are calling it the right way # when ad == b'' original_message = msg aead_mac, aead_ct = crypto_lock(key, nonce, msg) msg_size = len(msg) mac = ffi.new('uint8_t[16]') key = ffi.new('uint8_t[32]', key) nonce = ffi.new('uint8_t[24]', nonce) msg = ffi.new('uint8_t[]', msg) ct = ffi.new('uint8_t[]', msg_size) lib.crypto_lock(mac, ct, key, nonce, msg, msg_size) assert bytes(mac) == aead_mac assert bytes(ct) == aead_ct # check that we can decrypt this lib.crypto_wipe(msg, msg_size) rv = lib.crypto_unlock(msg, key, nonce, mac, ct, msg_size) assert rv == 0 assert bytes(msg)[:-1] == original_message
def crypto_blake2b(msg, key=b'', hash_size=64): ensure_range('len(key)', len(key), BLAKE2B_KEY_MIN, BLAKE2B_KEY_MAX) ensure_range('hash_size', hash_size, BLAKE2B_HASH_MIN, BLAKE2B_HASH_MAX) hash = ffi.new('uint8_t[]', hash_size) msg = ffi.from_buffer('uint8_t[]', msg) key = ffi.from_buffer('uint8_t[]', key) lib.crypto_blake2b_general( hash, hash_size, key, len(key), msg, len(msg), ) return bytes(hash)
def crypto_argon2i( password, salt, hash_size=64, nb_blocks=100000, nb_iterations=3, key=b'', ad=b'', ): ensure_range('len(salt)', len(salt), min=8) ensure_range('hash_size', hash_size, min=4) ensure_range('nb_blocks', nb_blocks, min=8) ensure_range('nb_iterations', nb_iterations, min=1) work_area = lib.malloc(nb_blocks * 1024) if work_area == ffi.NULL: # pragma: no cover raise RuntimeError('malloc() returned NULL') try: password = ffi.from_buffer('uint8_t[]', password) salt = ffi.from_buffer('uint8_t[]', salt) hash = ffi.new('uint8_t[]', hash_size) key = ffi.from_buffer('uint8_t[]', key) ad = ffi.from_buffer('uint8_t[]', ad) lib.crypto_argon2i_general( hash, hash_size, work_area, nb_blocks, nb_iterations, password, len(password), salt, len(salt), key, len(key), ad, len(ad), ) return bytes(hash) finally: lib.free(work_area)
def crypto_blake2b_final(ctx): hash = ffi.new('uint8_t[]', ctx.hash_size) lib.crypto_blake2b_final(ctx, hash) return bytes(hash)