mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-22 21:40:55 +01:00
Add PhabricatorHash::digestForIndex()
Summary: Does this seem reasonable? It's a bit more compact than digest() (6 bits / byte instead of 4 bits / byte) and 72 bits of entropy @ 12 bytes instead of 128 bits of entropy @ 32 bytes. I feel like it's important to preserve the printability, though, and this seemed like a fairly good balance of concerns. Test Plan: unit tests Reviewers: vrana Reviewed By: vrana CC: aran, yemao932 Differential Revision: https://secure.phabricator.com/D4253
This commit is contained in:
parent
141318fd01
commit
4af2e3c4e2
3 changed files with 85 additions and 0 deletions
|
@ -813,6 +813,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorGlobalUploadTargetView' => 'applications/files/view/PhabricatorGlobalUploadTargetView.php',
|
||||
'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/PhabricatorHandleObjectSelectorDataView.php',
|
||||
'PhabricatorHash' => 'infrastructure/util/PhabricatorHash.php',
|
||||
'PhabricatorHashTestCase' => 'infrastructure/util/__tests__/PhabricatorHashTestCase.php',
|
||||
'PhabricatorHeaderView' => 'view/layout/PhabricatorHeaderView.php',
|
||||
'PhabricatorHelpController' => 'applications/help/controller/PhabricatorHelpController.php',
|
||||
'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/PhabricatorHelpKeyboardShortcutController.php',
|
||||
|
@ -2098,6 +2099,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon',
|
||||
'PhabricatorGlobalLock' => 'PhutilLock',
|
||||
'PhabricatorGlobalUploadTargetView' => 'AphrontView',
|
||||
'PhabricatorHashTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorHeaderView' => 'AphrontView',
|
||||
'PhabricatorHelpController' => 'PhabricatorController',
|
||||
'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController',
|
||||
|
|
|
@ -2,6 +2,13 @@
|
|||
|
||||
final class PhabricatorHash {
|
||||
|
||||
|
||||
/**
|
||||
* Digest a string for general use, including use which relates to security.
|
||||
*
|
||||
* @param string Input string.
|
||||
* @return string 32-byte hexidecimal SHA1+HMAC hash.
|
||||
*/
|
||||
public static function digest($string) {
|
||||
$key = PhabricatorEnv::getEnvConfig('security.hmac-key');
|
||||
if (!$key) {
|
||||
|
@ -12,4 +19,39 @@ final class PhabricatorHash {
|
|||
return hash_hmac('sha1', $string, $key);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Digest a string for use in, e.g., a MySQL index. This produces a short
|
||||
* (12-byte), case-sensitive alphanumeric string with 72 bits of entropy,
|
||||
* which is generally safe in most contexts (notably, URLs).
|
||||
*
|
||||
* This method emphasizes compactness, and should not be used for security
|
||||
* related hashing (for general purpose hashing, see @{method:digest}).
|
||||
*
|
||||
* @param string Input string.
|
||||
* @return string 12-byte, case-sensitive alphanumeric hash of the string
|
||||
* which
|
||||
*/
|
||||
public static function digestForIndex($string) {
|
||||
$hash = sha1($string, $raw_output = true);
|
||||
|
||||
static $map;
|
||||
if ($map === null) {
|
||||
$map = "0123456789".
|
||||
"abcdefghij".
|
||||
"klmnopqrst".
|
||||
"uvwxyzABCD".
|
||||
"EFGHIJKLMN".
|
||||
"OPQRSTUVWX".
|
||||
"YZ._";
|
||||
}
|
||||
|
||||
$result = '';
|
||||
for ($ii = 0; $ii < 12; $ii++) {
|
||||
$result .= $map[(ord($hash[$ii]) & 0x3F)];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorHashTestCase extends PhabricatorTestCase {
|
||||
|
||||
public function testHashForIndex() {
|
||||
$map = array(
|
||||
'dog' => 'Aliif7Qjd5ct',
|
||||
'cat' => 'toudDsue3Uv8',
|
||||
'rat' => 'RswaKgTjqOuj',
|
||||
'bat' => 'rAkJKyX4YdYm',
|
||||
);
|
||||
|
||||
foreach ($map as $input => $expect) {
|
||||
$this->assertEqual(
|
||||
$expect,
|
||||
PhabricatorHash::digestForIndex($input),
|
||||
"Input: {$input}");
|
||||
}
|
||||
|
||||
// Test that the encoding produces 6 bits of entropy per byte.
|
||||
$entropy = array(
|
||||
'dog', 'cat', 'rat', 'bat', 'dig', 'fig', 'cot',
|
||||
'cut', 'fog', 'rig', 'rug', 'dug', 'mat', 'pat',
|
||||
'eat', 'tar', 'pot',
|
||||
);
|
||||
|
||||
$seen = array();
|
||||
foreach ($entropy as $input) {
|
||||
$chars = preg_split('//', PhabricatorHash::digestForIndex($input));
|
||||
foreach ($chars as $char) {
|
||||
$seen[$char] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertEqual(
|
||||
(1 << 6),
|
||||
count($seen),
|
||||
"Distinct characters in hash of: {$input}");
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue