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