Expiry time on php openssl? - php

I'm not very familiar with encryption, and we are now using PHP's openssl_encrypt/decrypt in our application.
Is it possible to make the encryption/decryption work only before an expiry time? e.g. maybe the keys expire?

Yes, it is possible, you have to append the creation timestamp as bytes before what you need to encrypt:
$time = pack('N', time());
$enc = openssl_encrypt($time . $other_data, ...);
When you decrypt:
$dec = openssl_decrypt($encrypted, ...);
$time = unpack('N', substr($dec, 0, 4));
$other_data = substr($dec, 4);
if (time() - $time[1] > $EXPIRY_SECONDS)
die('Expired');
The N flag i've used in pack/unpack is for Big Endian byte order, you can also use V for little endian or L for machine-dependent, because the timestamp fit in 32 bit integer (4 bytes).

Related

What's wrong with my implementation of 2 Factor Authorization?

I'm trying to implement my own PHP function to generate codes for Google Authenticator. I do it for fun and to learn something new. Here's what I did:
function twoFactorAuthorizationCode(string $secretBase32, int $digitsCount): string {
$counter = (int) (time() / 30);
$secret = Base32::decode($secretBase32);
$hash = hash_hmac('sha1', $counter, $secret, true); // 20 binary characters
$hexHash = unpack('H*', $hash)[1]; // 40 hex characters
$offset = hexdec($hexHash[-1]); // last 4 bits of $hash
$truncatedHash = hexdec(substr($hexHash, $offset * 2, 8)) & 0x7fffffff; // last 31 bits
$code = $truncatedHash % (10 ** $digitsCount);
return str_pad($code, $digitsCount, '0', STR_PAD_LEFT);
}
I'm not sure which step is wrong, but it doesn't generate the same results as Google Authenticator. Obviously, I tried to play with time offsets in case my clock is not in sync with Google Authenticator's.
Some of the things I'm not sure are:
Should the secret be decoded from Base32, or should it stay the Base32 string?
Is the counter a value or a key for SHA1 hash?
I did a lot of experiments and I can't get my algorithm to generate a valid result. Any advice is highly appreciated.
I have found the answer by trials and errors. So, the problem was in the $counter value that I've been hashing directly:
$hash = hash_hmac('sha1', $counter, $secret, true);
Instead, it should be a 64-bit binary string made from the $counter:
$packedCounter = pack('J', $counter);
$hash = hash_hmac('sha1', $packedCounter, $secret, true);
Explanation
Let's say our Unix timestamp is 1578977176.
That makes the counter as follows: (int) (1578977176 / 30) = 52632572.
The value used for hashing needs to be a 64-bit, big endian byte order string. It means that we need to left-pad it with zeros to make it 64-bit.
52632572 is 11001000110001101111111100 in binary. That's just 26 bits, so we need 38 more. What we have now is:
0000000000000000000000000000000000000011001000110001101111100010.
Every character is one byte, so we split it into the groups of 8:
00000000 00000000 00000000 00000000 00000011 00100011 00011011 11100010
We can now convert every group to a character by its code:
$packedCounter = chr(0b00000000)
. chr(0b00000000)
. chr(0b00000000)
. chr(0b00000000)
. chr(0b00000011)
. chr(0b00100011)
. chr(0b00011011)
. chr(0b11100010);
And that's the string we want to hash, which is exactly what pack('J', $string) does.
Voilà!

Decrypt random chunk of an encrypted AES-CTR file in PHP's mcrypt

