Simple reversible encrypt class for strings - php

I use the following class in my CakePHP app to create reversible encrypted ids:
class Tiny
{
public static $set = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
public static function toTiny($id)
{
$set = self::$set;
$HexN="";
$id = floor(abs(intval($id)));
$radix = strlen($set);
while (true)
{
$R=$id%$radix;
$HexN = $set{$R}.$HexN;
$id=($id-$R)/$radix;
if ($id==0) break;
}
return $HexN;
}
public static function reverseTiny($str)
{
$set = self::$set;
$radix = strlen($set);
$strlen = strlen($str);
$N = 0;
for($i=0;$i<$strlen;$i++)
{
$N += strpos($set,$str{$i})*pow($radix,($strlen-$i-1));
}
return "{$N}";
}
}
I'm looking for something just as simple for strings. Where it has a simple set variable and just two functions, one for encrypting and one for decrypting. All of the ones I have seen so far have been over-complicated and too framework-based. I want a class that is completely stand-alone and not reliant on other files and easily integrated into CakePHP.
Can anyone help? Does anyone know of one or could help me modify this script to work with strings instead so I can create another class. It needs to be simple to use: e.g. Class::encrypt(string);
Also security is not a major concern here as it's not being used for protecting anything rather just hashing a string in a way that can be reversed.
This is what I want:
class Encrypt
{
public static $set = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
public static function Encrypt($string)
{
$set = self::$set;
return;
}
public static function Decrypt($string)
{
$set = self::$set;
return;
}
}

Here is a modified version of the code that I use. It relies on openssl, so it may not be suitable if you need it to be portable.
<?php
class Cipher {
public static function strongRand($num_bytes, $raw = false) {
$rand = openssl_random_pseudo_bytes($num_bytes);
if (!$raw) {
$rand = base64_encode($rand);
}
return $rand;
}
public static function encrypt($data, $password = "default", $type = "aes128") {
$iv = Cipher::strongRand(12);
$encrypted = openssl_encrypt($data, $type, $password, false, $iv);
return $iv . $encrypted;
}
public static function decrypt($data, $password = "default", $type = "aes128") {
$iv = substr($data, 0, 16);
$data = substr($data, 16);
$decrypted = openssl_decrypt($data, $type, $password, false, $iv);
return $decrypted;
}
}
?>
An example of how to use the code:
<?php
include('cipher.php');
$enc = Cipher::encrypt('kachow');
echo $enc . "\n";
$dec = Cipher::decrypt($enc);
echo $dec . "\n";
?>
Outputs:
0fykYiBPJAL/C3fFuX+jApFRdRw7NY8uYmGaaQ==
kachow

If you need to crypt numbers, I use those simple functions (change the random letters in function _map_key to whatever you want, just be sure that they are unique). If you do not have numbers but string, you can STR2BIN them, then use this function:
function _map_kid($kid, $umap=false){
$map = array('M','Y','S','I','M','P','L','E','K');
$ret = '';
for($i = 0; $i<strlen($kid); $i++){
if($umap){
$ret .= array_search(substr($kid,$i,1),$map);
} else {
$ret .= $map[substr($kid,$i,1)];
}
}
return $ret;
}
function cript_customer_id($customer_id, $key=0){
if($key==0){
$key = trim(microtime(true));
}
$key = intval($key);
if(strlen($key)<=3) $key +=999;
if(substr($key,-1)==0) $key +=3;
$key = substr($key,-3);
$kid = ((($customer_id.substr($key,0,1))+substr($key,1,1))*substr($key,-1)).$key;
return _map_kid($kid);
}
function _decript_customer_id($kid){
if(trim($kid)=='') return false;
$kid = strtoupper($kid);
$kid = _qsc2_map_kid($kid, true);
$key = substr($kid, -3);
$customer_id = substr($kid, 0, -3);
if(substr($key,-1)>0){
$customer_id = substr((($customer_id/substr($key,-1))-substr($key,1,1)),0,-1);
} else {
$customer_id = 0;
}
return $customer_id;
}

