usawa

Signed, immutable accounting.
Log | Files | Refs | Submodules | LICENSE

commit 2c1cac3b1c2825d0838a320b19b68f8ee4b42359
parent a2ab491b7076208e5984a8ebedd3628e353dd2e8
Author: Carlosokumu <carlosokumu254@gmail.com>
Date:   Mon, 16 Mar 2026 07:15:39 +0300

chane hash algo to 512 for uniformity

Diffstat:
Mdummy/usawa/asset.py | 124++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mdummy/usawa/storage/ledger_repository.py | 23-----------------------
2 files changed, 73 insertions(+), 74 deletions(-)

diff --git a/dummy/usawa/asset.py b/dummy/usawa/asset.py @@ -11,7 +11,7 @@ import magic from .constant import NSPREFIX from .xml import nsmap -logg = logging.getLogger('usawa.asset') +logg = logging.getLogger("usawa.asset") BLOCKSIZE = 512 @@ -29,16 +29,21 @@ Does not check whether or not the file exists. :return: Stem name and extension. :rtype: Tuple """ + + def parse_path(path): if len(path) == 0 or path == None: - raise ValueError('empty path') + raise ValueError("empty path") s = os.path.basename(path) - v = s.rsplit('.', maxsplit=1) + v = s.rsplit(".", maxsplit=1) slug = v[0] ext = None if len(v) == 2: ext = v[1] - return (slug, ext,) + return ( + slug, + ext, + ) class Asset: @@ -50,6 +55,7 @@ class Asset: Asset.from_io() - Read from a io.BufferedIOBase input stream. Asset.from_tree() - Recreate from an XML tree. """ + def __init__(self, digest=None): self.digest = digest self.mime = None @@ -60,7 +66,6 @@ class Asset: self.uuid = None self.description = None - """Return the preferred filename with extension for the asset. Must always return a value. The filename may or may not have an extension. @@ -68,13 +73,13 @@ class Asset: :return: Filename :rtype: str """ + def get_filename(self): s = self.slug if self.ext != None: - s += '.' + self.ext + s += "." + self.ext return s - """Return the mime type. The encoding specifier will be added if it exists. @@ -84,28 +89,28 @@ class Asset: :return: Mime string :rtype: str """ + def get_mimestring(self): s = self.mime if s != None: if self.enc != None: - s += ';charset=' + self.enc + s += ";charset=" + self.enc return s - """Return the digest of the asset, in hex. :raises AttributeError: Digest not set :return: Digest hex :rtype: str """ + def get_digest(self, binary=False): if self.digest == None: - raise AttributeError('') + raise AttributeError("") if binary: return self.digest return self.digest.hex() - """Instantiate an asset object from a local file. File is opened and the file object is passed on to the from_io() method. @@ -121,11 +126,19 @@ class Asset: :raises IsADirectoryError: Path is a directory. :raises PermissionError: File cannot be read. """ + @staticmethod def from_file(filepath, description=None, slug=None, mimetype=None, extref=None): - f = open(filepath, 'rb') - return Asset.from_io(f, filepath, closer=f.close, description=description, slug=slug, mimetype=mimetype, extref=extref) - + f = open(filepath, "rb") + return Asset.from_io( + f, + filepath, + closer=f.close, + description=description, + slug=slug, + mimetype=mimetype, + extref=extref, + ) """Instantiate an asset object from an input stream. @@ -149,10 +162,13 @@ class Asset: :todo: make path uri/remote url friendly when implementing remote stream. :todo: document possible exceptions. """ + @staticmethod - def from_io(io, src, closer=None, description=None, slug=None, mimetype=None, extref=None): + def from_io( + io, src, closer=None, description=None, slug=None, mimetype=None, extref=None + ): o = Asset() - h = hashlib.sha256() + h = hashlib.sha512() b = io.read(BLOCKSIZE) if mimetype == None: v = mimetypes.guess_file_type(src, strict=True) @@ -176,23 +192,22 @@ class Asset: s = mimetypes.guess_extension(o.mime, strict=True) if s != None: - o.ext = s[1:] + o.ext = s[1:] (o.slug, o.ext) = parse_path(src) if slug != None: - logg.info('overriding file base name {} -> {}'.format(o.slug, slug)) + logg.info("overriding file base name {} -> {}".format(o.slug, slug)) o.slug = slug - logg.debug('asset read {} bytes from path {} mime {}'.format(c, src, o.mime)) + logg.debug("asset read {} bytes from path {} mime {}".format(c, src, o.mime)) h = hashlib.sha1() h.update(o.digest) - #o.uuid = str(uuid.uuid1(h.digest())) + # o.uuid = str(uuid.uuid1(h.digest())) o.extref = extref o.description = description return o - """Return canonical XML for use in signature message calculation. Elements in canonical XML for the asset is data that can be recreated by the actual file data. @@ -201,19 +216,19 @@ class Asset: For custom or arcane MIME types, the data needed to recreate the MIME type has to be retained outside the application. """ + def canon(self): - tree = lxml.etree.Element(NSPREFIX + 'attachment', nsmap=nsmap()) + tree = lxml.etree.Element(NSPREFIX + "attachment", nsmap=nsmap()) if self.mime != None: - tree.set('mime', self.get_mimestring()) + tree.set("mime", self.get_mimestring()) - o = lxml.etree.SubElement(tree, 'digest') - o.set('algo', 'sha256') + o = lxml.etree.SubElement(tree, "digest") + o.set("algo", "sha512") o.text = self.digest.hex() tree.append(o) return tree - """Generate and return an XML representation of the asset. :param canon: Return canonical results to use in signature material. @@ -223,31 +238,31 @@ class Asset: :todo: implement sigs :todo: implement lookup """ + def to_tree(self, canon=False): - tree = self.canon() + tree = self.canon() if canon: return tree - #if self.uuid != None: + # if self.uuid != None: # tree.set('uuid', self.uuid) if self.extref != None: - o = lxml.etree.SubElement(tree, 'extref') + o = lxml.etree.SubElement(tree, "extref") o.text = self.extref tree.append(o) - o = lxml.etree.SubElement(tree, 'filename') + o = lxml.etree.SubElement(tree, "filename") o.text = self.get_filename() tree.append(o) if self.description != None: - o = lxml.etree.SubElement(tree, 'description') + o = lxml.etree.SubElement(tree, "description") o.text = self.description tree.append(o) return tree - """Create object from an asset part defined as an XML tree. The XML expected is an ledger/entry[]/attachment[] element (in schema defined as the Attachment complexType) @@ -260,59 +275,59 @@ class Asset: :todo: implement sigs :todo: implement lookup """ + @staticmethod def from_tree(tree): o = Asset() - #o.uuid = tree.get('uuid') - o.mime = tree.get('mime') - v = tree.find('digest', namespaces=nsmap()).text + # o.uuid = tree.get('uuid') + o.mime = tree.get("mime") + v = tree.find("digest", namespaces=nsmap()).text o.digest = bytes.fromhex(v) - v = tree.find('extref', namespaces=nsmap()) + v = tree.find("extref", namespaces=nsmap()) if v != None: o.extref = v.text - v = tree.find('filename', namespaces=nsmap()) + v = tree.find("filename", namespaces=nsmap()) if v != None: v = parse_path(v.text) o.slug = v[0] o.ext = v[1] - v = tree.find('description', namespaces=nsmap()) + v = tree.find("description", namespaces=nsmap()) if v != None: o.description = v.text - logg.warning('asset sigs not yet implemented') - for v in tree.findall('sig', namespaces=nsmap()): - logg.debug('skipping sig from ' . v.get('keyid')) + logg.warning("asset sigs not yet implemented") + for v in tree.findall("sig", namespaces=nsmap()): + logg.debug("skipping sig from ".v.get("keyid")) return o - """Generate the simple data structure used for rencode serialization. :returns: data structure :rtype: list """ + def to_list(self): d = [] - #for k in ['mime', 'uuid', 'slug', 'ext', 'description', 'extref', 'enc']: - for k in ['mime', 'slug', 'ext', 'description', 'extref', 'enc']: + # for k in ['mime', 'uuid', 'slug', 'ext', 'description', 'extref', 'enc']: + for k in ["mime", "slug", "ext", "description", "extref", "enc"]: v = getattr(self, k) d.append(v) return d - """Generate the serialization format used to calculate the digest for the asset. :returns: String representation of the entry, in rencode format. :rtype: str """ + def serialize(self): b = self.to_list() return rencode.dumps(b) - """Create an entry object from serialized data. :param data: rencoded entry object, as produced by the serialize() method. @@ -320,16 +335,17 @@ class Asset: :returns: Entry object. :rtype: usawa.Entry """ + @staticmethod def deserialize(data, digest): o = Asset() v = rencode.loads(data) i = 0 - #for k in ['mime', 'uuid', 'slug', 'ext', 'description', 'extref', 'enc']: - for k in ['mime', 'slug', 'ext', 'description', 'extref', 'enc']: + # for k in ['mime', 'uuid', 'slug', 'ext', 'description', 'extref', 'enc']: + for k in ["mime", "slug", "ext", "description", "extref", "enc"]: vv = v[i] if isinstance(vv, bytes): - vv = vv.decode('utf-8') + vv = vv.decode("utf-8") setattr(o, k, vv) i += 1 if isinstance(digest, str): @@ -337,6 +353,12 @@ class Asset: o.digest = digest return o - def __str__(self): - return 'file ̈́' + self.get_filename() + ' mime ' + self.get_mimestring() + ' digest ' + self.digest.hex() + return ( + "file ̈́" + + self.get_filename() + + " mime " + + self.get_mimestring() + + " digest " + + self.digest.hex() + ) diff --git a/dummy/usawa/storage/ledger_repository.py b/dummy/usawa/storage/ledger_repository.py @@ -1,19 +1,15 @@ -import hashlib import logging from typing import List from usawa.core.usawa_wallet import UsawaWallet -import hexathon from usawa.storage.xml_utils import ( _build_export_root, _build_incoming_element, - _find_child, _find_entry_by_serial, _write_xml_to_file, resolve_namespace, ) from usawa.asset import Asset from usawa.crypto import ACL, DemoWallet, Wallet -from usawa.error import VerifyError from usawa.ledger import Ledger from usawa.resolve.fs import FSResolver from usawa.service import UnixClient @@ -27,30 +23,11 @@ import mimetypes import lxml.etree as ET from pathlib import Path import lxml.etree as ET -from copy import deepcopy logg = logging.getLogger("storage.ledger_repository") -# def sha256_verify(k, v=None): -# if isinstance(k, str): -# k = bytes.fromhex(k) - -# if len(k) != 32: -# raise ValueError("expect 256 bit key") - -# khx = hexathon.uniform(k.hex()) - -# if v is not None: -# h = hashlib.sha256() -# h.update(v) -# if k != h.digest(): -# raise VerifyError(khx) - -# return khx - - class LedgerRepository: """Repository that wraps LedgerStore and handles mapping"""