3
\$\begingroup\$

I've written a password encryption algorithm in PHP, which (I think) is not very vulnerable to rainbowtable attacks. It's just that I don't have a lot of experience with encryptions, nor PHP. But from the knowledge I have I think this is a hashing algorithm which automatically adds salt, but I actually have no idea if this is true or not, so please tell me.

This is the code I use to create a password hash:

<?php
  $pass = $argv[1];
  $pass_len = strlen($pass) - 1;
  $pass_chars = str_split($pass, 1);

  // Convert password characters to their ASCII values
  foreach ($pass_chars as $key => $val) {
    $pass_chars[$key] = ord($val);
  }

  // Generate an array of random characters the size of $pass_chars
  for ($i = 0; $i < $pass_len + 1; $i++) {
    $rand_chars[$i] = mt_rand(33, 126);
  }

  // Create list of added $pass_vals values
  foreach ($pass_chars as $key => $val) {
    $pass_vals[$key] = $val + $pass_chars[$key + 1];
  }
  $pass_vals[$pass_len] = $pass_chars[$pass_len] + $pass_chars[0];

  // Create list of added $rand_vals values
  foreach ($rand_chars as $key => $val) {
    $rand_vals[$key] = $val + $rand_chars[$key + 1];
  }
  $rand_vals[$pass_len] = $rand_chars[$pass_len] + $rand_chars[0];

  // Add $rand_vals to $pass_chars
  foreach ($pass_chars as $key => $val) {
    $pass_chars[$key] += $rand_vals[$key];
  }

  // Add $pass_vals to $rand_chars
  foreach ($rand_chars as $key => $val) {
    $rand_chars[$key] += $pass_vals[$key];
  }

  // Create combined array
  $i = 1;
  foreach ($pass_chars as $key => $val) {
    $combined[$key * 2] = str_pad($pass_chars[$key], 3, "0", STR_PAD_LEFT);
    $combined[$i] = str_pad($rand_chars[$key], 3, "0", STR_PAD_LEFT);
    $i += 2;
  }

  // Print $combined as string
  echo implode($combined) . "\n";
?>

And this code to reverse it:

<?php
  $pass_i = $argv[1];
  $hash = $argv[2];
  $pass_len_i = strlen($pass_i) - 1;
  $pass_chars_i = str_split($pass_i, 1);
  $hash_chars = str_split($hash, 3);
  $hash_len = sizeof($hash_chars);

  // Convert input password characters to their ASCII values
  foreach ($pass_chars_i as $key => $val) {
    $pass_chars_i[$key] = ord($val);
  }

  // Create list of added $pass_vals_i values
  foreach ($pass_chars_i as $key => $val) {
    $pass_vals_i[$key] = $val + $pass_chars_i[$key + 1];
  }
  $pass_vals_i[$pass_len_i] = $pass_chars_i[$pass_len_i] + $pass_chars_i[0];

  // Remove extra '0's at the left in $hash_chars if there are any
  foreach ($hash_chars as $key => $val) {
    $hash_chars[$key] = ltrim($val, "0");
  }

  // Extract $rand_chars and $pass_chars from $hash_chars
  $i = 1;
  foreach ($hash_chars as $key => $val) {
    $pass_chars[$key] = $hash_chars[$key * 2];
    $rand_chars[$key] = $hash_chars[$i];
    $i += 2;
  }

  // Subtract $pass_vals_i from $rand_chars
  foreach ($rand_chars as $key => $val) {
    $rand_chars[$key] -= $pass_vals_i[$key];
  }

  // Create list of $rand_vals by adding $rand_chars values
  foreach ($rand_chars as $key => $val) {
    $rand_vals[$key] = $val + $rand_chars[$key + 1];
  }
  $rand_vals[$pass_len_i] = $rand_chars[$pass_len_i] + $rand_chars[0];

  // Subtract $rand_vals from $pass_chars
  foreach ($pass_chars as $key => $val) {
    $pass_chars[$key] -= $rand_vals[$key];
  }

  foreach ($pass_chars as $key => $val) {
  $pass_chars[$key] = chr($val);
  }

  // Print $pass_chars as string
  echo implode($pass_chars) . "\n";
?>

I would like to know if this is secure, and why I can't get it to compare $pass_i with $pass in the reversing script.

I've hashed the word "password" 100 times, and this is the list of hashes:

279288252300292297275344234280242290239280238271
307317310299276356224269311304346343344331321325
262326212245250312203287197265207268259267309304
253272235290233290279292262336218262276284255304
207267169249210265239294255294297297298328228282
276268235317214263300300314349292301304319244297
288294257303251299289301346337268345218251252279
187242251254301342265308310306336340290324199278
322298319333269331246287241308258269328317300323
201253253257284341285292274342235268236295185253
227283196253249288238310262277311321335318291329
216244260281295324252320225281239280292287240317
258285249282269312231306215274204277268255289325
267303270273320342275327231297278270345336303321
262281226290283281330351316328249324203253222262
245290253264317334326332300343234293238269250281
259267258301281302291328234312269258341339260314
254294189269224265272308235313269258344339290317
231243278297271326209294211264251283277296215293
330322289317266317292298275343209268237269281280
203256267256289356256282297323237310222255214279
239250217298195264279280283348273271327330238309
265265280309314316286347282288253330193251198254
272303236278255303228301269276328329275327242260
326321290314322321330350326329340333296335273273
322296308335306318329337304341269299315298304329
219265226263294308303335264317229283293274275331
291311264289291320297320283326293293297328271281
253300261262337344343342365350335351336312315336
260250271319296297288348233289285280325333233304
274309281274315352273312319310348345341331310322
274245310338323317360355345354254327261255242318
251264252296239301224287227286228277266279242299
295312287292345340322354305317254324235258280289
268311249266250328254271332332334336278326254264
268264240313214272274291262332250266261312204261
244249280304267321298295367352360351293337196268
200249243260305328256326277279309334298303235307
201257198253297290326356322319330339336319265329
277305214281216278279287322341329317319340275291
277298298288333355248327280270265346234247276299
290290297309327333320343289326227299274256299330
308332248285245308288286296351253281250300273262
227267230269280306311323291337241290277279256310
258277248290313303294359287284303339249292225269
276292230293227282291294274346268264301332252281
197259198247251296292304298337226297247257240302
241303236247289334291304329336249329221248267285
240283234266277313309313315345307306273329218256
284298220295262270342341274350217260219285223246
226288210247268308265309270305234301217261235268
249275273283262335196276212269199279240248258304
288289232308266269281346210284206262263272271303
269304266274340337323352297320312313283327251268
248299211258257298222308230263290303318315293315
261295231275228301218276303291334348324314296322
305316300298288347288290283347251272263307263268
254298274265302354234297235286286285277329237260
331330229310210264231295230285287281328334315306
310313273306249312291286289354279271290336258266
234284255259290341291298323342268317248279244281
279313284275297354225292284282287338280277307315
248298234259267320297296326350321312312337264287
293275321327309339304319276334242278297292271317
185249231245300331272318303303309336291301230302
207252260264264341258272304335252305229275197266
281307224283221286275284314340304310329322305319
309312236306200275270274291345278282263324242251
277309211277228279246298256297244295283277306318
289319223279259289254319254284254306227276261263
269273251305302291293360233282295287287336215263
255267297297273345241277306313295329315294279333
249272267286295326267318304298349342268335196245
245275250279294316333327303355264284271308229275
307280308336277317270309294310259320250267254295
260303241266299320257328244278266302270292272290
319330289298266336225279307295288348289268342333
343320340332307353255303297301252332231248294295
240287200262223283267289298327309307310330258292
246264217291192271237270311316333331294330219276
269301198277224266242307252284253304235277250270
298325221282272284298337325310301351240278278274
262289255282272318231303238277292297322323279311
253248301314339332358356302351236287257277219292
306295240320227265316311363354295345279278287313
332308329333285341295293353351270338252260291304
245279279275342349281342243288297291304334240282
242282217269220293206276273279285330268283258297
231279204261280288330341345338347343282332220262
276296208289210264274295255328250263273315245270
316297324328327341257335245271297310305315278302
312310242311206276208279263278316321282323260271
351322307338276314311311341349304328258304267266
330312305327247323206273290282335344268319252261
285321214273231286272294250327235259289304297297
301302245308273282254340215263274288270314249268
298313221294204272242281252310216278250266288296
280251293338289300271338297282302351250279213283
224242266291310320270339225280265281292312213292
241285222265310302275357210267289279360338298334

All of them are unique, and all of them are reversible. This is why I think this algorithm is not very vulnerable to rainbowtables.

Let me know how to improve the code, and potential ways to exploit it.