There are various pure PHP implementations of standard crypto primitives like AES that you could use, like this or this one.
The RC4 algorithm, in particular, is something you could implement in about a dozen lines of PHP if you wanted.
However, those implementations are not likely to be very efficient; in general, when it comes to crypto in high-level languages like PHP, you can have any two out of "secure", "fast" and "self-contained".
OK, if all you want is obfuscation, here's something based on this example by "peter at NOSPAM jamit dot com" over at php.net:
class Obfuscate
{
public static function obfuscate($string)
{
return str_rot13( base64_encode( $string ) );
}
public static function deobfuscate($string)
{
return base64_decode( str_rot13( $string ) );
}
}
This should be fast and self-contained, but it provides very little security. At best it makes a nice little puzzle for amateur cryptographers, of a level perhaps comparable with a newspaper crossword puzzle.

Look into MCrypt functions. It'll be even shorter than the above code. And secure too!

I prefer to use mcrypt, but if don't wanna, the try to find something useful on phpclasses.org, -like this http://www.phpclasses.org/package/2995-PHP-Encode-and-decode-text-strings-with-random-keys.html

Related

Replace create_function with anonymous function

I have checked this before posting the question.
this is a small part of a code that uses create_function
$lambda_functions[$code_hash] = create_function('$action, &$self, $text', 'if ($action == "encrypt") { '.$encrypt.' } else { '.$decrypt.' }');
tried using this way
$lambda_functions[$code_hash] = function ( $action, &$self, $text ) use ( $encrypt, $decrypt ) {
if ($action == "encrypt") {
return $encrypt;
} else {
return $decrypt;
}
};
but doesn't work as expected $encrypt or $decrypt will contain code that looks something like this
$encrypt = $init_encryptBlock . '
$ciphertext = "";
$text = $self->_pad($text);
$plaintext_len = strlen($text);
$in = $self->encryptIV;
for ($i = 0; $i < $plaintext_len; $i+= '.$block_size.') {
$in = substr($text, $i, '.$block_size.') ^ $in;
'.$_encryptBlock.'
$ciphertext.= $in;
}
if ($self->continuousBuffer) {
$self->encryptIV = $in;
}
return $ciphertext;
';
It is working fine with create_function but not with anonymous function not sure where I am going wrong?
The difference is, that with create_function() your code was submitted as a string and interpreted as code, but with an anonymous function the string is interpreted as a string, not as the code it contains.
You can just extract the code you have from the string that you have in $encrypt and $decrypt. This would look like this:
/*
* Removed the "use ($encrypt, $decrypt)" part,
* because those were the strings that contained the code,
* but now the code itself is part of the anonymous function.
*
* Instead, i added "use ($block_size)", because this is a vairable,
* which is not defined inside of your function, but still used in it.
* The other code containing variables might include such variables as
* well, which you need to provide in the use block, too.
*/
$lambda_functions[$code_hash] = function ( $action, &$self, $text ) use ($block_size) {
if ($action == "encrypt") {
//Extract $init_encryptBlock here
$ciphertext = "";
$text = $self->_pad($text);
$plaintext_len = strlen($text);
$in = $self->encryptIV;
for ($i = 0; $i < $plaintext_len; $i+= $block_size) {
$in = substr($text, $i, $block_size) ^ $in;
// Extract $_encryptBlock here
$ciphertext.= $in;
}
if ($self->continuousBuffer) {
$self->encryptIV = $in;
}
return $ciphertext;
} else {
//Extract $decrypt here
}
};
Please keep in mind, that this is not a complete answer. You find numerous // Extract $variable here comments in the code, which stand for each code containing variable, that you have in your code and which needs to be extracted just in the way, in which i etracted the code from $encrypt.

Embedding data in a serial number

