Metadata-Version: 2.5
Name: pamoja-session
Version: 0.2.0
Summary: X25519 key agreement, HKDF, and ChaCha20-Poly1305 with an anti-replay window, with no TLS stack.
Project-URL: Repository, https://github.com/molexxxx/pamoja
Project-URL: Documentation, https://pamoja.molex.cloud/docs/guides/session.html
Author: molexxxx
License: MIT
License-File: LICENSE-MIT
Keywords: iot,pamoja,robotics,session
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: pamoja-native==0.2.0
Description-Content-Type: text/markdown

# pamoja-session

X25519 key agreement, HKDF, and ChaCha20-Poly1305 with an anti-replay window, with no TLS stack. One capability of [pamoja](https://github.com/molexxxx/pamoja), one memory-safe Rust core with bindings for TypeScript, Python, and C#.

[![read the guide](https://raw.githubusercontent.com/molexxxx/pamoja/main/.github/badges/btn-guide.svg)](https://pamoja.molex.cloud/docs/guides/session.html)
[![documentation](https://raw.githubusercontent.com/molexxxx/pamoja/main/.github/badges/btn-docs.svg)](https://pamoja.molex.cloud/docs/)
[![API reference](https://raw.githubusercontent.com/molexxxx/pamoja/main/.github/badges/btn-api.svg)](https://pamoja.molex.cloud/docs/reference/python/pamoja/session.html)

## Install

```sh
pip install pamoja-session
```

```python
from pamoja import session
```

This pulls in `pamoja-native`, the compiled engine. `pip install pamoja` is the whole framework in one package.

## Example

The script the test suite runs, spliced here as it ran.

From [`bindings/python/guides/session.py`](https://github.com/molexxxx/pamoja/blob/main/bindings/python/guides/session.py):

```python
import os

from pamoja.core import PamojaError
from pamoja.session import AgreementKey, Role, Session

# Each device is provisioned with a 32-byte seed and publishes the key it derives. A real
# seed comes from the factory or a secure element; any 32 bytes stand in here.
node = AgreementKey(bytes([7]) * 32)
gateway = AgreementKey(bytes([9]) * 32)

# Neither side sends the session key. Both derive it from the shared secret, a salt that
# travels in the clear, and both public keys, with opposite roles.
#
# The salt must be fresh for every session: reusing one derives the same key from the same
# pair of devices twice. The initiator draws it and sends it in the clear, so the responder
# uses the salt it received rather than one of its own.
salt = os.urandom(16)
uplink = Session(node, gateway.public_key, salt, Role.INITIATOR)
downlink = Session(gateway, node.public_key, salt, Role.RESPONDER)
print("agreed    both sides derived a key without sending one")

# The pump id is authenticated but not encrypted, so a router still reads it while any
# change to it fails the tag.
sealed = uplink.seal(b"flow=41.2", b"pump-3")
hidden = "still" if sealed.ciphertext == b"flow=41.2" else "no longer"
print(f"sealed    counter {sealed.counter}, and what goes on the wire is {hidden} the reading")
print(f"opened    {downlink.open(sealed, b'pump-3').decode()}")

# The anti-replay window refuses a counter it has already accepted, so a frame captured
# off the air and sent again is not delivered a second time.
try:
    downlink.open(sealed, b"pump-3")
    print("a replayed frame was accepted, which should never happen")
except PamojaError as error:
    print(f"replay    refused: {error}")

# A router that rewrites the pump id breaks the tag, so the gateway refuses the frame rather
# than file the reading under the wrong pump. A frame that fails to open leaves its counter
# unused.
later = uplink.seal(b"flow=41.3", b"pump-3")
try:
    downlink.open(later, b"pump-4")
    print("a rewritten pump id was accepted, which should never happen")
except PamojaError as error:
    print(f"altered   refused: {error}")

# Radio frames can arrive out of order. The window accepts any counter it has not seen
# among the 64 below the newest, so the frame that was held up still opens.
newest = uplink.seal(b"flow=41.5", b"pump-3")
first = downlink.open(newest, b"pump-3").decode()
second = downlink.open(later, b"pump-3").decode()
print(f"late      counter {newest.counter} opened first, then counter {later.counter}: {first}, then {second}")

# The gateway answers on the same session. Its frames carry the other direction in their
# nonce, so a reply can never be taken for, or replayed as, one from the node.
order = downlink.seal(b"valve=close", b"pump-3")
print(f"reply     {uplink.open(order, b'pump-3').decode()}, sealed by the gateway and opened by the node")
```

## The same capability in every language

| Language | Package | Reference |
| --- | --- | --- |
| Rust | [`pamoja-session`](https://crates.io/crates/pamoja-session) | [reference](https://pamoja.molex.cloud/docs/reference/rust/pamoja_session/index.html), [docs.rs](https://docs.rs/pamoja-session), [install](https://pamoja.molex.cloud/docs/reference/rust.html#rust-session) |
| TypeScript | [`@pamoja/session`](https://www.npmjs.com/package/@pamoja/session) | [reference](https://pamoja.molex.cloud/docs/reference/node/modules/_pamoja_session.html), [install](https://pamoja.molex.cloud/docs/reference/node.html#node-session) |
| Python | [`pamoja-session`](https://pypi.org/project/pamoja-session/) | [reference](https://pamoja.molex.cloud/docs/reference/python/pamoja/session.html), [install](https://pamoja.molex.cloud/docs/reference/python.html#python-session) |
| C# | [`Pamoja.Session`](https://www.nuget.org/packages/Pamoja.Session) | [reference](https://pamoja.molex.cloud/docs/reference/dotnet/api/Pamoja.Session.html), [install](https://pamoja.molex.cloud/docs/reference/dotnet.html#dotnet-session) |

## Documentation

- [`pamoja.session` reference](https://pamoja.molex.cloud/docs/reference/python/pamoja/session.html), every class and function in this module.
- [The Secured session guide](https://pamoja.molex.cloud/docs/guides/session.html), with the same example in Rust, TypeScript, and C#.
- [Every capability](https://pamoja.molex.cloud/docs/), and the [install page](https://pamoja.molex.cloud/docs/install.html).

## License

MIT
