I'm trying to use CodeIgniter to write up a small program for school which generates a random 'key' every time I click the 'generate' button. Looking to see if there's a way for me to create a function where I can fill up a 14 character array with a random number or letter and then set the array to a variable which I can call upon to display as my generated key.
Any and all help would be much appreciated as I am new to CodeIgniter.
A while back I wrote this function in PHP, it does what it does and gives you some flexibility as well through complexity modifiers, I used a default set of 5 different 'levels' of characters and the length is also variable ofcourse.
I'm just going to chuck it in here and 'try' to explain what is going on as well as I can by comments:
function rsg($length = 10, $complexity = 2) {
//available 'complexity' subsets of characters
$charSubSets = array(
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789',
'!##$%^&*()_+{}|:">?<[]\\\';,.`~',
'µñ©æáßðøäåé®þüúíóö'
);
// will be filled with subsets from above $charSubsets
$chars = '';
//concact each subset until complexity is reached onto the $chars variable
for ($i = 0; $i < $complexity; $i++)
$chars .= $charSubSets[$i];
//create array containing a single char per entry from the combined subset in the $chars variable.
$chars = str_split($chars);
//define length of array for mt_rand limit
$charCount = (count($chars) - 1);
//create string to return
$string = '';
//idk why I used a while but it won't really hurt you when the string is less than 100000 chars long ;)
$i = 0;
while ($i < $length) {
$randomNumber = mt_rand(0, $charCount); //generate number within array index range
$string .= $chars[$randomNumber]; //get that character out of the array
$i++; //increment counter
}
return $string; //return string created from random characters
}
This is what I currently use and it has satisfied my needs for quite some time now, if anyone reading over this has improvements I'd love to hear them as well!
$a=array(rand(10000000000000, 99999999999999));
is a quick way to get a 14 digit array.
It depends on how random you want it to be. You could specify all characters you want in a $characters string, then just create a string up to $length, picking a random substring of length 1 from the characters string.
What are the requirements?
Do you want it to be as random as possible (This link might be useful)
Are multiple occurrences of one character allowed in one random string?
Here's an example though: PHP random string generator
As the question states, would the following array require 5 bits of memory?
$flags = array(true, false, true, false, false);
[EDIT]: Apologies just found this duplicate.
Each element in the array stored in a separate memory location, you also need to store the hashtable for the array, along with the keys, so NOOOO, it's going to be a lot more.
No. PHP has internal metadata attached to every variable/array element definined. PHP does not support bit fields directly, so the smallest ACTUAL allocation is a byte, plus metadata overhead.
I doubt there is an application that uses less than system arcitecture's data word as a minimum data storage unit.
But I am sure it shouldn't be your concern at all.
It depends on the php interpreter. The standard interpreter is extremely wasteful, although this is not uncommon for a dynamic language. The massive overhead is caused by garbage collection, and the dynamic nature of every value; since the contents of an array can take arbitrary values of arbitrary types (i.e. you can write $ar[1] = 's';), the type and additional metainformation must be stored.
With the following test script:
<?php
$n = 20000000;
$ar = array();
$i = 0;
$before = memory_get_usage();
for ($i = 0;$i < $n;$i++) {
$ar[] = ($i % 2 == 0);
}
$after = memory_get_usage();
echo 'Using ' . ($after - $before) . ' Bytes for ' . $n . ' values';
echo ', per value: ' . (($after - $before) / $n) . "\n";
I get about 150 Bytes per array entry (x64, php 5.4.0-2). This seems to be at the higher end of implementations; ideone reports 73 Bytes/entry (php 5.2.11), and so does codepad.
So I was wonder what are some good/preferred methods for generating a 'hex-like' value in PHP? Preferably, I would want to restrict it to 5 characters long like such: 1e1f7
Currently this is what I am doing:
echo dechex(mt_rand(10000, 99999));
however this gives me values anywhere from 4-5 characters long, and I want to keep it at a consistent 4 or 5.
What are some ways to better generate something like this in PHP? Is there even a built in function?
Note: When I say 'hex-like' I really just mean a random combination of letters and numbers. There does not have to be a restriction on available letters.
Something simple like:
$length = 5;
$string = "";
while ($length > 0) {
$string .= dechex(mt_rand(0,15));
$length -= 1;
}
return $string;
(untested)
Or fix your mt_rand range to: mt_rand(65535, 1048575) (10000-fffff in hex) or if you like tinfoil hats: mt_rand(hexdec("10000"), hexdec("ffffff"))
The advantage of the while-loop approach is that it works for arbitrarily long strings. If you'd want 32 random characters you're well over the integer limit and a single mt_rand will not work.
If you really just want random stuff, I'd propose:
$length = 5;
$string = "";
$characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-=+!##$%^&*()[]"; // change to whatever characters you want
while ($length > 0) {
$string .= $characters[mt_rand(0,strlen($characters)-1)];
$length -= 1;
}
return $string;
(untested)
echo substr( base64_encode( mt_rand(1000, mt_getrandmax() ), 0, 5);
This uses more of the alphabet due to the base64, but remember that it will include upper and lower case letters along with numbers.
Why all the work sha1 is tested and evenly distributed:
substr(sha1(uniqid('moreentropyhere')),0,5);
I have used this to generate millions and millions of uniq uids for sharding tables, no collisions and remarkably evenly distributed regardless of the length you use...
you can even use binary form of sha1 hash for base 64:
base64_encode(sha1(uniqid('moreentropyhere'), true))
to limit characters, you can use a regex:
substr(preg_replace('~[^a-km-np-z2-9]~','',strtolower(base64_encode(sha1(uniqid(),true)))),0,6)
Here we limited 0,1,l (letter), and o (letter) from the string, trading a little entropy to prevent confusion (and service tickets) during entry for all ages...
I have some strings containing alpha numeric values, say
asdf1234,
qwerty//2345
etc..
I want to generate a specific constant number related with the string. The number should not match any number generated corresponding with other string..
Does it have to be a number?
You could simply hash the string, which would give you a unique value.
echo md5('any string in here');
Note: This is a one-way hash, it cannot be converted from the hash back to the string.
This is how passwords are typically stored (using this or another hash function, typically with a 'salt' method added.) Checking a password is then done by hashing the input and comparing to the stored hash.
edit: md5 hashes are 32 characters in length.
Take a look at other hash functions:
http://us3.php.net/manual/en/function.crc32.php (returns a number, possibly negative)
http://us3.php.net/manual/en/function.sha1.php (40 characters)
You can use a hashing function like md5, but that's not very interesting.
Instead, you can turn the string into its sequence of ASCII characters (since you said that it's alpha-numeric) - that way, it can easily be converted back, corresponds to the string's length (length*3 to be exact), it has 0 collision chance, since it's just turning it to another representation, always a number and it's a little more interesting... Example code:
function encode($string) {
$ans = array();
$string = str_split($string);
#go through every character, changing it to its ASCII value
for ($i = 0; $i < count($string); $i++) {
#ord turns a character into its ASCII values
$ascii = (string) ord($string[$i]);
#make sure it's 3 characters long
if (strlen($ascii) < 3)
$ascii = '0'.$ascii;
$ans[] = $ascii;
}
#turn it into a string
return implode('', $ans);
}
function decode($string) {
$ans = '';
$string = str_split($string);
$chars = array();
#construct the characters by going over the three numbers
for ($i = 0; $i < count($string); $i+=3)
$chars[] = $string[$i] . $string[$i+1] . $string[$i+2];
#chr turns a single integer into its ASCII value
for ($i = 0; $i < count($chars); $i++)
$ans .= chr($chars[$i]);
return $ans;
}
Example:
$original = 'asdf1234';
#will echo
#097115100102049050051052
$encoded = encode($original);
echo $encoded . "\n";
#will echo asdf1234
$decoded = decode($encoded);
echo $decoded . "\n";
echo $original === $decoded; #echoes 1, meaning true
You're looking for a hash function, such as md5. You probably want to pass it the $raw_output=true parameter to get access to the raw bytes, then cast them to whatever representation you want the number in.
A cryptographic hash function will give you a different number for each input string, but it's a rather large number — 20 bytes in the case of SHA-1, for example. In principle it's possible for two strings to produce the same hash value, but the chance of it happening is so extremely small that it's considered negligible.
If you want a smaller number — say, a 32-bit integer — then you can't use a hash function because the probability of collision is too high. Instead, you'll need to keep a record of all the mappings you've established. Make a database table that associates strings with numbers, and each time you're given a string, look it up in the table. If you find it there, return the associated number. If not, choose a new number that isn't used by any of the existing records, and add the new string and number to the table.
For a variety of stupid reasons, the maximum length of a given form variable that we are posting to an external server is 12 characters.
I wanted to obscure that value with md5, but obviously with 12 characters that isn't going to work. Is there a cipher with an already-made PHP function which will result in something 12 characters or less?
The security and integrity of the cipher isn't super important here. My last resort is to just write a function which moves each letter up or down an ascii value by x. So the goal isn't to obscure it from a cryptography expert, but just to not post it in plain text so a non-technical worker looking at it won't know what it is.
Thanks for any advice.
maybe this will help you generate a 12 char string that you can pass in a URL, without increasing the risk of collisions
substr(base_convert(md5($string), 16,32), 0, 12);
This is an addition to this answer.
The answer proposes to take the first twelve characters from a 32 character representation of md5. Thus 20 characters of information will be lost - this will result in way more possible collisions.
You can reduce the loss of information by taking the first twelve characters of a 16 character representation (the raw form):
substr(md5($string, true), 0, 12);
This will maintain 75% of the data, whereas the use of the 32 char form only maintains 37.5% of the data.
Try crc32() maybe?
If you just need a hash, you can still use the first 12 characters from the md5 hash.
substr(md5($yourString), 0, 12);
All the answers are suggesting loosing some of the data (higher collision possibility), but looks like using using base conversion is a better approach:
e.g. like described here http://proger.i-forge.net/Short_MD5/OMF
You may also generate any random string and insert it into database, checking if not already exists prior to saving. This will allow you to have short hashes, and ensure there are no collisions.
I have to put this suggestion across as I have to assume you are in control of the script that your encrypted value is sent to....
I also have to assume that you can create many form fields but they can't have a length larger than 12 characters each.
If that's the case, could you not simply create more than one form field and spread the md5 string across multiple hidden fields?
You could just split the md5 string into chunks of 8 and submit each chunk in a hidden form field and then join them together at the other end.
Just a thought...
You can make use of a larger alphabet and make hash shorter but still reversible to original value.
I implemented it here - for example, hash ee45187ab28b4814cf03b2b4224eb974 becomes 7fBKxltZiQd7TFsUkOp26w - it goes from 32 to 22 characters. And it can become even less if you use a larger alpahabet. If you use unicode, you can even encode hash with emoji...
This probably won't be of use to the OP since they were looking for 2 way function but may help someone looking for a shorter hash than md5. Here is what I came up with for my needs (thanks to https://rolandeckert.com/notes/md5 for highlighting the base64_encode function). Encode the md5 hash as base(64) and remove any undesirable base(64) characters. I'm removing vowels + and / so reducing the effective base from 64 to 52.
Note if you truncate a base(b) encoded hash after c characters it will allow for b ^ c unique hashes. Is this robust enough to avoid collisions? It depends on how many items (k) you are hashing. The probability of collision is roughly (k * k) / (b ^ c) / 2, so if you used the function below to hash k = 1 million items with base b = 52 encoding truncated after c = 12 characters the probability of collision is < 1 in 750 million. Compare to truncating the hex encoded (b = 16) hash after c = 12 characters. The probability of collision is roughly 1 in 500! Just say no to truncating hex encoded hashes. :)
I'll go out on a limb and say the function below (with length 12) is reasonably safe for 10 million items (< 1 in 7.5 million probability of collision), but if you want to be extra safe use base(64) encoding (comment out the $remove array) and/or truncate fewer characters.
// convert md5 to base64, remove undesirable characters and truncate to $length
function tinymd5($str, $length) { // $length 20-22 not advised unless $remove = '';
// remove vowels to prevent undesirable words and + / which may be problematic
$remove = array('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U', '+', '/');
$salt = $str;
do { // re-salt and loop if rebase removes too many characters
$salt = $base64 = base64_encode(md5($salt, TRUE));
$rebase = substr(str_replace($remove, '', $base64), 0, $length);
} while ($length < 20 && substr($rebase, -1) == '=');
return str_pad($rebase, min($length, 22), '='); // 22 is max possible length
}
$str = 'Lorem ipsum dolor sit amet 557726776';
echo '<br />' . md5($str); // 565a0bf7e0ba474fdaaec57b82e6504a
$x = md5($str, TRUE);
echo '<br />' . base64_encode($x); // VloL9+C6R0/arsV7guZQSg==
echo '<br />' . tinymd5($str, 12); // VlL9C6R0rsV7
echo '<br />' . tinymd5($str, 17); // VlL9C6R0rsV7gZQSg
$x = md5(base64_encode($x), TRUE); // re-salt triggered < 20
echo '<br />' . base64_encode($x); // fmkPW/OQLqp7PTex0nK3NQ==
echo '<br />' . tinymd5($str, 18); // fmkPWQLqp7PTx0nK3N
echo '<br />' . tinymd5($str, 19); // fmkPWQLqp7PTx0nK3NQ
echo '<br />' . tinymd5($str, 20); // fmkPWQLqp7PTx0nK3NQ=
echo '<br />' . tinymd5($str, 22); // fmkPWQLqp7PTx0nK3NQ===
$hashlen = 4;
$cxtrong = TRUE;
$sslk = openssl_random_pseudo_bytes($hashlen, $cxtrong);
$rand = bin2hex($sslk);
echo $rand;
You can change the hash length (in multiples of two) by changing the value of the variable $hashlen
I came up with base 90 for reducing md5 to 20 multi-byte characters (that I tested to fit properly in a mysql's varchar(20) column). Unfortunately this actually makes the string potentially larger than even the 32 bytes from php's md5, with the only advantage that they can be stored in varchar(20) columns. Of course you could just replace the alphabet with single-byte ones if your worries are about storage...
There are a couple of rules that are important to have in mind if your idea is to use this reduced hash as a lookup key in something like mysql and for other kinds of processing:
By default MySQL does not differentiate Upper Case from Lower Case in a typical where clause which takes out a lot of characters right out of the possible target alphabets. This include not only english character but also almost all characters in other languages.
It's important that your hash can be upper-cased and lower-cased transparently since many systems uppercase these keys, so to keep it consistent with md5 in that sense you should use only lowercase when using case-able characters.
This is the alphabet I used (I handpicked each character to make the hashes as nice as possible):
define('NICIESTCHARS', [
"0","1","2","3","4","5","6","7","8","9",
"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
"¢","£","¥","§","¶","ø","œ","ƒ","α","δ","ε","η","θ","ι","λ","μ","ν","π","σ","τ","φ","ψ","ω","ћ","џ","ѓ","ѝ","й","ќ","ў","ф","э","ѣ","ѷ","ѻ","ѿ","ҁ","∂","∆","∑","√","∫",
"!","#","$","%","&","*","+","=","#","~","¤","±"
]);
Here is the code in PHP (I suppose it's not the best code but does the job). And keep in mind that it only works for strings in hexa (0-F) that are a multiple of 8 in length like md5 in php which is 32 0-f bytes:
function mbStringToArray ($string) {
$strlen = mb_strlen($string);
while ($strlen) {
$array[] = mb_substr($string,0,1,"UTF-8");
$string = mb_substr($string,1,$strlen,"UTF-8");
$strlen = mb_strlen($string);
}
return $array;
}
class Base90{
static function toHex5($s){
// Converts a base 90 number with a multiple of 5 digits to hex (compatible with "hexdec").
$chars = preg_split('//u', $s, null, PREG_SPLIT_NO_EMPTY);
$map = array_flip(NICIESTCHARS);
$rt = '';
$part = [];
$b90part = '';
foreach($chars as $c){
$b90part .= $c;
$part[] = $map[$c];
if(count($part) == 5){
$int = base90toInt($part);
$rt .= str_pad(dechex($int), 8, "0", STR_PAD_LEFT);
$part = [];
$b90part = '';
}
}
return $rt;
}
static function fromHex8($m){
// Converts an hexadecimal number compatible with "hexdec" to base 90
$parts = [];
$part = '';
foreach(str_split($m) as $i => $c){
$part.= $c;
if(strlen($part) === 8){
$parts[] = intToBase90(hexdec($part));
$part = '';
}
}
return implode('', $parts);
}
}
function intToBase90($int){
$residue = $int;
$result = [];
while($residue){
$digit = $residue % 90;
$residue -= $digit;
$residue = $residue / 90;
array_unshift($result, NICIESTCHARS[$digit]);
}
$result = implode('', $result);
return $result;
}
function base90toInt($digits){
$weight = 1;
$rt = 0;
while(count($digits)){
$rt += array_pop($digits)*$weight;
$weight *= 90;
}
return $rt;
}