I am looking at implementing a PVKS as outlined here. I have it working as presented in the article (but in PHP), however I have an extra requirement I'm trying to figure out how to fulfill. I'm trying to figure out how I should embed a date and 3 digit number into the generated code. I'm not even sure where to start here, so I honestly haven't tried anything. They unfortunately won't be unique in combination, so I can't hash them for the seed value. As far as I can tell I can't have them be part of the getKeyByte function either, as the inputs to that function should be only the seed and some arguments, where those arguments define the algorithm for a valid key. They have to be the same between the generator and validator so they have to be static. Is there an accepted practice for this kind of task?
Managed to figure this one out myself. I realized I was over complicating things by trying to find a way I could encode a value into a keybyte. The way to do this (well, the way I did this) is just to add extra bytes after the keybytes and before the checksum. They don't need to be validated like the other keybytes, but do affect the checksum.
Below is the part of my class related to generating and validating keys, with the new sections I needed to add noted.
static public function generateKey($extraKeyArgs = array(), $encodedData = array())
{
$args = self::instanceKeyArgs($extraKeyArgs);
$keyBytes = array();
$seed = self::getSeed();
$hexSeed = self::intToHex($seed,self::$seedWidth);
$key = $hexSeed;
$numKeys = count($args);
for ($i=0; $i < $numKeys; $i++) {
list($a, $b, $c) = $args[$i];
$keyBytes[$i] = self::getKeyByte($seed, $a, $b, $c, self::$keyWidthBytes);
$key .= self::intToHex($keyBytes[$i],self::$keyWidthHex);
}
// Section added to handle encoded data
foreach ($encodedData as $data) {
// Make $data an integer value, one byte wide.
$data = (((int) $data) & 255);
$keyBytes[] = $data;
$numKeys++;
$key .= self::intToHex($data,self::$keyWidthHex);
}
// End Section
$checksum = self::getChecksum($key);
$key = $hexSeed . self::$seperator;
for ($i=0; $i < $numKeys; $i++) {
$key .= self::intToHex($keyBytes[$i],self::$keyWidthHex);
if ($i & 1) {
$key .= self::$seperator;
}
}
if (substr($key, -1) !== self::$seperator) {
$key .= self::$seperator;
}
$key .= $checksum;
return $key;
}
static public function checkKey($key, $extraKeyArgs = array(), &$data = array())
{
$args = self::instanceKeyArgs($extraKeyArgs);
$numKeys = count($args);
if (!self::checkKeyChecksum($key)) {
return false; // Failed checksum! Maybe a mistype or optical reader error?
}
$key = self::normalizeKey($key);
// TODO - we would check against a blacklist here if we wanted to implement that.
$seed = hexdec(substr($key,0,self::$seedWidth));
if (!is_int($seed) || $seed < 1) {
return false; // Failed to get seed. Are you sure this key came from here?
}
$key = substr($key, self::$seedWidth, (strlen($key) - (self::$seedWidth + self::$checksumWidth)));
for ($i=0; $i < $numKeys; $i++) {
$keyByte = substr($key, 0, self::$keyWidthHex);
$key = substr($key, self::$keyWidthHex);
list($a, $b, $c) = $args[$i];
if ($keyByte !== self::intToHex(self::getKeyByte($seed, $a, $b, $c, self::$keyWidthBytes),2)) {
return false; // Key byte failed check. Possible forgery attempt?
}
}
// This line added to handle encoded data
$data = array_map('hexdec', str_split($key, self::$keyWidthHex));
return true; // Valid Key, Yay!
}

How do I convert rounds of hashing with hash() to init/update/final?

Say I have a linear hashing algorithm:
<?php
$input = "password1";
$round1 = hash('sha512', $input, true);
$round2 = hash('sha512', $round1, true);
echo(base64_encode($round2) . "<br>\n");
?>
How would I convert this to a for loop using hash_init, hash_update, and hash_final? I have a algorithm using these in a for loop right now, but I am unable to post it.
Scratch what I said about closing the handle, that's what hash_copy() function is for. You're probably looking for something like:
$algo = 'sha512';
$input = 'password';
$rounds = 1000;
function extend($algo, $rounds, $input) {
$ctx = hash_init($algo);
hash_update($ctx, $input);
for($i=1; $i<$rounds; $i++) {
hash_update($ctx, hash_final(hash_copy($ctx), TRUE));
}
return hash_final($ctx, TRUE);
}
echo base64_encode(extend($algo, $rounds, $input));
But this essentially appends the hashes together, whereas your existing code re-hashes the hashes. You will not be able to get the same result as the code you posted using this method.
If you want to replicate the code you have, then something like:
$algo = 'sha512';
$input = 'password';
$rounds = 1000;
function cycle($algo, $rounds, $input) {
$curhash = reinvent_the_wheel($algo, $input);
for($i=1; $i<$rounds; $i++) {
$curhash = reinvent_the_wheel($algo, $curhash);
}
return $curhash;
}
//equivalent to hash($algo, $input, $true);
function reinvent_the_wheel($algo, $input) {
$ctx = hash_init($algo);
hash_update($ctx, $input);
return hash_final($ctx, TRUE);
}
echo base64_encode(cycle($algo, $rounds, $input)) . "\n";
Which is basically the same code as you posted, just with a for loop added.

Php change a number into another number that can be changed back to the original

Using PHP, I'm trying to encode a number into another number that I can decode back to the original number. The encoded string needs to be only numbers and should not contain anything else.
Eg: 10 becomes 573563547892 or something like that.
How can I do something like this in PHP? I tried quite a few encrypt decrypt functions, but none output only numbers.
I looking for something to use in a URL that isn't easy to guess.
So: http://www.me.com/index.PHP?page=20 becomes http://www.me.com/index.PHP?page=5705254782562466
Why not using a mathematicat operation on the original number? like x becomes x * y + z. you would only have to make the reverse operation to get the original number. consider using large enough prime numbers for y and/or z
Quite heavy, but very good encryption, by using ord & chr a bit. While this works, consider other options: just being able to use strings rather then numbers already makes it a lot simpler (base64_encode etc.):
<?php
class Crypter {
private $key = '';
private $iv = '';
function __construct($key,$iv){
$this->key = $key;
$this->iv = $iv;
}
protected function getCipher(){
$cipher = mcrypt_module_open(MCRYPT_BLOWFISH,'','cbc','');
mcrypt_generic_init($cipher, $this->key, $this->iv);
return $cipher;
}
function encrypt($string){
$binary = mcrypt_generic($this->getCipher(),$string);
$string = '';
for($i = 0; $i < strlen($binary); $i++){
$string .= str_pad(ord($binary[$i]),3,'0',STR_PAD_LEFT);
}
return $string;
}
function decrypt($encrypted){
//check for missing leading 0's
$encrypted = str_pad($encrypted, ceil(strlen($encrypted) / 3) * 3,'0', STR_PAD_LEFT);
$binary = '';
$values = str_split($encrypted,3);
foreach($values as $chr){
$chr = ltrim($chr,'0');
$binary .= chr($chr);
}
return mdecrypt_generic($this->getCipher(),$binary);
}
}
$crypt = new Crypter('secret key','12348765');
$encrypted = $crypt->encrypt(1234);
echo $encrypted.PHP_EOL;
//fake missing leading 0
$encrypted = ltrim($encrypted,'0');
echo $encrypted.PHP_EOL;
$decrypted = $crypt->decrypt($encrypted);
echo $decrypted.PHP_EOL;
Result:
057044206104214236155088
57044206104214236155088
1234

PHP fast random string function

