Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/python-app-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,19 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ macos-13, ubuntu-latest ]
os: [ macos-15, ubuntu-latest ]

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.12.2
uses: actions/setup-python@v2
with:
python-version: 3.12.2
- name: Config libsodium
if: runner.os == 'macOS'
run: |
echo "SODIUM_INSTALL=system" >> $GITHUB_ENV
echo "DYLD_LIBRARY_PATH=/opt/homebrew/lib:$DYLD_LIBRARY_PATH" >> $GITHUB_ENV
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
'multidict>=6.0.5',
'ordered-set>=4.1.0',
'hio==0.6.14',
'multicommand>=1.0.0',
'multicommand==1.0.0',
'jsonschema>=4.21.1',
'falcon>=3.1.3',
'hjson>=3.1.0',
Expand Down
48 changes: 37 additions & 11 deletions src/keri/app/forwarding.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from keri.db import dbing
from keri.kering import Roles
from keri.peer import exchanging
from keri.spac import payloading

logger = ogler.getLogger()

Expand All @@ -39,7 +40,7 @@ def __init__(self, hby, mbx=None, evts=None, cues=None, **kwa):
doers = [doing.doify(self.deliverDo)]
super(Poster, self).__init__(doers=doers, **kwa)

def deliverDo(self, tymth=None, tock=0.0):
def deliverDo(self, tymth=None, tock=0.0, **kwa):
"""
Returns: doifiable Doist compatible generator method that processes
a queue of messages and envelopes them in a `fwd` message
Expand Down Expand Up @@ -270,14 +271,32 @@ def deliver(self):
Usage:
add result of doify on this method to doers list
"""
doers = []
while self.evts:
doers += self._chunk()

return doers

def _chunk(self):
msg = bytearray()

if self.essr:
msg.extend(payloading.PayloadTyper(type=payloading.PayloadTypes.SCS).qb64b)
msg.extend(self.hab.kever.prefixer.qb64b)

# bext field can be randomized to reduce correlation based on packet size, empty for now
msg.extend(coring.Bexter(bext="").qb64b)

while self.evts:
evt = self.evts.popleft()

serder = evt["serder"]
atc = evt["attachment"] if "attachment" in evt else b''

if self.essr and len(msg) + len(serder.raw) + len(atc) > 16384:
self.evts.appendleft(evt)
break

msg.extend(serder.raw)
msg.extend(atc)

Expand Down Expand Up @@ -352,22 +371,20 @@ def sendDirect(self, hab, ends, msg):
return self.messagers

def _essrWrapper(self, hab, msg, ctrl):
ims = bytearray()

# Can be added in deliver() once mailbox support added to avoid list copy
ims.extend(coring.Tsper(tsp=coring.Tsps.SCS).qb64b)
ims.extend(self.hab.kever.prefixer.qb64b)
ims.extend(msg)
prefixer = coring.Prefixer(qb64=ctrl)
if prefixer.code in coring.NonTransDex: # e.g. witness mbx
verfer = coring.Verfer(qb64=ctrl)
else:
rkever = self.hby.kevers[ctrl]
verfer = rkever.verfers[0]

rkever = self.hby.kevers[ctrl]
pubkey = pysodium.crypto_sign_pk_to_box_pk(rkever.verfers[0].raw)
raw = pysodium.crypto_box_seal(bytes(ims), pubkey)
pubkey = pysodium.crypto_sign_pk_to_box_pk(verfer.raw)
raw = pysodium.crypto_box_seal(bytes(msg), pubkey)

texter = coring.Texter(raw=raw)
diger = coring.Diger(ser=raw, code=MtrDex.Blake3_256)
essr, _ = exchanging.exchange(route='/essr/req', sender=hab.pre, diger=diger,
modifiers=dict(src=hab.pre, dest=ctrl))

ims = hab.endorse(serder=essr, pipelined=False)
ims.extend(Counter(Codens.ESSRPayloadGroup, count=1,
gvrsn=kering.Vrsn_1_0).qb64b)
Expand Down Expand Up @@ -396,11 +413,20 @@ def forward(self, hab, ends, msg, topic):
# If we are one of the mailboxes, just store locally in mailbox
owits = oset(ends.keys())
if self.mbx and owits.intersection(hab.prefixes):
# Remove again if ESSR mode
if self.essr:
_tag = self.hby.psr.extract(msg, payloading.PayloadTyper)
_pre = self.hby.psr.extract(msg, coring.Prefixer)
_pad = self.hby.psr.extract(msg, coring.Bexter)
self.mbx.storeMsg(topic=f"{self.recp}/{topic}".encode("utf-8"), msg=msg)
return []

