Encrypting Passwords
Almost every project has a need to store user or device passwords. This can be achieved via the supported HMAC, PBKDF2, Bcrypt or Argon2 algorithms depending on the project’s requirements. Please do not reuse keys or salt strings for your users. In addition, make sure the generation and verification process do not provide any time-related comparison attacks or brute-force attacks. Here is a simple example using the Bcrypt algorithm that has its own salting in the digest but with a second custom salt:
use CryptoManana\Utilities\TokenGenerator;
use CryptoManana\Hashing\Bcrypt;
// 0. Set strong password policy
const USER_PASSWORD = ' pass1$X!Da';
// 1. Register or password change
$hasher = new Bcrypt();
$generator = new TokenGenerator();
// Generate a strong salt per every user
$secondSalt = $generator->getHashingSalt(
$generator::DIGESTION_SALT_512_BITS
);
// Note that Bcrypt has auto salting in the digest
$hasher->setSalt($secondSalt);
// Set the format modes
$hasher->setDigestFormat($hasher::DIGEST_OUTPUT_HEX_UPPER)
->setSaltingMode($hasher::SALTING_MODE_PALINDROME_MIRRORING);
// Chose a cost that needs at least 50-100 ms for hashing
$hasher->setAlgorithmicCost(12);
// Generate the digest
$digest = $hasher->hashData(trim(USER_PASSWORD));
// Store the digest and the salt inside a database
echo 'Digest: ' . $digest . '<br>';
echo 'Salt: ' . $secondSalt . '<br>';
unset($hasher, $generator);
// 2. Login process
$sentPassword = trim(USER_PASSWORD);
// Fetch from the database
$saltDatabase = $secondSalt; // from the database
$digestDatabase = $digest; // from the database
$hasher = new Bcrypt();
// Note that Bcrypt has auto salting in the digest
$hasher->setSalt($saltDatabase)
->setDigestFormat($hasher::DIGEST_OUTPUT_HEX_UPPER)
->setSaltingMode($hasher::SALTING_MODE_PALINDROME_MIRRORING)
->setAlgorithmicCost(12);
// Verify the password and log in
echo $hasher->verifyHash($sentPassword, $digest) ?
'The passwords match!' : 'The passwords are not the same!';
Encrypting Reusable Information
Most projects have a need for recovering some encrypted data, such as national ID numbers, photos or credit card numbers. They may be further needed for example to process payments. Again, try not to reuse cryptographic configurations between projects or even users. Most systems would you some kind of symmetric encryption layering to achieve that, for instance:
use CryptoManana\SymmetricEncryption\Camellia256;
use CryptoManana\SymmetricEncryption\Aes256;
use CryptoManana\Utilities\TokenGenerator;
// 0. Project configuration (layer one) - IN THE CODE BASE
const PROJECT_ENCRYPTION_KEY = 'fxaXXs6f5saf817@!4явр1XSFaslfas_';
const PROJECT_ENCRYPTION_IV = 'xQя1264%5_f@@$1!';
$cam = new Camellia256();
$cam->setSecretKey(PROJECT_ENCRYPTION_KEY) // Camellia256::KEY_SIZE
->setInitializationVector(PROJECT_ENCRYPTION_IV) // Camellia256::IV_SIZE
->setCipherFormat($cam::ENCRYPTION_OUTPUT_HEX_UPPER)
->setBlockOperationMode($cam::CTR_MODE)
->setPaddingStandard($cam::PKCS7_PADDING);
// 1. Per user configuration (layer two) - IN THE DATABASE
$generator = new TokenGenerator();
$aes = new Aes256();
// Generate and save at the database for the user when registering
$key = $generator->getEncryptionKey($aes::KEY_SIZE);
$iv = $generator->getEncryptionKey($aes::IV_SIZE);
$aes->setSecretKey($key)
->setInitializationVector($iv)
->setCipherFormat($aes::ENCRYPTION_OUTPUT_BASE_64_URL)
->setBlockOperationMode($aes::CBC_MODE)
->setPaddingStandard($aes::PKCS7_PADDING);
// 2. When data comes for insert or update
$data = 'credit card number here';
echo 'Data: ' . $data . '<br>';
// Encrypt with the project configuration
$cipherData = $cam->encryptData($data);
echo 'Cipher Data (Project): ' . $cipherData . '<br>';
// Encrypt with the user configuration and store it at the database
$cipherData = $aes->encryptData($cipherData);
echo 'Cipher Data (User): ' . $cipherData . '<br>';
// 3. When data is needed for reading
// Fetch the ciphertext and the user configuration
// Instance again both symmetric systems and decrypt
// Layer 1 (database) - user configuration decryption
$cipherData = $aes->decryptData($cipherData);
// Layer 2 (codebase) - project configuration decryption
$plainData = $cam->decryptData($cipherData);
// Use the data for something
echo $data === $plainData ?
'Data is decrypted successfully' : 'Wrong decryption!';
File and Data Verification
There are a lot of cases when you need to verify the integrity of a backup or installation file. This is usually done via checksum generation based on cryptographic hash functions or digital signatures. Most systems would only need some basic kind of file verification to proof that the file is not tempered with or physically corrupted while in storage. Here is an example to generate a secure checksum:
use CryptoManana\Hashing\ShaTwo256;
$hasher = new ShaTwo256();
// populate testing file
$path = trim(sys_get_temp_dir()) ?: (string)ini_get('upload_tmp_dir');
$fileName = $path . DIRECTORY_SEPARATOR . 'testing.bak';
file_put_contents($fileName, 'Records: 1024', LOCK_EX);
// calculate the checksum
echo 'File Location: ' . $fileName . '<br>';
echo 'File Content: ' . file_get_contents($fileName) . '<br>';
$backupDigest = $hasher->hashFile($fileName);
echo 'Checksum: ' . $backupDigest . '<br>';
echo 'Verified File: ';
echo ($backupDigest === $hasher->hashFile($fileName)) ? 'Yes' : 'No';
echo '<br><br>';
// simulate data manipulation or corruption
file_put_contents($fileName, '0', FILE_APPEND | LOCK_EX);
// Verify checksum again
echo 'File Location: ' . $fileName . '<br>';
echo 'File Content: ' . file_get_contents($fileName) . '<br>';
echo 'Checksum: ' . $hasher->hashFile($fileName) . '<br>';
echo 'Verified File: ';
echo ($backupDigest === $hasher->hashFile($fileName)) ? 'Yes' : 'No';
echo '<br>';
// Testing file cleanup
unlink($fileName);
Generating Passwords or Tokens
Commonly the end programmer may have to create some CSRF tokens, web API access tokens or useraccess passwords. This is easy to achieve with the framework’s token generator that uses the most secure source available:
use CryptoManana\Utilities\TokenGenerator;
$generator = new TokenGenerator();
$csrfToken = $generator->getTokenString($generator::STRONG_TOKEN_LENGTH);
$apiToken = $generator->getTokenString($generator::PARANOID_TOKEN_LENGTH);
$userPassword = $generator->getPasswordString($generator::PARANOID_PASSWORD_LENGTH);
$devicePassword = $generator->getPasswordString($generator::STRONG_PASSWORD_LENGTH);
$hashKey = $generator->getHashingKey($generator::DIGESTION_KEY_128_BITS);
$encryptionKey = $generator->getEncryptionKey($generator::SECRET_KEY_192_BITS);
$guid = $generator->getRandomGenerator()->getGloballyUniqueId('crypto');
$uuid = $generator->getRandomGenerator()->getStrongUniqueId('manana');