I have a 1MB test file and I want to decrypt it starting from its 500KB, not from the beginning. It doesn't need to start exactly from 500KB of the file, it can start at the beginning of any chunk as long as it's not the first, I just want to learn how to do it.
With this script I can decrypt the file as long as its starts from 0KB.
$file = file_get_contents("file.dat");
$aeskey = base64_decode("sVv2g7boc/pzCDepDfV1VA==");
$iv = base64_decode("A5chWWE3D4cAAAAAAAAAAA==");
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'ctr', '');
mcrypt_generic_init($td, $aeskey, $iv);
echo mdecrypt_generic($td, $file);
Could someone please explain me if it is at least possible?
In CTR mode, a counter (128 bit for AES) is encrypted to produce a key stream that is then XORed with the plaintext or ciphertext. Usually, it is assumed that the IV is either 64 bit or 96 bit and the remaining bits are actually set to 0. The initial 64 or 96 bit are called nonce.
The size of the nonce determines how much data can be encrypted in one go without creating a many-time pad: the larger the nonce, the smaller the safe message length, but also lower probability of collisions of two nonces when they are generated randomly. Since there is no specification how big the nonce is, many frameworks don't limit the size of a nonce to a specific size.
You can use the full block size for a nonce in mcrypt.
You can
take the IV that was used from the beginning,
parse that IV as a big integer (it doesn't fit into the PHP integer type),
add to it a number, which represents as many blocks (16 byte blocks for AES) as you want to skip,
convert the number back to a binary representation and
begin decrypting from a later byte.
Steps 2-4 are accomplished by the add function in the following code.
Let's say you have a big file but want to decrypt from byte 512 (multiple of the block size for simplicity). You would add 512/16=32 to the IV.
Here is some example code:
<?php
$d = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
$k = "k0k1k2k3k4k5k6k7"; // 16 byte AES key
$bs = 16; // 16 byte block size
$iv = mcrypt_create_iv($bs);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'ctr', '');
mcrypt_generic_init($td, $k, $iv);
$ct = mcrypt_generic($td, $d);
$dec_offset = 32;
$ct_slice = substr($ct, $dec_offset);
$iv_slice = add($iv, $dec_offset / $bs);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'ctr', '');
mcrypt_generic_init($td, $k, $iv_slice);
$d_slice = mdecrypt_generic($td, $ct_slice);
var_dump($d_slice);
function add($big_num_str, $to_add){
$big_num = str_split(strrev($big_num_str));
for($i = 0; $i < count($big_num) ; $i++){
$tmp = ord($big_num[$i]) + $to_add;
$big_num[$i] = $tmp % 256;
$to_add = floor( $tmp / 256 );
}
while($to_add){
$big_num[$i++] = $to_add % 256;
$to_add = floor( $to_add / 256 );
}
for($i = 0; $i < count($big_num) ; $i++){
$big_num[$i] = chr($big_num[$i]);
}
return strrev(implode('', $big_num) );
}
Output:
string(32) "101112131415161718191a1b1c1d1e1f"
This also work in the exact same way for the OpenSSL extension in PHP. Here is the code.
Of course, this would be a little more complicated if you want to get a chunk that doesn't begin on a block boundary. You would have to start a block earlier and remove the excess bytes.

Convert a random alphanumeric string into random Integer in php? [duplicate]

The goal is to store the hash on a mysql database, using INT (not BIGINT or MEDIUMINT).
md5('string', true) returns binary data, 16 bytes of hash. I thought i could grep the first 4 bytes and convert it to an INT(32bit/4bytes) integer, but i don't know how to do it.
What do you suggest? Thanks.
Use crc32, it will return a 32bit int.
var_dump (crc32 ("hello world"));
var_dump (crc32 ("world hello"));
output
int(222957957)
int(1292159901)
PHP: crc32 - Manual
Generates the cyclic redundancy checksum polynomial of 32-bit lengths
of the str. This is usually used to validate the integrity of data
being transmitted.
Because PHP's integer type is signed, and many crc32 checksums will
result in negative integers, you need to use the "%u" formatter of
sprintf() or printf() to get the string representation of the unsigned
crc32 checksum.
The php hash() function will be even better than crc32():
hash("crc32b", $str);
And if an integer is required:
intval(hash("crc32b", $str), 16);
Compared to the plain crc32(), it will not be affected by the signed/unsigned integer inconsistency between 32-bit and 64-bit systems (see PHP docs for the details: crc32)
ord($hash[0]) * 16777216 + ord($hash[1]) * 65536 + ord($hash[2]) * 256 + ord($hash[3]) ;
Or:
unpack("L", substr($hash,0,4));
But Filip Roséen's solution is better.
Another alternative is to use a map to link your hash to integer, such as Redis.
L43F34FLK34FL3K4 ==> 1
LKZLCKCLK32 ==> 2
etc....
Here is Daniel J. Bernstein hash that returns an 32bit integer.
function djb_hash($str) {
for ($i = 0, $h = 5381, $len = strlen($str); $i < $len; $i++) {
$h = (($h << 5) + $h + ord($str[$i])) & 0x7FFFFFFF;
}
return $h;
}

How to get current time in milliseconds in PHP?

