Esempio n. 1
0
def crypto_generichash(message, key, out_len=crypto_generichash_BYTES):
	#if out_len < crypto_generichash_BYTES_MIN:
		#raise CryptoError("out_len length shorter than crypto_generichash_BYTES_MIN")
	if out_len > crypto_generichash_BYTES_MAX:
		raise CryptoError("out_len length longer than crypto_generichash_BYTES_MAX")

	if not isinstance(message, (str)):
		raise CryptoError("message must be str")

	message_len = len(message)

	if not key:
		key = lib.ffi.NULL
		key_len = 0
	else:
		key_len = len(key)
		if key_len < crypto_generichash_KEYBYTES_MIN:
			raise CryptoError("key length shorter than crypto_generichash_keybytes_min")
		if key_len > crypto_generichash_KEYBYTES_MAX:
			raise CryptoError("key length longer than crypto_generichash_keybytes_max")

	out = lib.ffi.new("unsigned char[]", out_len)

	if lib.crypto_generichash(out, out_len, message, message_len, key, key_len) != 0:
		raise CryptoError("An error occurred while crypto_generichash")

	return lib.ffi.buffer(out, out_len)[:]
Esempio n. 2
0
def sodium_init():
    """
    Initializes sodium, picking the best implementations available for this
    machine.
    """
    if lib.sodium_init() != 0:
        raise CryptoError("Could not initialize sodium")
Esempio n. 3
0
def crypto_secretbox_open(key, ciphertext, nonce):
    """
    Decrypt and returns the encrypted message ``ciphertext`` with the secret
    ``key`` and the nonce ``nonce``.

    :param key: bytes
    :param ciphertext: bytes
    :param nonce: 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 = lib.ffi.new("unsigned char[]", len(padded))

    if lib.crypto_secretbox_open(plaintext, padded, len(padded), nonce,
                                 key) != 0:
        raise CryptoError("Decryption failed. Ciphertext failed verification")

    plaintext = lib.ffi.buffer(plaintext, len(padded))
    return plaintext[crypto_secretbox_ZEROBYTES:]
Esempio n. 4
0
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:]
Esempio n. 5
0
def _get_skpk_from_decrypted_private_blob(blob):

    checkint1 = blob.read(4)
    checkint2 = blob.read(4)
    if checkint1 != checkint2:
        LOG.error('Check: %s != %s', checkint1, checkint2)
        raise CryptoError()

    # We should parse n keys, but n is 1
    decode_string(blob)  # ignore key name
    decode_string(blob)  # ignore pubkey
    skpk = decode_string(blob)
    LOG.debug('Private Key blob: %s', skpk.hex().upper())
    assert len(skpk) == 64

    sk = skpk[:32]  # first half = priv key
    LOG.debug('ed25519 sk: %s', sk.hex().upper())

    pk = skpk[32:]  # second half = pub key
    LOG.debug('ed25519 pk: %s', pk.hex().upper())

    seckey = crypto_sign_ed25519_sk_to_curve25519(skpk)
    LOG.debug('x25519 sk: %s', seckey.hex().upper())

    pubkey = crypto_sign_ed25519_pk_to_curve25519(pk)
    LOG.debug('x25519 pk: %s', pubkey.hex().upper())

    return (seckey, pubkey)
def crypto_hash_sha512(message):
    """
    Hashes and returns the message ``message``.

    :param message: bytes
    :rtype: bytes
    """
    digest = lib.ffi.new("unsigned char[]", crypto_hash_sha512_BYTES)
    if lib.crypto_hash_sha512(digest, message, len(message)) != 0:
        raise CryptoError("Hashing failed")
    return lib.ffi.buffer(digest, crypto_hash_sha512_BYTES)[:]
Esempio n. 7
0
def crypto_shorthash(in_, k):

    if not in_:
        in_ = lib.ffi.new("unsigned char []", 1)
        in_len = 0
    else:
        in_len = len(in_)

    out = lib.ffi.new("unsigned char []", crypto_shorthash_BYTES)

    if lib.crypto_shorthash(out, in_, in_len, k) != 0:
        raise CryptoError("An error occurred while crypto_shorthash")

    return lib.ffi.buffer(out, crypto_shorthash_BYTES)[:]
Esempio n. 8
0
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 = lib.ffi.new("unsigned char[]", crypto_scalarmult_BYTES)

    if lib.crypto_scalarmult_base(q, n) != 0:
        raise CryptoError(
            "An error occurred while computing the scalar product")

    return lib.ffi.buffer(q, crypto_scalarmult_SCALARBYTES)[:]
Esempio n. 9
0
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 = lib.ffi.new("unsigned char[]", len(message) + crypto_sign_BYTES)
    signed_len = lib.ffi.new("unsigned long long *")

    if lib.crypto_sign(signed, signed_len, message, len(message), sk) != 0:
        raise CryptoError("Failed to sign the message")

    return lib.ffi.buffer(signed, signed_len[0])[:]
Esempio n. 10
0
def crypto_sign_keypair():
    """
    Returns a randomly generated public key and secret key.

    :rtype: (bytes(public_key), bytes(secret_key))
    """
    pk = lib.ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES)
    sk = lib.ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)

    if lib.crypto_sign_keypair(pk, sk) != 0:
        raise CryptoError("An error occurred while generating keypairs")

    return (
        lib.ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:],
        lib.ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
    )
Esempio n. 11
0
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 = lib.ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES)
    sk = lib.ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)

    if lib.crypto_sign_seed_keypair(pk, sk, seed) != 0:
        raise CryptoError("An error occurred while generating keypairs")

    return (
        lib.ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:],
        lib.ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
    )
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 = lib.ffi.new("unsigned char[]", crypto_box_BEFORENMBYTES)

    if lib.crypto_box_beforenm(k, pk, sk) != 0:
        raise CryptoError("An error occurred computing the shared key.")

    return lib.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 ValueError("Invalid nonce")

    if len(k) != crypto_box_BEFORENMBYTES:
        raise ValueError("Invalid shared key")

    padded = b"\x00" * crypto_box_ZEROBYTES + message
    ciphertext = lib.ffi.new("unsigned char[]", len(padded))

    if lib.crypto_box_afternm(ciphertext, padded, len(padded), nonce, k) != 0:
        raise CryptoError("An error occurred trying to encrypt the message")

    return lib.ffi.buffer(ciphertext, len(padded))[crypto_box_BOXZEROBYTES:]
Esempio n. 14
0
def crypto_secretbox(key, message, nonce):
    """
    Encrypts and returns the message ``message`` with the secret ``key`` and
    the nonce ``nonce``.

    :param key: bytes
    :param message: bytes
    :param nonce: 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 = lib.ffi.new("unsigned char[]", len(padded))

    if lib.crypto_secretbox(ciphertext, padded, len(padded), nonce, key) != 0:
        raise CryptoError("Encryption failed")

    ciphertext = lib.ffi.buffer(ciphertext, len(padded))
    return ciphertext[crypto_secretbox_BOXZEROBYTES:]
