PHP Hex to Integer - php

I'm integrating a PHP application with an API that uses permissions in the form of 0x00000008, 0x20000000, etc. where each of these represents a given permission. Their API returned an integer. In PHP, how do I interpret something like 0x20000000 into an integer so I can utilize bitwise operators?
Another dev told me he thought these numbers were hex annotation, but googling that in PHP I'm finding limited results. What kind of numbers are these and how can I take an integer set of permissions and using bitwise operators determine if the user can 0x00000008.

As stated in the php documentation, much like what happens in the C language, this format is recognized in php as a native integer (in hexadecimal notation).
<?php
echo 0xDEADBEEF; // outputs 3735928559
Demo : https://3v4l.org/g1ggf
Ref : http://php.net/manual/en/language.types.integer.php#language.types.integer.syntax
Thus you could perform on them any bitwise operation since they are plain integers, with respect to the size of the registers on your server's processor (nowadays you are likely working on a 64-bit architecture, just saying in case it applies, you can confirm it with phpinfo() and through other means in doubt).
echo 0x00000001<<3; // 1*2*2*2, outputs 8
Demo : https://3v4l.org/AKv7H
Ref : http://php.net/manual/en/language.operators.bitwise.php
As suggested by #iainn and #SaltyPotato in their respective answer and comment, since you are reading the data from an API you might have to deal with these values obtained as strings instead of native integers. To overcome this you would just have to go through the hexdec function to convert them before use.
Ref : http://php.net/manual/en/function.hexdec.php

Since you are receiving it from an API php will interpret it as a string
echo hexdec('0x00000008');
returns the integer 8
This should work.

Related

Hash Function that works identically on ColdFusion 10+ and PHP 7.x?

