nips/76.md

92 lines
2.9 KiB
Markdown
Raw Normal View History

2024-09-13 12:56:50 -04:00
NIP-76
======
Relay Read Permissions
----------------------
`draft` `optional`
2024-09-15 14:16:02 -04:00
Tags `rp` (read permission) and `prp` (probabilistic read permission) specify which keys are authorized to download an event from the relay.
2024-09-13 12:56:50 -04:00
2024-09-15 14:16:02 -04:00
Events tagged with `rp` or `prp` require AUTH for download.
2024-09-13 12:56:50 -04:00
## Read Permission
2024-09-15 14:16:02 -04:00
The `rp` tag takes a pubkey in lowercase hex as value.
2024-09-13 12:56:50 -04:00
```json
["rp", "<pubkey1>"]
["rp", "<pubkey2>"]
["rp", "<pubkey3>"]
2024-09-13 12:56:50 -04:00
```
2024-09-15 14:16:02 -04:00
When responding to `REQ`s, if an event contains `rp` tags, relays MUST verify that the authenticated user is either the event's author or one of the keys in the `rp` set before delivering it to the client.
2024-09-13 12:56:50 -04:00
## Probabilistic Read Permissions
2024-09-15 14:16:02 -04:00
Probabilistic permissions are implemented using Bloom filters that represent a set of authorized pubkeys. These permissions are expressed as a colon-separated value comprising:
1. the number of bits in the bit array,
2. the number of hash rounds applied, and
3. the bit array encoded in Base64.
4. the salt encoded in Base64.
2024-09-13 12:56:50 -04:00
```json
2024-09-15 14:16:02 -04:00
["prp", "<bits>:<rounds>:<base64>:<salt>"]
2024-09-13 12:56:50 -04:00
```
2024-09-15 14:16:02 -04:00
Bloom filters MUST use `SHA256` functions applied to the concatenation of the key, salt, and index, as demonstrated in the pseudocode below:
2024-09-13 12:56:50 -04:00
```js
2024-09-15 14:16:02 -04:00
class BloomFilter(size: Int, rounds: Int, buffer: ByteArray, salt: ByteArray) {
2024-09-13 12:56:50 -04:00
val bits = BitArray(buffer)
fun bitIndex(value: ByteArray, index: Byte) {
2024-09-15 14:16:02 -04:00
return BigInt(sha256(value || salt || index)) % size
2024-09-13 12:56:50 -04:00
}
fun add(pubkey: HexKey) {
val value = pubkey.hexToByteArray()
2024-09-13 13:10:20 -04:00
for (index in 0 until rounds) {
2024-09-13 12:56:50 -04:00
bits[bitIndex(value, index)] = true
}
}
fun mightContains(pubkey: HexKey): Boolean {
val value = pubkey.hexToByteArray()
2024-09-13 13:10:20 -04:00
for (index in 0 until rounds) {
2024-09-13 12:56:50 -04:00
if (!bits[bitIndex(value, index)]) {
return false
}
}
return true
}
2024-09-13 15:10:28 -04:00
fun encode() {
2024-09-15 14:16:02 -04:00
return size + ":" + rounds + ":" + base64Encode(bits.toByteArray()) + ":" + base64Encode(salt)
2024-09-13 15:10:28 -04:00
}
2024-09-13 12:56:50 -04:00
fun decode(str: String): BloomFilter {
2024-09-15 14:16:02 -04:00
val [sizeStr, roundsStr, bufferB64, saltB64] = str.split(":")
return BloomFilter(sizeStr.toInt(), roundsStr.toInt(), base64Decode(bufferB64), base64Decode(saltB64))
2024-09-13 12:56:50 -04:00
}
}
```
2024-09-15 14:16:02 -04:00
When responding to `REQ`s, if an event contains `prp` tags, relays MUST verify that the authenticated user is either the event's author or matches any of the filters before delivering it to the client.
2024-09-13 12:56:50 -04:00
2024-09-15 14:16:02 -04:00
If both `rp` and `prp` tags are present, the authenticated user MUST either be in the `rp` set or match any `prp` filter.
2024-09-13 12:56:50 -04:00
### Test cases
2024-09-15 14:16:02 -04:00
The filter below has 100 bits and uses 10 rounds of hashing, which should be capable of handling up to 10,000,000 keys without producing any false positives.
2024-09-13 12:56:50 -04:00
```json
2024-09-15 14:16:02 -04:00
["prp", "100:10:AAAkAQANcYQFCQoB:hZkZYqqdxcE="]
2024-09-13 12:56:50 -04:00
```
It includes keys `ca29c211f1c72d5b6622268ff43d2288ea2b2cb5b9aa196ff9f1704fc914b71b` and `460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c`