usawa

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

commit 05d924a33e0d5643a4698015c73ad8b38d273230
parent 0a1344624667dfcf15f89655329109ec4280c55f
Author: lash <dev@holbrook.no>
Date:   Sat, 28 Mar 2026 08:53:15 -0600

Make pwhash params settable

Diffstat:
Mdummy/usawa/crypto.py | 35++++++++++++++++++++++++-----------
Mdummy/usawa/data/usawa.ini | 2++
Mdummy/usawa/runnable/create.py | 23+++++++++++++++++++++--
Mdummy/usawa/runnable/setup_wallet.py | 4+++-
Mdummy/usawa/store.py | 8++++----
5 files changed, 54 insertions(+), 18 deletions(-)

diff --git a/dummy/usawa/crypto.py b/dummy/usawa/crypto.py @@ -50,16 +50,22 @@ class DID: return "did:" + self.m + ":" + self.v -def key_from_export(v, passphrase="", did=None): +def key_from_export(v, passphrase="", did=None, opslimit=0, memlimit=0): if passphrase is None: passphrase = b"" if isinstance(passphrase, str): passphrase = passphrase.encode("utf-8") salt = v[: argon2i.SALTBYTES] - ciphertext = v[argon2i.SALTBYTES :] + ciphertext = v[argon2i.SALTBYTES:] + if not opslimit: + opslimit = nacl.pwhash.OPSLIMIT_INTERACTIVE + if not memlimit: + memlimit = nacl.pwhash.MEMLIMIT_INTERACTIVE + + logg.debug('pwhash ops {} mem {} salt {} pw {}'.format(opslimit, memlimit, salt, passphrase.hex())) - key = argon2i.kdf(nacl.secret.SecretBox.KEY_SIZE, passphrase, salt) + key = argon2i.kdf(nacl.secret.SecretBox.KEY_SIZE, passphrase, salt, opslimit=opslimit, memlimit=memlimit) box = nacl.secret.SecretBox(key) try: @@ -133,17 +139,16 @@ class Wallet: :rtype: bytes :todo: Raise local error if sign fail """ - def sign(self, v): raise NotImplementedError + """Return the public key data in the wallet. :returns: Public key data. :rtype: bytes :todo: Raise local error if sign fail """ - def pubkey(self): raise NotImplementedError @@ -153,10 +158,10 @@ class Wallet: :rtype: bytes :todo: Raise local error if sign fail """ - def privkey(self): raise NotImplementedError + """Verify signature data against the given message. :param v: Data to verify @@ -170,7 +175,8 @@ class Wallet: def verify(self, v, sig): raise NotImplementedError - def export(self, passphrase=None): + + def export(self, passphrase=None, opslimit=0, memlimit=0): if passphrase is None: passphrase = b"" if isinstance(passphrase, str): @@ -179,16 +185,23 @@ class Wallet: logg.warning("exporting key with no passphrase") salt = os.urandom(argon2i.SALTBYTES) - key = argon2i.kdf(nacl.secret.SecretBox.KEY_SIZE, passphrase, salt) + if not opslimit: + opslimit = nacl.pwhash.OPSLIMIT_INTERACTIVE + if not memlimit: + memlimit = nacl.pwhash.MEMLIMIT_INTERACTIVE + logg.debug('pwhash ops {} mem {} salt {} pw {}'.format(opslimit, memlimit, salt, passphrase.hex())) + key = argon2i.kdf(nacl.secret.SecretBox.KEY_SIZE, passphrase, salt, opslimit=opslimit, memlimit=memlimit) box = nacl.secret.SecretBox(key) k = self.privkey() r = box.encrypt(k) return salt + r # prepend salt + @staticmethod - def from_export(v, passphrase=None): + def from_export(v, passphrase=None, **kwargs): raise NotImplementedError() + """Generate an identity XML tree entry from the wallet. The element generated is valid to be inserted as an identity sub-element in the ledger element. @@ -283,8 +296,8 @@ class DemoWallet(Wallet): return r @staticmethod - def from_export(v, passphrase=None): - k = key_from_export(v, passphrase=passphrase) + def from_export(v, passphrase=None, opslimit=0, memlimit=0): + k = key_from_export(v, passphrase=passphrase, opslimit=opslimit, memlimit=memlimit) return DemoWallet(privatekey=k) diff --git a/dummy/usawa/data/usawa.ini b/dummy/usawa/data/usawa.ini @@ -16,6 +16,8 @@ socket_file_path= [wallet] key_passphrase = +opslimit = 3 +memlimit = 33554432 [fs_resolver] store_path= diff --git a/dummy/usawa/runnable/create.py b/dummy/usawa/runnable/create.py @@ -10,6 +10,8 @@ from usawa import Ledger, DemoWallet, UnitIndex, ACL import usawa.config from usawa.store import LedgerStore from whee.valkey import ValkeyStore +from whee.fs import FsStore +from xdg_base_dirs import xdg_data_home logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger() @@ -117,6 +119,7 @@ argp.add_argument('-c', type=str, help='override config dir') argp.add_argument('--unit-precision', type=int, default=UnitIndex.default_precision, help='Unit precision') arg = argp.parse_args() ctx = Context.from_args(arg) +cfg = usawa.config.load_config(config_dir=arg.c) def do_interactive(ctx): @@ -137,7 +140,21 @@ ctx = ctx.open(ctx.output) ctx.validate() ledger = Ledger(ctx.uidx, topic=ctx.topic, src=ctx.uri) -db = ValkeyStore('') +store_type = cfg.get('STORE_TYPE') +db = None +if store_type == 'valkey': + db = ValkeyStore( + "", + host=cfg.get("VALKEY_HOST"), + port=cfg.get("VALKEY_PORT"), + ) +elif store_type == 'fs': + db = FsStore( + base=cfg.get('FSSTORE_BASE', xdg_data_home()), + dbname='usawa', + ) +else: + raise ValueError('invalid store type: ' + store_type) store = LedgerStore(db, ledger) pk = None wallet = None @@ -145,9 +162,11 @@ dt = datetime.datetime.now() cfg = usawa.config.load_config(config_dir=arg.c) +ops = int(cfg.get('WALLET_OPSLIMIT', 0)) +mem = int(cfg.get('WALLET_MEMLIMIT', 0)) try: #pk = store.get_key() - wallet = store.get_key(DemoWallet, passphrase=cfg.get('WALLET_KEY_PASSPHRASE')) + wallet = store.get_key(DemoWallet, passphrase=cfg.get('WALLET_KEY_PASSPHRASE'), opslimit=ops, memlimit=mem) except FileNotFoundError: logg.info('no default key found') wallet = DemoWallet() diff --git a/dummy/usawa/runnable/setup_wallet.py b/dummy/usawa/runnable/setup_wallet.py @@ -85,9 +85,11 @@ def setup_wallet(cfg, wallet_dir=None): with open(publickey_path, "wb") as f: f.write(pk.encode()) logg.info("public key saved to: %s", publickey_path) + ops = int(cfg.get('WALLET_OPSLIMIT', 0)) + mem = int(cfg.get('WALLET_MEMLIMIT', 0)) wallet = DemoWallet(privatekey=random_bytes) - store.add_key(wallet, passphrase=passphrase_confirm) + store.add_key(wallet, passphrase=passphrase_confirm, opslimit=ops, memlimit=mem) logg.info("key written to store") logg.info("setup complete.") diff --git a/dummy/usawa/store.py b/dummy/usawa/store.py @@ -170,7 +170,7 @@ class KeyStore(BaseStore): :type passphrase: bytes :todo: Implement the ACL lookup """ - def add_key(self, wallet, acl=None, default=False, passphrase=None): + def add_key(self, wallet, acl=None, default=False, passphrase=None, opslimit=0, memlimit=0): k = pfx_key() try: self.db.get(k) @@ -180,7 +180,7 @@ class KeyStore(BaseStore): if default: self.db.put(k, pubkey, exist_ok=True) k = pfx_key(pubkey=pubkey) - v = wallet.export(passphrase=passphrase) + v = wallet.export(passphrase=passphrase, opslimit=opslimit, memlimit=memlimit) self.db.put(k, v) @@ -199,14 +199,14 @@ class KeyStore(BaseStore): :return: Resulting wallet :rtype: usawa.crypto.Wallet """ - def get_key(self, wallet_class, pubkey=None, passphrase=None): + def get_key(self, wallet_class, pubkey=None, passphrase=None, opslimit=0, memlimit=0): if pubkey == None: k = pfx_key() pubkey = self.db.get(k) k = pfx_key(pubkey=pubkey) #return self.db.get(k) r = self.db.get(k) - return wallet_class.from_export(r, passphrase=passphrase) + return wallet_class.from_export(r, passphrase=passphrase, opslimit=opslimit, memlimit=memlimit) class EntryStore(BaseStore):