I am currently working on a new PHP site for a site currently utilizing ColdFusion 10. When the new site is ready the ColdFusion site will be decommissioned and I won't have access to anything related to ColdFusion. I don't want to have to reset all the previous passwords so need to be able to duplicate the one-way SHA-512 hash that is utilized in ColdFusion in PHP.
This question on Stack Overflow is very relevant to this problem:
hash function that works identically on ColdFusion MX7 and PHP 5.x?
The difference is they manually looped in 1024 iterations in ColdFusion. The ColdFusion site I am translating uses the built in iterations feature. I have tried what they did in the above question, plus a few variations including XOR in the end but ultimately I can't find documentation on what ColdFusion is doing during those iterations.
ColdFusion:
<cfset hpswd = Hash(FORM.npswd & salt, "SHA-512", "UTF-8", 1000) >
PHP (without iterations logic):
$hpswd = strtoupper(hash("sha512", $npswd.$salt));
Given this password: q7+Z6Wp#&#hQ
With this salt: F4DD573A-EC09-0A78-61B5DA6CBDB39F36
ColdFusion gives this Hash (with 1000 iterations): 1FA341B135918B61CB165AA67B33D024CC8243C679F20967A690C159D1A48FACFA4C57C33DDDE3D64539BF4211C44C8D1B18C787917CD779B2777856438E4D21
Even with making sure to strtoupper with PHP I have not managed to duplicate the iterations step so the question, what operands is ColdFusion 10+ doing during the iterations step?
Regardless of language, a SHA-512 hashing function should return the same output given the same inputs. Here, it looks like you may just need to ensure that your inputs are the same. This includes the encoding of the text you are inputting. Then you'll hash over it the same total number of times.
As of today, the CFDocs documentation of ColdFusion hash() is incorrect, but I have submitted a correction for that. See my comments above about why I believe Adobe lists their defaults this way. A Default of 1 Iteration is correct for Lucee CFML, but not for Adobe CF. You are correct that the ACF default is 0. CF2018 clarifies this parameter.
Now, to your issue, your original code in ACF10 is:
<cfset hpswd = Hash(FORM.npswd & salt, "SHA-512", "UTF-8", 1000) >
This says that you are hashing with the SHA-512 algorithm, using UTF-8 encoding, and repeating an additional 1000 times. This means that your hash() function is actually being called 1001 times for your final output.
So:
<cfset npswd="q7+Z6Wp#&##hQ">
<cfset salt = "F4DD573A-EC09-0A78-61B5DA6CBDB39F36">
<cfset hpswd = Hash(npswd & salt, "SHA-512","UTF-8",1000) >
<cfoutput>#hpswd#</cfoutput>
Gives us 1FA341B135918B61CB165AA67B33D024CC8243C679F20967A690C159D1A48FACFA4C57C33DDDE3D64539BF4211C44C8D1B18C787917CD779B2777856438E4D21.
https://trycf.com/gist/7212b3ee118664c5a7f1fb744b30212d/acf?theme=monokai
One thing to note is that the ColdFusion hash() function returns a HEXIDECIMAL string of the hashed input, but when it uses it's iteration argument, it iterates over the BINARY output of the hashed value. This will make a big difference in the final output.
https://trycf.com/gist/c879e9e900e8fd0aa23e766bc308e072/acf?theme=monokai
To do this in PHP, we'd do something like this:
NOTE: I am not a PHP developer, so this is probably not the best way to do this. Don't judge me, please. :-)
<?php
mb_internal_encoding("UTF-8");
$npswd="q7+Z6Wp#&#hQ";
$salt = "F4DD573A-EC09-0A78-61B5DA6CBDB39F36";
$hpswd = $npswd.$salt ;
for($i=1; $i<=1001; $i++){
$hpswd = hash("SHA512",$hpswd,true); // raw_output=true argument >> raw binary data.
// > https://www.php.net/manual/en/function.hash.php
}
echo(strtoupper(bin2hex($hpswd)));
?>
The first thing I do is ensure that the encoding we are using is UTF-8. Then I iterate over the given input string 1+1000 times. Using the raw_output argument of PHP hash() gives us binary representations each loop, which will give us the same final output. Afterwards, we use bin2hex() to convert the final binary value to a hexidecimal value, and then strtoupper() to uppercase it. Giving us 1FA341B135918B61CB165AA67B33D024CC8243C679F20967A690C159D1A48FACFA4C57C33DDDE3D64539BF4211C44C8D1B18C787917CD779B2777856438E4D21, matching the CF-hashed value.
Also note that CF returns an uppercase value whereas PHP is lowercase.
And final note: There are better methods for storing and using hashed passwords in PHP. This will help convert between CF and PHP hashes, but it would probably be better to ultimately convert all stored hashes into the PHP equivalents. https://www.php.net/manual/en/faq.passwords.php
=============================================================
A point of clarification:
Both Adobe and Lucee changed the name of this parameter to clarify their intent, however they behave differently.
Lucee named the parameter numIterations with default 1. This is the total times that hash() will run.
In CF2018, with the introduction of Named Parameters, Adobe renamed the parameter additionalIterations from the original (and still documented) iterations. The original improper parameter name didn't matter prior to CF2018 because you couldn't use named params anyway. On their hash() documentation page, their verbiage is "Hence, this parameter is the number of iterations + 1. The default number of additional iterations is 0." (emphasis mine) The behavior has always (since CF10) matched this description, but there is clearly some confusion about its actual meaning, especially since there is a difference with Lucee's behavior and with Adobe's incorrect initial name of the parameter.
The parameter name iterations is incorrect and doesn't work with either Adobe CF 2018 or Lucee 4.5 or 5.x. And this is a function that is not currently compatible as-is between Lucee and Adobe ColdFusion.
The important thing to remember, especially if working with both Adobe and Lucee code, is that this function with the ???Iterations named param specified will produce two different outputs if the same-ish code is run. Adobe will run a hash() one additional time vs Lucee. The good news is that since the param names aren't the same, then if they are used, an error will be thrown instead of silently producing different hashes.
hash("Stack Overflow","md5","UTF-8",42) ;
// Lucee: C0F20A4219490E4BF9F03ED51A546F27
// Adobe: 42C57ECBF9FF2B4BEC61010B7807165A
hash(input="Stack Overflow", algorithm="MD5", encoding="UTF-8", numIterations=42) ;
// Lucee: C0F20A4219490E4BF9F03ED51A546F27
// Adobe: Error: Parameter validation error
hash(string="Stack Overflow", algorithm="MD5", encoding="UTF-8", additionalIterations=42) ;
// Lucee: Error: argument [ADDITIONALITERATIONS] is not allowed
// Adobe: 42C57ECBF9FF2B4BEC61010B7807165A
https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-functions/functions-h-im/hash.html
https://docs.lucee.org/reference/functions/hash.html
See https://cfdocs.org/hash "...in Adobe CF the value is the number of additional iterations."

