starekrow / lockbox

Encrypted storage with built-in key management facilities
MIT License
95 stars 6 forks source link

libsodium support #2

Open starekrow opened 6 years ago

starekrow commented 6 years ago

It would be nice to have optional support for libsodium as an alternative to the openssl extension, since libsodium is moving into core.

This could be hacked in place, or done by isolating the crypto use in CryptoKey. Either way might complicate cipher selection (needs research).

KJLJon commented 6 years ago

I think isolating the crypto and defaulting it to keep the package easy to use.

Let me know your thoughts, and I can work on implementing it

Here is a quick idea of what I am thinking

starekrow\lockbox\CryptoKey::Lock()

public function Lock( $message )
{
    if (!$this->data) {
        return false;
    }
    $cryptoLibrary = $this->getCryptoLibrary()
    $ciphertext_raw = $cryptoLibrary->encrypt($message, $this->getEncryptionKey());
    $hmac = $cryptoLibrary->generateHmac($ciphertext_raw, $this->getHmacKey());

    $ciphertext = base64_encode($iv.$hmac.$ciphertext_raw);

    return $ciphertext;
}

Interface so anyone can swap it out with their own implementation

I was debating if the encryption interface should be separate from the hmac interface.

interface CryptoLibraryInterface
{
    public function generateRandomBytes($length);

    public function encrypt($message);
    public function decrypt($message);

    public function setEncryptionCipher($cipher);
    public function getEncryptionCipher();
    public function getEncryptionKeyLength();
    public function getEncryptionKey();
    public function setEncryptionKey($key);

    public function generateHmac($message);
    public function isHmacValid($hmac, $message);

    public function setHmacAlgorithm($algo);
    public function getHmacAlgorithm();
    public function getHmacKeyLength();
    public function getHmacKey();
    public function setHmacKey($key);
}

Abstract class to implement common functions

abstract class AbstractCryptoLibrary implements CryptoLibraryInterface
{
    private $hmacKey;
    private $encryptionKey;
    private $cipher;

    public function setEncryptionCipher($cipher)
    {
        $this->cipher = $cipher;
    }

    public function getEncryptionCipher()
    {
        return $this->cipher;
    }

    public function setEncryptionKey($key)
    {
        $this->encryptionKey = $key;
    }

    public function getEncryptionKey()
    {
        if (isset($this->encryptionKey) === false) {
            $keyLength = $this->getEncryptionKeyLength();
            $key = $this->generateRandomBytes($keyLength);
            $this->setEncryptionKey($key);
        }

        return $this->encryptionKey;
    }

    public function isHmacValid($hmac, $message) {
        return $this->generateHmac($message) === $hmac;
    }

    //... rest of the common functions
}

OpenSSL version:

class OpenSSLCryptoLibrary extends AbstractCryptoLibrary
{
    public function generateRandomBytes($length)
    {
        return openssl_random_pseudo_bytes($length);
    }
    //... rest of the openssl specific functions
}
starekrow commented 6 years ago

Hmm. I think it would work out better to move the abstraction deeper, along these lines:

public function hash( $alg, $data );
public function hmac( $alg, $key, $data );
public function hkdf( $alg, $ikm, $len, $salt, $info );
public function encrypt( $alg, $key, $iv, $data );
public function decrypt( $alg, $key, $iv, $data );
public function hashcmp( $h1, $h2 );
public function random( $count );

Just a couple of assumptions (always binary strings, no wierd operational modes) cleans up the interface a lot. This then becomes a useful and clean building block for any number of related problems. It also leaves all the decisions of what to do inside CryptoKey instead of burying or splitting them.

Still need to work out how to handle algorithm names and discovery. It should be as permissive as possible without tolerating ambiguous input.

starekrow commented 6 years ago

FYI all, I'm building this out now. I'm going with a static API and a driver model, should be pretty slick.