blerpc-protocol 0.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ Copyright 2026 tdaira
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: blerpc-protocol
3
+ Version: 0.8.0
4
+ Summary: Container and command protocol layers for BLE RPC
5
+ License-Expression: Apache-2.0
6
+ Project-URL: Homepage, https://github.com/tdaira/blerpc-protocol
7
+ Project-URL: Repository, https://github.com/tdaira/blerpc-protocol
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ Classifier: Topic :: System :: Networking
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: cryptography>=41.0
20
+ Dynamic: license-file
21
+
22
+ # blerpc-protocol
23
+
24
+ BLE RPC protocol library for Python and C.
25
+
26
+ Part of the [bleRPC](https://blerpc.net) project.
27
+
28
+ ## Overview
29
+
30
+ Python and C implementation of the bleRPC binary protocol:
31
+
32
+ - Container fragmentation and reassembly with MTU-aware splitting
33
+ - Command packet encoding/decoding with protobuf payload support
34
+ - Control messages (timeout, stream end, capabilities, error)
35
+ - **Encryption layer** — E2E encryption with X25519 key exchange, Ed25519 signatures, and AES-128-GCM
36
+
37
+ The Python and C implementations are fully compatible and share the same wire format.
38
+
39
+ ## Installation
40
+
41
+ ```
42
+ pip install blerpc-protocol
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ ```python
48
+ from blerpc_protocol import ContainerSplitter, ContainerAssembler, CommandPacket, CommandType
49
+
50
+ # Encode a command
51
+ packet = CommandPacket(CommandType.REQUEST, "Echo", protobuf_bytes)
52
+ payload = packet.serialize()
53
+
54
+ # Split into BLE-sized containers
55
+ splitter = ContainerSplitter(mtu=247)
56
+ containers = splitter.split(payload)
57
+
58
+ # Send containers over BLE, then reassemble on the other side
59
+ assembler = ContainerAssembler()
60
+ for container in received_containers:
61
+ result = assembler.feed(container)
62
+ if result is not None:
63
+ response = CommandPacket.deserialize(result)
64
+ ```
65
+
66
+ ## Encryption
67
+
68
+ The library provides E2E encryption using a 4-step key exchange protocol (X25519 ECDH + Ed25519 signatures) and AES-128-GCM session encryption.
69
+
70
+ ```python
71
+ from blerpc_protocol.crypto import central_perform_key_exchange, BlerpcCryptoSession
72
+
73
+ # Perform key exchange (central side)
74
+ session = await central_perform_key_exchange(send=ble_send, receive=ble_receive)
75
+
76
+ # Encrypt outgoing commands
77
+ ciphertext = session.encrypt(plaintext)
78
+
79
+ # Decrypt incoming commands
80
+ plaintext = session.decrypt(ciphertext)
81
+ ```
82
+
83
+ ## C Library
84
+
85
+ The C implementation is a Zephyr module with zero external dependencies. Add it to your `west.yml` manifest:
86
+
87
+ ```yaml
88
+ - name: blerpc-protocol
89
+ url: https://github.com/tdaira/blerpc-protocol
90
+ revision: main
91
+ path: modules/lib/blerpc-protocol
92
+ ```
93
+
94
+ Headers are in `c/include/blerpc_protocol/`. See [container.h](c/include/blerpc_protocol/container.h), [command.h](c/include/blerpc_protocol/command.h), and [crypto.h](c/include/blerpc_protocol/crypto.h) for the API.
95
+
96
+ ## Requirements
97
+
98
+ - Python 3.11+
99
+
100
+ ## License
101
+
102
+ [Apache-2.0](LICENSE)
@@ -0,0 +1,81 @@
1
+ # blerpc-protocol
2
+
3
+ BLE RPC protocol library for Python and C.
4
+
5
+ Part of the [bleRPC](https://blerpc.net) project.
6
+
7
+ ## Overview
8
+
9
+ Python and C implementation of the bleRPC binary protocol:
10
+
11
+ - Container fragmentation and reassembly with MTU-aware splitting
12
+ - Command packet encoding/decoding with protobuf payload support
13
+ - Control messages (timeout, stream end, capabilities, error)
14
+ - **Encryption layer** — E2E encryption with X25519 key exchange, Ed25519 signatures, and AES-128-GCM
15
+
16
+ The Python and C implementations are fully compatible and share the same wire format.
17
+
18
+ ## Installation
19
+
20
+ ```
21
+ pip install blerpc-protocol
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ```python
27
+ from blerpc_protocol import ContainerSplitter, ContainerAssembler, CommandPacket, CommandType
28
+
29
+ # Encode a command
30
+ packet = CommandPacket(CommandType.REQUEST, "Echo", protobuf_bytes)
31
+ payload = packet.serialize()
32
+
33
+ # Split into BLE-sized containers
34
+ splitter = ContainerSplitter(mtu=247)
35
+ containers = splitter.split(payload)
36
+
37
+ # Send containers over BLE, then reassemble on the other side
38
+ assembler = ContainerAssembler()
39
+ for container in received_containers:
40
+ result = assembler.feed(container)
41
+ if result is not None:
42
+ response = CommandPacket.deserialize(result)
43
+ ```
44
+
45
+ ## Encryption
46
+
47
+ The library provides E2E encryption using a 4-step key exchange protocol (X25519 ECDH + Ed25519 signatures) and AES-128-GCM session encryption.
48
+
49
+ ```python
50
+ from blerpc_protocol.crypto import central_perform_key_exchange, BlerpcCryptoSession
51
+
52
+ # Perform key exchange (central side)
53
+ session = await central_perform_key_exchange(send=ble_send, receive=ble_receive)
54
+
55
+ # Encrypt outgoing commands
56
+ ciphertext = session.encrypt(plaintext)
57
+
58
+ # Decrypt incoming commands
59
+ plaintext = session.decrypt(ciphertext)
60
+ ```
61
+
62
+ ## C Library
63
+
64
+ The C implementation is a Zephyr module with zero external dependencies. Add it to your `west.yml` manifest:
65
+
66
+ ```yaml
67
+ - name: blerpc-protocol
68
+ url: https://github.com/tdaira/blerpc-protocol
69
+ revision: main
70
+ path: modules/lib/blerpc-protocol
71
+ ```
72
+
73
+ Headers are in `c/include/blerpc_protocol/`. See [container.h](c/include/blerpc_protocol/container.h), [command.h](c/include/blerpc_protocol/command.h), and [crypto.h](c/include/blerpc_protocol/crypto.h) for the API.
74
+
75
+ ## Requirements
76
+
77
+ - Python 3.11+
78
+
79
+ ## License
80
+
81
+ [Apache-2.0](LICENSE)
@@ -0,0 +1,41 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "blerpc-protocol"
7
+ version = "0.8.0"
8
+ description = "Container and command protocol layers for BLE RPC"
9
+ license = "Apache-2.0"
10
+ readme = "README.md"
11
+ requires-python = ">=3.11"
12
+ dependencies = [
13
+ "cryptography>=41.0",
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Topic :: Software Development :: Libraries",
23
+ "Topic :: System :: Networking",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/tdaira/blerpc-protocol"
28
+ Repository = "https://github.com/tdaira/blerpc-protocol"
29
+
30
+ [tool.setuptools.packages.find]
31
+ where = ["src"]
32
+
33
+ [tool.ruff]
34
+ line-length = 88
35
+ target-version = "py311"
36
+
37
+ [tool.ruff.lint]
38
+ select = ["E", "F", "W", "I"]
39
+
40
+ [tool.pytest.ini_options]
41
+ testpaths = ["tests/python"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,62 @@
1
+ """blerpc-protocol: Container and command protocol layers for BLE RPC."""
2
+
3
+ from blerpc_protocol.command import CommandPacket, CommandType
4
+ from blerpc_protocol.container import (
5
+ ATT_OVERHEAD,
6
+ CAPABILITY_FLAG_ENCRYPTION_SUPPORTED,
7
+ CONTROL_HEADER_SIZE,
8
+ FIRST_HEADER_SIZE,
9
+ SUBSEQUENT_HEADER_SIZE,
10
+ Container,
11
+ ContainerAssembler,
12
+ ContainerSplitter,
13
+ ContainerType,
14
+ ControlCmd,
15
+ make_capabilities_request,
16
+ make_capabilities_response,
17
+ make_error_response,
18
+ make_key_exchange,
19
+ make_stream_end_c2p,
20
+ make_stream_end_p2c,
21
+ make_timeout_request,
22
+ make_timeout_response,
23
+ )
24
+ from blerpc_protocol.crypto import (
25
+ BlerpcCrypto,
26
+ BlerpcCryptoSession,
27
+ CentralKeyExchange,
28
+ KnownKeyStore,
29
+ PeripheralKeyExchange,
30
+ central_perform_key_exchange,
31
+ tofu_verify,
32
+ )
33
+
34
+ __all__ = [
35
+ "ATT_OVERHEAD",
36
+ "BlerpcCrypto",
37
+ "BlerpcCryptoSession",
38
+ "CAPABILITY_FLAG_ENCRYPTION_SUPPORTED",
39
+ "CentralKeyExchange",
40
+ "CONTROL_HEADER_SIZE",
41
+ "FIRST_HEADER_SIZE",
42
+ "SUBSEQUENT_HEADER_SIZE",
43
+ "CommandPacket",
44
+ "CommandType",
45
+ "Container",
46
+ "ContainerAssembler",
47
+ "ContainerSplitter",
48
+ "ContainerType",
49
+ "ControlCmd",
50
+ "make_capabilities_request",
51
+ "make_capabilities_response",
52
+ "make_error_response",
53
+ "make_key_exchange",
54
+ "make_stream_end_c2p",
55
+ "make_stream_end_p2c",
56
+ "make_timeout_request",
57
+ "make_timeout_response",
58
+ "PeripheralKeyExchange",
59
+ "KnownKeyStore",
60
+ "central_perform_key_exchange",
61
+ "tofu_verify",
62
+ ]
@@ -0,0 +1,76 @@
1
+ """Command encode/decode layer for blerpc.
2
+
3
+ Command format (bits):
4
+ | type(1) | reserved(7) | cmd_name_len(8) | cmd_name(N*8) |
5
+ | data_len(16) | data(data_len*8) |
6
+
7
+ - type: 0=request, 1=response
8
+ - cmd_name: ASCII command name
9
+ - data_len: little-endian uint16
10
+ - data: protobuf-encoded bytes
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import struct
16
+ from dataclasses import dataclass
17
+ from enum import IntEnum
18
+
19
+
20
+ class CommandType(IntEnum):
21
+ REQUEST = 0
22
+ RESPONSE = 1
23
+
24
+
25
+ @dataclass
26
+ class CommandPacket:
27
+ """A single command packet."""
28
+
29
+ cmd_type: CommandType
30
+ cmd_name: str
31
+ data: bytes = b""
32
+
33
+ def serialize(self) -> bytes:
34
+ """Serialize command to bytes."""
35
+ name_bytes = self.cmd_name.encode("ascii")
36
+ if len(name_bytes) > 255:
37
+ raise ValueError(f"cmd_name too long: {len(name_bytes)} > 255")
38
+ if len(self.data) > 65535:
39
+ raise ValueError(f"data too long: {len(self.data)} > 65535")
40
+
41
+ # Byte 0: type in MSB (bit 7), reserved bits 6-0 = 0
42
+ byte0 = (self.cmd_type & 0x01) << 7
43
+ return (
44
+ bytes([byte0])
45
+ + struct.pack("<B", len(name_bytes))
46
+ + name_bytes
47
+ + struct.pack("<H", len(self.data))
48
+ + self.data
49
+ )
50
+
51
+ @staticmethod
52
+ def deserialize(data: bytes) -> CommandPacket:
53
+ """Deserialize bytes into a CommandPacket."""
54
+ if len(data) < 2:
55
+ raise ValueError(f"Command packet too short: {len(data)} bytes")
56
+
57
+ # Byte 0: type in MSB
58
+ cmd_type = CommandType((data[0] >> 7) & 0x01)
59
+ cmd_name_len = data[1]
60
+
61
+ offset = 2
62
+ if len(data) < offset + cmd_name_len + 2:
63
+ raise ValueError("Command packet truncated")
64
+
65
+ cmd_name = data[offset : offset + cmd_name_len].decode("ascii")
66
+ offset += cmd_name_len
67
+
68
+ data_len = struct.unpack_from("<H", data, offset)[0]
69
+ offset += 2
70
+
71
+ payload = data[offset : offset + data_len]
72
+ return CommandPacket(
73
+ cmd_type=cmd_type,
74
+ cmd_name=cmd_name,
75
+ data=payload,
76
+ )