\$\endgroup\$
13
  • 3
    \$\begingroup\$ If this is to store passwords, don't do that. Instead, read this: security.stackexchange.com/questions/211/… (Basically: don't roll your own!) \$\endgroup\$ Aug 19, 2015 at 14:34
  • \$\begingroup\$ @IsmaelMiguel Why exactly shouldn't I use this? Even if it would be a good algorithm, should I still use a commonly used one, or is this algorithm flawed and that's why I shouldn't use it? \$\endgroup\$
    – insanikov
    Aug 19, 2015 at 14:41
  • 1
    \$\begingroup\$ You shouldn't use this because it isn't tested and re-tested over any attack vector. All common algorithms are tested over the years, released into the public. One of the things that occurs me is that your code is vulnerable against malicious changes in the encrypted data, cold boot attacks and timming attacks. Remember this: if it is reversible, it is breakable. If it isn't reversible, you have an hash and you only need to find a colision. \$\endgroup\$ Aug 19, 2015 at 15:37
  • \$\begingroup\$ @IsmaelMiguel Okay, I understand, thank you for the feedback. \$\endgroup\$
    – insanikov
    Aug 19, 2015 at 15:41
  • \$\begingroup\$ One thing I've noticed is that your results all start with 1, 2 or 3 being 2 the most commum one. The first 3 digits in most results have some relationship with the last 3. \$\endgroup\$ Aug 19, 2015 at 15:41

1 Answer 1

3
\$\begingroup\$

Security

I'm assuming this is for educational purposes. Otherwise, don't roll your own, use bcrypt.

From a quick look at it, it seems that the core of your algorithm basically boils down to this:

foreach character in password:
    character += random(33, 126);

There are more additions, but they do not seem to affect the basic principle.

This means that an attacker can gain at least some information about the password by looking at the encrypted password. An encrypted a will statistically have a smaller value than an encrypted z.

You can see this easily by adjusting the range from which random numbers are selected. Using for example mt_rand(33, 35);, encrypting az will result in this: 166253191254, 165253190253, 167254192254, etc. You can see that 166 is smaller than 191, 165 is smaller than 190, or 167 is smaller than 192.

Making the random range larger makes this problem less sever, but it still exists. z for example can reach values which other characters can never reach. So whenever you see 374, you know for sure that it's a z. When you see 167 you can be pretty sure that it's an a, and so on. This makes brute-force attempts significantly easier for an attacker.

Additionally, the size of the password is easily calculated given the encrypted password. This should not happen.

Also, note the documentation of mt_rand: Caution: This function does not generate cryptographically secure values, and should not be used for cryptographic purposes.

I haven't looked at the decryption code in-depth, so I'm not sure if there are more weaknesses, but there very well might be (I'm not yet 100% convinced that the original password is even needed for decryption).

Code

  • Your variable naming is very confusing. What's the difference between a val and a char? They seem to stand for the same thing? In that case, use the same name. If they stand for different concepts, use clearer names, or add comments.
  • Comments: Your comments basically describe what your code does. But I already know that from looking at the code. As this is relatively complicated code, I would actually like to have comments that describe why the code does what it does; what's the use of each of the for loops? What's the general idea of the algorithm? etc.
  • order of code blocks: your different foreach blocks seem to be randomly ordered. First, you work on the pass_chars, then on rand_chars, then on pass_chars/pass_vals again, then again on rand_chars/rand_vals. As these do not depend on each other yet, it would make more sense to first work on pass_*, then on rand_*.
  • With an input of pass, I get a warning: Notice: Undefined offset: 4 in /var/www/e.php on line 19
\$\endgroup\$
4
  • \$\begingroup\$ This is for educational purposes indeed, I have realized this is not solid and I won't use this in real situations. About the code blocks order; these need to be executed in a specific order, first I convert the characters to their ascii values, add a random character inbetween each one, a sum of the ascii value of the random characters will be added to the original character inbetween, then I add the sum of the original input to the random character inbetween, so this has to be done in the correct order. And I don't understand what causes the error, is it when you are encrypting or reversing? \$\endgroup\$
    – insanikov
    Aug 20, 2015 at 17:54
  • \$\begingroup\$ @insanikov I mean the first 4 loops of the encryption, those do not yet depend on each other, so I would order them differently. And the notice is when I'm encrypting \$\endgroup\$
    – tim
    Aug 20, 2015 at 17:56
  • \$\begingroup\$ You are completely correct, I will rearrange those loops. About the notice; I never get that notice when I use terminal to run, I will try to run it in my browser later (at least I think that's what you do). \$\endgroup\$
    – insanikov
    Aug 20, 2015 at 18:11
  • \$\begingroup\$ @insanikov Yes, right, I used a browser, and I have php set to show all notices. \$\endgroup\$
    – tim
    Aug 20, 2015 at 18:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.