From 1f3efa173c1e2c84b007bfda65562c5abe80b53f Mon Sep 17 00:00:00 2001 From: Samuel M Smith Date: Fri, 9 Aug 2024 11:28:35 -0600 Subject: [PATCH 1/4] refactor Diger to simplify --- src/keri/core/coring.py | 160 +++++++++++++------------------------- tests/core/test_coring.py | 5 +- 2 files changed, 58 insertions(+), 107 deletions(-) diff --git a/src/keri/core/coring.py b/src/keri/core/coring.py index 2a55d879..8db1148f 100644 --- a/src/keri/core/coring.py +++ b/src/keri/core/coring.py @@ -55,6 +55,9 @@ ECDSA_256r1_SEEDBYTES = 32 ECDSA_256k1_SEEDBYTES = 32 +# digest algorithm klas, digest size (not default), digest length +# size and length are needed for some digest types as function parameters +Digestage = namedtuple("Digestage", "klas size length") # SAID field labels Saidage = namedtuple("Saidage", "dollar at id_ i d") @@ -269,6 +272,8 @@ def __delitem__(self, name): + + @dataclass(frozen=True) class MatterCodex: """ @@ -3094,68 +3099,77 @@ class Diger(Matter): """ - def __init__(self, raw=None, ser=None, code=MtrDex.Blake3_256, **kwa): - """ - Assign digest verification function to ._verify + # Maps digest codes to Digestages of algorithms for computing digest. + # Should be based on the same set of codes as in DigestCodex + # so Matter.digestive property works. + # Use unit tests to ensure codex elements sets match - See Matter for inherited parameters + Digests = { + DigDex.Blake3_256: Digestage(klas=blake3.blake3, size=None, length=None), + DigDex.Blake2b_256: Digestage(klas=hashlib.blake2b, size=32, length=None), + DigDex.Blake2s_256: Digestage(klas=hashlib.blake2s, size=None, length=None), + DigDex.SHA3_256: Digestage(klas=hashlib.sha3_256, size=None, length=None), + DigDex.SHA2_256: Digestage(klas=hashlib.sha256, size=None, length=None), + DigDex.Blake3_512: Digestage(klas=blake3.blake3, size=None, length=64), + DigDex.Blake2b_512: Digestage(klas=hashlib.blake2b, size=None, length=None), + DigDex.SHA3_512: Digestage(klas=hashlib.sha3_512, size=None, length=None), + DigDex.SHA2_512: Digestage(klas=hashlib.sha512, size=None, length=None), + } + + def __init__(self, raw=None, ser=None, code=DigDex.Blake3_256, **kwa): + """Initialize attributes Inherited Parameters: - raw is bytes of unqualified crypto material usable for crypto operations - qb64b is bytes of fully qualified crypto material - qb64 is str or bytes of fully qualified crypto material - qb2 is bytes of fully qualified crypto material - code is str of derivation code - index is int of count of attached receipts for CryCntDex codes + See Matter Parameters: - ser is bytes serialization from which raw is computed if not raw + ser (bytes): serialization from which raw is computed if not raw """ - # Should implement all digests in DigCodex instance DigDex + try: super(Diger, self).__init__(raw=raw, code=code, **kwa) except EmptyMaterialError as ex: if not ser: raise ex - if code == MtrDex.Blake3_256: - dig = blake3.blake3(ser).digest() - elif code == MtrDex.Blake2b_256: - dig = hashlib.blake2b(ser, digest_size=32).digest() - elif code == MtrDex.Blake2s_256: - dig = hashlib.blake2s(ser, digest_size=32).digest() - elif code == MtrDex.SHA3_256: - dig = hashlib.sha3_256(ser).digest() - elif code == MtrDex.SHA2_256: - dig = hashlib.sha256(ser).digest() - else: - raise InvalidValueError("Unsupported code={code} for diger.") - - super(Diger, self).__init__(raw=dig, code=code, **kwa) - - if self.code == MtrDex.Blake3_256: - self._verify = self._blake3_256 - elif self.code == MtrDex.Blake2b_256: - self._verify = self._blake2b_256 - elif self.code == MtrDex.Blake2s_256: - self._verify = self._blake2s_256 - elif self.code == MtrDex.SHA3_256: - self._verify = self._sha3_256 - elif self.code == MtrDex.SHA2_256: - self._verify = self._sha2_256 - else: - raise InvalidValueError("Unsupported code={self.code} for diger.") + + raw = self._digest(ser, code=code) + + super(Diger, self).__init__(raw=raw, code=code, **kwa) + + if self.code not in DigDex: + raise InvalidCodeError(f"Unsupported Digest {code=}.") + + + def _digest(self, ser, code=DigDex.Blake3_256): + """Returns raw digest of ser using digest algorithm given by code + + Parameters: + ser (bytes): serialization from which raw digest is computed + code (str): derivation code used to lookup digest algorithm + """ + if code not in self.Digests: + raise InvalidCodeError(f"Unsupported Digest {code=}.") + + klas, size, length = self.Digests[code] # digest algo size & length + ikwa = dict(digest_size=size) if size else dict() # opt digest size + dkwa = dict(length=length) if length else dict() # opt digest length + raw = klas(ser, **ikwa).digest(**dkwa) + return (raw) + def verify(self, ser): """ Returns True if raw digest of ser bytes (serialization) matches .raw - using .raw as reference digest for ._verify digest algorithm determined + using .raw as reference digest for digest algorithm determined by .code Parameters: - ser (bytes): serialization to be digested and compared to .ser + ser (bytes): serialization to be digested and compared to .raw + """ - return (self._verify(ser=ser, raw=self.raw)) + return (self._digest(ser=ser, code=self.code) == self.raw) + def compare(self, ser, dig=None, diger=None): """ @@ -3202,65 +3216,6 @@ def compare(self, ser, dig=None, diger=None): return (False) - @staticmethod - def _blake3_256(ser, raw): - """ - Returns True if verified False otherwise - Verifiy blake3_256 digest of ser matches raw - - Parameters: - ser is bytes serialization - dig is bytes reference digest - """ - return (blake3.blake3(ser).digest() == raw) - - @staticmethod - def _blake2b_256(ser, raw): - """ - Returns True if verified False otherwise - Verifiy blake2b_256 digest of ser matches raw - - Parameters: - ser is bytes serialization - dig is bytes reference digest - """ - return (hashlib.blake2b(ser, digest_size=32).digest() == raw) - - @staticmethod - def _blake2s_256(ser, raw): - """ - Returns True if verified False otherwise - Verifiy blake2s_256 digest of ser matches raw - - Parameters: - ser is bytes serialization - dig is bytes reference digest - """ - return (hashlib.blake2s(ser, digest_size=32).digest() == raw) - - @staticmethod - def _sha3_256(ser, raw): - """ - Returns True if verified False otherwise - Verifiy blake2s_256 digest of ser matches raw - - Parameters: - ser is bytes serialization - dig is bytes reference digest - """ - return (hashlib.sha3_256(ser).digest() == raw) - - @staticmethod - def _sha2_256(ser, raw): - """ - Returns True if verified False otherwise - Verifiy blake2s_256 digest of ser matches raw - - Parameters: - ser is bytes serialization - dig is bytes reference digest - """ - return (hashlib.sha256(ser).digest() == raw) class Prefixer(Matter): @@ -3291,9 +3246,6 @@ def __init__(self, **kwa): -# digest algorithm klas, digest size (not default), digest length -# size and length are needed for some digest types as function parameters -Digestage = namedtuple("Digestage", "klas size length") class Saider(Matter): diff --git a/tests/core/test_coring.py b/tests/core/test_coring.py index 7ffc5649..fdd4cffb 100644 --- a/tests/core/test_coring.py +++ b/tests/core/test_coring.py @@ -4439,11 +4439,9 @@ def test_diger(): ser = b'abcdefghijklmnopqrstuvwxyz0123456789' dig = blake3.blake3(ser).digest() - with pytest.raises(coring.InvalidValueError): + with pytest.raises(kering.InvalidCodeError): diger = Diger(raw=dig, code=MtrDex.Ed25519) - with pytest.raises(coring.InvalidValueError): - diger = Diger(ser=ser, code=MtrDex.Ed25519) diger = Diger(raw=dig) # defaults provide Blake3_256 digester assert diger.code == MtrDex.Blake3_256 @@ -5389,6 +5387,7 @@ def test_tholder(): test_ilker() test_traitor() test_verser() + test_diger() #test_texter() #test_prodex() #test_indexer() From 5969fd511ad78d9330f7fcb16af7569478e3267f Mon Sep 17 00:00:00 2001 From: Samuel M Smith Date: Fri, 9 Aug 2024 14:36:53 -0600 Subject: [PATCH 2/4] refactored Saider and Serder to use new Diger and Diger._digest class method so code is much more DRY --- src/keri/core/coring.py | 33 ++++++--------------------------- src/keri/core/serdering.py | 35 ++--------------------------------- tests/core/test_coring.py | 6 ++++-- 3 files changed, 12 insertions(+), 62 deletions(-) diff --git a/src/keri/core/coring.py b/src/keri/core/coring.py index 8db1148f..64383402 100644 --- a/src/keri/core/coring.py +++ b/src/keri/core/coring.py @@ -3140,18 +3140,18 @@ def __init__(self, raw=None, ser=None, code=DigDex.Blake3_256, **kwa): if self.code not in DigDex: raise InvalidCodeError(f"Unsupported Digest {code=}.") - - def _digest(self, ser, code=DigDex.Blake3_256): + @classmethod + def _digest(cls, ser, code=DigDex.Blake3_256): """Returns raw digest of ser using digest algorithm given by code Parameters: ser (bytes): serialization from which raw digest is computed code (str): derivation code used to lookup digest algorithm """ - if code not in self.Digests: + if code not in cls.Digests: raise InvalidCodeError(f"Unsupported Digest {code=}.") - klas, size, length = self.Digests[code] # digest algo size & length + klas, size, length = cls.Digests[code] # digest algo size & length ikwa = dict(digest_size=size) if size else dict() # opt digest size dkwa = dict(length=length) if length else dict() # opt digest length raw = klas(ser, **ikwa).digest(**dkwa) @@ -3277,19 +3277,6 @@ class Saider(Matter): """ Dummy = "#" # dummy spaceholder char for said. Must not be a valid Base64 char - # should be same set of codes as in coring.DigestCodex coring.DigDex so - # .digestive property works. Unit test ensures code sets match - Digests = { - MtrDex.Blake3_256: Digestage(klas=blake3.blake3, size=None, length=None), - MtrDex.Blake2b_256: Digestage(klas=hashlib.blake2b, size=32, length=None), - MtrDex.Blake2s_256: Digestage(klas=hashlib.blake2s, size=None, length=None), - MtrDex.SHA3_256: Digestage(klas=hashlib.sha3_256, size=None, length=None), - MtrDex.SHA2_256: Digestage(klas=hashlib.sha256, size=None, length=None), - MtrDex.Blake3_512: Digestage(klas=blake3.blake3, size=None, length=64), - MtrDex.Blake2b_512: Digestage(klas=hashlib.blake2b, size=None, length=None), - MtrDex.SHA3_512: Digestage(klas=hashlib.sha3_512, size=None, length=None), - MtrDex.SHA2_512: Digestage(klas=hashlib.sha512, size=None, length=None), - } def __init__(self, raw=None, *, code=None, sad=None, kind=None, label=Saids.d, ignore=None, **kwa): @@ -3434,18 +3421,10 @@ def _derive(clas, sad: dict, *, for f in ignore: del ser[f] - # string now has - # correct size - klas, size, length = clas.Digests[code] + # string now has correct size # sad as 'v' verision string then use its kind otherwise passed in kind cpa = [clas._serialize(ser, kind=kind)] # raw pos arg class - ckwa = dict() # class keyword args - if size: - ckwa.update(digest_size=size) # optional digest_size - dkwa = dict() # digest keyword args - if length: - dkwa.update(length=length) - return klas(*cpa, **ckwa).digest(**dkwa), sad # raw digest and sad + return (Diger._digest(ser=cpa[0], code=code), sad) # raw digest and sad def derive(self, sad, code=None, **kwa): diff --git a/src/keri/core/serdering.py b/src/keri/core/serdering.py index 08d2280e..abb74a9b 100644 --- a/src/keri/core/serdering.py +++ b/src/keri/core/serdering.py @@ -348,22 +348,6 @@ class Serder: # Spans dict keyed by version (Versionage instance) of version string span (size) Spans = {Vrsn_1_0: VER1FULLSPAN, Vrsn_2_0: VER2FULLSPAN} - # Maps digest codes to Digestages of algorithms for computing digest. - # Should be based on the same set of codes as in coring.DigestCodex - # coring.DigDex so .digestive property works. - # Use unit tests to ensure codex elements sets match - Digests = { - DigDex.Blake3_256: Digestage(klas=blake3.blake3, size=None, length=None), - DigDex.Blake2b_256: Digestage(klas=hashlib.blake2b, size=32, length=None), - DigDex.Blake2s_256: Digestage(klas=hashlib.blake2s, size=None, length=None), - DigDex.SHA3_256: Digestage(klas=hashlib.sha3_256, size=None, length=None), - DigDex.SHA2_256: Digestage(klas=hashlib.sha256, size=None, length=None), - DigDex.Blake3_512: Digestage(klas=blake3.blake3, size=None, length=64), - DigDex.Blake2b_512: Digestage(klas=hashlib.blake2b, size=None, length=None), - DigDex.SHA3_512: Digestage(klas=hashlib.sha3_512, size=None, length=None), - DigDex.SHA2_512: Digestage(klas=hashlib.sha512, size=None, length=None), - } - # map seal clan names to seal counter code for grouping seals in anchor list ClanCodes = dict() ClanCodes[SClanDom.SealDigest.__name__] = SealDex_2_0.DigestSealSingles @@ -777,14 +761,7 @@ def _verify(self): raw = self.dumps(sad, kind=self.kind) # serialize dummied sad copy for label, code in saids.items(): if code in DigDex: # subclass override if non digestive allowed - klas, size, length = self.Digests[code] # digest algo size & length - ikwa = dict() # digest algo class initi keyword args - if size: - ikwa.update(digest_size=size) # optional digest_size - dkwa = dict() # digest method keyword args - if length: - dkwa.update(length=length) - dig = Matter(raw=klas(raw, **ikwa).digest(**dkwa), code=code).qb64 + dig = Diger(ser=raw, code=code).qb64 if dig != self._sad[label]: # compare to original raise ValidationError(f"Invalid said field '{label}' in sad" f" = {self._sad}, should be {dig}.") @@ -1005,15 +982,7 @@ def makify(self, sad, *, proto=None, vrsn=None, kind=None, raw = self.dumps(sad, kind=kind, proto=proto, vrsn=vrsn) # serialize sized dummied sad for label, code in _saids.items(): if code in DigDex: # subclass override if non digestive allowed - klas, dsize, dlen = self.Digests[code] # digest algo size & length - ikwa = dict() # digest algo class initi keyword args - if dsize: - ikwa.update(digest_size=dsize) # optional digest_size - dkwa = dict() # digest method keyword args - if dlen: - dkwa.update(length=dlen) - dig = Matter(raw=klas(raw, **ikwa).digest(**dkwa), code=code).qb64 - sad[label] = dig + sad[label] = Diger(ser=raw, code=code).qb64 raw = self.dumps(sad, kind=kind, proto=proto, vrsn=vrsn) # compute final raw if kind == Kinds.cesr:# cesr kind version string does not set size diff --git a/tests/core/test_coring.py b/tests/core/test_coring.py index fdd4cffb..9261b016 100644 --- a/tests/core/test_coring.py +++ b/tests/core/test_coring.py @@ -4432,6 +4432,10 @@ def test_diger(): """ Test the support functionality for Diger subclass of CryMat """ + # Ensure keyspace of Diger.Digests is same as codes in DigDex + assert set(coring.DigDex) == set(Diger.Digests.keys()) + + with pytest.raises(EmptyMaterialError): diger = Diger() @@ -4612,8 +4616,6 @@ def test_saider(): """ Test Saider object """ - # Test class attribute Digest matches DigDex (i.e.DigestCodex) - assert set(Saider.Digests.keys()) == set(code for code in DigDex) code = MtrDex.Blake3_256 kind = Kinds.json From 837ffff17a2c24669797b49fb9f7010f67b74941 Mon Sep 17 00:00:00 2001 From: Samuel M Smith Date: Fri, 9 Aug 2024 15:41:39 -0600 Subject: [PATCH 3/4] fixed bug --- src/keri/core/coring.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/keri/core/coring.py b/src/keri/core/coring.py index 64383402..cf7b669a 100644 --- a/src/keri/core/coring.py +++ b/src/keri/core/coring.py @@ -3407,8 +3407,8 @@ def _derive(clas, sad: dict, *, ignore (list): fields to ignore when generating SAID """ - if code not in DigDex or code not in clas.Digests: - raise ValueError("Unsupported digest code = {}.".format(code)) + if code not in DigDex: + raise ValueError(f"Unsupported digest {code = }.") sad = dict(sad) # make shallow copy so don't clobber original sad # fill id field denoted by label with dummy chars to get size correct @@ -3423,8 +3423,8 @@ def _derive(clas, sad: dict, *, # string now has correct size # sad as 'v' verision string then use its kind otherwise passed in kind - cpa = [clas._serialize(ser, kind=kind)] # raw pos arg class - return (Diger._digest(ser=cpa[0], code=code), sad) # raw digest and sad + cpa = clas._serialize(ser, kind=kind) # raw pos arg class + return (Diger._digest(ser=cpa, code=code), sad) # raw digest and sad def derive(self, sad, code=None, **kwa): From 2dfb282ef5891c5c694c6ebd18e6319870fd4bd9 Mon Sep 17 00:00:00 2001 From: Samuel M Smith Date: Fri, 9 Aug 2024 15:43:22 -0600 Subject: [PATCH 4/4] fixed some more --- src/keri/core/coring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keri/core/coring.py b/src/keri/core/coring.py index cf7b669a..be89859d 100644 --- a/src/keri/core/coring.py +++ b/src/keri/core/coring.py @@ -3408,7 +3408,7 @@ def _derive(clas, sad: dict, *, """ if code not in DigDex: - raise ValueError(f"Unsupported digest {code = }.") + raise ValueError(f"Unsupported digest {code=}.") sad = dict(sad) # make shallow copy so don't clobber original sad # fill id field denoted by label with dummy chars to get size correct