Why are there binary safe AND binary unsafe functions in php?

Is there any reason for this behavior/implementation ?Example:
$array = array("index_of_an_array" => "value");
class Foo {
private $index_of_an_array;
function __construct() {}
}
$foo = new Foo();
$array = (array)$foo;
$key = str_replace("Foo", "", array_keys($array)[0]);
echo $array[$key];
Gives us an error which is complete:
NOTICE Undefined index: on line number 9
Example #2:
echo date("Y\0/m/d");
Outputs:
2016
BUT! echo or var_dump(), for example, and some other functions, would output the string "as it is", just \0 bytes are being hidden by browsers.
$string = "index-of\0-an-array";
$strgin2 = "Y\0/m/d";
echo $string;
echo $string2;
var_dump($string);
var_dump($string2);
Outputs:
index-of-an-array
"Y/m/d"
string(18) "index-of-an-array"
string(6) "Y/m/d"
Notice, that $string lenght is 18, but 17 characters are shown.
EDIT
From possible duplicate and php manual:
The key can either be an integer or a string. The value can be of any type.
Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer. So in short, any string can be a key. And a string can contain any binary data (up to 2GB). Therefore, a key can be any binary data (since a string can be any binary data).
From php string details:
There are no limitations on the values the string can be composed of;
in particular, bytes with value 0 (“NUL bytes”) are allowed anywhere
in the string (however, a few functions, said in this manual not to be
“binary safe”, may hand off the strings to libraries that ignore data
after a NUL byte.)
But I still do not understand why the language is designed this way? Are there reasons for this behavior/implementation? Why PHP does'nt handle input as binary safe everywhere but just in some functions?
From comment:
The reason is simply that many PHP functions like printf use the C library's implementation behind the scenes, because the PHP developers were lazy.
Arent those such as echo, var_dump, print_r ? In other words, functions that output something. They are in fact binary safe if we take a look at my first example. Makes no sense to me to implement some binary-safe and binary-unsafe functions for output. Or just use some as they are in std lib in C and write some completely new functions.
The short answer to "why" is simply history.
PHP was originally written as a way to script C functions so they could be called easily while generating HTML. Therefore PHP strings were just C strings, which are a set of any bytes. So in modern PHP terms we would say nothing was binary-safe, simply because it wasn't planned to be anything else.
Early PHP was not intended to be a new programming language, and grew organically, with Lerdorf noting in retrospect: "I don’t know how to stop it, there was never any intent to write a programming language […] I have absolutely no idea how to write a programming language, I just kept adding the next logical step on the way."
Over time the language grew to support more elaborate string-processing functions, many taking the string's specific bytes into account and becoming "binary-safe". According to the recently written formal PHP specification:
As to how the bytes in a string translate into characters is unspecified. Although a user of a string might choose to ascribe special semantics to bytes having the value \0, from PHP's perspective, such null bytes have no special meaning. PHP does not assume strings contain any specific data or assign special values to any bytes or sequences.
As a language that has grown organically, there hasn't been a move to universally treat strings in a manner different from C. Therefore functions and libraries are binary-safe on a case-by-case basis.
Fist Example from Question
Your first example is a confusing because the error message is the part that's terminating on the null character not because the string is being handled incorrectly by the array. The original code you posted with the error message follows:
$array = array("index-of-an-array" => "value");
$string = "index-of\0-an-array";
echo $array[$string];
Notice: Undefined index: index-of in
Note, the error message above has been truncated index-of due to the null character, the array is working as expected because if you try it this way it will work just fine:
$array = array("index-of\0-an-array" => "value");
$string = "index-of\0-an-array";
echo $array[$string];
The error message correctly identified the that the two keys were wrong, which
they are
"index-of\0-an-array" != "index-of-an-array"
The problem is that the error message printed out everything up to the null character. If that's the case then it might be considered a bug by some.
Second Example is starting plumb the depths of PHP :)
I've added some code to it so we can see what's happening
<?php
class Foo {
public $index_public;
protected $index_prot;
private $index_priv;
function __construct() {
$this->index_public = 0;
$this->index_prot = 1;
$this->index_priv = 2;
}
}
$foo = new Foo();
$array = (array)$foo;
print_r($foo);
print_r($array);
//echo $array["\0Foo\0index_of_an_array2"];//This prints 2
//echo $foo->{"\0Foo\0index_of_an_array2"};//This fails
var_dump($array);
echo array_keys($array)[0] . "\n";
echo $array["\0Foo\0index_priv"] . "\n";
echo $array["\0*\0index_prot"] . "\n";
The above codes output is
Foo Object
(
[index_public] => 0
[index_prot:protected] => 1
[index_priv:Foo:private] => 2
)
Array
(
[index_public] => 0
[*index_prot] => 1
[Fooindex_priv] => 2
)
array(3) {
'index_public' =>
int(0)
'\0*\0index_prot' =>
int(1)
'\0Foo\0index_priv' =>
int(2)
}
index_public
2
1
The PHP developers choose to use the \0 character as a way to split member variable types. Note, protected fields use a * to indicate that the member variable may actually belong to many classes. It's also used to protect private access ie this code would not work.
echo $foo->{"\0Foo\0index_priv"}; //This fails
but once you cast it to an array then there is no such protection ie this works
echo $array["\0Foo\0index_priv"]; //This prints 2
Is there any reason for this behavior/implementation?
Yes. On any system that you need to interface with you need to make system
calls, if you want the current time or to convert a date etc you need to talk
to the operating system and this means calling the OS API, in the case of Linux
this API is in C.
PHP was original developed as a thin wrapper around C quite a few languages
start out this way and evolve, PHP is no exception.
Is there any reason for this behavior/implementation?
In the absence of any backwards compatibility issues I'd say some of the choices are less than optimal but my suspicion is that backwards compatibility is a large factor.
But I still do not understand why the language is designed this way?
Backwards compatibility is almost always the reason why features that people don't like remain in a language. Over time languages evolve and remove things but it's incremental and prioritized. If you had asked all the PHP developers do they want better binary string handling for some functions or a JIT compiler I think a JIT might win which it did in PHP 7. Note, the people doing the actual work ultimately decide what they work on and working on a JIT compiler is more fun than fixing libraries that do things in seemingly odd ways.
I'm not aware of a any language implementor that doesn't wish they'd done some things differently from the outset. Anyone implementing a compiler before a
language is popular is under a lot of pressure to get something that works for
them and that means cutting corners, not all languages in existence today had a
huge company backing them, most often it was a small dedicated team and they
made mistakes, some were lucky enough to get paid to do it. Calling them lazy
is a bit unfair.
All language have dark corners warts and boils and features you'll eventually hate. Some more than others and PHP has a bad rep because it has/had a lot more than most. Note, PHP 5 is a vast leap forward from PHP 4. I'd imagine that PHP 7 will improve things even more.
Anyone that thinks their favorite language is free from problems is delusional and has almost certainly not plumbed the depths of the tool their using to any great depth.
Functions in PHP which internally operate with C strings are "not binary safe" in PHP terminology. C string is an array of bytes ending with byte 0. When a PHP function internally uses C strings, it reads one by one character and when it encounters byte 0 it considers it as an end of string. Byte 0 tells C string functions where is the end of string since C string does not contain any information about string length.
"Not binary safe" means that, if function which operates with C string is somehow handed a C string not terminated with byte 0, behavior is unpredictable because function will read/write bytes beyond end of the string, adding garbage to string and/or potentially crashing PHP.
In C++, for example, we have string object. This object also contains an array of characters, but it has also a length field which it updates on any length change. So it does not require byte 0 to tell it where the end is. This is why string object can contain any number of 0 bytes, although this is generally not valid since it should contain only valid characters.
In order for this to be corrected, the whole PHP core, including any modules which operate with C strings, need to be rewritten in order to send "non binary safe" functions to history. The amount of job needed for this is huge and all the modules' creators need to produce new code for their modules. This can introduce new bugs and instabilities into the whole story.
Issue with byte 0 and "non binary safe" functions is not that much critical to justify rewriting PHP and PHP modules code. Maybe in some newer PHP version where some things need to be coded from scratch it would make sense to correct this.
Until then, you just need to know that any arbitrary binary data put to some string by using binary-safe functions needs to have byte 0 added at the end. Usually you will notice this when there is unexpected garbage at end of your string or PHP crashes.