# Its not us, randomly select a mailbox and forward it on
mbx, mailbox = random.choice(list(ends.items()))

if self.essr:
msg = self._essrWrapper(hab, msg, mbx)

ims = bytearray()
ims.extend(introduce(hab, mbx))
ims.extend(msg)
Expand Down
111 changes: 0 additions & 111 deletions src/keri/core/coring.py
Original file line number Diff line number Diff line change
Expand Up @@ -2543,117 +2543,6 @@ def b64ToVer(b64, *, texted=False):
return Versionage(major=b64ToInt(b64[0]), minor=b64ToInt(b64[1:3]))



# Trust Spanning Protocol protocol packet (message) types
Tspage = namedtuple("Tspage", 'HOP REL SCS')

Tsps = Tspage(HOP='HOP', REL='REL', SCS='SCS')


class Tsper(Tagger):
"""
Tsper is subclass of Tagger, cryptographic material, for formatted
message types (tsps) in Base64. Leverages Tagger support compact special
fixed size primitives with non-empty soft part and empty raw part.

Tsper provides a more compact representation than would be obtained by
converting the raw ASCII representation to Base64.

Attributes:

Inherited Properties: (See Tagger)
code (str): hard part of derivation code to indicate cypher suite
hard (str): hard part of derivation code. alias for code
soft (str): soft part of derivation code fs any.
Empty when ss = 0.
both (str): hard + soft parts of full text code
size (int | None): Number of quadlets/triplets of chars/bytes including
lead bytes of variable sized material (fs = None).
Converted value of the soft part (of len ss) of full
derivation code.
Otherwise None when not variably sized (fs != None)
fullSize (int): full size of primitive
raw (bytes): crypto material only. Not derivation code or lead bytes.
qb64 (str): Base64 fully qualified with derivation code + crypto mat
qb64b (bytes): Base64 fully qualified with derivation code + crypto mat
qb2 (bytes): binary with derivation code + crypto material
transferable (bool): True means transferable derivation code False otherwise
digestive (bool): True means digest derivation code False otherwise
prefixive (bool): True means identifier prefix derivation code False otherwise
special (bool): True when soft is special raw is empty and fixed size
composable (bool): True when .qb64b and .qb2 are 24 bit aligned and round trip
tag (str): B64 primitive without prepad (strips prepad from soft)


Properties:
tsp (str): message type from Tsps of Tspage

Inherited Hidden: (See Tagger)
_code (str): value for .code property
_soft (str): soft value of full code
_raw (bytes): value for .raw property
_rawSize():
_leadSize():
_special():
_infil(): creates qb64b from .raw and .code (fully qualified Base64)
_binfil(): creates qb2 from .raw and .code (fully qualified Base2)
_exfil(): extracts .code and .raw from qb64b (fully qualified Base64)
_bexfil(): extracts .code and .raw from qb2 (fully qualified Base2)

Hidden:


Methods:

"""


def __init__(self, qb64b=None, qb64=None, qb2=None, tag='', tsp='', **kwa):
"""
Inherited Parameters: (see Tagger)
raw (bytes | bytearray | None): unqualified crypto material usable
for crypto operations.
code (str): stable (hard) part of derivation code
soft (str | bytes): soft part for special codes
rize (int | None): raw size in bytes when variable sized material not
including lead bytes if any
Otherwise None
qb64b (bytes | None): fully qualified crypto material Base64
qb64 (str | bytes | None): fully qualified crypto material Base64
qb2 (bytes | None): fully qualified crypto material Base2
strip (bool): True means strip (delete) matter from input stream
bytearray after parsing qb64b or qb2. False means do not strip
tag (str | bytes): Base64 plain. Prepad is added as needed.

Parameters:
tsp (str): message type from Tsps of Tspage

"""
if not (qb64b or qb64 or qb2):
if tsp:
tag = tsp

super(Tsper, self).__init__(qb64b=qb64b, qb64=qb64, qb2=qb2, tag=tag, **kwa)

if self.code not in (MtrDex.Tag3, ):
raise InvalidCodeError(f"Invalid code={self.code} for Tsper "
f"{self.tsp=}.")
if self.tsp not in Tsps:
raise InvalidSoftError(f"Invalid tsp={self.tsp} for Tsper.")



@property
def tsp(self):
"""Returns:
tag (str): B64 primitive without prepad (strips prepad from soft)

Alias for self.tag

"""
return self.tag


