Fix folder hashing: Murmur hash didn't work reliably

This commit is contained in:
Marcel Klehr 2019-05-07 21:44:41 +02:00
parent e806b07aaf
commit ea2408b944
3 changed files with 11 additions and 72 deletions

7
API.md
View File

@ -346,12 +346,13 @@ The algorithm works as follows:
- add to `childrenHashes`: `hashFolder(folderId, fields)`
- if it's a bookmark
- add to `childrenHashes`: `hashBookmark(bookmarkId, fields)`
- Return `murmur2(to_json({title: folderTitle, children: childrenHashes}))`
- Return `sha256(to_json({title: folderTitle, children: childrenHashes}))` with the title being unset in case of the root folder
- `hashBookmark(id, fields)`
- for all entries in `fields`
- set `object[field]` to the value of the associated field of the bookmark
- Return `murmur2(to_json(object))`
- `murmur2`: [The murmur2 hashing algorithm](https://en.wikipedia.org/wiki/MurmurHash)
- Return `sha256(to_json(object))`
- `to_json`: A JSON stringification algorithm that adds no unnecessary white-space and doesn't use JSON's backslash escaping unless necessary (character set is UTF-8)
- `sha256`: The SHA-256 hashing algorithm
### Delete folders

View File

@ -333,8 +333,12 @@ class Bookmarks {
throw new UnexpectedValueException('Expected bookmark or folder, but not '.$item['type']);
}
}, $children);
$folder = ['title' => $folderRecord['title'], 'children' => $childHashes];
return Murmur2Hash::hash(json_encode($folder));
$folder = [];
if (isset($folderRecord['title'])) {
$folder['title'] = $folderRecord['title'];
}
$folder['children'] = $childHashes;
return hash('sha256', json_encode($folder, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
}
public function deleteFolder($userId, $folderId) {
@ -419,7 +423,7 @@ class Bookmarks {
$bookmark[$field] = $bookmarkRecord[$field];
}
}
return Murmur2Hash::hash(json_encode($bookmark));
return hash('sha256', json_encode($bookmark, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
}

View File

@ -1,66 +0,0 @@
<?php
namespace OCA\Bookmarks;
class Murmur2Hash {
public static function hash($str) {
$l = strlen($str);
$h = $l;
$i = 0;
while ($l >= 4) {
$k = ((ord(substr($str, $i)) & 0xff)) |
((ord(substr($str, ++$i)) & 0xff) << 8) |
((ord(substr($str, ++$i)) & 0xff) << 16) |
((ord(substr($str, ++$i)) & 0xff) << 24);
$k = ((($k & 0xffff) * 0x5bd1e995) + (((self::unsignedRightShift($k, 16) * 0x5bd1e995) & 0xffff) << 16));
$k ^= self::unsignedRightShift($k, 24);
$k = ((($k & 0xffff) * 0x5bd1e995) + (((self::unsignedRightShift($k, 16) * 0x5bd1e995) & 0xffff) << 16));
$h = ((($h & 0xffff) * 0x5bd1e995) + (((self::unsignedRightShift($h, 16) * 0x5bd1e995) & 0xffff) << 16)) ^ $k;
$l -= 4;
++$i;
}
switch ($l) {
case 3: $h ^= (ord(substr($str, $i + 2)) & 0xff) << 16;
// no break
case 2: $h ^= (ord(substr($str, $i + 1)) & 0xff) << 8;
// no break
case 1: $h ^= (ord(substr($str, $i)) & 0xff);
$h = ((($h & 0xffff) * 0x5bd1e995) + (((self::unsignedRightShift($h, 16) * 0x5bd1e995) & 0xffff) << 16));
}
$h ^= self::unsignedRightShift($h, 13);
$h = ((($h & 0xffff) * 0x5bd1e995) + (((self::unsignedRightShift($h, 16) * 0x5bd1e995) & 0xffff) << 16));
$h ^= self::unsignedRightShift($h, 15);
return self::unsignedRightShift($h, 0);
}
private static function unsignedRightShift($a, $b) {
if ($b >= 32 || $b < -32) {
$m = (int)($b/32);
$b = $b-($m*32);
}
if ($b < 0) {
$b = 32 + $b;
}
if ($b == 0) {
return (($a>>1) & 0x7fffffff) * 2 + (($a>>$b) & 1);
}
if ($a < 0) {
$a = ($a >> 1);
$a &= 0x7fffffff;
$a |= 0x40000000;
$a = ($a >> ($b - 1));
} else {
$a = ($a >> $b);
}
return $a;
}
}