I need fast way for generating random strings a-Z0-9 in PHP. I've been doing some thinking and testing, this is what I've got so far:
function randStr($length) {
$result = null;
$replace = array('/', '+', '=');
while(!isset($result[$length-1])) {
$result.= str_replace($replace, NULL, base64_encode(mcrypt_create_iv($length, MCRYPT_RAND)));
}
return substr($result, 0, $length);
}
Function seems to be working fast compared to functions which iterate and choose random ASCII value for each char, but I'm concerned with 'quality' of my implementation. I do not know much about cryptography, so I'd like to ask whether this kind of function creates 'good' random values or not.
mcrypt_create_iv seems to return some kind of random binary values, actually used for encrypting/decrypting data with mcrypt library. What is base64_encode effect on this kind of binary data, do I actually decrease entropy, when I base64_encode it?
How does second parameter for mcrypt_create_iv affect my results? php.net manual states that MCRYPT_RAND is 'system random number generator'. Is it OS specific and if so, how good values are created?
This supposed to be secure on most of the systems and fast:
bin2hex(openssl_random_pseudo_bytes($length / 2));
benchmarks (1000000 records, string length 100 chars)
rstr1: 198.93383002281
rstr2: 35.5827729702
rstr3: 6.8811790943146
rstr4: 5.4545040130615
this:: 3.9310231208801
For anyone looking for an updated version of the "best" algorithm:
function randomString($length) {
$result = null;
$replace = array('/', '+', '=');
while(!isset($result[$length-1])) {
$result.= str_replace($replace, NULL, base64_encode(random_bytes($length)));
}
return substr($result, 0, $length);
}
I use the term "best" because it is faster than the random string manipulations of rstr1 and rstr2 and in comparison to the other solutions offers a full spectrum of letters (lower- and uppercased).
From my tests, your function is already very fast, but i managed to get to a faster one, even if it decreases the entropy
fcn time
rstr1: 1.074s (slowest)
rstr2: 0.917s
rstr3: 0.028s (yours)
rstr4: 0.022s (mine)
In my scenario, i needed 1k strings, as fast as possible.
function rstr1($length)
{
// #see http://stackoverflow.com/a/853846/11301
$alphabet='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return substr(str_shuffle(str_repeat($alphabet, $length)), 0, $length);
}
function rstr2($length)
{
// #see http://stackoverflow.com/a/853870/11301
$alphabet='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$str = '';
$count = strlen($alphabet);
while ($length--) {
$str .= $alphabet[mt_rand(0, $count-1)];
}
return $str;
}
function rstr3($length) {
// #see http://stackoverflow.com/q/4757392/11301
$result = null;
$replace = array('/', '+', '=');
while(!isset($result[$length-1])) {
$result.= str_replace($replace, NULL, base64_encode(mcrypt_create_iv($length, MCRYPT_RAND)));
}
return substr($result, 0, $length);
}
function rstr4($length)
{
// uses md5 & mt_rand. Not as "random" as it could be, but it works, and its fastest from my tests
return str_shuffle(substr(str_repeat(md5(mt_rand()), 2+$length/32), 0, $length));
}
// test the functions
for($i=0; $i<1000; $i++){
#$x = rstr1(1024); #
#$x = rstr2(1024); # 0.917s
#$x = rstr3(1024); # 0.028s
#$x = rstr4(1024); # 0.022s
#dlog($x); return;
}
base64_encoding won't decrease entropy, it is just a different representation of the same data.
It is OS specific, but I think the random values created are good enough with this function. Under PHP 5.3 you have to seed the generator beforehand, it can be a problem if you use this code on different servers.
I usually work with this one.
Also I can choose if I don't want certain characters
function rstr5($length = 1) {
return substr(str_shuffle(str_repeat("0123456789abcdefghijklmnopqrstuvwxyz", $length)), 0, $length);
}
This is how I'm doing it, though it's not exactly cryptographic; The Mersenne Twister is fast and reliable, but not the most secure.
function str_rand($chars, $len)
{
$str = '';
for ($max = strlen($chars) - 1, $i = 0; $i < $len; ++$i)
{
$str .= $chars[mt_rand(0, $max)];
}
return $str;
}
$strRand = str_rand('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 40);

Categories