Merge 7c8a9170c3
into 2ace01cf1a
This commit is contained in:
commit
cb4736c9f8
|
@ -107,6 +107,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
||||||
- [NIP-C0: Code Snippets](C0.md)
|
- [NIP-C0: Code Snippets](C0.md)
|
||||||
- [NIP-C7: Chats](C7.md)
|
- [NIP-C7: Chats](C7.md)
|
||||||
- [NIP-EE: E2EE Messaging using MLS Protocol](EE.md)
|
- [NIP-EE: E2EE Messaging using MLS Protocol](EE.md)
|
||||||
|
- [NIP-XXX: Encrypted Binary Attachments | DRAFT](nip-xxx-binary-attachments.md)
|
||||||
|
|
||||||
## Event Kinds
|
## Event Kinds
|
||||||
| kind | description | NIP |
|
| kind | description | NIP |
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
---
|
||||||
|
nip: XXX
|
||||||
|
title: Encrypted Binary Attachments for DMs and MLS
|
||||||
|
author: Jonathan Borden (jonathan@loxation.com)
|
||||||
|
status: Draft
|
||||||
|
type: Standards Track
|
||||||
|
created: 2025-08-23
|
||||||
|
license: CC0-1.0
|
||||||
|
---
|
||||||
|
|
||||||
|
## Abstract
|
||||||
|
|
||||||
|
This NIP profile standardizes encrypted binary attachments (images, audio, video, documents) for private Nostr messaging only: NIP‑17 direct messages and MLS groups. It defines:
|
||||||
|
|
||||||
|
- A normalized JSON structure for NIP‑17 DMs (keys live only inside the DM ciphertext)
|
||||||
|
- An MLS profile that derives per‑attachment AEAD key/nonce via the MLS exporter
|
||||||
|
- A per‑attachment AEAD scheme (AES‑256‑GCM) with normative sizes
|
||||||
|
- Integrity requirements (SHA‑256 over ciphertext)
|
||||||
|
- HTTP storage guidance (presigned upload/finalize), optionally compatible with NIP‑96 and NIP‑98
|
||||||
|
|
||||||
|
Public, unencrypted tags (attach/eattach) are explicitly out of scope for this profile.
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
Clients and servers need a consistent, interoperable, privacy‑preserving mechanism for sharing files in private contexts. This document scopes the solution to encrypted‑only delivery via NIP‑17 (1:1) and MLS (1:group), which matches our deployment and avoids ambiguity and leakage associated with public note tags.
|
||||||
|
|
||||||
|
## Rationale
|
||||||
|
|
||||||
|
- Keep symmetric keys in encrypted channels only (DM content or MLS exporter). Never in public tags or relay‑visible metadata.
|
||||||
|
- Use per‑attachment AEAD to avoid key reuse and enable granular sharing and revocation.
|
||||||
|
- Compute integrity over ciphertext so clients can verify prior to decryption and rendering.
|
||||||
|
- Keep events lightweight; store bytes off‑relay at canonical HTTPS URLs.
|
||||||
|
|
||||||
|
## Definitions
|
||||||
|
|
||||||
|
- **Attachment**: A binary resource referenced by a message.
|
||||||
|
- **Ciphertext attachment**: Uploaded bytes are encrypted; clients decrypt locally to render.
|
||||||
|
- **Per‑attachment key**: A random 32‑byte AES‑256 key generated uniquely per attachment (DMs). For MLS, the key is derived via the exporter.
|
||||||
|
|
||||||
|
## Specification
|
||||||
|
|
||||||
|
### 1. Cipher and integrity
|
||||||
|
|
||||||
|
- AEAD: AES‑256‑GCM
|
||||||
|
- key: 32 bytes (base64) [DMs only; MLS derives]
|
||||||
|
- iv/nonce: 12 bytes (base64)
|
||||||
|
- tag: 16 bytes (base64)
|
||||||
|
- Integrity: sha256 MUST be computed over ciphertext and verified before decrypt/render.
|
||||||
|
|
||||||
|
### 2. NIP‑17 direct messages (DMs)
|
||||||
|
|
||||||
|
Attachment parameters MUST live inside the DM’s encrypted content (not tags). The DM plaintext embeds a normalized JSON array of attachment objects:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "message",
|
||||||
|
"text": "optional user text",
|
||||||
|
"attachments": [
|
||||||
|
{
|
||||||
|
"url": "https://storage.example/enc/blob",
|
||||||
|
"ct": "image/jpeg",
|
||||||
|
"size": 23011,
|
||||||
|
"sha256": "<hex_of_ciphertext>",
|
||||||
|
"fn": "photo.jpg",
|
||||||
|
"enc": {
|
||||||
|
"mode": "dm",
|
||||||
|
"algo": "A256GCM",
|
||||||
|
"k": "<b64-32-bytes>",
|
||||||
|
"iv": "<b64-12-bytes>",
|
||||||
|
"t": "<b64-16-bytes>"
|
||||||
|
},
|
||||||
|
"alt": "a cat",
|
||||||
|
"blurhash": "..."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Receiver processing:
|
||||||
|
1) Decrypt the DM per NIP‑17.
|
||||||
|
2) Fetch the ciphertext bytes from `url`.
|
||||||
|
3) Verify `sha256` over ciphertext.
|
||||||
|
4) Decrypt with `enc.k/iv/t`.
|
||||||
|
5) Render using `ct`, `fn`, `alt`, and optional hints (e.g., `blurhash`).
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- The `size` field SHOULD reflect ciphertext length.
|
||||||
|
- Clients SHOULD cache both ciphertext and decrypted plaintext for efficient re‑rendering.
|
||||||
|
|
||||||
|
### 3. MLS group attachments
|
||||||
|
|
||||||
|
For MLS application messages, the attachment AEAD key and nonce are derived via the MLS exporter; no symmetric key material is placed in relay‑visible metadata.
|
||||||
|
|
||||||
|
Key/nonce derivation (normative):
|
||||||
|
- key = MLS.exporter(label="attachment", context=concat(epoch, "|", ctx), length=32)
|
||||||
|
- nonce = MLS.exporter(label="attachment-nonce", context=concat(epoch, "|", ctx), length=12)
|
||||||
|
|
||||||
|
Where `ctx` is a stable, mutually known identifier for this attachment (e.g., server `blobId` or a message‑scoped `attachmentId`). Publishers MUST include enough metadata for receivers to compute the same `ctx`.
|
||||||
|
|
||||||
|
Attachment metadata embedded in or adjacent to the MLS application message SHOULD include:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"url": "https://storage.example/enc/blob",
|
||||||
|
"ct": "image/jpeg",
|
||||||
|
"size": 23011,
|
||||||
|
"sha256": "<hex_of_ciphertext>",
|
||||||
|
"fn": "photo.jpg",
|
||||||
|
"enc": {
|
||||||
|
"mode": "mls",
|
||||||
|
"algo": "A256GCM",
|
||||||
|
"t": "<b64-16-bytes>",
|
||||||
|
"mls": { "group_id": "<groupId>", "epoch": 42, "ctx": "<blobId|attachmentId>" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Receiver processing:
|
||||||
|
1) Use MLS state for `group_id`/`epoch` to derive key and nonce with the exporter and `ctx`.
|
||||||
|
2) Fetch ciphertext, verify `sha256`, then decrypt with derived key/nonce and verify auth tag `t`.
|
||||||
|
|
||||||
|
### 4. Out of scope
|
||||||
|
|
||||||
|
- Public note tags for unencrypted or encrypted media (attach/eattach).
|
||||||
|
- NIP‑92 “imeta” and NIP‑94 file‑metadata records for public media.
|
||||||
|
This profile targets encrypted attachments delivered via NIP‑17 and MLS only.
|
||||||
|
|
||||||
|
### 5. Storage and transport
|
||||||
|
|
||||||
|
- Storage: Off‑relay HTTP object storage with presigned upload + finalize flows that return canonical download URLs and server‑computed metadata (size/checksum). These flows are compatible with NIP‑96 where applicable.
|
||||||
|
- Auth: Publishers and storage providers MAY require NIP‑98 (HTTP Auth) for upload/finalize/download.
|
||||||
|
- Alternate device transports (e.g., BLE/Noise) MAY carry the same JSON payloads; this does not change the on‑wire format for Nostr DMs or MLS.
|
||||||
|
|
||||||
|
### 6. Client behavior
|
||||||
|
|
||||||
|
- Verify `sha256` over ciphertext before decrypt/render.
|
||||||
|
- Verify GCM auth tag during decryption.
|
||||||
|
- Show filename/thumbnail; respect accessibility fields like `alt`.
|
||||||
|
- Cache intelligently; apply quotas and safe‑content policies when fetching.
|
||||||
|
|
||||||
|
### 7. Security considerations
|
||||||
|
|
||||||
|
- Do not place keys, IVs, or tags in public tags or content.
|
||||||
|
- Do not include encryption material in NIP‑92 `imeta` or any relay‑visible metadata.
|
||||||
|
- Treat URL‑based key delivery or external key references as non‑confidential; this profile forbids such patterns.
|
||||||
|
- Ensure unique IVs per key; per‑attachment keys simplify this, but libraries MUST still generate fresh IVs.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### NIP‑17 DM with encrypted attachment
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "message",
|
||||||
|
"attachments": [
|
||||||
|
{
|
||||||
|
"url": "https://cdn.example/enc/xyz",
|
||||||
|
"ct": "image/jpeg",
|
||||||
|
"size": 23011,
|
||||||
|
"sha256": "55aa...",
|
||||||
|
"fn": "photo.jpg",
|
||||||
|
"enc": { "mode": "dm", "algo": "A256GCM", "k": "...", "iv": "...", "t": "..." }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### MLS attachment metadata (ciphertext at URL; key/nonce via exporter)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"url": "https://cdn.example/enc/xyz",
|
||||||
|
"ct": "video/mp4",
|
||||||
|
"size": 8329001,
|
||||||
|
"sha256": "2f3a...",
|
||||||
|
"fn": "talk.mp4",
|
||||||
|
"enc": {
|
||||||
|
"mode": "mls",
|
||||||
|
"algo": "A256GCM",
|
||||||
|
"t": "....",
|
||||||
|
"mls": { "group_id": "deadbeef", "epoch": 42, "ctx": "blob:e3b0c442..." }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Status
|
||||||
|
|
||||||
|
- Reference client: **loxation‑sw**
|
||||||
|
- Supports per‑attachment AES‑256‑GCM, presigned upload/finalize, NIP‑17 DM JSON payloads, and UI rendering.
|
||||||
|
- MLS exporter‑based attachments are supported in the MLS messaging flows used by Loxation.
|
||||||
|
|
Loading…
Reference in New Issue