time() is in seconds - is there one in milliseconds?
The short answer is:
$milliseconds = floor(microtime(true) * 1000);
Use microtime. This function returns a string separated by a space. The first part is the fractional part of seconds, the second part is the integral part. Pass in true to get as a number:
var_dump(microtime()); // string(21) "0.89115400 1283846202"
var_dump(microtime(true)); // float(1283846202.89)
Beware of precision loss if you use microtime(true).
There is also gettimeofday that returns the microseconds part as an integer.
var_dump(gettimeofday());
/*
array(4) {
["sec"]=>
int(1283846202)
["usec"]=>
int(891199)
["minuteswest"]=>
int(-60)
["dsttime"]=>
int(1)
}
*/
Short answer:
64 bits platforms only!
function milliseconds() {
$mt = explode(' ', microtime());
return intval( $mt[1] * 1E3 ) + intval( round( $mt[0] * 1E3 ) );
}
[ If you are running 64 bits PHP then the constant PHP_INT_SIZE equals to 8 ]
Long answer:
If you want an equilvalent function of time() in milliseconds first you have to consider that as time() returns the number of seconds elapsed since the "epoch time" (01/01/1970), the number of milliseconds since the "epoch time" is a big number and doesn't fit into a 32 bits integer.
The size of an integer in PHP can be 32 or 64 bits depending on platform.
From http://php.net/manual/en/language.types.integer.php
The size of an integer is platform-dependent, although a maximum value of about two billion is the usual value (that's 32 bits signed). 64-bit platforms usually have a maximum value of about 9E18, except for Windows, which is always 32 bit. PHP does not support unsigned integers. Integer size can be determined using the constant PHP_INT_SIZE, and maximum value using the constant PHP_INT_MAX since PHP 4.4.0 and PHP 5.0.5.
If you have 64 bits integers then you may use the following function:
function milliseconds() {
$mt = explode(' ', microtime());
return intval( $mt[1] * 1E3 ) + intval( round( $mt[0] * 1E3 ) );
}
microtime() returns the number of seconds since the "epoch time" with precision up to microseconds with two numbers separated by space, like...
0.90441300 1409263371
The second number is the seconds (integer) while the first one is the decimal part.
The above function milliseconds() takes the integer part multiplied by 1000
1409263371000
then adds the decimal part multiplied by 1000 and rounded to 0 decimals
1409263371904
Note that both $mt[1] and the result of round are casted to int via intval(). This is necessary because they are floats and the operation on them without casting would result in the function returning a float with a loss in precision.
Finally, that function is slightly more precise than
round(microtime(true)*1000);
that with a ratio of 1:10 (approx.) returns 1 more millisecond than the correct result.
This is due to the limited precision of the float type (microtime(true) returns a float).
Anyway if you still prefer the shorter round(microtime(true)*1000); I would suggest casting to int the result.
Even if it's beyond the scope of the question it's worth mentioning that if your platform supports 64 bits integers then you can also get the current time in microseconds without incurring in overflow.
If fact 2^63 - 1 (biggest signed integer) divided by 10^6 * 3600 * 24 * 365 (approximately the microseconds in one year) gives 292471.
That's the same value you get with
echo intdiv( PHP_INT_MAX, 1E6 * 3600 * 24 * 365 );
In other words, a signed 64 bits integer have room to store a timespan of over 200,000 years measured in microseconds.
You may have then
function microseconds() {
$mt = explode(' ', microtime());
return intval( $mt[1] * 1E6 ) + intval( round( $mt[0] * 1E6 ) );
}
As other have stated, you can use microtime() to get millisecond precision on timestamps.
From your comments, you seem to want it as a high-precision UNIX Timestamp. Something like DateTime.Now.Ticks in the .NET world.
You may use the following function to do so:
function millitime() {
$microtime = microtime();
$comps = explode(' ', $microtime);
// Note: Using a string here to prevent loss of precision
// in case of "overflow" (PHP converts it to a double)
return sprintf('%d%03d', $comps[1], $comps[0] * 1000);
}
Shortest version of string variant (32-bit compatibile):
$milliseconds = date_create()->format('Uv');
echo date('Y-m-d H:i:s.') . gettimeofday()['usec'];
output:
2016-11-19 15:12:34.346351
Use microtime(true) in PHP 5, or the following modification in PHP 4:
array_sum(explode(' ', microtime()));
A portable way to write that code would be:
function getMicrotime()
{
if (version_compare(PHP_VERSION, '5.0.0', '<'))
{
return array_sum(explode(' ', microtime()));
}
return microtime(true);
}
This works even if you are on 32-bit PHP:
list($msec, $sec) = explode(' ', microtime());
$time_milli = $sec.substr($msec, 2, 3); // '1491536422147'
$time_micro = $sec.substr($msec, 2, 6); // '1491536422147300'
Note this doesn't give you integers, but strings. However this works fine in many cases, for example when building URLs for REST requests.
If you need integers, 64-bit PHP is mandatory.
Then you can reuse the above code and cast to (int):
list($msec, $sec) = explode(' ', microtime());
// these parentheses are mandatory otherwise the precedence is wrong!
// ↓ ↓
$time_milli = (int) ($sec.substr($msec, 2, 3)); // 1491536422147
$time_micro = (int) ($sec.substr($msec, 2, 6)); // 1491536422147300
Or you can use the good ol' one-liners:
$time_milli = (int) round(microtime(true) * 1000); // 1491536422147
$time_micro = (int) round(microtime(true) * 1000000); // 1491536422147300
try this:
public function getTimeToMicroseconds() {
$t = microtime(true);
$micro = sprintf("%06d", ($t - floor($t)) * 1000000);
$d = new DateTime(date('Y-m-d H:i:s.' . $micro, $t));
return $d->format("Y-m-d H:i:s.u");
}
PHP 5.2.2 <
$d = new DateTime();
echo $d->format("Y-m-d H:i:s.u"); // u : Microseconds
PHP 7.0.0 < 7.1
$d = new DateTime();
echo $d->format("Y-m-d H:i:s.v"); // v : Milliseconds
$timeparts = explode(" ",microtime());
$currenttime = bcadd(($timeparts[0]*1000),bcmul($timeparts[1],1000));
echo $currenttime;
NOTE: PHP5 is required for this function due to the improvements with
microtime() and the bc math module is also required (as we’re dealing
with large numbers, you can check if you have the module in phpinfo).
Hope this help you.
$the_date_time = new DateTime($date_string);
$the_date_time_in_ms = ($the_date_time->format('U') * 1000) +
($the_date_time->format('u') / 1000);
This is my implementation, should work on 32bit as well.
function mstime(){
$mstime = explode(' ',microtime());
return $mstime[1].''.(int)($mstime[0]*1000);
}
If you want to see real microseconds, you will need to change the precision setting in php.ini to 16.
After that, microsecond(true) gave me the output of 1631882476.298437.
So I thought that I need to divide the remainder (298437) with 1000, but in fact, the remainder is 0.298437 of a second. So I need to multiply that by 1000 to get the correct result.
function get_milliseconds()
{
$timestamp = microtime(true);
return (int)(($timestamp - (int)$timestamp) * 1000);
}
I personaly use this:
public static function formatMicrotimestamp(DateTimeInterface $dateTime): int
{
return (int) substr($dateTime->format('Uu'), 0, 13);
}
Use this:
function get_millis(){
list($usec, $sec) = explode(' ', microtime());
return (int) ((int) $sec * 1000 + ((float) $usec * 1000));
}
Bye

PHP - Generate an 8 character hash from an integer

Is there a way to take any number, from say, 1 to 40000 and generate an 8 character hash?
I was thinking of using base_convert but couldn't figure out a way to force it to be an 8 character hash.
Any help would be appreciated!
Why don't you just run md5 and take the first 8 characters?
Because you are wanting a hash, it doesn't matter whether portions are discarded, but rather that the same input will produce the same hash.
$hash = substr(md5($num), 0, 8);
>>> math.exp(math.log(40000)/8)
3.7606030930863934
Therefore you need 4 digit-symbols to produce a 8-character hash from 40000:
sprintf("%08s", base_convert($n, 10, 4))
For php:
$seed = 'JvKnrQWPsThuJteNQAuH';
$hash = sha1(uniqid($seed . mt_rand(), true));
# To get a shorter version of the hash, just use substr
$hash = substr($hash, 0, 10);
http://snipplr.com/view.php?codeview&id=20236
As of PHP 8.1 you can use xxHash to get 8 characters hash of any string or number variable.
$number = random_int(1, 40_000);
$hash = hash('xxh32', (string) $number);
var_dump($hash); // Output is string(8) "af863d37"
there are many ways ...
one example
$x = ?
$s = '';
for ($i=0;$i<8;++$i)
{
$s .= chr( $x%26 + ord('a') );
$x /= 26;
}
$hash = substr(hash("sha256",$num), 0, 8);
So you want to convert a 6 digit number into a 8 digit string reproducibly?
sprintf("%08d", $number);
Certainly a hash is not reversible - but without a salt / IV it might be a bit easy to hack. A better solution might be:
substr(sha1($number . $some_secret),0,8);
C.

Categories