Passwordless Cryptography Library
Symmetric encryption without storing or sending a key: each message derives its own key from a shared key seed.
Why
Encrypting with AES is easy; the hard part is the key. A fixed key used for everything is one leak away from exposing all the data, and rotating it or sending it to the other side is where most implementations go wrong.
Here the two sides share a key seed once, and no key is ever stored or transmitted. Each encryption picks a key and IV at random from the seed and writes only a 4-byte header that says how to rebuild them, meaningless without the seed:
$crypto = new OpenSSLCrypto($algorithm, new KeySet($keySeed));
$payload = $crypto->encrypt('My text'); // key and IV picked at random from the seed
echo $crypto->decrypt($payload);
How it works
The algorithm is well-known, but the major problem is HOW to store and exchange symmetric keys securely. This library solves this with an innovative "keyless" exchange mechanism:
Key Seed Instead of Keys
Instead of storing or transmitting actual encryption keys, this library uses a key seed - a list of 2-255 entries of 32 bytes each (the default is 32 entries). Both the sender and receiver must have the same key seed.
Dynamic Key Generation with Scrolling Window
For each encryption operation:
- The library uses an 8-byte scrolling window to extract random portions from the key seed entries
- A 4-byte header encodes the key seed entry indices and extraction offsets
- The encrypted data includes this header (embedded in the payload)
- The actual key and IV are never stored or transmitted
The scrolling window can extract 8 bytes from any position (0-24) within each 32-byte key seed entry, providing 25 possible positions per entry. With the default 32 entries and AES that is 435,200 possible key/IV combinations, and more than 27 million with 255 entries (details).
Keyless Decryption
When decrypting:
- The 4-byte header is extracted from the encrypted payload
- Using the same key seed and the header, the exact key and IV are reconstructed using the same offsets
- The data is decrypted and authenticated
This means you can securely exchange encrypted data without ever transmitting the actual encryption keys - only a 4-byte header that's meaningless without the key seed!
Usage
<?php
$keySet = new \ByJG\Crypto\KeySet(
[
// 2-255 entries of 32 bytes each
]
);
$object = new \ByJG\Crypto\OpenSSLCrypto(
$algorithm,
$keySet
);
$text = 'My text needs to be cryptographed';
$encrypted = $object->encrypt($text);
echo $object->decrypt($encrypted);
- The algorithm needs to be one of the algorithms returned by
openssl_get_cipher_methods()that takes a key and, at most, an IV: the CBC, CTR, CFB, OFB and ECB modes andchacha20. The AEAD modes (GCM, CCM, OCB, SIV), the key-wrap modes and the-hmac-variants are not supported; the library authenticates with its own HMAC. - The second parameter is the key seed. It is a list of 2-255 entries of 32 bytes each. You can generate it using the command
\ByJG\Crypto\KeySet::generateKeySet()
Example
Below a full example:
<?php
require "vendor/autoload.php";
$keySet = new \ByJG\Crypto\KeySet(
[
'14dca647bcc087f67b1528cea11094838f5bd2276a08dcabc491c1823afc51dd',
'9cc0fd22a3dc2fb3d444e0721e5d02f5c39f9d6b7c41c010a28e06e861f54c8b',
'e7b965f8b401c06d5180f50f49eb9797ad24fef62b20bcde03456d4ea4006e83',
'd06b7ff23bd76b19bc1283f28a00bb91cccdf6bd163354f099710898e31ac487',
'b4bed7d50032556780b303f8cfea612b637ab8935443af4219dd9eb06d4d7b01',
'0b2f4cb0107ab6946938f2c836cdca74f7e2b1c7482dd2942720ccc755b20097',
'dcd24b8aa48d2bcd2d0b19764088e7d4343cfee6a15c9f805b58e45b6224c2f5',
'628b889c7471e149724973ee96a1c5728c61f11c45e3ae6314a321c7b3488bde',
'4e10b6af85f83951f23514d3c9d1248d1a1777ff114a6768ae116c2a72bc4bc3',
'da13fce62e22f5919efe8f0cb498f067797e5fc68a94c45c9ae9d1717f82555d',
'5bd0600dbca418c8166ffc0617e24f472f147424c58dfd4859cde2cd6a98dee7',
'533c32c8e010920471e2462ba88f9c63278f9cdd7f12adde4a6e15595a56783d',
'13beb307499b0d911c6ea4c12b9e1131c51693b8918ac5a76c09e86477b28b5a',
'e966e4d659c99a58da41c305de0e479b4885f83ddb30476955ad28fd9b9e2d7a',
'b7a6b7535547dd27963e2bb34630edcf81364ae998fdd68356772b300b65dbbc',
'f00a1fd6ac5e8bb774c66ef908052f95c9d654df117958fc13a1b5056ddb331b',
'297e13efdd279687af8c70158b446a4724c4a17989eb0fc93ee87606e958fc9c',
'27a7e110e61076e4901822c940c294f29abbc659370480cd234473c0c90e10ef',
'd28e4c29007269902711ae177e4c882a4893f1fa47a987872879b0a785cf8c20',
'f3be6dc7b34df6aafdc3bd5705fd37d73291ac5a15fe7fd4d39497b43e87dd28',
'8782a68a904d269ae01bb1705dd1b59047749dd07b5b486e5b79b04660054c1e',
'21c05ba1cf9028f35ab3fd02e46dab733f8957e6b003e5ba8ea9917fa1ad2809',
'722a9fe048b6aef9407c5ef7cb76896422ef0add38e5db4afd649a7c7ea1f905',
'6b233fb3e56e55236ab6c862cb982c4df5dde4ca44361b02cd5915160966d3c5',
'bd6802bc1252316c44e277bbdfdac8712223a445899d77f9d996286f2c499668',
'e58088cdef444792501a21813ff520c3fd05cf249b958e0b92fd50142eff74d5',
'9ca20ff424531314ccfd0e067ced0fbc078df65c77d9c5d30470058e6e2fc83c',
'd50f461eab2cb5855d44bb753710193970c646b6017ce2522081d337188ee28d',
'9473c14be30ae90db3d16014a538ce6b19d2477bbf1294793540c19559ed1363',
'20ad5fed300d150305ad48eb1f9b72cc5d24645d3e736ad5c66e6aeee6dcea88',
'522b058ea3c9cb29c010c431b30e6b6449994e03dc6434965c941e8c465881eb',
'dec0adad3df3e8c9f5eb135902970c59cd75fc7c1b52ba41ce8ec5b1351e74dc',
]
);
$object = new \ByJG\Crypto\OpenSSLCrypto(
'aes-256-cbc',
$keySet
);
$enc = $object->encrypt('My secret text needs to be encrypted');
echo $object->decrypt($enc) . "\n";
Important
- Do not share the key seed.
- You'll only be able to decrypt the text if you have the same key seed used to encrypt it.
Documentation
- Advanced uses of KeySet class
- Interoperability with JavaScript
- Keyless Exchange - How it works in detail
Installation
composer require "byjg/crypto"
Running Tests
vendor/bin/phpunit