class Texter(Matter):
"""
Texter is subclass of Matter, cryptographic material, for variable length
Expand Down
6 changes: 6 additions & 0 deletions src/keri/spac/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- encoding: utf-8 -*-
"""
KERI
keri.spac Package
"""

116 changes: 116 additions & 0 deletions src/keri/spac/payloading.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# -*- encoding: utf-8 -*-
"""
keri.spac.payloading module

"""

from collections import namedtuple

from keri.core import coring, MtrDex
from keri.kering import InvalidSoftError, InvalidCodeError


PayloadTypage = namedtuple("PayloadTypage", 'HOP RFI RFA RFD SCS')

PayloadTypes = PayloadTypage(HOP='HOP', RFI='RFI', RFA='RFA', RFD='RFD', SCS='SCS')


class PayloadTyper(coring.Tagger):
"""
PayloadTyper is subclass of Tagger, cryptographic material, for formatted
message types (PayloadTypes) in Base64. Leverages Tagger support compact special
fixed size primitives with non-empty soft part and empty raw part.

PayloadTyper provides a more compact representation than would be obtained by
converting the raw ASCII representation to Base64.

Attributes:

Inherited Properties: (See Tagger)
code (str): hard part of derivation code to indicate cypher suite
hard (str): hard part of derivation code. alias for code
soft (str): soft part of derivation code fs any.
Empty when ss = 0.
both (str): hard + soft parts of full text code
size (int | None): Number of quadlets/triplets of chars/bytes including
lead bytes of variable sized material (fs = None).
Converted value of the soft part (of len ss) of full
derivation code.
Otherwise None when not variably sized (fs != None)
fullSize (int): full size of primitive
raw (bytes): crypto material only. Not derivation code or lead bytes.
qb64 (str): Base64 fully qualified with derivation code + crypto mat
qb64b (bytes): Base64 fully qualified with derivation code + crypto mat
qb2 (bytes): binary with derivation code + crypto material
transferable (bool): True means transferable derivation code False otherwise
digestive (bool): True means digest derivation code False otherwise
prefixive (bool): True means identifier prefix derivation code False otherwise
special (bool): True when soft is special raw is empty and fixed size
composable (bool): True when .qb64b and .qb2 are 24 bit aligned and round trip
tag (str): B64 primitive without prepad (strips prepad from soft)


Properties:
type (str): message type from PayloadTypes of PayloadTypage

Inherited Hidden: (See Tagger)
_code (str): value for .code property
_soft (str): soft value of full code
_raw (bytes): value for .raw property
_rawSize():
_leadSize():
_special():
_infil(): creates qb64b from .raw and .code (fully qualified Base64)
_binfil(): creates qb2 from .raw and .code (fully qualified Base2)
_exfil(): extracts .code and .raw from qb64b (fully qualified Base64)
_bexfil(): extracts .code and .raw from qb2 (fully qualified Base2)

Hidden:


Methods:

"""

def __init__(self, qb64b=None, qb64=None, qb2=None, tag='', type='', **kwa):
"""
Inherited Parameters: (see Tagger)
raw (bytes | bytearray | None): unqualified crypto material usable
for crypto operations.
code (str): stable (hard) part of derivation code
soft (str | bytes): soft part for special codes
rize (int | None): raw size in bytes when variable sized material not
including lead bytes if any
Otherwise None
qb64b (bytes | None): fully qualified crypto material Base64
qb64 (str | bytes | None): fully qualified crypto material Base64
qb2 (bytes | None): fully qualified crypto material Base2
strip (bool): True means strip (delete) matter from input stream
bytearray after parsing qb64b or qb2. False means do not strip
tag (str | bytes): Base64 plain. Prepad is added as needed.

Parameters:
tsp (str): message type from Tsps of Tspage

"""
if not (qb64b or qb64 or qb2):
if type:
tag = type

super(PayloadTyper, self).__init__(qb64b=qb64b, qb64=qb64, qb2=qb2, tag=tag, **kwa)

if self.code not in (MtrDex.Tag3, ):
raise InvalidCodeError(f"Invalid code={self.code} for Tsper "
f"{self.type=}.")
if self.type not in PayloadTypes:
raise InvalidSoftError(f"Invalid tsp={self.type} for Tsper.")

@property
def type(self):
"""Returns:
tag (str): B64 primitive without prepad (strips prepad from soft)

Alias for self.tag

"""
return self.tag
Loading
Loading