Esempio n. 15
0
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:]
Esempio n. 16
0
def parse_private_key(stream, callback):

    ciphername = decode_string(stream)
    kdfname = decode_string(stream)
    kdfoptions = decode_string(stream)

    LOG.info("Ciphername: %s", ciphername)
    LOG.info("KDF: %s", kdfname)
    # LOG.debug("KDF options %s", kdfoptions)

    if kdfname not in (b"none", b"bcrypt"):
        raise ValueError("Invalid SSH Key format")

    if kdfname != b"none" and ciphername == b"none":
        raise ValueError("Invalid SSH Key format")

    if kdfname != b'none':
        # assert (kdfname == b"bcrypt")
        assert kdfoptions
        kdfoptions = io.BytesIO(kdfoptions)
        salt = decode_string(kdfoptions)
        rounds = int.from_bytes(kdfoptions.read(4), byteorder='big')
        LOG.info("Salt: %s", salt.hex())
        LOG.info("Rounds: %d", rounds)
        assert not kdfoptions.read(), "There should be no trailing data in the kdfoptions buffer"
    else:
        LOG.debug("Not Encrypted")
        assert( not kdfoptions )


    n = int.from_bytes(stream.read(4), byteorder='big') # u32
    LOG.debug("Number of keys: %d", n)
    assert( n == 1 ) # Apparently always 1: https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L3857

    decode_string(stream) # ignore the public keys
    private_ciphertext = decode_string(stream) # padded list of private keys
    assert not stream.read(), "There should be no trailing data"

    if ciphername == b'none':
        # LOG.debug('Non-Encrypted private data: %s', private_data)
        private_data = io.BytesIO(private_ciphertext) # no need to unpad
        return _get_skpk_from_private_blob(private_data) # ignore the comment

    # Encrypted content
    # LOG.debug('------ Encrypted private data (%d): %s', len(private_ciphertext), private_ciphertext)
    assert( callback and callable(callback) )
    passphrase = callback().encode()
    # LOG.debug('Passphrase: %s', passphrase)

    if not passphrase and ciphername != b"none":
        raise ValueError("Passphrase required")

    dklen = get_derived_key_length(ciphername) # key length + IV length
    LOG.debug('Derived Key len: %d', dklen)
    derived_key = derive_key(kdfname, passphrase, salt, rounds, dklen=dklen) 
    LOG.debug('Derived Key: %s', derived_key)
    decryptor = get_cipher(ciphername, derived_key).decryptor()
    assert( len(private_ciphertext) % _block_size(ciphername) == 0 ), "Invalid cipher block length"
    private_data = decryptor.update(private_ciphertext) + decryptor.finalize()
    # LOG.debug('------- Private data (%d): %s', len(private_data), private_data)

    if private_data[:4] != private_data[4:8]: # check don't pass
        LOG.debug('Check: %s != %s', private_data[:4], private_data[4:8])
        raise CryptoError()
    private_data = io.BytesIO(private_data[8:])
    # Note: we ignore the comment and padding after the priv blob
    return _get_skpk_from_private_blob(private_data) # no need to unpad