mirror of
https://github.com/gtbu/Typesetter-5.3-p8.git
synced 2024-11-24 15:19:12 +01:00
Added sodium-wrapper
This commit is contained in:
parent
d76ed1233d
commit
438ba114f5
5 changed files with 815 additions and 0 deletions
26
include/thirdparty/crypt/phpencrypt/LICENSE
vendored
Normal file
26
include/thirdparty/crypt/phpencrypt/LICENSE
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
https://github.com/dwgebler/php-encryption
|
||||
|
||||
A cryptography API wrapping the Sodium library, providing a simple object interface for symmetrical and asymmetrical encryption, decryption, digital signing and message authentication.
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 David Gebler
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
239
include/thirdparty/crypt/phpencrypt/README.md
vendored
Normal file
239
include/thirdparty/crypt/phpencrypt/README.md
vendored
Normal file
|
@ -0,0 +1,239 @@
|
|||
# PHP Encryption
|
||||
|
||||
![Build Status!](https://app.travis-ci.com/dwgebler/php-encryption.svg?token=uj4HfXm5wqJXVuPAd984&branch=master)
|
||||
|
||||
A cryptography API wrapping the Sodium library, providing a simple object interface for symmetrical and asymmetrical encryption, decryption, digital signing and message authentication.
|
||||
|
||||
The `Encryption` class is able to generate secrets and keypairs, encrypt and decrypt data, sign and verify data, and generate and verify digital signatures.
|
||||
|
||||
Encrypted messages are returned base64 encoded, while keys and secrets are returned as hexadecimal strings.
|
||||
|
||||
The transformation of these to and from binary data makes use of the `sodium_*` timing-safe functions.
|
||||
|
||||
All underlying cryptography is performed using the [Sodium](https://www.php.net/manual/en/book.sodium.php) library.
|
||||
|
||||
This library requires PHP 7.2 or higher with `libsodium` installed (this is bundled with PHP 7.2 or above,
|
||||
so you probably already have it).
|
||||
|
||||
## Installation
|
||||
|
||||
Install via Composer
|
||||
|
||||
```bash
|
||||
composer require dwgebler/encryption
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
For a quick start, see the included `demo.php` file.
|
||||
|
||||
Create an instance of the Encryption class.
|
||||
|
||||
```php
|
||||
<?php
|
||||
use Gebler\Encryption\Encryption;
|
||||
|
||||
$crypt = new Encryption();
|
||||
```
|
||||
|
||||
### Symmetric Encryption
|
||||
|
||||
Use the function `encryptWithSecret(string $message, string $password, bool $hexEncoded = true)` to encrypt a message with a secret key.
|
||||
This function expects the message or data to be encrypted as a string, and the secret key as a hexadecimal string.
|
||||
If your secret is not a hexadecimal encoded, you can pass `false` as the third parameter to indicate that the secret is not encoded.
|
||||
|
||||
You can either generate a secret key with `generateSecret()` or use a pre-existing one.
|
||||
|
||||
Alternatively, you can pass in a reference to a null or empty string to generate a secret key.
|
||||
|
||||
```php
|
||||
$mySecret = null;
|
||||
$data = "Hello world! This is a secret message.";
|
||||
$result = $crypt->encryptWithSecret($data, $mySecret);
|
||||
// $mySecret has now been populated with a new secret key
|
||||
|
||||
// Alternatively, generate a new key.
|
||||
$mySecret = $crypt->generateEncryptionSecret();
|
||||
$result = $crypt->encryptWithSecret($data, $mySecret);
|
||||
|
||||
// Alternatively, create a key and encode it as hex.
|
||||
// Keys should be 32 bytes long - shorter keys are forced to this length by a deterministic hash,
|
||||
// but this is not recommended. Longer keys will throw an InvalidArgumentException.
|
||||
$mySecret = bin2hex("my_super_secret_key");
|
||||
// ...or use random_bytes() to generate a random key.
|
||||
$mySecret = bin2hex(random_bytes(32));
|
||||
$result = $crypt->encryptWithSecret($data, $mySecret);
|
||||
|
||||
// Or, pass in a raw binary key by setting the `hex` parameter to false.
|
||||
$mySecret = random_bytes(32);
|
||||
$result = $crypt->encryptWithSecret($data, $mySecret, false);
|
||||
// $result is now base64 encoded, e.g.
|
||||
echo $result;
|
||||
// wgYwuB/by9bz+CvHj1EtylicXnRH6hl9hLALsUUPUHaZeO3sEj4hgi8+pKBZGZIG6ueRKw3xpvrG8dRWU9OCn3aMtlwLz8aapUX/oK3L
|
||||
```
|
||||
|
||||
To decrypt your message, use the function `decryptWithSecret()`.
|
||||
|
||||
```php
|
||||
$mySecret = "my_super_secret_key";
|
||||
$message = "This is a test message.";
|
||||
$encrypted = $crypt->encryptWithSecret($message, $mySecret, false);
|
||||
echo $encrypted, PHP_EOL;
|
||||
$decrypted = $crypt->decryptWithSecret($encrypted, $mySecret, false);
|
||||
echo $decrypted, PHP_EOL;
|
||||
```
|
||||
|
||||
### Asymmetric Encryption
|
||||
|
||||
To carry out authenticated asymmetric encryption (i.e. where the message is both encrypted and the sender of the message can be verified), you need to generate a public and private key pair for the sender.
|
||||
You will also need the public key of the recipient.
|
||||
|
||||
```php
|
||||
// Generate a new random keypair.
|
||||
$keypair = $crypt->generateEncryptionKeypair();
|
||||
// Or provide a password to generate a deterministic keypair.
|
||||
$keypair = $crypt->generateEncryptionKeypair("my_super_secret_password");
|
||||
// Or use a pre-existing keypair.
|
||||
|
||||
// Once you have a keypair, you can export the public key as a hexadecimal string,
|
||||
// for storage or transmission.
|
||||
$publicKey = $keypair['publicKey'];
|
||||
|
||||
// The keypair also includes the private key.
|
||||
$privateKey = $keypair['privateKey'];
|
||||
|
||||
// The full keypair is also provided. This is a string containing both the private and public key.
|
||||
$fullKeypair = $keypair['keypair'];
|
||||
```
|
||||
|
||||
As an example, let's encrypt a message from Alice to Bob.
|
||||
|
||||
```php
|
||||
$aliceKeypair = $crypt->generateEncryptionKeypair("alice_secret");
|
||||
// In the real-world, Bob has provided Alice with his public key, but for demo purposes
|
||||
// we'll generate a keypair for him too.
|
||||
$bobKeypair = $crypt->generateEncryptionKeypair("bob_secret");
|
||||
|
||||
// Alice encrypts a message for Bob, using his public key and her private key.
|
||||
$message = "Hello Bob! This is a secret message from Alice.";
|
||||
$encrypted = $crypt->encryptWithKey($message, $bobKeypair['publicKey'], $aliceKeypair['privateKey']);
|
||||
// Alice can now transmit $encrypted to Bob. It will look something like this:
|
||||
// hMvdJf2L78ZWcF38WRXJ16q3xXnlsWWfOsbJISPVwJhBtdcWbZ8SquS3oyJD1k6H/lAs+VHXPpDNfYLWO3wMLl+FB8rYUyCe+IZzti3dFL0YljeJ3QreGlrv
|
||||
echo $encrypted, PHP_EOL;
|
||||
|
||||
// Bob decrypts the message using his private key and the public key of Alice.
|
||||
$decrypted = $crypt->decryptWithKey($encrypted, $bobKeypair['privateKey'], $aliceKeypair['publicKey']);
|
||||
// Hello Bob! This is a secret message from Alice.
|
||||
echo $decrypted, PHP_EOL;
|
||||
```
|
||||
|
||||
You can also use this library to carry out anonymous asymmetric encryption, using only the public key of the
|
||||
recipient. In this case, the sender's private key is not required and although only the recipient (the holder of the corresponding private key) can decode the message,
|
||||
they cannot identify or authenticate the sender. This is similar to `openssl_public_encrypt `.
|
||||
|
||||
```php
|
||||
$bobKeypair = $crypt->generateEncryptionKeypair("bob_secret");
|
||||
// Alice encrypts a message for Bob, using his public key.
|
||||
$message = "Hello Bob! This is a secret message from an unknown sender.";
|
||||
$encrypted = $crypt->encryptWithKey($message, $bobKeypair['publicKey']);
|
||||
// Alice can now transmit $encrypted to Bob.
|
||||
echo $encrypted, PHP_EOL;
|
||||
|
||||
// Bob decrypts the message using his full keypair.
|
||||
$decrypted = $crypt->decryptWithKey($encrypted, $bobKeypair['keypair']);
|
||||
// Hello Bob! This is a secret message from an unknown sender.
|
||||
echo $decrypted, PHP_EOL;
|
||||
```
|
||||
|
||||
### Digital Signing
|
||||
|
||||
Asymmetric encryption is useful for securing messages, but it is also useful for authenticating the sender of a message.
|
||||
|
||||
Digital signatures are a way to authenticate the sender of a message, as well the message itself, ensuring it
|
||||
has not been tampered with or altered during transmission.
|
||||
|
||||
```php
|
||||
// Generate a new random keypair.
|
||||
// Like generateEncryptionKeypair, you can also optionally provide a password to generate a deterministic keypair.
|
||||
$aliceSigningKeypair = $crypt->generateSigningKeypair();
|
||||
|
||||
// Alice signs a message for Bob, using her private key.
|
||||
$message = "This is a message signed by Alice.";
|
||||
$signedMessage = $crypt->getSignedMessage($message, $aliceSigningKeypair['privateKey']);
|
||||
// Alice can now transmit $signedMessage to Bob. It will look something like this:
|
||||
// JaI6p6jb5qQ041DiK1Yqbk8u1r/wVAovzy57ELfwrWfhqLCUU9jTzBLH6K6v1VF/8vOxaOZe2r8ch/GUKmfgC1RoaXMgaXMgYSBtZXNzYWdlIHNpZ25lZCBieSBBbGljZS4=
|
||||
// Note: The message itself is NOT encrypted and can be viewed by anyone, by decoding the base64-encoded signed message.
|
||||
echo $signedMessage, PHP_EOL;
|
||||
|
||||
// Bob can now use Alice's public key to verify the signature and obtain the message part.
|
||||
// If the message has been tampered with, the signature will be invalid and the message will be rejected.
|
||||
$verifiedMessage = $crypt->verifySignedMessage($signedMessage, $aliceSigningKeypair['publicKey']);
|
||||
// This is a message signed by Alice.
|
||||
echo $verifiedMessage, PHP_EOL;
|
||||
|
||||
```
|
||||
|
||||
We can also generate a signature for a message without attaching it to the message itself.
|
||||
|
||||
```php
|
||||
$aliceSigningKeypair = $crypt->generateSigningKeypair();
|
||||
|
||||
// Alice signs a message for Bob, using her private key.
|
||||
$message = "This is a message signed by Alice.";
|
||||
$signature = $crypt->getMessageSignature($message, $aliceSigningKeypair['privateKey']);
|
||||
|
||||
// Alice can now transmit the message and signature separately to Bob.
|
||||
// Bob can now use Alice's public key to verify the signature.
|
||||
// If the message has been tampered with, the signature will be invalid and the message will be rejected.
|
||||
$messageAuthenticated = $crypt->verifyMessageSignature($message, $signature, $aliceSigningKeypair['publicKey']);
|
||||
if ($messageAuthenticated === true) {
|
||||
echo "The message has not been tampered with.", PHP_EOL;
|
||||
}
|
||||
```
|
||||
|
||||
### Message Authentication
|
||||
|
||||
Instead of asymmetric keys, we can also use a shared secret to generate a Message Authentication Code (MAC)
|
||||
and use this to sign and authenticate messages.
|
||||
|
||||
```php
|
||||
$message = "This is a message signed anonymously with a secret key.";
|
||||
// We can generate a secure, random 32 byte key, which is returned as a hexadecimal string.
|
||||
$secret = $crypt->generateSigningSecret();
|
||||
// Or, as long as the key is 32 bytes (256 bits), you can use any other string.
|
||||
$secret = hash("sha256", "my secret key");
|
||||
|
||||
// Like with the symmetric encryption functions, you can pass an optional third parameter
|
||||
// to signWithSecret to specify that the secret key is NOT a hexadecimal string.
|
||||
$secret = hash("sha256", "my secret key", true);
|
||||
$signature = $crypt->signWithSecret($message, $secret, false);
|
||||
|
||||
// Or omit this parameter if the secret is a hexadecimal string.
|
||||
$secret = $crypt->generateSigningSecret();
|
||||
$signature = $crypt->signWithSecret($message, $secret);
|
||||
|
||||
// The message can now be either transmitted to someone else who also has the shared secret,
|
||||
// or later verified on the same system, e.g. after being retrieved from a database.
|
||||
$messageAuthenticated = $crypt->verifyWithSecret($signature, $message, $secret);
|
||||
|
||||
if ($messageAuthenticated === true) {
|
||||
echo "The message has not been tampered with.", PHP_EOL;
|
||||
}
|
||||
|
||||
// Similarly, pass false as the third parameter if the secret is NOT a hexadecimal string.
|
||||
$secret = hash("sha256", "my secret key", true);
|
||||
$signature = $crypt->signWithSecret($message, $secret, false);
|
||||
$messageAuthenticated = $crypt->verifyWithSecret($signature, $message, $secret, false);
|
||||
if ($messageAuthenticated === true) {
|
||||
echo "The message has not been tampered with.", PHP_EOL;
|
||||
}
|
||||
```
|
||||
|
||||
### Licence
|
||||
|
||||
This software is released under the [MIT License](https://opensource.org/licenses/MIT).
|
||||
|
||||
### Bugs, questions, comments
|
||||
|
||||
Please raise a GitHub issue if you encounter any problems or have any questions.
|
||||
|
17
include/thirdparty/crypt/phpencrypt/phpunit.xml.dist
vendored
Normal file
17
include/thirdparty/crypt/phpencrypt/phpunit.xml.dist
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false" bootstrap="./vendor/autoload.php" colors="true" testdox="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" verbose="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</include>
|
||||
<report>
|
||||
<clover outputFile="build/logs/clover.xml"/>
|
||||
</report>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="All">
|
||||
<directory>./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<logging/>
|
||||
</phpunit>
|
434
include/thirdparty/crypt/phpencrypt/src/Encryption.php
vendored
Normal file
434
include/thirdparty/crypt/phpencrypt/src/Encryption.php
vendored
Normal file
|
@ -0,0 +1,434 @@
|
|||
<?php
|
||||
|
||||
namespace Gebler\Encryption;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use SodiumException;
|
||||
use Exception;
|
||||
|
||||
class Encryption
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('sodium')) {
|
||||
throw new RuntimeException('The sodium extension is not loaded.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a deterministic 32 byte hash from the given password and encode as hex.
|
||||
* Use this to obtain a text representation of the password that can be easily stored or transmitted.
|
||||
* @param string $password
|
||||
* @return string
|
||||
*/
|
||||
public function hashPassword(string $password): string
|
||||
{
|
||||
try {
|
||||
return sodium_bin2hex(sodium_crypto_generichash($password, "", 32));
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not generate password hash.', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetrical (shared secret) encryption of a message with a password.
|
||||
* @param string $data
|
||||
* @param string|null $password
|
||||
* @param bool $keyIsHex
|
||||
* @return string
|
||||
* @throws InvalidArgumentException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function encryptWithSecret(string $data, ?string &$password = "", bool $keyIsHex = true): string
|
||||
{
|
||||
try {
|
||||
if ($data === '') {
|
||||
throw new InvalidArgumentException('Data to encrypt cannot be empty.');
|
||||
}
|
||||
if ($password === null || $password === '') {
|
||||
$password = sodium_bin2hex(sodium_crypto_secretbox_keygen());
|
||||
$keyIsHex = true;
|
||||
}
|
||||
|
||||
$key = $password;
|
||||
if ($keyIsHex) {
|
||||
$key = sodium_hex2bin($password);
|
||||
}
|
||||
|
||||
if (strlen($key) < SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
|
||||
$key = sodium_crypto_generichash($key, "", 32);
|
||||
}
|
||||
|
||||
if (strlen($key) !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
|
||||
throw new InvalidArgumentException('Key must be 32 bytes long');
|
||||
}
|
||||
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
|
||||
$encrypted = sodium_crypto_secretbox($data, $nonce, $key);
|
||||
$encrypted = sodium_bin2base64($nonce . $encrypted, SODIUM_BASE64_VARIANT_ORIGINAL);
|
||||
sodium_memzero($data);
|
||||
sodium_memzero($key);
|
||||
sodium_memzero($nonce);
|
||||
return $encrypted;
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not encrypt data', 0, $e);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
throw new RuntimeException('Unable to generate random bytes', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetrical (shared secret) decryption of a message with a password.
|
||||
* @param string $data
|
||||
* @param string $password
|
||||
* @param bool $keyIsHex
|
||||
* @return string
|
||||
* @throws RuntimeException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function decryptWithSecret(string $data, string $password, bool $keyIsHex = true): string
|
||||
{
|
||||
try {
|
||||
if ($keyIsHex) {
|
||||
$password = sodium_hex2bin($password);
|
||||
}
|
||||
if (strlen($password) < SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
|
||||
$password = sodium_crypto_generichash($password, "", 32);
|
||||
}
|
||||
if (strlen($password) !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
|
||||
throw new InvalidArgumentException('Key must be 32 bytes long');
|
||||
}
|
||||
$decoded = sodium_base642bin($data, SODIUM_BASE64_VARIANT_ORIGINAL);
|
||||
if (strlen($decoded) < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) {
|
||||
throw new InvalidArgumentException('Encrypted data is too short');
|
||||
}
|
||||
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
|
||||
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
|
||||
$plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $password);
|
||||
if ($plaintext === false) {
|
||||
throw new InvalidArgumentException('Could not decrypt data; probably the wrong password');
|
||||
}
|
||||
sodium_memzero($ciphertext);
|
||||
sodium_memzero($nonce);
|
||||
sodium_memzero($password);
|
||||
return $plaintext;
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not decrypt data; probably password wrong format', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetrical message authentication code (MAC) of a message with a password.
|
||||
* Returns a hex string of the message signature.
|
||||
*/
|
||||
public function signWithSecret(string $data, ?string &$key = "", bool $keyIsHex = true): string
|
||||
{
|
||||
try {
|
||||
if ($key === null || $key === "") {
|
||||
$key = $this->generateSigningSecret();
|
||||
}
|
||||
|
||||
$realKey = $key;
|
||||
|
||||
if ($keyIsHex) {
|
||||
$realKey = sodium_hex2bin($key);
|
||||
}
|
||||
|
||||
if (strlen($realKey) !== SODIUM_CRYPTO_AUTH_KEYBYTES) {
|
||||
throw new InvalidArgumentException('The key must be ' . SODIUM_CRYPTO_AUTH_KEYBYTES . ' long.');
|
||||
}
|
||||
|
||||
if (strlen($data) === 0) {
|
||||
throw new InvalidArgumentException('The data must not be empty.');
|
||||
}
|
||||
|
||||
$result = sodium_bin2hex(sodium_crypto_auth($data, $realKey));
|
||||
sodium_memzero($data);
|
||||
sodium_memzero($realKey);
|
||||
return $result;
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not sign data', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the MAC of a message with a password.
|
||||
*/
|
||||
public function verifyWithSecret(string $signature, string $data, string $key, bool $keyIsHex = true): bool
|
||||
{
|
||||
try {
|
||||
$signature = sodium_hex2bin($signature);
|
||||
if ($keyIsHex) {
|
||||
$key = sodium_hex2bin($key);
|
||||
}
|
||||
if (strlen($key) !== SODIUM_CRYPTO_AUTH_KEYBYTES) {
|
||||
throw new InvalidArgumentException('The key must be ' . SODIUM_CRYPTO_AUTH_KEYBYTES . ' long.');
|
||||
}
|
||||
$result = sodium_crypto_auth_verify($signature, $data, $key);
|
||||
sodium_memzero($signature);
|
||||
sodium_memzero($key);
|
||||
return $result;
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not verify data', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random key for use with symmetrical signing and return as a hex string.
|
||||
* @return string
|
||||
*/
|
||||
public function generateSigningSecret(): string
|
||||
{
|
||||
try {
|
||||
return sodium_bin2hex(sodium_crypto_auth_keygen());
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not generate signing key', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random key for use with symmetrical encryption and return as a hex string.
|
||||
*/
|
||||
public function generateEncryptionSecret(): string
|
||||
{
|
||||
try {
|
||||
return sodium_bin2hex(sodium_crypto_secretbox_keygen());
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not generate encryption key', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an X25519 key pair and return the public and private key as an array of hex strings.
|
||||
* If a password is supplied, the generated key pair will be deterministic.
|
||||
*/
|
||||
public function generateEncryptionKeypair(?string $password = ''): array
|
||||
{
|
||||
try {
|
||||
$keypair = empty($password) ?
|
||||
sodium_crypto_box_keypair() :
|
||||
sodium_crypto_box_seed_keypair(sodium_crypto_generichash($password, "", 32));
|
||||
|
||||
return [
|
||||
'keypair' => sodium_bin2hex($keypair),
|
||||
'publicKey' => sodium_bin2hex(sodium_crypto_box_publickey($keypair)),
|
||||
'privateKey' => sodium_bin2hex(sodium_crypto_box_secretkey($keypair)),
|
||||
];
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not generate keypair.', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an Ed25519 key pair and return the public and private key as an array of hex strings.
|
||||
* If a password is supplied, the generated key pair will be deterministic.
|
||||
*/
|
||||
public function generateSigningKeypair(?string $password = ''): array
|
||||
{
|
||||
try {
|
||||
$keypair = empty($password) ?
|
||||
sodium_crypto_sign_keypair() :
|
||||
sodium_crypto_sign_seed_keypair(sodium_crypto_generichash($password, "", 32));
|
||||
|
||||
return [
|
||||
'keypair' => sodium_bin2hex($keypair),
|
||||
'publicKey' => sodium_bin2hex(sodium_crypto_sign_publickey($keypair)),
|
||||
'privateKey' => sodium_bin2hex(sodium_crypto_sign_secretkey($keypair)),
|
||||
];
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not generate keypair.', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message with an Ed25519 private key and return the signed message.
|
||||
*/
|
||||
public function getSignedMessage(string $message, string $privateKey): string
|
||||
{
|
||||
try {
|
||||
$privateKey = sodium_hex2bin($privateKey);
|
||||
if (strlen($privateKey) !== SODIUM_CRYPTO_SIGN_SECRETKEYBYTES) {
|
||||
throw new InvalidArgumentException('The key must be ' . SODIUM_CRYPTO_SIGN_SECRETKEYBYTES . ' long.');
|
||||
}
|
||||
|
||||
if (strlen($message) === 0) {
|
||||
throw new InvalidArgumentException('The message must not be empty.');
|
||||
}
|
||||
|
||||
return sodium_bin2base64(sodium_crypto_sign($message, $privateKey), SODIUM_BASE64_VARIANT_ORIGINAL);
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not sign data', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a signed message with an Ed25519 public key, ensuring it hasn't been tampered with and return the message.
|
||||
*/
|
||||
public function verifySignedMessage(string $signedMessage, string $publicKey): string
|
||||
{
|
||||
try {
|
||||
$publicKey = sodium_hex2bin($publicKey);
|
||||
$signedMessage = sodium_base642bin($signedMessage, SODIUM_BASE64_VARIANT_ORIGINAL);
|
||||
if (strlen($publicKey) !== SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES) {
|
||||
throw new InvalidArgumentException('The key must be ' . SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES . ' long.');
|
||||
}
|
||||
|
||||
if (strlen($signedMessage) === 0) {
|
||||
throw new InvalidArgumentException('The message must not be empty.');
|
||||
}
|
||||
|
||||
$result = sodium_crypto_sign_open($signedMessage, $publicKey);
|
||||
if ($result === false) {
|
||||
throw new RuntimeException('Could not verify message.');
|
||||
}
|
||||
return $result;
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not verify data', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message with an ED25519 private key and return the signature.
|
||||
*/
|
||||
public function getMessageSignature(string $message, string $privateKey): string
|
||||
{
|
||||
try {
|
||||
$privateKey = sodium_hex2bin($privateKey);
|
||||
if (strlen($privateKey) !== SODIUM_CRYPTO_SIGN_SECRETKEYBYTES) {
|
||||
throw new InvalidArgumentException('The key must be ' . SODIUM_CRYPTO_SIGN_SECRETKEYBYTES . ' long.');
|
||||
}
|
||||
|
||||
if (strlen($message) === 0) {
|
||||
throw new InvalidArgumentException('The message must not be empty.');
|
||||
}
|
||||
|
||||
return sodium_bin2hex(sodium_crypto_sign_detached($message, $privateKey));
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not sign data', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a signed message with an ED25519 public key, ensuring it hasn't been tampered with and return true
|
||||
* if the signature is valid.
|
||||
*/
|
||||
public function verifyMessageSignature(string $message, string $signature, string $publicKey): bool
|
||||
{
|
||||
try {
|
||||
$publicKey = sodium_hex2bin($publicKey);
|
||||
$signature = sodium_hex2bin($signature);
|
||||
if (strlen($publicKey) !== SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES) {
|
||||
throw new InvalidArgumentException('The key must be ' . SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES . ' long.');
|
||||
}
|
||||
|
||||
if (strlen($message) === 0) {
|
||||
throw new InvalidArgumentException('The message must not be empty.');
|
||||
}
|
||||
|
||||
if (strlen($signature) !== SODIUM_CRYPTO_SIGN_BYTES) {
|
||||
throw new InvalidArgumentException('The signature must be ' . SODIUM_CRYPTO_SIGN_BYTES . ' long.');
|
||||
}
|
||||
|
||||
return sodium_crypto_sign_verify_detached($signature, $message, $publicKey);
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not verify data', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a message with the recipient public key and optionally sign with the sender private key.
|
||||
* @param string $data
|
||||
* @param string $recipientPublicKey
|
||||
* @param string|null $senderPrivateKey
|
||||
* @return string
|
||||
*/
|
||||
public function encryptWithKey(string $data, string $recipientPublicKey, ?string $senderPrivateKey = null): string
|
||||
{
|
||||
try {
|
||||
if ($senderPrivateKey !== null) {
|
||||
return $this->encryptAuthenticated($data, $recipientPublicKey, $senderPrivateKey);
|
||||
}
|
||||
// Anonymous encryption
|
||||
$recipientPublicKey = sodium_hex2bin($recipientPublicKey);
|
||||
$encrypted = sodium_crypto_box_seal($data, $recipientPublicKey);
|
||||
sodium_memzero($recipientPublicKey);
|
||||
sodium_memzero($data);
|
||||
return sodium_bin2base64($encrypted, SODIUM_BASE64_VARIANT_ORIGINAL);
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not encrypt data', 0, $e);
|
||||
} catch (Exception $e) {
|
||||
throw new RuntimeException('Unable to generate random bytes', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt a message with either the recipient keypair for anonymous decryption, or
|
||||
* the recipient private key and sender public key for authenticated decryption.
|
||||
*/
|
||||
public function decryptWithKey(string $data, string $recipientKey, ?string $senderPublicKey = null): string
|
||||
{
|
||||
try {
|
||||
if ($senderPublicKey !== null) {
|
||||
return $this->decryptAuthenticated($data, $recipientKey, $senderPublicKey);
|
||||
}
|
||||
// Anonymous decryption
|
||||
$recipientKeyPair = sodium_hex2bin($recipientKey);
|
||||
$decoded = sodium_base642bin($data, SODIUM_BASE64_VARIANT_ORIGINAL);
|
||||
$plaintext = sodium_crypto_box_seal_open($decoded, $recipientKeyPair);
|
||||
sodium_memzero($recipientKeyPair);
|
||||
sodium_memzero($decoded);
|
||||
sodium_memzero($data);
|
||||
return $plaintext;
|
||||
} catch (SodiumException $e) {
|
||||
throw new RuntimeException('Could not decrypt data', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SodiumException
|
||||
* @throws Exception
|
||||
*/
|
||||
private function encryptAuthenticated(string $data, string $recipientPublicKey, string $senderPrivateKey): string
|
||||
{
|
||||
$senderPrivateKey = sodium_hex2bin($senderPrivateKey);
|
||||
$recipientPublicKey = sodium_hex2bin($recipientPublicKey);
|
||||
|
||||
$key = sodium_crypto_box_keypair_from_secretkey_and_publickey($senderPrivateKey, $recipientPublicKey);
|
||||
|
||||
$nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES);
|
||||
$ciphertext = sodium_crypto_box($data, $nonce, $key);
|
||||
|
||||
sodium_memzero($data);
|
||||
sodium_memzero($key);
|
||||
|
||||
return sodium_bin2base64($nonce . $ciphertext, SODIUM_BASE64_VARIANT_ORIGINAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SodiumException
|
||||
*/
|
||||
private function decryptAuthenticated(string $data, string $recipientPrivateKey, string $senderPublicKey): string
|
||||
{
|
||||
$senderPublicKey = sodium_hex2bin($senderPublicKey);
|
||||
$recipientPrivateKey = sodium_hex2bin($recipientPrivateKey);
|
||||
|
||||
$key = sodium_crypto_box_keypair_from_secretkey_and_publickey($recipientPrivateKey, $senderPublicKey);
|
||||
|
||||
$data = sodium_base642bin($data, SODIUM_BASE64_VARIANT_ORIGINAL);
|
||||
|
||||
$nonce = substr($data, 0, SODIUM_CRYPTO_BOX_NONCEBYTES);
|
||||
$ciphertext = substr($data, SODIUM_CRYPTO_BOX_NONCEBYTES);
|
||||
|
||||
$plaintext = sodium_crypto_box_open($ciphertext, $nonce, $key);
|
||||
|
||||
sodium_memzero($data);
|
||||
sodium_memzero($key);
|
||||
|
||||
if ($plaintext === false) {
|
||||
throw new RuntimeException('Decryption failed.');
|
||||
}
|
||||
return $plaintext;
|
||||
}
|
||||
}
|
99
include/thirdparty/crypt/phpencrypt/src/demo.php
vendored
Normal file
99
include/thirdparty/crypt/phpencrypt/src/demo.php
vendored
Normal file
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
use Gebler\Encryption\Encryption;
|
||||
|
||||
require_once('vendor/autoload.php');
|
||||
|
||||
$crypt = new Encryption();
|
||||
|
||||
/**
|
||||
* Example 1: Symmetric (anonymous) message authentication.
|
||||
*/
|
||||
|
||||
$message = "This is a message signed anonymously with a secret key.";
|
||||
$secret = $crypt->generateSigningSecret();
|
||||
$signature = $crypt->signWithSecret($message, $secret);
|
||||
$messageAuthenticated = $crypt->verifyWithSecret($signature, $message, $secret);
|
||||
if ($messageAuthenticated === true) {
|
||||
echo "The message has not been tampered with.", PHP_EOL;
|
||||
}
|
||||
|
||||
echo PHP_EOL,"-------------------------------------------------------",PHP_EOL;
|
||||
|
||||
/**
|
||||
* Example 2: Asymmetric (identified) message authentication.
|
||||
*/
|
||||
|
||||
$aliceSigningKeypair = $crypt->generateSigningKeypair();
|
||||
$message = "This is a message signed by Alice.";
|
||||
$signedMessage = $crypt->getSignedMessage($message, $aliceSigningKeypair['privateKey']);
|
||||
echo $signedMessage, PHP_EOL;
|
||||
$verifiedMessage = $crypt->verifySignedMessage($signedMessage, $aliceSigningKeypair['publicKey']);
|
||||
echo $verifiedMessage, PHP_EOL;
|
||||
|
||||
echo PHP_EOL,"-------------------------------------------------------",PHP_EOL;
|
||||
|
||||
/**
|
||||
* Example 3: Asymmetric (identified) message signature (detached).
|
||||
*/
|
||||
|
||||
$aliceSigningKeypair = $crypt->generateSigningKeypair();
|
||||
$message = "This is a message signed by Alice.";
|
||||
$signature = $crypt->getMessageSignature($message, $aliceSigningKeypair['privateKey']);
|
||||
$messageAuthenticated = $crypt->verifyMessageSignature($message, $signature, $aliceSigningKeypair['publicKey']);
|
||||
if ($messageAuthenticated === true) {
|
||||
echo "The message has not been tampered with.", PHP_EOL;
|
||||
}
|
||||
|
||||
echo PHP_EOL,"-------------------------------------------------------",PHP_EOL;
|
||||
|
||||
/**
|
||||
* Example 4: Symmetric encryption with secret key.
|
||||
*/
|
||||
|
||||
$mySecret = $crypt->generateEncryptionSecret();
|
||||
$message = "This is a test message.";
|
||||
$encrypted = $crypt->encryptWithSecret($message, $mySecret);
|
||||
echo $encrypted, PHP_EOL;
|
||||
$decrypted = $crypt->decryptWithSecret($encrypted, $mySecret);
|
||||
echo $decrypted, PHP_EOL;
|
||||
|
||||
$mySecret = random_bytes(32);
|
||||
$message = "This is another test message.";
|
||||
// Password is raw binary data.
|
||||
$encrypted = $crypt->encryptWithSecret($message, $mySecret, false);
|
||||
echo $encrypted, PHP_EOL;
|
||||
$decrypted = $crypt->decryptWithSecret($encrypted, $mySecret, false);
|
||||
echo $decrypted, PHP_EOL;
|
||||
|
||||
echo PHP_EOL,"-------------------------------------------------------",PHP_EOL;
|
||||
|
||||
/**
|
||||
* Example 5: Asymmetric encryption with public key and signed by sender.
|
||||
*/
|
||||
|
||||
$aliceKeypair = $crypt->generateEncryptionKeypair("alice_secret");
|
||||
$bobKeypair = $crypt->generateEncryptionKeypair("bob_secret");
|
||||
|
||||
$message = "Hello Bob! This is a secret message from Alice.";
|
||||
$encrypted = $crypt->encryptWithKey($message, $bobKeypair['publicKey'], $aliceKeypair['privateKey']);
|
||||
echo $encrypted, PHP_EOL;
|
||||
|
||||
$decrypted = $crypt->decryptWithKey($encrypted, $bobKeypair['privateKey'], $aliceKeypair['publicKey']);
|
||||
echo $decrypted, PHP_EOL;
|
||||
|
||||
echo PHP_EOL,"-------------------------------------------------------",PHP_EOL;
|
||||
|
||||
/**
|
||||
* Example 6: Anonymous asymmetric encryption to recipient with public key only.
|
||||
*/
|
||||
|
||||
$bobKeypair = $crypt->generateEncryptionKeypair("bob_secret");
|
||||
$message = "Hello Bob! This is a secret message from an unknown sender.";
|
||||
$encrypted = $crypt->encryptWithKey($message, $bobKeypair['publicKey']);
|
||||
echo $encrypted, PHP_EOL;
|
||||
|
||||
$decrypted = $crypt->decryptWithKey($encrypted, $bobKeypair['keypair']);
|
||||
echo $decrypted, PHP_EOL;
|
||||
|
||||
echo PHP_EOL,"-------------------------------------------------------",PHP_EOL;
|
Loading…
Reference in a new issue