Revise author info and improve encryption details

This commit is contained in:
jab-r 2025-08-26 11:29:06 -04:00 committed by GitHub
parent a0f0162947
commit befaa2882d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 120 additions and 11 deletions

View File

@ -1,7 +1,7 @@
---
nip: XXX
title: Binary Attachments for Notes and DMs
author: Jonathan Borden (jonathan@@openhealth.org)
author: Jonathan Borden (jonathan@loxation.com)
status: Draft
type: Standards Track
created: 2025-08-23
@ -77,8 +77,17 @@ Integrity: sha256 MUST be computed over ciphertext.
#### 2.2 Private DMs (NIP-17)
Keys MUST live inside encrypted content, not tags.
Plaintext before DM encryption:
Keys MUST live inside the DMs encrypted content, not tags.
To avoid exposing symmetric keys in plaintext, this NIP specifies an envelope-wrapped key for DMs:
- Payload encryption: AES-256-GCM (per-attachment K)
- Key wrapping: X25519-HKDF-AESGCM
- Sender generates an ephemeral X25519 keypair (epk, esk)
- Shared secret s = X25519(esk, recipient_static_x25519_pub)
- wrapKey = HKDF-SHA256(s, info="attachment-key-wrap", salt=random16)
- ek = AES-GCM-Encrypt(wrapKey, nonce=random12, K)
Plaintext (before NIP17 DM encryption) embedding the metadata:
```json
{
@ -86,16 +95,23 @@ Plaintext before DM encryption:
"text": "optional message",
"attachments": [
{
"url": "https://storage.example/encrypted/blob",
"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>"
"t": "<b64-16-bytes>",
"ek": "<b64-wrapped-key-ciphertext>",
"epk": "<b64-x25519-ephemeral-pub>",
"wrap": {
"alg": "X25519-HKDF-AESGCM",
"nonce": "<b64-12-bytes>",
"salt": "<b64-16-bytes>"
}
},
"alt": "a cat"
}
@ -103,12 +119,77 @@ Plaintext before DM encryption:
}
```
On receive, clients decrypt the DM, unwrap K using their static X25519 private key and epk, then decrypt the blob with K/iv/t. No plaintext symmetric keys appear in metadata.
#### 2.3 MLS group attachments
For MLS groups, the attachment AEAD key and nonce are derived from the MLS group secret and epoch via the MLS exporter (no ek/epk in metadata):
- key = MLS.exporter(label="attachment", context=concat(epoch, "|", blobId or messageId), length=32)
- nonce = MLS.exporter(label="attachment-nonce", context=concat(epoch, "|", blobId or messageId), length=12)
Metadata embedded in the MLS application message (or carried adjacent as client policy) 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",
"iv": "<b64-12-bytes>",
"t": "<b64-16-bytes>",
"mls": { "group_id": "<groupId>", "epoch": 42 }
}
}
```
Receivers derive the same key/nonce via exporter using (groupId, epoch, context) and decrypt the blob. No key material is placed in metadata.
### 3. Relationship to Existing NIPs
- **NIP-17 (DMs):** Keys/attachments inside encrypted payload.
- **NIP-94 (File Metadata):** Optional companion metadata.
- **NIP-96 (HTTP File Storage):** Compatible with presigned uploads.
- **NIP-98 (HTTP Auth):** May secure uploads/downloads.
#### 3.1 NIP-17 (DMs)
- Placement: For private messages, all attachment details that include encryption material MUST live inside the DMs encrypted content (see 2.2). Do not place keys in public tags.
- Rendering: Receivers decrypt the DM per NIP17, fetch bytes, verify sha256 over ciphertext, then decrypt with enc.k/iv/t.
#### 3.2 NIP-92 (Media Attachments) — Interoperability
- Purpose: NIP92 defines “imeta” tags that annotate media URLs present in the event content. Many mediafocused clients render using imeta.
- Coexistence with NIPXXX:
- attach/eattach MAY be used alongside NIP92 imeta in the same event.
- When the event content contains a media URL, publishers SHOULD include a corresponding imeta tag so NIP92aware clients render consistently.
- When both attach/eattach and imeta are present, fields SHOULD be kept consistent.
- Field mapping (informational):
- url (positional in attach/eattach) ↔ imeta: "url <https://...>"
- m ↔ imeta: "m <mime>"
- size ↔ imeta: "size <bytes>" (if used)
- sha256 ("sha256=<hex>" in attach/eattach) ↔ imeta: "x <hex>" (per NIP94)
- alt, dim, blurhash ↔ imeta: "alt …", "dim WxH", "blurhash …"
- NIP92 "fallback" URLs MAY be included in imeta; there is no attach/eattach equivalent.
- Encrypted media:
- The imeta "url" MUST point to the ciphertext URL (the same as in eattach).
- The imeta "x" hash MUST match the sha256 over ciphertext (same value as eattach).
- Do NOT include keys in imeta or content.
- Recommended client behavior:
- If a public note includes media URLs in content, add imeta derived from attach/eattach to maximize compatibility with NIP92 renderers.
- If a note does not inline the URL in content (tagsonly publishing), imeta is not applicable; attach/eattach alone is sufficient.
#### 3.3 NIP-94 (File Metadata)
- Publication guidance (normative):
- Publishers MAY emit a NIP94 filemetadata event for each public attachment (attach or eattach) using the same url/m/size/sha256. This provides durable, relayindexable metadata.
- Clients that publish a NIP94 event SHOULD reference it from the note (e.g., "e" or "a" tag per client policy).
- For encrypted attachments, NIP94 events MUST describe the ciphertext (url/m/size/sha256) and MUST NOT include decryption keys; keys live only in private channels (e.g., NIP17 DM).
- When both inline attach/eattach and a NIP94 record are present, clients SHOULD prefer inline fields for immediate render and also index the NIP94 record.
- Hash alignment:
- In attach/eattach, sha256=<hex> corresponds to the NIP94 "x" field value used by NIP92 imeta. Implementations SHOULD ensure these values match across representations.
#### 3.4 NIP-96 (HTTP File Storage)
- Compatibility: Presigned upload/finalize flows align with NIP96. A NIP96compliant server can provide initiation, direct upload, and finalize endpoints returning canonical download URLs and servercomputed metadata.
#### 3.5 NIP-98 (HTTP Auth)
- Usage: Publishers and storage providers MAY require NIP98 for upload/finalize/download operations. Include an Authorization header per NIP98; servers verify signatures and apply rate limits/quotas.
### 4. Client Behavior
@ -120,7 +201,9 @@ Plaintext before DM encryption:
### 5. Security Considerations
- Do not leak keys in public tags (no confidentiality).
- Always verify sha256 and GCM auth tag.
- Do not include encryption material (k/iv/t) in NIP92 imeta or any public content.
- Clients MUST verify sha256 and GCM auth tag before render/decrypt.
- Clients MUST NOT autoresolve ekref via untrusted schemes; keys SHOULD be conveyed via private DMs (NIP17) or other trusted mechanisms.
- Apply quotas/content scanning on downloads.
## Examples
@ -173,6 +256,32 @@ Plaintext before DM encryption:
}
```
### Public note with attach + imeta (unencrypted image)
```json
{
"kind": 1,
"content": "beach pic https://cdn.example/a.jpg",
"tags": [
["attach","https://cdn.example/a.jpg","sha256=2f3a...","m=image/jpeg","size=24567","fn=beach.jpg","alt=beach at dusk","dim=1920x1080","blurhash=..."],
["imeta","url https://cdn.example/a.jpg","m image/jpeg","x 2f3a...","dim 1920x1080","alt beach at dusk","blurhash ..."]
]
}
```
### Public note with eattach + imeta (encrypted video; key via DM)
```json
{
"kind": 1,
"content": "members-only video (see your DM for the key) https://cdn.example/enc/v1/xyz",
"tags": [
["eattach","https://cdn.example/enc/v1/xyz","sha256=55aa...","m=video/mp4","size=8329001","fn=talk.mp4","algo=A256GCM","ekref=event:9b3e..."],
["imeta","url https://cdn.example/enc/v1/xyz","m video/mp4","x 55aa...","alt members-only talk"]
]
}
```
## Implementation Status
- Reference client implementation: **loxation-sw**