Skip to content
Merged
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ BRC | Standard
174 | [Consensus-Unique Name Tokens — Identity Binding and Verified Resolution](./tokens/0174.md)
176 | [BSV-21 — Validity Proofs](./tokens/0176.md)
177 | [Wallet-Enforced Expiry for `noSend` Actions](./wallet/0177.md)
188 | [Encrypted Group Messaging over BRC-78](./peer-to-peer/0188.md)
190 | [Access Gates for Metanet Rooms](./apps/0190.md)
210 | [Derived Collectibles](./apps/0210.md)
218 | [Chat-Native Command Grammar for the Metanet](./apps/0218.md)
Expand Down
1 change: 1 addition & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@
* [Single-Use Signed Proofs for Request Authentication](./peer-to-peer/0138.md)
* [Fountain-Coded Air-Gap Transport for Arbitrary Payloads](./peer-to-peer/0141.md)
* [Universal Handle Addressing and Resolution for the Metanet](./peer-to-peer/0169.md)
* [Encrypted Group Messaging over BRC-78](./peer-to-peer/0188.md)
* [Animated-QR Air-Gap Transport for Arbitrary Payloads (TKQR1)](./peer-to-peer/0225.md)
* [Keyed Content and Conditional Key Release](./peer-to-peer/0369.md)

Expand Down
66 changes: 66 additions & 0 deletions peer-to-peer/0188.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# BRC-188: Encrypted Group Messaging over BRC-78

**Authors:** Luke Rohenaz; contributors Kurt Wuckert Jr., David Case, Michael Boyd, and Dan Wagner. **Status: Draft request for comments.**

## Abstract

This document defines one interoperable v1 baseline: pairwise fan-out using unmodified BRC-78. A sender signs one canonical group-content frame and encrypts identical bytes separately to every other member. The former sender-key/ratchet Profile B is informative future work only; v1 accepts no such packets and has no fallback negotiation. This reuses deployed primitives while stating static-identity forward-secrecy and post-compromise limits explicitly.

## Normative baseline

The key words MUST, MUST NOT, SHOULD and MAY are interpreted as in RFC 2119. In the formulas, `||` denotes byte concatenation, `BGM1` is ASCII `42 47 4d 31`, and `\0` denotes one terminal byte `00`, never a literal backslash and digit. Roster sorting is lexicographic over the 33 encoded key bytes. Parsers MUST enforce the following rules. Integers are unsigned big-endian. Fixed fields have exactly their stated length; parsers reject trailing bytes, duplicate members, invalid UTF-8, malformed compressed secp256k1 points, signatures other than 64-byte `r||s` with `1<=r<n` and `1<=s<=n/2`, and frames over 1 MiB. A roster has 1–256 sorted unique compressed public keys. Signatures verify `SHA256(domain ASCII including its terminal NUL || unsigned frame)`. Metadata is opaque display UTF-8, at most 4096 bytes; it is not canonical JSON or an authority statement.

### Genesis

The unsigned genesis `G` is `BGM1 || 0x01 || creatorKey[33] || nonce[32] || count:u16 || memberKeys[count*33] || metadataLength:u32 || metadata`. The creator MUST be in the roster. Append the creator signature under domain `BSV-GROUP-GENESIS\0`; `groupID = SHA256(G)` and epoch 0 hash equals `groupID`. The creator is the sole immutable administrator in v1: there is no delegation, rotation, or silent migration after loss or compromise. Invitation requires an authenticated expected creator identity and explicit user acceptance of this group ID; self-signed unknown creators are never trusted automatically. Retain the complete signed genesis.

### Epochs

The unsigned epoch `E` is `BGM1 || 0x02 || groupID[32] || epoch:u32 || previousEpochHash[32] || count:u16 || memberKeys || metadataLength:u32 || metadata`; append a creator signature under `BSV-GROUP-EPOCH\0`, and hash the unsigned bytes. Epoch `k>0` MUST follow exactly `k-1` and the pinned creator; the complete roster and metadata replace the prior snapshot. The creator cannot be removed. A duplicate hash is idempotent. Gaps buffer within implementation limits or report sync-needed and never skip. Two valid hashes for one epoch freeze the durable group, retain both proofs, and never choose by first-seen order. Newer acceptance stops old-epoch live sends and accepts; old frames are historical-only and rejected by default. This detects equivocation only after conflicting records meet and cannot prove a global latest head; freshness, censorship, and stale offline views remain application responsibilities. Pending or known bans pause sending until a matching creator epoch is accepted. BRC-190 room gates are policy input to creator epochs, never consensus or packet keys.

### Content