PHP's Infinit value in Bitwise Operations returns strange values

Today I just made an interesting discovery while testing what happens calculating bitwisely in php like INF ^ 0 (^ => Bitwise Operator for Exclusive OR (XOR)) what gave me int(-9223372036854775808) => greatest possible negative value in a 64-Bit system.
But then I was asking myself: "Why is the result going negative in XOR when the "positive infinit" means 9223372036854775807 (63 Bits on 1 with a leading 0) and 0 (64 Bits on 0 => 0 xor 0 = 0) What is PHP's infinit value though and what is the calculation behind it? And why do I get a (correct?) negative value when I use "negative infinit"(A leading 1 against a leading 0 on 0 => 1 xor 0 = 1?".
Another interesting point is that this just happens on PHP Version 5.5.9-1, and not e.g. on 5.3.x. and 5.6.x (where i've tested it)! Maybe someone has an idea what happens there? Tested it on three versions but just mine (5.5.9-1) gives those results:
Just to let you guys know, it's just an abstract playaround i've done for fun but I find it's interesting. Maybe someone can help here or explain me a wrong thought I have? Just tell me if someone needs more informations about anything!
EDIT: Accordingly to jbafford it would be great to get a complete answere, so i'll just quote him: why does 5.5 and 5.6 result in PHP_INT_MIN, and everything else return 0?
First off, ^ itself isn't what's special here. If you XOR anything with zero, or OR anything with zero, you just get back the original answer. What you're seeing here is not part of the operation itself, but rather what happens before the operation: the bitwise operators take integers, so PHP converts the float to an integer. It's in the float-to-integer conversion that the weird behaviour appears, and it's not exclusive to the bitwise operators. It also happens for (int), for example.
Why does it produce these weird results? Simply because that's what the C code PHP is written in produces when converting a float to an integer. In the C standard, C's behaviour for float-to-integer conversions is undefined for the special values of INF, -INF and NAN (or, more accurately, for "integral parts" an integer can't represent: §6.3.1.4). This undefined behaviour means the compiler is free to do whatever it wants. It just so happens in this case that the code it generates produces the minimum integer value here, but there's no guarantee that will always happen, and it's not consistent across platforms or compilers.1 Why did the behaviour change between 5.4 and 5.5? Because PHP's code for converting floats to integers changed to always perform a modulo conversion. This fixed the undefined behaviour for very large floating-point numbers,2 but it still didn't check for special values, so for that case it still produced undefined behaviour, just slightly different this time.
In PHP 7, I decided to clean up this part of PHP's behaviour with the Integer Semantics RFC, which makes PHP check for the special values (INF, -INF and NAN) and convert them consistently: they always convert to integer 0. There's no longer undefined behaviour at work here.
1 For example, a test program I wrote in C to try to convert Infinity to an integer (specifically a C long) has different results on 32-bit and 64-bit builds. The 64-bit build always produces -9223372036854775808, the minimum integer value, while the 32-bit build always produces 0. This behaviour is the same for GCC and clang, so I guess they're both producing very similar machine code.
2 If you tried to convert a float to an integer, and that float's value was too big to fit in an integer (e.g. PHP_INT_MAX * 2, or PHP_INT_MIN * 2), the result was undefined. PHP 5.5 makes the result consistent, though unintuitive (it acts if the float was converted to a very large integer, and the most significant bits were discarded).
Your float(INF) gets implicitly casted to an Integer.
and XOR with 0 does not change the first parameter. So basically this is just a cast from float to int which is undefined for values which are not in the integer range. (for all other values it will be truncated towards zero)
https://3v4l.org/52bA5

PHP - How to eval math with big integer result?

I have a problem like this
when i eval this
$a='820725059 + 35 * 4082615 + 6209777 - 6476390779 * 3376849 + 3 - 8089 * 3967379273 - 0 * 55111452';
eval('return ('.$a.');');
i got this result -2.1901884886788E+16
instead of -21901884886788304
i know about BCMath, but how to calculate multiple operation like this?
Note:
My problem is about print out as BIG INT
Check if you are using 32bit or 64bit PHP
echo PHP_INT_SIZE; //4 is 32bit, 8 is 64bit
32bit won't handle this integer, for 64bit the result is -21901884886788304
if you have to use 32bit, try using GMP, and parse the integer yourself (good luck...)
Another solution is to write very basic Python program (or any other language that handles large numbers easily) and send it as an argument with exec function and let the other language to parse it for you (Python can handle very large numbers easily)
I assume that the line "how to calculate multiple operation like this?" means "how to write a calculator for arbitrary user-submitted math expressions?", which is why you use eval in first place.
If you use eval over unsanitized user-submitted input, you fail, remember this as a rule of thumb.
You just can't solve this with eval anyway, you have to use BCMath operations if you want to work with bignums.
Proper solution is to tokenize the input and construct the tree of math operations, essentially writing an interpreter for simple language for describing math expressions.
Here is result of 5 seconds of googling for something already done before: https://github.com/michellesanver/Calculator You can take her work and replace usage of plain math operators in classes like Addition with BCMath methods.

Comparing different strings in PHP with == returns true

I was just debugging a script and found that an if-statement wasn't working the way I expected it to.
var_dump("6064365413078728979" == "6064365413078728452");
die();
The code above will result in the following:
bool(true)
With the === operator it works as expected. Anyone got any ideas why?
I'm using PHP Version 5.3.13 with a wamp installation on a x64 windows machine.
PHP has loose type comparison behavior, so your numerical strings are getting converted to integer types before == non strict comparison, and the conversion result is overflowing.
That is the principal reason to use === when it's possible.
Take a look at this page for further details on type juggling.
<?php
$a=6064365413078728979;
$b=6064365413078728452;
echo $a."<br>".$b;
//var_dump( $a==$b );
die();
?>
When you run that, then on your machine that might be exceeding limit for a number and that is a numeric comparison taking place. Try the script above and see value for $a will probably be different than the value you gave.
That is why when both are compared numerically they are equal. Hence use === as suggested by others
Edit: Explanation based upon #Axel's Advice.
PHP Manual explains
The size of a float is platform-dependent, although a maximum of
~1.8e308 with a precision of roughly 14 decimal digits is a common
value (the 64 bit IEEE format).
And this website is offering and explanation on the Overflow phenomenon and a small php code to test your system's integer and float range. Getting to know the limit on your servers will most probably explain it best why the offerflow occured

Categories