substr( $keymats, 0, 32 ), 'hmac' => substr( $keymats, 32, 32 ), ]; } /** * Actually encrypt the data, using a new random IV, and prepend the hmac * of the encrypted data + IV, using a separate hmac key. * @param string $data * @param string $encKey * @param string $hmacKey * @return string $hmac.$iv.$ciphertext, each component b64 encoded */ private static function seal( $data, $encKey, $hmacKey ) { $iv = MWCryptRand::generate( 16, true ); $ciphertext = openssl_encrypt( $data, 'aes-256-ctr', $encKey, OPENSSL_RAW_DATA, $iv ); $sealed = base64_encode( $iv ) . '.' . base64_encode( $ciphertext ); $hmac = hash_hmac( 'sha256', $sealed, $hmacKey, true ); return base64_encode( $hmac ) . '.' . $sealed; } /** * Decrypt data sealed using seal(). First checks the hmac to prevent various * attacks. * @param string $encrypted * @param string $encKey * @param string $hmacKey * @return string plaintext * @throws Exception */ private static function unseal( $encrypted, $encKey, $hmacKey ) { $pieces = explode( '.', $encrypted ); if ( count( $pieces ) !== 3 ) { throw new InvalidArgumentException( 'Invalid sealed-secret format' ); } list( $hmac, $iv, $ciphertext ) = $pieces; $integCalc = hash_hmac( 'sha256', $iv . '.' . $ciphertext, $hmacKey, true ); if ( !hash_equals( $integCalc, base64_decode( $hmac ) ) ) { throw new Exception( 'Sealed secret has been tampered with, aborting.' ); } return openssl_decrypt( base64_decode( $ciphertext ), 'aes-256-ctr', $encKey, OPENSSL_RAW_DATA, base64_decode( $iv ) ); } }