The unsigned content frame `M` is `BGM1 || 0x10 || groupID[32] || epoch:u32 || epochHash[32] || sender[33] || messageID[16] || contentType:u8 || bodyLength:u32 || body`; append a sender identity signature under `BSV-GROUP-CONTENT\0`. Type 1 is UTF-8 text; type 2 is opaque application bytes; all other types reject. The message ID is CSPRNG-generated once per logical message. Encrypt the identical signed bytes separately with unmodified BRC-78 to each other roster member; the sender stores a local copy and never sends to non-members. Accept only when BRC-78 sender equals `sender`, its recipient is local, both keys are in the accepted epoch, and epoch/hash is exactly the current live epoch. Retries resend identical signed bytes, though each outer BRC-78 wrapper may be fresh. Deduplicate for the group lifetime by `(groupID,sender,messageID)`; different unsigned content bytes authenticated by a valid signature under that key are equivocation and is rejected/reported. No cross-sender total order exists.

Control records (genesis and epochs) travel inside normal BRC-78 and may be forwarded; signatures still bind the pinned creator and membership is checked before join. BRC-78 shared-key authentication is not public authorship proof. Authorization uses cryptographic identity keys only; BAP, social, room, and labels are external associations.

BRC-78 interoperation is pinned to version bytes `42 42 10 33` and invoice `2-message encryption-` plus base64(keyID32), matching [EncryptedMessage.ts at 4090d4b](https://github.com/bsv-blockchain/ts-sdk/blob/4090d4ba0b42440a9bbe4869ee594aed2c91ab33/src/messages/EncryptedMessage.ts). The reversed byte example in BRC-78 prose is not used here.

## Security and scope

Member roster history is encrypted in transit but visible to members; transport leaks routing and fan-out. Removing a member stops local old-epoch sends/accepts but cannot revoke delivered plaintext or stale/offline peers; synchronize the complete authenticated chain before resuming after restore. Static identities mean recorded BRC-78 ciphertext can become readable after identity-key compromise. No blanket forward-secrecy or post-compromise guarantee is claimed. MLS and a sender-key ratchet offer different tradeoffs and remain future extensions. Production deployment requires application-specific security and recovery review; those implementation activities are outside this proposal.

## Acceptance state and recovery

A participant MUST retain the accepted signed genesis, all accepted signed epoch records, their unsigned hashes, and its highest accepted epoch durably. Genesis acceptance is an explicit trust decision, outside decryption: a valid signature under an unknown creator key does not make a group trusted. A joining member MUST authenticate the expected creator and accept the specific group ID, then verify the complete signed chain through its joining epoch. Forwarded control records are allowed, but their outer BRC-78 sender does not replace the creator signature. Control records MUST be encrypted only for their intended existing member or joining recipient; former members are not entitled to new control delivery.

A creator MUST serialize and durably record epoch updates before distribution. The uint32 epoch MUST NOT wrap; exhaustion requires a new group. On uncertain restore, a creator MUST NOT issue more epochs for the old group until it can establish its prior history without reusing an epoch. Loss or compromise of the immutable administrator requires a new group with explicit acceptance; neither BAP rotation nor a room label silently replaces that key.

A received update must have the group's ID and the pinned creator's valid signature. For an unseen epoch, accept only the next epoch with the current hash as predecessor. A gap reports synchronization required (bounded buffering is optional); it never authorizes skipping records. A byte-identical unsigned hash at an already accepted epoch is idempotent. A different structurally valid, creator-signed record for any already accepted epoch MUST durably freeze the group, including when that older fork is discovered after later epochs. Retain both signed records as evidence and stop sending and live acceptance. No automatic tie-break or branch selection is specified. A malicious creator can keep partitions apart; fork detection is not consensus or prevention of equivocation.

For content, a future epoch requires synchronization; a stale epoch or wrong current epoch hash is rejected for live use. Applications MAY offer separate historical display after validating the corresponding old roster and signatures, but MUST NOT label those packets as newly accepted live messages or rewind authority. The receiver's local identity and the sender must both be members of the packet's authenticated epoch, with outer sender/recipient bindings checked as specified above.

Deduplication state MUST be durable for the group lifetime. A receiver treats a repeated `(groupID, sender, messageID)` with the same unsigned content bytes as a duplicate; different unsigned content bytes authenticated by a valid signature under that tuple are sender equivocation and MUST be rejected and reported. If storage or synchronization limits prevent retaining the required history or deduplication state, report an explicit incomplete/limit condition and pause live operation rather than silently forgetting replay protection. Message IDs are freshly generated 16-byte CSPRNG values, retained for retries of the same signed packet.

After offline operation or restore, synchronize before resuming. Even a complete authenticated chain cannot prove it is the globally newest chain: a relay can withhold a newer revocation. Accepting a new epoch enforces removal locally, not instantaneously across stale peers. Known pending bans pause sending until a corresponding creator-signed roster update is accepted. An application requiring a stronger online freshness guarantee needs an explicit additional service/trust policy; v1 makes no such guarantee.

## Design tradeoffs and extensions

This baseline has one public administrator key, explicit signed membership snapshots and one BRC-78 envelope per recipient. The inner frame gives all recipients identical publicly verifiable author bytes; outer encryption alone would let a shared-key recipient construct a transcript. Per-recipient delivery costs grow with roster size, bounded here to 256 members.

A future sender-key extension could reduce message fan-out but would need a separate specification for key distribution, nonce allocation, counters, skipped messages, rotation and compromise recovery. No sender-key packets, ratchet Profile B or heuristic negotiation are accepted in v1. [MLS (RFC 9420)](https://www.rfc-editor.org/rfc/rfc9420) provides a different group key-management design with forward-secrecy and post-compromise properties under its assumptions; v1 deliberately reuses BRC-78 and does not claim those properties. [BRC-186](https://github.com/opldotdev/BRCs/blob/codex/brc-186-bap/peer-to-peer/0186.md) identities and [BRC-190](../apps/0190.md) gates can inform creator policy, but do not change the cryptographic authorization rules here.

## Conformance fixtures

[Signed examples](./media/0188-vectors.json) contain exact genesis, add-member,
remove-member, metadata-update and content frames, with unsigned hashes and
expected identities. These illustrate the inner wire format; BRC-78 defines
the outer encryption. Keys 1, 2 and 3 are public synthetic example keys and
MUST NEVER be used in production. The five example signatures were checked
with `@bsv/sdk` 2.4.2; this is not a security-audit or deployed-client claim.

## Implementations

No complete implementation of this group protocol is claimed. This is a new
proposal built on BRC-78; clients and transport integrations belong in their
respective implementation repositories.
1 change: 1 addition & 0 deletions peer-to-peer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ BRC | Standard
138 | [Single-Use Signed Proofs for Request Authentication](./0138.md)
141 | [Fountain-Coded Air-Gap Transport for Arbitrary Payloads](./0141.md)
169 | [Universal Handle Addressing and Resolution for the Metanet](./0169.md)
188 | [Encrypted Group Messaging over BRC-78](./0188.md)
225 | [Animated-QR Air-Gap Transport for Arbitrary Payloads (TKQR1)](./0225.md)
369 | [Keyed Content and Conditional Key Release](./0369.md)
25 changes: 25 additions & 0 deletions peer-to-peer/media/0188-vectors.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"warning": "PUBLIC SYNTHETIC PRIVATE KEYS 1,2,3; NEVER FOR PRODUCTION",
"vectors": {
"genesis": {
"frame": "42474d31010279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f00020279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee50000000464656d6f6f450cc8d639f21a4068e234c1a4cfde836f2af0bef8155d90f459dd3caa1bd13cc1cad69b6c698fcb28cd3f931d9076f6bb7958fe3cf9a5aaabf7612a6bbb8a",
"unsignedHash": "e57547be3deab18377c043482c12147760b2335df0dd23c444a60e71dbb02ce6"
},
"add": {
"frame": "42474d3102e57547be3deab18377c043482c12147760b2335df0dd23c444a60e71dbb02ce600000001e57547be3deab18377c043482c12147760b2335df0dd23c444a60e71dbb02ce600030279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee502f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f90000000464656d6f600aa44199771c4a0a448f51a7df1fb759d62783f45c0e2359804325e0b7b8235af9247955409850f288b023e97bc884a9789b0fad67e1ada79473fa47ba3d83",
"unsignedHash": "7e31375bcc12ec446f3ea71d584d2ab6ae80c783a1b3ed26aa82cffce0527c8e"
},
"remove": {
"frame": "42474d3102e57547be3deab18377c043482c12147760b2335df0dd23c444a60e71dbb02ce6000000027e31375bcc12ec446f3ea71d584d2ab6ae80c783a1b3ed26aa82cffce0527c8e00020279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee50000000464656d6f20c222d8b31a6d36ef8fef6fdc5729ecc032ba38809baf15ee4365f340ee816e3f04422bbef35bb0842b7e51b7fed65a6b5b5ad33afa7f0023b008860194f522",
"unsignedHash": "88c1610c4590f5e2911e6d4fa7d578979941fb80f06cf05f6c431758a1c399cb"
},
"metadata": {
"frame": "42474d3102e57547be3deab18377c043482c12147760b2335df0dd23c444a60e71dbb02ce60000000388c1610c4590f5e2911e6d4fa7d578979941fb80f06cf05f6c431758a1c399cb00020279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5000000096e6577206c6162656c51914228c48335fef3d327bee8c0edcce6a0c6849a1efda94b7aa94fc056e04018f580922c58619a4a0af40630e415e3dc17e69527c36003b7b1c598b0d22176",
"unsignedHash": "d41b06a77f3f04710df8af0db868ff772b346d393d9a79491c3d5d2c2b5ec7b2"
},
"content": {
"frame": "42474d3110e57547be3deab18377c043482c12147760b2335df0dd23c444a60e71dbb02ce6000000017e31375bcc12ec446f3ea71d584d2ab6ae80c783a1b3ed26aa82cffce0527c8e0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179830313233343536373839616263646566010000000568656c6c6f3815e46a6ca4253dec0f181ff26e45202c6d9a23cd3589a2d04f57b55bb640df098e2220389028ea75d8a33537f88bab5892ec527eeb31149b75fcfb70391466",
"unsignedHash": "ef650bd97a870d5600e41a0d7026ce6a9f5fce59f4c77d3a136977a22435e422"
}
}
}