-
Notifications
You must be signed in to change notification settings - Fork 0
feat(roomcrypto): direct AES-256-GCM room encryption; add client decoder #207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
47 commits
Select commit
Hold shift + click to select a range
fdd327f
docs(roomcrypto): add ECDH performance analysis + Stage-2 design spec
claude 3eff9f7
docs(roomcrypto): expand spec to include chat-frontend decoder in scope
claude e2e5a5c
plan(roomcrypto): implementation plan for HKDF-only encryption migration
claude fc4a639
feat(roomcrypto): add HKDF-only Encoder type alongside legacy Encode
claude 79fc822
fix(roomcrypto): wrap aeadFor errors, document eviction policy, valid…
claude 706383f
test(roomcrypto): assert Encoder caches AEAD per (roomID, version)
claude 4068964
test(roomcrypto): assert Encoder evicts lowest version when full
claude 28475cb
test(roomcrypto): cover Encoder error paths (nonce reader, key length)
claude c113626
test(roomcrypto): round-trip Encoder output via inline HKDF/GCM
claude ccdcad0
bench(roomcrypto): add BenchmarkEncoder_Encode for the new hot path
claude 5a24336
feat(broadcast-worker): use HKDF-only roomcrypto.Encoder
claude 950f423
fix(broadcast-worker): rename ROOMCRYPTO_CACHE_SIZE to ROOM_CRYPTO_CA…
claude 0c63e9e
refactor(roomcrypto): remove legacy ECIES Encode, drop crypto/ecdh dep
claude 6025812
fix(roomkeystore,roomcrypto): rewrite keygen_test to use direct curve…
claude c71b480
test(roomcrypto): integration test uses HKDF-only scheme and rewrites…
claude 97d9ad6
feat(subject): add RoomsKeysBootstrap subject builders
claude 8f75953
feat(model): add RoomsKeysResponse and RoomsKeysEntry types
claude b6f666c
feat(room-service): add chat.user.{account}.request.rooms.keys RPC
claude 54ca2ac
feat(chat-frontend): scaffold lib/roomcrypto with b64decode helper
claude 5a6ced7
feat(chat-frontend): add deriveAesKey via Web Crypto HKDF-SHA-256
claude dd7b6fc
feat(chat-frontend): add decryptRoomMessage via Web Crypto AES-GCM
claude d486fdb
test(chat-frontend): cross-language fixture for Go encode → TS decode
claude 51cbbdd
feat(chat-frontend/api): subject builders for room-key events and boo…
claude 00532a9
feat(chat-frontend/api): add RoomKeyEvent + RoomKeys{Entry,Response} …
claude 4f7b3f5
feat(chat-frontend/api): subscribeToRoomKeyEvents
claude 2d244c2
feat(chat-frontend/api): fetchRoomKeysBootstrap RPC wrapper
claude 946cbc6
feat(chat-frontend): RoomKeysContext reducer with idempotent key insert
claude 67bb9c5
feat(chat-frontend): RoomKeysProvider with bootstrap + live subscription
claude 1bd8e37
feat(chat-frontend): mount RoomKeysProvider in app tree
claude e3c185b
feat(chat-frontend): decrypt new-message + edit events before dispatch
claude f452468
test(chat-frontend): reducer handles decrypted event identically to p…
claude 759b860
docs(client-api): document HKDF-only encryption and keys-bootstrap RPC
claude aae57e4
fix: integration tests + tighten natsListRoomKeys subject validation
claude d8e474c
fix(roomkeysender): update TS integration client to HKDF-only scheme
claude 63bc00a
fix: address PR #207 review feedback
claude 748b462
simplify: shrink subscriptions index + guard redundant AES cache evic…
claude 96f3dc4
chore: tighten code to reduce PR review surface
claude 3ca1807
fix(roomcrypto): add nonce + ciphertext length guards in TS decryptor
claude 82bfbfd
refactor(broadcast-worker): make roomcrypto.Encoder an internal detail
claude 8918725
refactor(roomkeystore): drop room public key, store only the 32-byte …
claude b87f84f
fix: address PR #207 review — version mismatch, nil keystore, decode …
claude ee67744
fix(room-worker): use 32-byte seed key in CreateRoom integration test
claude 595b6ce
fix: address 3 more bugs from extended review pass
claude 113608e
test: integration coverage for ListSubscriptionsByAccount; unit test …
claude 5e57c28
refactor: remove HKDF — use room secret directly as AES-256-GCM key
claude 5c97bd2
Merge remote-tracking branch 'origin/main' into claude/ecdh-performan…
claude 654122c
refactor: remove natsListRoomKeys bootstrap RPC; drop legacy Ephemera…
claude File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| //go:build ignore | ||
|
|
||
| // gen-crypto-fixtures generates a known-plaintext encrypted message for | ||
| // chat-frontend's lib/roomcrypto round-trip tests. Run with: | ||
| // | ||
| // go run chat-frontend/scripts/gen-crypto-fixtures.go > chat-frontend/test/fixtures/encrypted-message.json | ||
| // | ||
| // Commit the output. The fixture exists to lock the cross-language wire | ||
| // format: any change in the server encoder's AES-GCM parameters or wire | ||
| // shape MUST update this fixture together with the corresponding TS | ||
| // decoder. | ||
| package main | ||
|
|
||
| import ( | ||
| "encoding/base64" | ||
| "encoding/json" | ||
| "fmt" | ||
| "os" | ||
|
|
||
| "github.com/hmchangw/chat/pkg/roomcrypto" | ||
| ) | ||
|
|
||
| func main() { | ||
| // Deterministic private key so the fixture is stable across runs. | ||
| priv := make([]byte, 32) | ||
| for i := range priv { | ||
| priv[i] = byte(i + 1) | ||
| } | ||
| const plaintext = "fixture plaintext — encoded by the Go server, decoded by chat-frontend" | ||
| const version = 1 | ||
| const roomID = "fixture-room" | ||
|
|
||
| // Construct an Encoder with a fixed-nonce reader so the ciphertext is | ||
| // reproducible. Twelve-byte zero nonce is fine for a fixture (it's a | ||
| // known-key test, not real traffic). | ||
| enc := roomcrypto.NewEncoder(roomcrypto.WithRand(zeroReader{})) | ||
| msg, err := enc.Encode(roomID, plaintext, priv, version) | ||
| if err != nil { | ||
| fmt.Fprintln(os.Stderr, "encode:", err) | ||
| os.Exit(1) | ||
| } | ||
|
|
||
| out := struct { | ||
| PrivateKey string `json:"privateKey"` | ||
| Plaintext string `json:"plaintext"` | ||
| Message *roomcrypto.EncryptedMessage `json:"message"` | ||
| }{ | ||
| PrivateKey: base64.StdEncoding.EncodeToString(priv), | ||
| Plaintext: plaintext, | ||
| Message: msg, | ||
| } | ||
| data, err := json.MarshalIndent(out, "", " ") | ||
| if err != nil { | ||
| fmt.Fprintln(os.Stderr, "marshal:", err) | ||
| os.Exit(1) | ||
| } | ||
| fmt.Println(string(data)) | ||
| } | ||
|
|
||
| type zeroReader struct{} | ||
|
|
||
| func (zeroReader) Read(p []byte) (int, error) { | ||
| for i := range p { | ||
| p[i] = 0 | ||
| } | ||
| return len(p), nil | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
chat-frontend/src/api/subscribeToRoomKeyEvents/index.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { describe, it, expect, vi } from 'vitest' | ||
| import { subscribeToRoomKeyEvents } from './index' | ||
|
|
||
| describe('subscribeToRoomKeyEvents', () => { | ||
| it('subscribes to chat.user.{account}.event.room.key with the given callback', () => { | ||
| const subscribe = vi.fn().mockReturnValue({ unsubscribe: vi.fn() }) | ||
| const nats = { subscribe, user: { account: 'alice', id: '' }, request: vi.fn(), publish: vi.fn(), requestWithAsyncResult: vi.fn(), connected: true, error: null } | ||
| const cb = vi.fn() | ||
|
|
||
| const sub = subscribeToRoomKeyEvents(nats as never, cb) | ||
|
|
||
| expect(subscribe).toHaveBeenCalledWith('chat.user.alice.event.room.key', cb) | ||
| expect(sub).toBeDefined() | ||
| }) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { userRoomKey } from '../_transport/subjects' | ||
| import type { Nats, NatsSubscription, SubscriptionCallback } from '../types' | ||
|
|
||
| /** Subscribe to the calling user's room-key event stream. */ | ||
| export function subscribeToRoomKeyEvents( | ||
| { subscribe, user }: Pick<Nats, 'subscribe' | 'user'>, | ||
| callback: SubscriptionCallback, | ||
| ): NatsSubscription { | ||
| return subscribe(userRoomKey(user.account), callback) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Use the standard API operation signature and pass
optstosubscribe.Please update this op to match the required
operationName(nats, args, opts?)signature and forwardoptsto the NATS primitive for consistency acrosssrc/api/*/index.ts.As per coding guidelines: “API operations must have …
operationName(nats, args, opts?)” and “Always passoptsparameter through to NATS primitives … use three-argument shape.”🤖 Prompt for AI Agents