commit 01f614ede0eb194c85ace7d1adce97f9aba63cd2
parent 36c53b68d2a334d04c9d0e84b299a35a8be74626
Author: lash <dev@holbrook.no>
Date: Sat, 14 Feb 2026 23:05:49 +0000
Add basic resolver for fs
Diffstat:
5 files changed, 131 insertions(+), 0 deletions(-)
diff --git a/dummy/setup.py b/dummy/setup.py
@@ -16,6 +16,7 @@ setup(
"PyNaCl~=1.6.0",
"python-gnupg~=0.4.9",
"rencode~=1.0.8",
+ "hexathon~=0.1.7",
"varints@file://" + varints_dir,
],
)
diff --git a/dummy/tests/test_resolver.py b/dummy/tests/test_resolver.py
@@ -0,0 +1,53 @@
+import unittest
+import logging
+import tempfile
+import hashlib
+import shutil
+import os
+
+from usawa.resolve.fs import FSResolver
+from usawa.error import VerifyError
+
+
+logging.basicConfig(level=logging.DEBUG)
+logg = logging.getLogger()
+
+hash_of_foo = 'f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7'
+hash_of_bar = 'd82c4eb5261cb9c8aa9855edd67d1bd10482f41529858d925094d173fa662aa91ff39bc5b188615273484021dfb16fd8284cf684ccf0fc795be3aa2fc1e6c181'
+
+class TestResolver(unittest.TestCase):
+
+ def setUp(self):
+ self.path = tempfile.mkdtemp()
+ self.backend = FSResolver(self.path)
+
+
+ def tearDown(self):
+ shutil.rmtree(self.path)
+
+
+ def test_resolve_putget(self):
+ h = hashlib.sha512()
+ v = os.urandom(1337)
+ h.update(v)
+ k = h.digest()
+ self.backend.put(k, v)
+ r = self.backend.get(k)
+ self.assertEqual(r, v)
+
+ k_wrong = os.urandom(32)
+ with self.assertRaises(ValueError):
+ r = self.backend.get(k_wrong)
+
+
+ def test_resolve_get_evil(self):
+ fp = os.path.join(self.path, hash_of_foo)
+ f = open(fp, 'wb')
+ f.write(b'bar')
+ f.close()
+ with self.assertRaises(VerifyError):
+ self.backend.get(hash_of_foo)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/dummy/usawa/resolve/__init__.py b/dummy/usawa/resolve/__init__.py
diff --git a/dummy/usawa/resolve/base.py b/dummy/usawa/resolve/base.py
@@ -0,0 +1,33 @@
+import hashlib
+
+import hexathon
+
+from usawa.error import VerifyError
+
+
+def sha512_verify(k, v=None):
+ if isinstance(k, str):
+ k = bytes.fromhex(k)
+ if len(k) != 64:
+ raise ValueError('expect 512 bit key')
+ khx = hexathon.uniform(k.hex())
+ if v != None:
+ h = hashlib.sha512()
+ h.update(v)
+ if k != h.digest():
+ raise VerifyError(khx)
+ return khx
+
+
+class BaseResolver:
+
+ def get(self, k, v, verifier=None):
+ raise NotImplementedError()
+
+
+ def put(self, k):
+ raise NotImplementedError()
+
+
+ def delete(self, k):
+ raise NotImplementedError()
diff --git a/dummy/usawa/resolve/fs.py b/dummy/usawa/resolve/fs.py
@@ -0,0 +1,44 @@
+import os
+import logging
+
+from .base import BaseResolver, sha512_verify
+from usawa.error import VerifyError
+
+logg = logging.getLogger('usawa.fsresolver')
+
+
+def normalize_keyname(k):
+ if isinstance(k, bytes):
+ k = k.hex()
+ else:
+ bytes.fromhex(k)
+ return hexathon.uniform(k)
+
+
+class FSResolver(BaseResolver):
+
+ def __init__(self, path, verifier=sha512_verify):
+ self.path = os.path.realpath(path)
+ self.verifier = verifier
+ os.makedirs(self.path, exist_ok=True)
+
+
+ def get(self, k):
+ khx = self.verifier(k)
+ fp = os.path.join(self.path, khx)
+ f = open(fp, 'rb')
+ v = f.read()
+ f.close()
+ if not self.verifier(k, v):
+ raise VerifyError(khx)
+ return v
+
+
+ def put(self, k, v):
+ khx = self.verifier(k, v=v)
+ fp = os.path.join(self.path, khx)
+ f = open(fp, 'wb')
+ c = f.write(v)
+ logg.debug('{} bytes written for key {}'.format(c, k))
+ f.close()
+ return v