Skip to content

Commit

Permalink
Deprecate Symmetric Key (#53)
Browse files Browse the repository at this point in the history
* Deprecate Symmetric Key

* Fix changelog
  • Loading branch information
Menelion authored Jul 9, 2022
1 parent ea20997 commit ea5cb63
Show file tree
Hide file tree
Showing 27 changed files with 354 additions and 99 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

* [#52](https://github.com/Oire/Iridium-php/pull/52):
Add release notes config; Add PHP CodeSniffer to Github Actions CI.
* [#53](https://github.com/Oire/Iridium-php/pull/53):
Deprecated Symmetric Key in favor of Shared Key for simpler naming.

# Version 1.1

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright © 2021 Andre Polykanine also known as Menelion Elensúlë, The Magical Kingdom of Oirë, https://github.com/Oire
Copyright © 2021-2022 Andre Polykanine also known as Menelion Elensúlë, The Magical Kingdom of Oirë, https://github.com/Oire

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
61 changes: 31 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,18 @@ The Base64 class has the following methods:

The Crypt module is used to encrypt and decrypt data.
**Note**! Do not use this for managing passwords! Passwords must not be encrypted, they must be *hashed* instead. To manage passwords, use the Password module (see below).
Currently the Crypt module supports only symmetric-key encryption, i.e., encryption and decryption is performed with one shared key.
Currently the Crypt module supports only shared key encryption, i.e., encryption and decryption is performed with one single key.

### Symmetric Key
### Shared Key

**Note**! the `SymmetricKey` class is deprecated since version 1.2 and will be removed in version 2.0. It holds the same object but was renamed to `SharedKey` for simplicity.
This objects holds a key used to encrypt and decrypt data with the Crypt module. First you need to create a key and save it somewhere (i.e., in a .env file):

```php
use Oire\Iridium\Key\SymmetricKey;
use Oire\Iridium\Key\SharedKey;

$symmetricKey = new SymmetricKey();
$key = $symmetricKey->getKey();
$sharedKey = new SharedKey();
$key = $sharedKey->getKey();
// Save the key instead
echo $key . PHP_EOL;
```
Expand All @@ -114,56 +115,56 @@ This will output a readable and storable string, something similar to this:
AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8
```

#### Symmetric Key Methods
#### SharedKey Methods

Generally, you will only need the `getKey()` method for storing the key in a safe place. You can also benefit from using the `__toString()` method and treat the key object as a string. However, let’s describe all the methods for the sake of completeness:

* `__construct(string|null $key = null)` — Class constructor. If a key is provided, it will be applied to create a new SymmetricKey instance. If not, a random key will be generated instead.
* `__construct(string|null $key = null)` — Class constructor. If a key is provided, it will be applied to create a new SharedKey instance. If not, a random key will be generated instead.
* `getRawKey(): string` — Returns the key in raw binary form. Needed mostly for internal use.
* `getKey(): string` — Returns the key in readable and storable form. Use this to retrieve a newly generated random key.
* `deriveKeys(string|null $salt = null): DerivedKeys` — Uses [hash key derivation function](https://en.wikipedia.org/wiki/HKDF) to derive encryption and authentication keys and returns a `DerivedKeys` object, see below. Use this only if you really know what you are doing. It is used internally by the Crypt module. If the salt is provided, derives the keys based on that salt (used for decryption). In 99,(9)% of cases you don’t need to use this method directly.
* `__toString(): string` — Returns the readable and storable key when the object is called as a string.

### Derived Keys

The DerivedKeys object holds the keys derived by the `deriveKeys()` method of the symmetric key. Again, in 99,(9)% of cases you don’t want to use it, but let’s enumerate its methods.
The DerivedKeys object holds the keys derived by the `deriveKeys()` method of the shared key. Again, in 99,(9)% of cases you don’t want to use it, but let’s enumerate its methods.

* `__construct(string $salt, string $encryptionKey, string $authenticationKey)` — Class constructor. Is instantiated by the `deriveKeys()` method of the `SymmetricKey` object.
* `__construct(string $salt, string $encryptionKey, string $authenticationKey)` — Class constructor. Is instantiated by the `deriveKeys()` method of the `SharedKey` object.
* `getSalt(): string` — Gets the encryption salt.
* `getEncryptionKey(): string` — Gets the derived encryption key.
* `getAuthenticationKey(): string` — Gets the derived authentication key.
* `areValid(): bool` — Checks if the derived keys are valid. Returns `true` if the keys are valid, `false` otherwise.

### Crypt Usage Examples

If you created a Symmetric key as shown above, you can encrypt your data with this key:
If you created a shared key as shown above, you can encrypt your data with this key:

```php
use Oire\Iridium\Crypt;
use Oire\Iridium\Key\SymmetricKey;
use Oire\Iridium\Key\SharedKey;

$data = 'Mischief managed!';
$symmetricKey = new SymmetricKey($key);
$encrypted = Crypt::encrypt($data, $symmetricKey);
$sharedKey = new SharedKey($key);
$encrypted = Crypt::encrypt($data, $sharedKey);
```

That's it, you may store your encrypted data in a database or perform other actions with them.
To decrypt the data with the same key, use the following:

```php
$decrypted = Crypt::decrypt($encrypted, $symmetricKey);
$decrypted = Crypt::decrypt($encrypted, $sharedKey);
```

### Exceptions
Crypt throws `EncryptionException`, `DecryptionException` and sometimes a more general `CryptException`. If something is wrong with the key, a `SymmetricKeyException` is thrown.
Crypt throws `EncryptionException`, `DecryptionException` and sometimes a more general `CryptException`. If something is wrong with the key, a `SharedKeyException` is thrown.

### Methods

The Crypt class has the following methods:

* `static encrypt(string $data, SymmetricKey $key): string` — Encrypts given data with a given key. Returns the encrypted data in readable and storable form.
* `static Decrypt(string $encryptedData, SymmetricKey $key): string` — Decrypts previously encrypted data with the same key they were encrypted with and returns the original string.
* `static swapKey(string $data, SymmetricKey $oldKey, SymmetricKey $newKey): string` — Reencrypts encrypted data with a different key and returns the newly encrypted data.
* `static encrypt(string $data, SharedKey $key): string` — Encrypts given data with a given key. Returns the encrypted data in readable and storable form.
* `static Decrypt(string $encryptedData, SharedKey $key): string` — Decrypts previously encrypted data with the same key they were encrypted with and returns the original string.
* `static swapKey(string $data, SharedKey $oldKey, SharedKey $newKey): string` — Reencrypts encrypted data with a different key and returns the newly encrypted data.

## 🔒 Password

Expand All @@ -175,14 +176,14 @@ To lock, i.e., hash a password, use the following:

```php
use Oire\Iridium\Exception\PasswordException;
use Oire\Iridium\Key\SymmetricKey;
use Oire\Iridium\Key\SharedKey;
use Oire\Iridium\Password;

// You should have $key somewhere in an environment variable
$symmetricKey = new SymmetricKey($key);
$sharedKey = new SharedKey($key);

try {
$storeMe = Password::lock($_POST['password'], $symmetricKey);
$storeMe = Password::lock($_POST['password'], $sharedKey);
} catch (PasswordException $e) {
// Handle errors
}
Expand All @@ -193,7 +194,7 @@ To check whether a provided password is valid, use the following:

```php
try {
$isPasswordValid = Password::check($_POST['password'], $hashFromDatabase, $symmetricKey);
$isPasswordValid = Password::check($_POST['password'], $hashFromDatabase, $sharedKey);
} catch (PasswordException $e) {
// Handle errors. Something went wrong: most often it's a wrong or corrupted key
}
Expand All @@ -212,8 +213,8 @@ Remember that you cannot "decrypt" a password and obviously must not store unhas

The Password class has the following methods:

* `static Lock(string $password, SymmetricKey $key): string` — Locks, i.e., hashes a password and encrypts it with a given key. Returns the encrypted hash in readable and storable format. A hashed password cannot be restored, so it is safe to be stored in a database.
* `static Check(string $password, string $encryptedHash, SymmetricKey $key): bool` — Verifies whether a given password matches the provided hash. Returns `true` on success and `false` on failure.
* `static Lock(string $password, SharedKey $key): string` — Locks, i.e., hashes a password and encrypts it with a given key. Returns the encrypted hash in readable and storable format. A hashed password cannot be restored, so it is safe to be stored in a database.
* `static Check(string $password, string $encryptedHash, SharedKey $key): bool` — Verifies whether a given password matches the provided hash. Returns `true` on success and `false` on failure.

## 🍪 Osst, Simple Yet Secure Tokens Suitable for Authentication Cookies and Password Recovery

Expand Down Expand Up @@ -340,13 +341,13 @@ You may set expiration time in three different ways, as you like:

You may store some sensitive data in the additional information for the token such as old and new e-mail address and similar things.
**Note**! Do **not** store plain-text passwords in this property, it can be decrypted! Passwords must not be decryptable, they must be *hashed* instead. If you need to handle passwords, use the Password class, it is suitable for proper password hashing (see above). You may store password hashes in this property, though.
If your additional info contains sensitive data, you can encrypt it. To do this, you first need to have an Iridium symmetric key (see above):
If your additional info contains sensitive data, you can encrypt it. To do this, you first need to have an Iridium key (see above):

```php
use Oire\Iridium\Key\SymmetricKey;
use Oire\Iridium\Key\SharedKey;
use Oire\Iridium\Osst;

$key = new SymmetricKey();
$key = new SharedKey();
// Store the key somewhere safe, i.e., in an environment variable. You can safely cast it to string for that (see above)
$additionalInfo = '{"oldEmail": "[email protected]", "newEmail": "[email protected]"}';
$osst = (new Osst($dbConnection))
Expand All @@ -371,7 +372,7 @@ Osst throws two types of exceptions:

Below all of the Osst methods are outlined.

* `__construct(PDO $dbConnection, string|null $token, Oire\Iridium\Key\SymmetricKey|null $additionalInfoDecryptionKey)` — Instantiate a new Osst object. Provide a PDO instance as the first parameter, the user-provided token as the second one, and the Iridium key for decrypting additional info as the third one. **Note**! Provide the token only if you received it from the user. If you want to create a fresh token, the second and third parameters must not be set.
* `__construct(PDO $dbConnection, string|null $token, Oire\Iridium\Key\SharedKey|null $additionalInfoDecryptionKey)` — Instantiate a new Osst object. Provide a PDO instance as the first parameter, the user-provided token as the second one, and the Iridium key for decrypting additional info as the third one. **Note**! Provide the token only if you received it from the user. If you want to create a fresh token, the second and third parameters must not be set.
* `getDbConnection(): PDO` — Get the database connection for the current Osst instance as a PDO object.
* `getToken(): string` — Get the token for the current Osst instance as a string. Throws `OsstException` if the token was not created or set before.
* `getUserId(): int` — Get the ID of the user the token belongs to, as an integer.
Expand All @@ -388,7 +389,7 @@ Below all of the Osst methods are outlined.
* `getTokenType(): int|null` — Get the type for the current token. Returns integer if the token type was set before, or null if the token has no type.
* `setTokenType(int|null $tokenType): self` — Set the type for the current token, as integer or null. Returns `$this` for chainability.
* `getAdditionalInfo(): string|null` — Get additional info for the token. Returns string or null, if additional info was not set before.
* `setAdditionalInfo(string|null $additionalInfo, Oire\Iridium\Key\SymmetricKey|null $encryptionKey = null): self` — Set additional info for the current token. If the `$encryptionKey` parameter is not empty, tries to encrypt the additional information using the Crypt class. Returns `$this` for chainability.
* `setAdditionalInfo(string|null $additionalInfo, Oire\Iridium\Key\SharedKey|null $encryptionKey = null): self` — Set additional info for the current token. If the `$encryptionKey` parameter is not empty, tries to encrypt the additional information using the Crypt class. Returns `$this` for chainability.
* `persist(): self` — Store the token into the database. Returns `$this` for chainability.
* `revokeToken(bool $deleteToken = false): void` — Revoke. i.e., invalidate the current token after it is used. If the `$deleteToken` parameter is set to `true`, the token will be deleted from the database, and `getToken()` will return `null`. If it is set to `false` (default), the expiration time for the token will be updated and set to a value in the past. The method returns no value.
* `static clearExpiredTokens(PDO $dbConnection): int` — Delete all expired tokens from the database. As it is a static method, it receives the database connection as a PDO object. Returns the number of deleted tokens, as integer.
Expand All @@ -415,5 +416,5 @@ When your pull request is submitted, make sure all checks passed on CI.

## License

Copyright © 2021-2022, Andre Polykanine also known as Menelion Elensúlë, [The Magical Kingdom of Oirë](https://github.com/Oire/).
Copyright © 2021-2022 Andre Polykanine also known as Menelion Elensúlë, [The Magical Kingdom of Oirë](https://github.com/Oire/).
This software is licensed under an MIT license.
2 changes: 1 addition & 1 deletion captainhook.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"action": "php vendor/bin/php-cs-fixer fix"
},
{
"action": "php vendor/bin/phpcs"
"action": "php vendor/bin/phpcs src tests"
},
{
"action": "php vendor/bin/phpunit"
Expand Down
2 changes: 1 addition & 1 deletion src/Base64.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

/**
* Iridium, a security library for hashing passwords, encrypting data and managing secure tokens
* Copyright © 2021, Andre Polykanine also known as Menelion Elensúlë, https://github.com/Oire
* Copyright © 2021-2022 Andre Polykanine also known as Menelion Elensúlë, https://github.com/Oire
* Portions copyright © 2016 Paragon Initiative Enterprises.
* Portions copyright © 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
Expand Down
34 changes: 17 additions & 17 deletions src/Crypt.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
use Oire\Iridium\Exception\CryptException;
use Oire\Iridium\Exception\DecryptionException;
use Oire\Iridium\Exception\EncryptionException;
use Oire\Iridium\Exception\SymmetricKeyException;
use Oire\Iridium\Exception\SharedKeyException;
use Oire\Iridium\Key\DerivedKeys;
use Oire\Iridium\Key\SymmetricKey;
use Oire\Iridium\Key\SharedKey;

/**
* Iridium, a security library for hashing passwords, encrypting data and managing secure tokens
* Performs Authenticated Encryption.
* Copyright © 2021, Andre Polykanine also known as Menelion Elensúlë, https://github.com/Oire
* Copyright © 2021-2022 Andre Polykanine also known as Menelion Elensúlë, https://github.com/Oire
* Copyright © 2016 Scott Arciszewski, Paragon Initiative Enterprises, https://paragonie.com.
* Portions copyright © 2016 Taylor Hornby, Defuse Security Research and Development, https://defuse.ca.
*
Expand Down Expand Up @@ -47,11 +47,11 @@ final class Crypt
/**
* Encrypt data with a given key.
* @param string $plainText The data to be encrypted
* @param SymmetricKey $key The Iridium key used for encryption
* @param SharedKey $key The Iridium key used for encryption
* @throws EncryptionException
* @return string Returns the encrypted data
*/
public static function encrypt(string $plainText, SymmetricKey $key): string
public static function encrypt(string $plainText, SharedKey $key): string
{
if (!function_exists('openssl_encrypt')) {
throw new EncryptionException('OpenSSL encryption not available.');
Expand All @@ -63,7 +63,7 @@ public static function encrypt(string $plainText, SymmetricKey $key): string

try {
$derivedKeys = $key->deriveKeys();
} catch (SymmetricKeyException $e) {
} catch (SharedKeyException $e) {
throw new EncryptionException(sprintf('Unable to derive keys: %s', $e->getMessage()), $e);
}

Expand Down Expand Up @@ -99,11 +99,11 @@ public static function encrypt(string $plainText, SymmetricKey $key): string
/**
* Decrypt data with a given key.
* @param string $cipherText The encrypted data, as a string
* @param SymmetricKey $key The Iridium key the data was encrypted with
* @param SharedKey $key The Iridium key the data was encrypted with
* @throws DecryptionException
* @return string the decrypted plain text
*/
public static function decrypt(string $cipherText, SymmetricKey $key): string
public static function decrypt(string $cipherText, SharedKey $key): string
{
if (!function_exists('openssl_decrypt')) {
throw new DecryptionException('OpenSSL decryption not available.');
Expand Down Expand Up @@ -132,7 +132,7 @@ public static function decrypt(string $cipherText, SymmetricKey $key): string

try {
$derivedKeys = $key->deriveKeys($salt);
} catch (SymmetricKeyException $e) {
} catch (SharedKeyException $e) {
throw new DecryptionException(sprintf('Unable to derive keys: %s.', $e->getMessage()), $e);
}

Expand Down Expand Up @@ -196,25 +196,25 @@ public static function decrypt(string $cipherText, SymmetricKey $key): string

/**
* Change encryption key (for instance, if the old one is compromised).
* @param string $cipherText The encrypted data
* @param SymmetricKey $oldKey The key the data was encrypted before
* @param SymmetricKey $newKey The key for re-encrypting the data
* @throws SymmetricKeyException
* @return string Returns the re-encrypted data
* @param string $cipherText The encrypted data
* @param SharedKey $oldKey The key the data was encrypted before
* @param SharedKey $newKey The key for re-encrypting the data
* @throws SharedKeyException
* @return string Returns the re-encrypted data
*/
public static function swapKey(string $cipherText, SymmetricKey $oldKey, SymmetricKey $newKey): string
public static function swapKey(string $cipherText, SharedKey $oldKey, SharedKey $newKey): string
{
try {
$plainText = self::decrypt($cipherText, $oldKey);
} catch (SymmetricKeyException $e) {
} catch (SharedKeyException $e) {
throw new CryptException(sprintf('Invalid old key: %s', $e->getMessage()), $e);
} catch (DecryptionException $e) {
throw new CryptException(sprintf('Decryption failed: %s.', $e->getMessage()), $e);
}

try {
return self::encrypt($plainText, $newKey);
} catch (SymmetricKeyException $e) {
} catch (SharedKeyException $e) {
throw new CryptException(sprintf('Invalid new key: %s', $e->getMessage()), $e);
} catch (EncryptionException $e) {
throw new CryptException(sprintf('Encryption failed: %s.', $e->getMessage()), $e);
Expand Down
2 changes: 1 addition & 1 deletion src/Exception/Base64Exception.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

/**
* Iridium, a security library for hashing passwords, encrypting data and managing secure tokens
* Copyright © 2021, Andre Polykanine also known as Menelion Elensúlë, https://github.com/Oire
* Copyright © 2021-2022 Andre Polykanine also known as Menelion Elensúlë, https://github.com/Oire
* Portions copyright © 2016 Paragon Initiative Enterprises.
* Portions copyright © 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
Expand Down
Loading

0 comments on commit ea5cb63

Please sign in to comment.