diff --git a/src/keri/core/__init__.py b/src/keri/core/__init__.py index 734d8259c..ec83d8bbb 100644 --- a/src/keri/core/__init__.py +++ b/src/keri/core/__init__.py @@ -14,5 +14,5 @@ from .coring import Tholder from .indexing import Indexer, Siger, IdrDex, IdxSigDex -from .signing import Signer, Salter, Cipher, Encrypter, Decrypter -from .counting import Counter, Codens +from .signing import Signer, Salter, Cipher, CiXDex, Encrypter, Decrypter +from .counting import Counter, Codens, CtrDex_2_0 diff --git a/src/keri/core/coring.py b/src/keri/core/coring.py index 2c47104e1..5896ffe09 100644 --- a/src/keri/core/coring.py +++ b/src/keri/core/coring.py @@ -465,115 +465,6 @@ def __iter__(self): TexDex = TextCodex() # Make instance -@dataclass(frozen=True) -class CipherX25519VarCodex: - """ - CipherX25519VarCodex is codex all variable sized cipher bytes derivation codes - for sealed box encryped ciphertext. Plaintext is B2. - Only provide defined codes. - Undefined are left out so that inclusion(exclusion) via 'in' operator works. - """ - X25519_Cipher_L0: str = '4D' # X25519 sealed box cipher bytes of sniffable plaintext lead size 0 - X25519_Cipher_L1: str = '5D' # X25519 sealed box cipher bytes of sniffable plaintext lead size 1 - X25519_Cipher_L2: str = '6D' # X25519 sealed box cipher bytes of sniffable plaintext lead size 2 - X25519_Cipher_Big_L0: str = '7AAD' # X25519 sealed box cipher bytes of sniffable plaintext big lead size 0 - X25519_Cipher_Big_L1: str = '8AAD' # X25519 sealed box cipher bytes of sniffable plaintext big lead size 1 - X25519_Cipher_Big_L2: str = '9AAD' # X25519 sealed box cipher bytes of sniffable plaintext big lead size 2 - - def __iter__(self): - return iter(astuple(self)) - - -CiXVarDex = CipherX25519VarCodex() # Make instance - - -@dataclass(frozen=True) -class CipherX25519FixQB64Codex: - """ - CipherX25519FixQB64Codex is codex all fixed sized cipher bytes derivation codes - for sealed box encryped ciphertext. Plaintext is B64. - Only provide defined codes. - Undefined are left out so that inclusion(exclusion) via 'in' operator works. - """ - X25519_Cipher_Seed: str = 'P' # X25519 sealed box 124 char qb64 Cipher of 44 char qb64 Seed - X25519_Cipher_Salt: str = '1AAH' # X25519 sealed box 100 char qb64 Cipher of 24 char qb64 Salt - - def __iter__(self): - return iter(astuple(self)) - - -CiXFixQB64Dex = CipherX25519FixQB64Codex() # Make instance - - -@dataclass(frozen=True) -class CipherX25519VarQB64Codex: - """ - CipherX25519VarQB64Codex is codex all variable sized cipher bytes derivation codes - for sealed box encryped ciphertext. Plaintext is QB64. - Only provide defined codes. - Undefined are left out so that inclusion(exclusion) via 'in' operator works. - """ - X25519_Cipher_QB64_L0: str = '4D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 0 - X25519_Cipher_QB64_L1: str = '5E' # X25519 sealed box cipher bytes of QB64 plaintext lead size 1 - X25519_Cipher_QB64_L2: str = '6E' # X25519 sealed box cipher bytes of QB64 plaintext lead size 2 - X25519_Cipher_QB64_Big_L0: str = '7AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 0 - X25519_Cipher_QB64_Big_L1: str = '8AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 1 - X25519_Cipher_QB64_Big_L2: str = '9AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 2 - - def __iter__(self): - return iter(astuple(self)) - - -CiXVarQB64Dex = CipherX25519VarQB64Codex() # Make instance - - -@dataclass(frozen=True) -class CipherX25519AllQB64Codex: - """ - CipherX25519AllQB64Codex is codex all both fixed and variable sized cipher bytes - derivation codes for sealed box encryped ciphertext. Plaintext is B64. - Only provide defined codes. - Undefined are left out so that inclusion(exclusion) via 'in' operator works. - """ - X25519_Cipher_Seed: str = 'P' # X25519 sealed box 124 char qb64 Cipher of 44 char qb64 Seed - X25519_Cipher_Salt: str = '1AAH' # X25519 sealed box 100 char qb64 Cipher of 24 char qb64 Salt - X25519_Cipher_QB64_L0: str = '4D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 0 - X25519_Cipher_QB64_L1: str = '5E' # X25519 sealed box cipher bytes of QB64 plaintext lead size 1 - X25519_Cipher_QB64_L2: str = '6E' # X25519 sealed box cipher bytes of QB64 plaintext lead size 2 - X25519_Cipher_QB64_Big_L0: str = '7AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 0 - X25519_Cipher_QB64_Big_L1: str = '8AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 1 - X25519_Cipher_QB64_Big_L2: str = '9AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 2 - - def __iter__(self): - return iter(astuple(self)) - - -CiXAllQB64Dex = CipherX25519AllQB64Codex() # Make instance - - -@dataclass(frozen=True) -class CipherX25519QB2VarCodex: - """ - CipherX25519QB2VarCodex is codex all variable sized cipher bytes derivation codes - for sealed box encryped ciphertext. Plaintext is B2. - Only provide defined codes. - Undefined are left out so that inclusion(exclusion) via 'in' operator works. - """ - X25519_Cipher_L0: str = '4E' # X25519 sealed box cipher bytes of QB2 plaintext lead size 0 - X25519_Cipher_L1: str = '5E' # X25519 sealed box cipher bytes of QB2 plaintext lead size 1 - X25519_Cipher_L2: str = '6E' # X25519 sealed box cipher bytes of QB2 plaintext lead size 2 - X25519_Cipher_Big_L0: str = '7AAE' # X25519 sealed box cipher bytes of QB2 plaintext big lead size 0 - X25519_Cipher_Big_L1: str = '8AAE' # X25519 sealed box cipher bytes of QB2 plaintext big lead size 1 - X25519_Cipher_Big_L2: str = '9AAE' # X25519 sealed box cipher bytes of QB2 plaintext big lead size 2 - - def __iter__(self): - return iter(astuple(self)) - - -CiXVarQB2Dex = CipherX25519QB2VarCodex() # Make instance - - - @dataclass(frozen=True) class NonTransCodex: @@ -731,7 +622,7 @@ class Matter: Names (dict): maps code to code name Pad (str): B64 pad char for xtra size pre-padded soft values - Calss Methods: + Class Methods: Attributes: diff --git a/src/keri/core/signing.py b/src/keri/core/signing.py index 875ce179b..dcfaec05a 100644 --- a/src/keri/core/signing.py +++ b/src/keri/core/signing.py @@ -4,6 +4,7 @@ Provides support Signer class """ +from dataclasses import dataclass, astuple, asdict import pysodium @@ -11,12 +12,12 @@ from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat from cryptography.hazmat.primitives.asymmetric import ec, utils -from ..kering import (EmptyMaterialError,) +from ..kering import (EmptyMaterialError, InvalidCodeError, InvalidSizeError) from ..help import helping from .coring import (Tiers, ) -from .coring import (Matter, MtrDex, Verfer, Cigar) +from .coring import (SmallVrzDex, LargeVrzDex, Matter, MtrDex, Verfer, Cigar) from .indexing import IdrDex, Siger @@ -25,6 +26,8 @@ ECDSA_256k1_SEEDBYTES = 32 + + class Signer(Matter): """ Signer is Matter subclass with method to create signature of serialization @@ -476,6 +479,186 @@ def signers(self, count=1, start=0, path="", **kwa): return [self.signer(path=f"{path}{i + start:x}", **kwa) for i in range(count)] + + +# Codes for for ciphers of variable sized sniffable QB2 or QB64 plain text +@dataclass(frozen=True) +class CipherX25519VarSnifCodex: + """ + CipherX25519VarCodex is codex all variable sized cipher bytes derivation codes + for sealed box encryped ciphertext. Plaintext is Sniffable QB2 or QB64. + Only provide defined codes. + Undefined are left out so that inclusion(exclusion) via 'in' operator works. + """ + X25519_Cipher_L0: str = '4C' # X25519 sealed box cipher bytes of sniffable plaintext lead size 0 + X25519_Cipher_L1: str = '5C' # X25519 sealed box cipher bytes of sniffable plaintext lead size 1 + X25519_Cipher_L2: str = '6C' # X25519 sealed box cipher bytes of sniffable plaintext lead size 2 + X25519_Cipher_Big_L0: str = '7AAC' # X25519 sealed box cipher bytes of sniffable plaintext big lead size 0 + X25519_Cipher_Big_L1: str = '8AAC' # X25519 sealed box cipher bytes of sniffable plaintext big lead size 1 + X25519_Cipher_Big_L2: str = '9AAC' # X25519 sealed box cipher bytes of sniffable plaintext big lead size 2 + + def __iter__(self): + return iter(astuple(self)) + +CiXVarSnifDex = CipherX25519VarSnifCodex() # Make instance + + +# Codes for for ciphers of variable sized QB64 plain text +@dataclass(frozen=True) +class CipherX25519VarQB64Codex: + """ + CipherX25519VarQB64Codex is codex all variable sized cipher bytes derivation codes + for sealed box encryped ciphertext. Plaintext is QB64. + Only provide defined codes. + Undefined are left out so that inclusion(exclusion) via 'in' operator works. + """ + X25519_Cipher_QB64_L0: str = '4D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 0 + X25519_Cipher_QB64_L1: str = '5D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 1 + X25519_Cipher_QB64_L2: str = '6D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 2 + X25519_Cipher_QB64_Big_L0: str = '7AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 0 + X25519_Cipher_QB64_Big_L1: str = '8AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 1 + X25519_Cipher_QB64_Big_L2: str = '9AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 2 + + def __iter__(self): + return iter(astuple(self)) + +CiXVarQB64Dex = CipherX25519VarQB64Codex() # Make instance + +# Codes for for ciphers of fixed sized QB64 plain text +@dataclass(frozen=True) +class CipherX25519FixQB64Codex: + """ + CipherX25519FixQB64Codex is codex all fixed sized cipher bytes derivation codes + for sealed box encryped ciphertext. Plaintext is B64. + Only provide defined codes. + Undefined are left out so that inclusion(exclusion) via 'in' operator works. + """ + X25519_Cipher_Seed: str = 'P' # X25519 sealed box 124 char qb64 Cipher of 44 char qb64 Seed + X25519_Cipher_Salt: str = '1AAH' # X25519 sealed box 100 char qb64 Cipher of 24 char qb64 Salt + + def __iter__(self): + return iter(astuple(self)) + +CiXFixQB64Dex = CipherX25519FixQB64Codex() # Make instance + +# Codes for for ciphers of all sizes fixed and variable of QB64 plain text +@dataclass(frozen=True) +class CipherX25519AllQB64Codex: + """ + CipherX25519AllQB64Codex is codex all both fixed and variable sized cipher bytes + derivation codes for sealed box encryped ciphertext. Plaintext is B64. + Only provide defined codes. + Undefined are left out so that inclusion(exclusion) via 'in' operator works. + """ + X25519_Cipher_Seed: str = 'P' # X25519 sealed box 124 char qb64 Cipher of 44 char qb64 Seed + X25519_Cipher_Salt: str = '1AAH' # X25519 sealed box 100 char qb64 Cipher of 24 char qb64 Salt + X25519_Cipher_QB64_L0: str = '4D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 0 + X25519_Cipher_QB64_L1: str = '5D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 1 + X25519_Cipher_QB64_L2: str = '6D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 2 + X25519_Cipher_QB64_Big_L0: str = '7AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 0 + X25519_Cipher_QB64_Big_L1: str = '8AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 1 + X25519_Cipher_QB64_Big_L2: str = '9AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 2 + + def __iter__(self): + return iter(astuple(self)) + +CiXAllQB64Dex = CipherX25519AllQB64Codex() # Make instance + + + +# Codes for for ciphers of variable sized QB2 plain text +@dataclass(frozen=True) +class CipherX25519QB2VarCodex: + """ + CipherX25519QB2VarCodex is codex all variable sized cipher bytes derivation codes + for sealed box encryped ciphertext. Plaintext is B2. + Only provide defined codes. + Undefined are left out so that inclusion(exclusion) via 'in' operator works. + """ + X25519_Cipher_QB2_L0: str = '4E' # X25519 sealed box cipher bytes of QB2 plaintext lead size 0 + X25519_Cipher_QB2_L1: str = '5E' # X25519 sealed box cipher bytes of QB2 plaintext lead size 1 + X25519_Cipher_QB2_L2: str = '6E' # X25519 sealed box cipher bytes of QB2 plaintext lead size 2 + X25519_Cipher_QB2_Big_L0: str = '7AAE' # X25519 sealed box cipher bytes of QB2 plaintext big lead size 0 + X25519_Cipher_QB2_Big_L1: str = '8AAE' # X25519 sealed box cipher bytes of QB2 plaintext big lead size 1 + X25519_Cipher_QB2_Big_L2: str = '9AAE' # X25519 sealed box cipher bytes of QB2 plaintext big lead size 2 + + def __iter__(self): + return iter(astuple(self)) + +CiXVarQB2Dex = CipherX25519QB2VarCodex() # Make instance + +# Codes for for ciphers of all varibale sizes and all types of plain text +@dataclass(frozen=True) +class CipherX25519AllVarCodex: + """ + CipherX25519AllVarCodex is codex all variable size codes of cipher bytes + for sealed box encryped ciphertext. Plaintext maybe sniffable or qb64 or qb2. + Only provide defined codes. + Undefined are left out so that inclusion(exclusion) via 'in' operator works. + """ + X25519_Cipher_L0: str = '4C' # X25519 sealed box cipher bytes of sniffable plaintext lead size 0 + X25519_Cipher_L1: str = '5C' # X25519 sealed box cipher bytes of sniffable plaintext lead size 1 + X25519_Cipher_L2: str = '6C' # X25519 sealed box cipher bytes of sniffable plaintext lead size 2 + X25519_Cipher_Big_L0: str = '7AAC' # X25519 sealed box cipher bytes of sniffable plaintext big lead size 0 + X25519_Cipher_Big_L1: str = '8AAC' # X25519 sealed box cipher bytes of sniffable plaintext big lead size 1 + X25519_Cipher_Big_L2: str = '9AAC' # X25519 sealed box cipher bytes of sniffable plaintext big lead size 2 + X25519_Cipher_QB64_L0: str = '4D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 0 + X25519_Cipher_QB64_L1: str = '5D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 1 + X25519_Cipher_QB64_L2: str = '6D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 2 + X25519_Cipher_QB64_Big_L0: str = '7AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 0 + X25519_Cipher_QB64_Big_L1: str = '8AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 1 + X25519_Cipher_QB64_Big_L2: str = '9AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 2 + X25519_Cipher_QB2_L0: str = '4E' # X25519 sealed box cipher bytes of QB2 plaintext lead size 0 + X25519_Cipher_QB2_L1: str = '5E' # X25519 sealed box cipher bytes of QB2 plaintext lead size 1 + X25519_Cipher_QB2_L2: str = '6E' # X25519 sealed box cipher bytes of QB2 plaintext lead size 2 + X25519_Cipher_QB2_Big_L0: str = '7AAE' # X25519 sealed box cipher bytes of QB2 plaintext big lead size 0 + X25519_Cipher_QB2_Big_L1: str = '8AAE' # X25519 sealed box cipher bytes of QB2 plaintext big lead size 1 + X25519_Cipher_QB2_Big_L2: str = '9AAE' # X25519 sealed box cipher bytes of QB2 plaintext big lead size 2 + + def __iter__(self): + return iter(astuple(self)) + +CiXVarDex = CipherX25519AllVarCodex() # Make instance + + +# Codes for for ciphers of all sizes and all types of plain text +@dataclass(frozen=True) +class CipherX25519AllCodex: + """ + CipherX25519AllCodex is codex all codes and types of cipher bytes + for sealed box encryped ciphertext. Plaintext maybe sniffable or qb64 or qb2. + Only provide defined codes. + Undefined are left out so that inclusion(exclusion) via 'in' operator works. + """ + X25519_Cipher_L0: str = '4C' # X25519 sealed box cipher bytes of sniffable plaintext lead size 0 + X25519_Cipher_L1: str = '5C' # X25519 sealed box cipher bytes of sniffable plaintext lead size 1 + X25519_Cipher_L2: str = '6C' # X25519 sealed box cipher bytes of sniffable plaintext lead size 2 + X25519_Cipher_Big_L0: str = '7AAC' # X25519 sealed box cipher bytes of sniffable plaintext big lead size 0 + X25519_Cipher_Big_L1: str = '8AAC' # X25519 sealed box cipher bytes of sniffable plaintext big lead size 1 + X25519_Cipher_Big_L2: str = '9AAC' # X25519 sealed box cipher bytes of sniffable plaintext big lead size 2 + X25519_Cipher_Seed: str = 'P' # X25519 sealed box 124 char qb64 Cipher of 44 char qb64 Seed + X25519_Cipher_Salt: str = '1AAH' # X25519 sealed box 100 char qb64 Cipher of 24 char qb64 Salt + X25519_Cipher_QB64_L0: str = '4D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 0 + X25519_Cipher_QB64_L1: str = '5D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 1 + X25519_Cipher_QB64_L2: str = '6D' # X25519 sealed box cipher bytes of QB64 plaintext lead size 2 + X25519_Cipher_QB64_Big_L0: str = '7AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 0 + X25519_Cipher_QB64_Big_L1: str = '8AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 1 + X25519_Cipher_QB64_Big_L2: str = '9AAD' # X25519 sealed box cipher bytes of QB64 plaintext big lead size 2 + X25519_Cipher_QB2_L0: str = '4E' # X25519 sealed box cipher bytes of QB2 plaintext lead size 0 + X25519_Cipher_QB2_L1: str = '5E' # X25519 sealed box cipher bytes of QB2 plaintext lead size 1 + X25519_Cipher_QB2_L2: str = '6E' # X25519 sealed box cipher bytes of QB2 plaintext lead size 2 + X25519_Cipher_QB2_Big_L0: str = '7AAE' # X25519 sealed box cipher bytes of QB2 plaintext big lead size 0 + X25519_Cipher_QB2_Big_L1: str = '8AAE' # X25519 sealed box cipher bytes of QB2 plaintext big lead size 1 + X25519_Cipher_QB2_Big_L2: str = '9AAE' # X25519 sealed box cipher bytes of QB2 plaintext big lead size 2 + + def __iter__(self): + return iter(astuple(self)) + +CiXDex = CipherX25519AllCodex() # Make instance + + + + class Cipher(Matter): """ Cipher is Matter subclass holding a cipher text of a secret that may be @@ -486,33 +669,43 @@ class Cipher(Matter): private key for decryption. The default is to use X25519 sealed box encryption. The Cipher instances .raw is the raw binary encrypted cipher text and its - .code indicates what type of secret has been encrypted. The cipher suite used + .code indicates what type of plain text has been encrypted. The cipher suite used for the encryption/decryption is implied by the context where the cipher is used. See Matter for inherited attributes and properties """ + Codex = CiXDex + Codes = asdict(CiXDex) # map code name to code def __init__(self, raw=None, code=None, **kwa): """ Parmeters: - raw (Union[bytes, str]): cipher text + raw (bytes | str): cipher text (not plain text) code (str): cipher suite + """ + # default when raw is not None and code is None is to use fixed size + # code given by raw size. Otherwise provided code fixed or variable size + # is handled by Matter superclass. if raw is not None and code is None: - if len(raw) == Matter._rawSize(MtrDex.X25519_Cipher_Salt): - code = MtrDex.X25519_Cipher_Salt - elif len(raw) == Matter._rawSize(MtrDex.X25519_Cipher_Seed): - code = MtrDex.X25519_Cipher_Seed + if len(raw) == Matter._rawSize(MtrDex.X25519_Cipher_Salt): + code = MtrDex.X25519_Cipher_Salt + elif len(raw) == Matter._rawSize(MtrDex.X25519_Cipher_Seed): + code = MtrDex.X25519_Cipher_Seed + else: + raise InvalidSizeError(f"Unsupported fixed raw size" + f" {len(raw)} for {code=}.") if hasattr(raw, "encode"): raw = raw.encode("utf-8") # ensure bytes not str super(Cipher, self).__init__(raw=raw, code=code, **kwa) - if self.code not in (MtrDex.X25519_Cipher_Salt, MtrDex.X25519_Cipher_Seed): - raise ValueError("Unsupported cipher code = {}.".format(self.code)) + if self.code not in CiXDex: + raise InvalidCodeError(f"Unsupported cipher code = {self.code}.") + def decrypt(self, prikey=None, seed=None): """ @@ -521,7 +714,7 @@ def decrypt(self, prikey=None, seed=None): qualified (qb64) so derivaton code of plain text preserved through encryption/decryption round trip. - Uses either decryption key given by prikey or derives prikey from + Decrypter uses either decryption key given by prikey or derives prikey from signing key derived from private seed. Parameters: diff --git a/src/keri/help/helping.py b/src/keri/help/helping.py index 838f69bf4..5333887e5 100644 --- a/src/keri/help/helping.py +++ b/src/keri/help/helping.py @@ -266,7 +266,7 @@ def fromIso8601(dts): B64REX = rb'^[0-9A-Za-z_-]*\Z' # [A-Za-z0-9\-\_] Reb64 = re.compile(B64REX) # compile is faster -# to use if Reb64.match(bext): +# Usage: if Reb64.match(bext): or if not Reb64.match(bext): def intToB64(i, l=1): diff --git a/src/keri/kering.py b/src/keri/kering.py index 725e3b961..2177b1be8 100644 --- a/src/keri/kering.py +++ b/src/keri/kering.py @@ -461,7 +461,7 @@ class MaterialError(KeriError): class RawMaterialError(MaterialError): """ - Not Enough bytes in buffer bytearray for raw material + Invalid raw material Usage: raise RawMaterialError("error message") """ @@ -469,7 +469,7 @@ class RawMaterialError(MaterialError): class SoftMaterialError(MaterialError): """ - Not Enough chars in soft for soft material + Invalid soft material Usage: raise SoftMaterialError("error message") """ diff --git a/tests/core/test_signing.py b/tests/core/test_signing.py index f0e519db2..9b8ec4d16 100644 --- a/tests/core/test_signing.py +++ b/tests/core/test_signing.py @@ -3,16 +3,15 @@ tests.core.test_indexing module """ - +from base64 import urlsafe_b64decode as decodeB64 import pysodium import pytest -from keri import kering +from keri import kering, core from keri.core import (Indexer, IdrDex, ) from keri.core import (Matter, MtrDex, Cigar, Verfer, Prefixer) -from keri.core import (Signer, Salter, - Cipher, Encrypter, Decrypter, ) +from keri.core import (Signer, Salter, Cipher, CiXDex, Encrypter, Decrypter, ) from keri.core import (Tiers, ) @@ -375,33 +374,6 @@ def test_signer(): """ Done Test """ -def test_generatesigners(): - """ - Test the support function genSigners - - """ - signers = Salter().signers(count=2, temp=True, transferable=False) - assert len(signers) == 2 - for signer in signers: - assert signer.verfer.code == MtrDex.Ed25519N - - # raw = pysodium.randombytes(pysodium.crypto_pwhash_SALTBYTES) # raw salt - raw = b'g\x15\x89\x1a@\xa4\xa47\x07\xb9Q\xb8\x18\xcdJW' - assert len(raw) == 16 - signers = Salter(raw=raw).signers(count=4) - assert len(signers) == 4 - for signer in signers: - assert signer.code == MtrDex.Ed25519_Seed - assert signer.verfer.code == MtrDex.Ed25519 - - sigkeys = [signer.qb64 for signer in signers] - assert sigkeys == ['AK8F6AAiYDpXlWdj2O5F5-6wNCCNJh2A4XOlqwR_HwwH', - 'AOs8-zNPPh0EhavdrCfCiTk9nGeO8e6VxUCzwdKXJAd0', - 'AHMBU5PsIJN2U9m7j0SGyvs8YD8fkym2noELzxIrzfdG', - 'AJZ7ZLd7unQ4IkMUwE69NXcvDO9rrmmRH_Xk3TPu9BpP'] - - """ End Test """ - def test_salter(): """ @@ -452,6 +424,66 @@ def test_salter(): """ Done Test """ +def test_gensignerswithsalter(): + """ + Test generating a set of signers using Salter + + """ + signers = Salter().signers(count=2, temp=True, transferable=False) + assert len(signers) == 2 + for signer in signers: + assert signer.verfer.code == MtrDex.Ed25519N + + # raw = pysodium.randombytes(pysodium.crypto_pwhash_SALTBYTES) # raw salt + raw = b'g\x15\x89\x1a@\xa4\xa47\x07\xb9Q\xb8\x18\xcdJW' + assert len(raw) == 16 + signers = Salter(raw=raw).signers(count=4) + assert len(signers) == 4 + for signer in signers: + assert signer.code == MtrDex.Ed25519_Seed + assert signer.verfer.code == MtrDex.Ed25519 + + sigkeys = [signer.qb64 for signer in signers] + assert sigkeys == ['AK8F6AAiYDpXlWdj2O5F5-6wNCCNJh2A4XOlqwR_HwwH', + 'AOs8-zNPPh0EhavdrCfCiTk9nGeO8e6VxUCzwdKXJAd0', + 'AHMBU5PsIJN2U9m7j0SGyvs8YD8fkym2noELzxIrzfdG', + 'AJZ7ZLd7unQ4IkMUwE69NXcvDO9rrmmRH_Xk3TPu9BpP'] + + """ End Test """ + +def test_cipher_closs(): + """ + Test class attributes of Cipher + """ + assert Cipher.Codex == CiXDex + + assert Cipher.Codes == \ + { + 'X25519_Cipher_L0': '4C', + 'X25519_Cipher_L1': '5C', + 'X25519_Cipher_L2': '6C', + 'X25519_Cipher_Big_L0': '7AAC', + 'X25519_Cipher_Big_L1': '8AAC', + 'X25519_Cipher_Big_L2': '9AAC', + 'X25519_Cipher_Seed': 'P', + 'X25519_Cipher_Salt': '1AAH', + 'X25519_Cipher_QB64_L0': '4D', + 'X25519_Cipher_QB64_L1': '5D', + 'X25519_Cipher_QB64_L2': '6D', + 'X25519_Cipher_QB64_Big_L0': '7AAD', + 'X25519_Cipher_QB64_Big_L1': '8AAD', + 'X25519_Cipher_QB64_Big_L2': '9AAD', + 'X25519_Cipher_QB2_L0': '4E', + 'X25519_Cipher_QB2_L1': '5E', + 'X25519_Cipher_QB2_L2': '6E', + 'X25519_Cipher_QB2_Big_L0': '7AAE', + 'X25519_Cipher_QB2_Big_L1': '8AAE', + 'X25519_Cipher_QB2_Big_L2': '9AAE' + } + + """End Test""" + + def test_cipher(): """ Test Cipher subclass of Matter @@ -489,6 +521,11 @@ def test_cipher(): uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) assert uncb == seedqb64b + cipher = Cipher(raw=raw, code=CiXDex.X25519_Cipher_Seed) + assert cipher.code == MtrDex.X25519_Cipher_Seed + uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) + assert uncb == seedqb64b + # test .decrypt method needs qb64 prikeyqb64 = Matter(raw=prikey, code=MtrDex.X25519_Private).qb64b assert cipher.decrypt(prikey=prikeyqb64).qb64b == seedqb64b @@ -496,12 +533,27 @@ def test_cipher(): cryptseedqb64 = Matter(raw=cryptseed, code=MtrDex.Ed25519_Seed).qb64b assert cipher.decrypt(seed=cryptseedqb64).qb64b == seedqb64b + # wrong but shorter code so instance creation succeeds + cipher = Cipher(raw=raw, code=CiXDex.X25519_Cipher_Salt) + assert cipher.code == MtrDex.X25519_Cipher_Salt + with pytest.raises(ValueError): # but decryption fails + uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) + + raw = pysodium.crypto_box_seal(saltqb64b, pubkey) # uses nonce so different everytime cipher = Cipher(raw=raw) assert cipher.code == MtrDex.X25519_Cipher_Salt uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) assert uncb == saltqb64b + cipher = Cipher(raw=raw, code=CiXDex.X25519_Cipher_Salt) + assert cipher.code == MtrDex.X25519_Cipher_Salt + uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) + assert uncb == saltqb64b + + with pytest.raises(kering.RawMaterialError): # wrong code to big for raw bytes + cipher = Cipher(raw=raw, code=CiXDex.X25519_Cipher_Seed) + # test .decrypt method needs qb64 prikeyqb64 = Matter(raw=prikey, code=MtrDex.X25519_Private).qb64b assert cipher.decrypt(prikey=prikeyqb64).qb64b == saltqb64b @@ -509,8 +561,184 @@ def test_cipher(): cryptseedqb64 = Matter(raw=cryptseed, code=MtrDex.Ed25519_Seed).qb64b assert cipher.decrypt(seed=cryptseedqb64).qb64b == saltqb64b - with pytest.raises(ValueError): # bad code + with pytest.raises(kering.InvalidCodeError): # bad code cipher = Cipher(raw=raw, code=MtrDex.Ed25519N) + + # Test bad raw size + raw = pysodium.crypto_box_seal(seedqb64b, pubkey) # uses nonce so different everytime + with pytest.raises(kering.InvalidSizeError): + cipher = Cipher(raw=raw[:len(raw)-1]) # make raw too small + with pytest.raises(kering.InvalidSizeError): + cipher = Cipher(raw=raw + b'_') # make raw too big + + raw = pysodium.crypto_box_seal(saltqb64b, pubkey) # uses nonce so different everytime + with pytest.raises(kering.InvalidSizeError): + cipher = Cipher(raw=raw[:len(raw)-1]) # make raw too small + with pytest.raises(kering.InvalidSizeError): + cipher = Cipher(raw=raw + b'_') # make raw too big + + # variable sized ciphers + # Always init with L0 code and let Matter auto correct the code for the + # actual lead size of raw + + # qb64 lead 0 + plain = "The quick brown fox jumps over the lazy " + texter = core.Texter(text=plain) + assert texter.text == plain + raw = pysodium.crypto_box_seal(texter.qb64b, pubkey) # uses nonce so different everytime + assert len(raw) == 108 + assert (3 - (len(raw) % 3)) % 3 == 0 + cipher = Cipher(raw=raw, code=CiXDex.X25519_Cipher_QB64_L0) + assert cipher.code == CiXDex.X25519_Cipher_QB64_L0 + uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) + assert uncb == texter.qb64b + texter = core.Texter(qb64b=uncb) + assert texter.text == plain + + # qb64 lead 1 + plain = "The quick brown fox jumps over the lazy dogcats" + texter = core.Texter(text=plain) + assert texter.text == plain + raw = pysodium.crypto_box_seal(texter.qb64b, pubkey) # uses nonce so different everytime + assert len(raw) == 116 + assert (3 - (len(raw) % 3)) % 3 == 1 + cipher = Cipher(raw=raw, code=CiXDex.X25519_Cipher_QB64_L0) + assert cipher.code == CiXDex.X25519_Cipher_QB64_L1 + uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) + assert uncb == texter.qb64b + texter = core.Texter(qb64b=uncb) + assert texter.text == plain + + # qb64 lead 2 + plain = "The quick brown fox jumps over the lazy dog" + texter = core.Texter(text=plain) + assert texter.text == plain + raw = pysodium.crypto_box_seal(texter.qb64b, pubkey) # uses nonce so different everytime + assert len(raw) == 112 + assert (3 - (len(raw) % 3)) % 3 == 2 + cipher = Cipher(raw=raw, code=CiXDex.X25519_Cipher_QB64_L0) + assert cipher.code == CiXDex.X25519_Cipher_QB64_L2 + uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) + assert uncb == texter.qb64b + texter = core.Texter(qb64b=uncb) + assert texter.text == plain + + # qb2 lead 0 (always lead 0 when qb2 from texter) + plain = "The quick brown fox jumps over the lazy dog" + texter = core.Texter(text=plain) + assert texter.text == plain + raw = pysodium.crypto_box_seal(texter.qb2, pubkey) # uses nonce so different everytime + assert len(raw) == 96 + assert (3 - (len(raw) % 3)) % 3 == 0 + cipher = Cipher(raw=raw, code=CiXDex.X25519_Cipher_QB2_L0) + assert cipher.code == CiXDex.X25519_Cipher_QB2_L0 + uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) + assert uncb == texter.qb2 + texter = core.Texter(qb2=uncb) + assert texter.text == plain + + # sniffable qb64 lead 0 + plain = "The quick brown fox jumps over the lazy" + texter = core.Texter(text=plain) + assert texter.text == plain + counter = core.Counter(core.Codens.GenericGroup, count=texter.size) + peb = counter.qb64b + texter.qb64b + raw = pysodium.crypto_box_seal(peb, pubkey) # uses nonce so different everytime + assert len(raw) == 108 + assert (3 - (len(raw) % 3)) % 3 == 0 + cipher = Cipher(raw=raw, code=CiXDex.X25519_Cipher_L0) + assert cipher.code == CiXDex.X25519_Cipher_L0 + uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) + assert uncb == peb + peb = bytearray(peb) + counter = core.Counter(qb64b=peb, strip=True) + assert counter.code == core.CtrDex_2_0.GenericGroup + texter = core.Texter(qb64b=peb, strip=True) + assert texter.text == plain + + # sniffable qb64 lead 1 + plain = "The quick brown fox jumps over the lazy dog" + texter = core.Texter(text=plain) + assert texter.text == plain + counter = core.Counter(core.Codens.GenericGroup, count=texter.size) + peb = counter.qb64b + texter.qb64b + raw = pysodium.crypto_box_seal(peb, pubkey) # uses nonce so different everytime + assert len(raw) == 116 + assert (3 - (len(raw) % 3)) % 3 == 1 + cipher = Cipher(raw=raw, code=CiXDex.X25519_Cipher_L0) + assert cipher.code == CiXDex.X25519_Cipher_L1 + uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) + assert uncb == peb + peb = bytearray(peb) + counter = core.Counter(qb64b=peb, strip=True) + assert counter.code == core.CtrDex_2_0.GenericGroup + texter = core.Texter(qb64b=peb, strip=True) + assert texter.text == plain + + # sniffable qb64 lead 2 + plain = "The quick brown fox jumps over the lazy " + texter = core.Texter(text=plain) + assert texter.text == plain + counter = core.Counter(core.Codens.GenericGroup, count=texter.size) + peb = counter.qb64b + texter.qb64b + raw = pysodium.crypto_box_seal(peb, pubkey) # uses nonce so different everytime + assert len(raw) == 112 + assert (3 - (len(raw) % 3)) % 3 == 2 + cipher = Cipher(raw=raw, code=CiXDex.X25519_Cipher_L0) + assert cipher.code == CiXDex.X25519_Cipher_L2 + uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) + assert uncb == peb + peb = bytearray(peb) + counter = core.Counter(qb64b=peb, strip=True) + assert counter.code == core.CtrDex_2_0.GenericGroup + texter = core.Texter(qb64b=peb, strip=True) + assert texter.text == plain + + # sniffable qb2 lead 0 + plain = "The quick brown fox jumps over the lazy" + texter = core.Texter(text=plain) + assert texter.text == plain + counter = core.Counter(core.Codens.GenericGroup, count=texter.size) + assert counter.code == core.CtrDex_2_0.GenericGroup + peb = counter.qb64b + texter.qb64b + pebqb2 = decodeB64(peb) + raw = pysodium.crypto_box_seal(pebqb2, pubkey) # uses nonce so different everytime + assert len(raw) == 93 + assert (3 - (len(raw) % 3)) % 3 == 0 + cipher = Cipher(raw=raw, code=CiXDex.X25519_Cipher_L0) + assert cipher.code == CiXDex.X25519_Cipher_L0 + uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) + assert uncb == pebqb2 + pebqb2 = bytearray(pebqb2) + counter = core.Counter(qb2=pebqb2, strip=True) + assert counter.code == core.CtrDex_2_0.GenericGroup + texter = core.Texter(qb2=pebqb2, strip=True) + assert texter.text == plain + + + # sniffable qb2 lead 0 Big + + plain = "The quick brown fox jumps over the lazy" * 324 + texter = core.Texter(text=plain) + assert texter.text == plain + counter = core.Counter(core.Codens.GenericGroup, count=texter.size) + assert counter.code == core.CtrDex_2_0.BigGenericGroup + peb = counter.qb64b + texter.qb64b + pebqb2 = decodeB64(peb) + raw = pysodium.crypto_box_seal(pebqb2, pubkey) # uses nonce so different everytime + assert len(raw) == 12696 + assert (3 - (len(raw) % 3)) % 3 == 0 + assert (len(raw) // 3 ) > (64 ** 2 - 1) # triplets + cipher = Cipher(raw=raw, code=CiXDex.X25519_Cipher_L0) + assert cipher.code == CiXDex.X25519_Cipher_Big_L0 + uncb = pysodium.crypto_box_seal_open(cipher.raw, pubkey, prikey) + assert uncb == pebqb2 + pebqb2 = bytearray(pebqb2) + counter = core.Counter(qb2=pebqb2, strip=True) + assert counter.code == core.CtrDex_2_0.BigGenericGroup + texter = core.Texter(qb2=pebqb2, strip=True) + assert texter.text == plain + """ Done Test """ @@ -702,8 +930,9 @@ def test_decrypter(): if __name__ == "__main__": test_signer() - test_generatesigners() test_salter() + test_gensignerswithsalter() + test_cipher_closs() test_cipher() test_encrypter() test_decrypter()