Daniel Sheffield пре 1 година
родитељ
комит
90cc062739
2 измењених фајлова са 75 додато и 24 уклоњено
  1. 9 17
      app/rest/hash_util.py
  2. 66 7
      test/rest/test_hash_util.py

+ 9 - 17
app/rest/hash_util.py

@@ -6,21 +6,10 @@ import time
 from base64 import b64encode, b64decode, b85encode, b85decode
 import base32_lib as b32
 
-#DIGEST_SIZE_BYTES = 4
-#DIGEST_SIZE_BITMASK = 0xffffffff
-#DIGEST_SIZE_SIGNED_TO_UNSIGNED_BITMASK = 0x1ffffffff
-#DIGEST_SIZE_SIGNED_TO_UNSIGNED_BIT = 0x100000000
-
 DIGEST_SIZE_BYTES = 3
 DIGEST_SIZE_BITMASK = 0xffffff
 DIGEST_SIZE_SIGNED_TO_UNSIGNED_BITMASK = 0x1ffffff
 DIGEST_SIZE_SIGNED_TO_UNSIGNED_BIT = 0x1000000
-
-#DIGEST_SIZE_BYTES = 2
-#DIGEST_SIZE_BITMASK = 0xffff
-#DIGEST_SIZE_SIGNED_TO_UNSIGNED_BITMASK = 0x1ffff
-#DIGEST_SIZE_SIGNED_TO_UNSIGNED_BIT = 0x10000
-
 DIGEST_SIZE_NIBBLES = DIGEST_SIZE_BYTES * 2
 B64ALTCHARS = b'.-'
 
@@ -98,6 +87,11 @@ def fix_padding(f):
         return f(fixed, *args, **kwargs)
     return wrap
 
+def add_padding_base32(f):
+    def wrap(*args, **kwargs):
+        return f(*args, **kwargs).zfill(DIGEST_SIZE_BYTES*8//5+1)
+    return wrap
+
 @remove_padding
 def hash_to_base64(_hash: int) -> str:
     return b64encode(hash_to_bytes(_hash), altchars=B64ALTCHARS).decode("utf-8")
@@ -146,17 +140,15 @@ def base85_to_hex(_b64: str) -> str:
 def base85_to_bytes(_b64: str) -> str:
     return normalize_bytes(b85decode(_b64))
 
-#@remove_padding
+@add_padding_base32
 def hash_to_base32(_hash: int) -> str:
-    return b32.encode(_hash).upper()
+    return b32.encode(_hash & DIGEST_SIZE_BITMASK).upper().zfill(DIGEST_SIZE_BYTES*8//5+1)
 
-#@remove_padding
 def hex_to_base32(_hex: str) -> str:
-    return b32.encode(hex_to_hash(_hex)).upper()
+    return b32.encode(hex_to_hash(_hex)).upper().zfill(DIGEST_SIZE_BYTES*8//5+1)
 
-#@remove_padding
 def bytes_to_base32(_bytes: str) -> str:
-    return b32.encode(bytes_to_hash(_bytes)).upper()
+    return b32.encode(bytes_to_hash(_bytes)).upper().zfill(DIGEST_SIZE_BYTES*8//5+1)
 
 #@fix_padding
 def base32_to_hash(_b64: str) -> str:

+ 66 - 7
test/rest/test_hash_util.py

@@ -5,6 +5,7 @@
 # THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
 from pytest import fixture, mark
 import app.rest.hash_util as hash_util
+import base32_lib as b32
 from app.rest.hash_util import(
     hash_to_hex,
     hash_to_bytes,
@@ -35,6 +36,8 @@ from app.rest.hash_util import(
     hex_to_hash,
 )
 
+def to_unsigned(_hash: int) -> int:
+    return _hash & hash_util.DIGEST_SIZE_BITMASK
 
 def digest_size():
     for g in ( two_bytes, three_bytes, four_bytes):
@@ -106,7 +109,7 @@ def test_three_byte_assertions():
 
 
 @mark.parametrize('_hash, _hex', [
-    (_hash, _hex) for _hash, _hex, _ in hash_codes()
+    (_hash, _hex) for _hash, _hex, *_ in hash_codes()
 ])
 @mark.parametrize('digest_size_gen', digest_size())
 def test_hash_to_hex(_hash, _hex, digest_size_gen):
@@ -116,8 +119,31 @@ def test_hash_to_hex(_hash, _hex, digest_size_gen):
         # test hex is zero padded
         assert hash_to_hex(_hash & digest_size_bitmask(i*4)) == _hex[-i:].zfill(digest_size*2)
 
+@mark.parametrize('_hash, _bytes', [
+    (_hash, _bytes) for _hash, _, _bytes, *_ in hash_codes()
+])
+@mark.parametrize('digest_size_gen', digest_size())
+def test_hash_to_base32(_hash, _bytes, digest_size_gen):
+    digest_size = digest_size_gen()
+    norm = hash_to_base32(_hash)
+    assert norm == b32.encode(
+        to_unsigned(int.from_bytes(
+            _bytes[-digest_size:], byteorder='big', signed=False)
+        )
+    ).upper().zfill(digest_size*8//5+1)
+    assert len(norm) == digest_size*8//5 +1
+    for i in range(1, digest_size+1):
+        norm = hash_to_base32(_hash & digest_size_bitmask(i*8))
+        # test base32 is zero padded
+        assert norm == b32.encode(
+            to_unsigned(int.from_bytes(
+                (b'\x00'*digest_size + _bytes[-i:])[-digest_size:], byteorder='big', signed=False)
+            )
+        ).upper().zfill(digest_size*8//5+1)
+        assert len(norm) == digest_size*8//5 +1
+
 @mark.parametrize('_hash, _hex', [
-    (_hash, _hex) for _hash, _hex, _ in hash_codes()
+    (_hash, _hex) for _hash, _hex, *_ in hash_codes()
 ])
 @mark.parametrize('digest_size_gen', digest_size())
 def test_hex_to_hash(_hash, _hex, digest_size_gen):
@@ -135,7 +161,7 @@ def test_hex_to_hash(_hash, _hex, digest_size_gen):
         assert hex_to_hash(_input) == _hash & digest_size_bitmask(i*4)
 
 @mark.parametrize('_hex, _bytes', [
-    (_hex, _bytes) for _, _hex, _bytes in hash_codes()
+    (_hex, _bytes) for _, _hex, _bytes, *_ in hash_codes()
 ])
 @mark.parametrize('digest_size_gen', digest_size())
 def test_hex_to_bytes(_hex, _bytes, digest_size_gen):
@@ -173,7 +199,7 @@ def test_hex_to_bytes(_hex, _bytes, digest_size_gen):
             assert hex_to_bytes(_input) == (b'\x00'*digest_size + _bytes[-i//2:])[-digest_size:]
 
 @mark.parametrize('_hash, _bytes', [
-    (_hash, _bytes) for _hash, _, _bytes in hash_codes()
+    (_hash, _bytes) for _hash, _, _bytes, *_ in hash_codes()
 ])
 @mark.parametrize('digest_size_gen', digest_size())
 def test_bytes_to_hash(_hash, _bytes, digest_size_gen):
@@ -190,8 +216,41 @@ def test_bytes_to_hash(_hash, _bytes, digest_size_gen):
         assert len(_input) == digest_size
         assert bytes_to_hash(_input) == _hash & digest_size_bitmask(i*8)
 
+@mark.parametrize('_hash, _bytes', [
+    (_hash, _bytes) for _hash, _, _bytes, *_ in hash_codes()
+])
+@mark.parametrize('digest_size_gen', digest_size())
+def test_base32_to_hash(_hash, _bytes, digest_size_gen):
+    digest_size = digest_size_gen()
+    _base32 = b32.encode(
+        to_unsigned(int.from_bytes(
+            _bytes[-digest_size:], byteorder='big', signed=False)
+        )
+    ).upper().zfill(digest_size*8//5+1)
+    norm = base32_to_hash(_base32)
+    assert norm == _hash & digest_size_bitmask(digest_size*8)
+
+    for i in range(1, digest_size+1):
+        # test non-zero padded base32
+        _base32 = b32.encode(
+            to_unsigned(int.from_bytes(
+                (b'\x00'*digest_size + _bytes[-i:])[-digest_size:], byteorder='big', signed=False)
+            )
+        ).upper()
+        norm = base32_to_hash(_base32)
+        assert norm == _hash & digest_size_bitmask(i*8)
+
+        # test zero padded base32
+        _base32 = b32.encode(
+            to_unsigned(int.from_bytes(
+                (b'\x00'*digest_size + _bytes[-i:])[-digest_size:], byteorder='big', signed=False)
+            )
+        ).upper().zfill(digest_size*8//5+1)
+        norm = base32_to_hash(_base32)
+        assert norm == _hash & digest_size_bitmask(i*8)
+
 @mark.parametrize('_hex', [
-    _hex for _, _hex, _bytes in hash_codes()
+    _hex for _, _hex, *_ in hash_codes()
 ])
 @mark.parametrize('digest_size_gen', digest_size())
 def test_normalize_hex(_hex, digest_size_gen):
@@ -206,7 +265,7 @@ def test_normalize_hex(_hex, digest_size_gen):
         assert len(normalize_hex(_hex[-i:])) == digest_size*2
 
 @mark.parametrize('_bytes', [
-    _bytes for _, _, _bytes in hash_codes()
+    _bytes for _, _, _bytes, *_ in hash_codes()
 ])
 @mark.parametrize('digest_size_gen', digest_size())
 def test_normalize_bytes(_bytes, digest_size_gen):
@@ -221,7 +280,7 @@ def test_normalize_bytes(_bytes, digest_size_gen):
         assert len(normalize_bytes(_bytes[-i:])) == digest_size
 
 @mark.parametrize('_hash', [
-    _hash for _hash, _, _ in hash_codes()
+    _hash for _hash, *_ in hash_codes()
 ])
 @mark.parametrize('digest_size_gen', digest_size())
 def test_normalize_hash(_hash, digest_size_gen):