usawa

Unnamed repository; edit this file 'description' to name the repository.
Info | Log | Files | Refs | Submodules | LICENSE

commit 01f614ede0eb194c85ace7d1adce97f9aba63cd2
parent 36c53b68d2a334d04c9d0e84b299a35a8be74626
Author: lash <dev@holbrook.no>
Date:   Sat, 14 Feb 2026 23:05:49 +0000

Add basic resolver for fs

Diffstat:
Mdummy/setup.py | 1+
Adummy/tests/test_resolver.py | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Adummy/usawa/resolve/__init__.py | 0
Adummy/usawa/resolve/base.py | 33+++++++++++++++++++++++++++++++++
Adummy/usawa/resolve/fs.py | 44++++++++++++++++++++++++++++++++++++++++++++
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