docs: document identity derivation algorithm and simplify bootstrap servers
- Added detailed "Identity Derivation" section to main README.md, including Go code example, properties, and security benefits for deterministic, collision-resistant IDs. - Expanded "Identity Derivation Algorithm" in portal/core/cryptoops/README.md with step-by-step process, code snippet, and security properties to clarify protocol-specific binding. - Updated authentication section in cryptoops README to precisely describe identity binding via HMAC-SHA256 with Base32 encoding. - Simplified bootstrap servers in webclient to use only the production relay, removing localhost and alternative endpoints for cleaner configuration.
lemon-mint committed
Nov 3, 2025 at 16:52 UTC
da750dff63c19e8b3eef7ff4298353b1a5a8f146
3 files changed
+65
-5
README.md
+24
@@ -180,6 +180,30 @@ sequenceDiagram
180
- **HKDF-SHA256**: Key derivation for session keys
181
- **HMAC-SHA256**: Identity derivation from public keys
182
183
+### Identity Derivation
184
+
185
+Each peer's identity ID is derived from their Ed25519 public key using a deterministic process:
186
+
187
+```go
188
+func DeriveID(publickey ed25519.PublicKey) string {
189
+ // HMAC-SHA256 with protocol-specific key
190
+ h := hmac.New(sha256.New, []byte("RDVERB_PROTOCOL_VER_01_SHA256_ID"))
191
+ h.Write(publickey) // 32-byte Ed25519 public key
192
+ hash := h.Sum(nil) // 32-byte SHA256 output
193
+
194
+ // Take first 128 bits and encode with Base32 (no padding)
195
+ encoding := base32.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567").WithPadding(base32.NoPadding)
196
+ return encoding.EncodeToString(hash[:16])
197
+}
198
+```
199
+
200
+**Properties:**
201
+- **Deterministic**: Same public key always produces same ID
202
+- **Collision-resistant**: 128-bit security against birthday attacks
203
+- **Protocol-bound**: HMAC key prevents cross-protocol ID reuse
204
+- **Human-readable**: Base32 encoding produces 26-character alphanumeric IDs
205
+- **Compact**: Fixed-length IDs enable efficient storage and routing
206
+
207
### Security Properties
208
209
- **Mutual Authentication**: Both parties verify each other's identities
cmd/webclient/main_js.go
+1
-1
@@ -28,7 +28,7 @@ import (
28
)
29
30
var (
31
- bootstrapServers = []string{"ws://localhost:4017/relay", "wss://portal.gosuda.org/relay", "wss://220-71-14-177.sslip.io/relay"}
31
+ bootstrapServers = []string{"wss://portal.gosuda.org/relay"}
32
rdClient *sdk.RDClient
33
)
34
portal/core/cryptoops/README.md
+40
-4
@@ -39,6 +39,41 @@ type Credential struct {
39
}
40
```
41
42
+#### Identity Derivation Algorithm
43
+
44
+The ID field is deterministically derived from the Ed25519 public key using a secure hashing process:
45
+
46
+```go
47
+var _id_magic = []byte("RDVERB_PROTOCOL_VER_01_SHA256_ID")
48
+var _base32_encoding = base32.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567").WithPadding(base32.NoPadding)
49
+
50
+func DeriveID(publickey ed25519.PublicKey) string {
51
+ h := hmac.New(sha256.New, _id_magic)
52
+ h.Write(publickey)
53
+ hash := h.Sum(nil)
54
+ return _base32_encoding.EncodeToString(hash[:16])
55
+}
56
+```
57
+
58
+**Algorithm Steps:**
59
+1. **HMAC-SHA256**: Compute HMAC-SHA256(publicKey, "RDVERB_PROTOCOL_VER_01_SHA256_ID")
60
+ - Input: 32-byte Ed25519 public key
61
+ - HMAC Key: Protocol-specific magic string
62
+ - Output: 32-byte hash
63
+2. **Truncation**: Take first 128 bits (16 bytes) of hash
64
+3. **Base32 Encoding**: Encode with custom alphabet (no padding)
65
+ - Alphabet: `ABCDEFGHIJKLMNOPQRSTUVWXYZ234567`
66
+ - Padding: None
67
+ - Output: 26-character alphanumeric string
68
+
69
+**Security Properties:**
70
+- **Deterministic**: Same public key → same ID (enables caching and verification)
71
+- **One-way**: Computationally infeasible to derive public key from ID
72
+- **Collision-resistant**: 128-bit security provides ~10³⁸ unique IDs
73
+- **Protocol-bound**: HMAC key prevents cross-protocol ID collision attacks
74
+- **Compact**: 26 characters enable efficient URL encoding and database indexing
75
+
76
+
77
### 2. X25519 Key Exchange (Curve25519)
78
- **Purpose**: Ephemeral session key agreement
79
- **Key Size**: 32 bytes (256 bits)
@@ -272,10 +307,11 @@ func (sc *SecureConnection) Read(p []byte) (int, error) {
307
### 1. Authentication
308
- **Mutual**: Both parties authenticate each other's long-term identities
309
- **Signature-based**: Ed25519 signatures over handshake payloads
275
-- **Identity binding**: Public keys are cryptographically bound to identity IDs
276
- ```go
277
- id := Base32Encode(HMAC_SHA256(publicKey, "RDVERB_PROTOCOL_VER_01_SHA256_ID"))
278
- ```
310
+- **Identity binding**: Public keys are cryptographically bound to identity IDs via HMAC-SHA256
311
+ - HMAC key: `"RDVERB_PROTOCOL_VER_01_SHA256_ID"`
312
+ - Output: First 128 bits of HMAC-SHA256(publicKey, magic)
313
+ - Encoding: Base32 (custom alphabet, no padding)
314
+ - Result: 26-character deterministic ID
315
316
### 2. Forward Secrecy
317
- **Ephemeral Keys**: Fresh X25519 keypair per connection