可驗證資格的匿名投票系統。
國立彰化師範大學學生會的電子投票系統。學生以校園 SSO 登入確認資格,瀏覽器在本機產生秘密值並註冊 commitment;投票時於瀏覽器產生零知識證明,伺服器驗證後將選票存入不含學號與 commitment 的資料表。
我的角色 團隊專案(GDG on Campus NCUE 與學生會資訊部)。我建立專案並撰寫初版系統規格,負責校園 SSO 登入、commitment 註冊、部署腳本與電路建置流程、管理端的 Keycloak 登入與權限,以及選舉公告、使用說明與驗票中心(後由隊友改為公開開票結果頁)等頁面。
- 零知識證明系統(BN128 曲線)
- Groth16
- 零知識證明系統(BN128 曲線)
- 電路內使用的雜湊函數
- Poseidon
- 電路內使用的雜湊函數
- 每場選舉一組金鑰,開票時才解密
- RSA-2048
- 每場選舉一組金鑰,開票時才解密
- 個登入入口,選民與管理員分離
- 2
- 個登入入口,選民與管理員分離

01
要解決的問題
資格與選票分離
系統必須確認資格,但不能將「投票者」與「投票內容」綁在一起。
結果可驗證
隱私與可驗證性必須同時成立,而非僅仰賴對伺服器的信任。
一人一票
每位選民在每場選舉只能投一次,重複投票必須被拒絕。
選民與管理員分離
學生使用校園 SSO,管理員使用學生會的 Keycloak;管理介面不提供任何投票功能。
02
系統架構
身分驗證採中心化的校園 SSO,選票匿名性則由零知識證明與加密保障。下圖為目前程式碼的實際流程。
← 左右滑動看完整流程 →
使用技術
- 前端
- ReactViteTailwind CSSZustand
- 後端
- NestJSPrismaPostgreSQL
- 零知識證明
- CircomsnarkjsGroth16Poseidon
- 身分與部署
- OIDCKeycloakDockerNginx
03
主要功能
校園 SSO 登入
學生以學校帳號登入,伺服器僅保存學號雜湊。
匿名身分註冊
瀏覽器產生秘密值,僅將 commitment 提交給伺服器。
投票亭
可選擇候選人或投空白票;證明於背景執行緒產生,不影響操作。
公告與開票結果
選舉公告、使用說明與公開的開票結果頁。
管理員權限
以 Keycloak 登入;超級管理員可新增、刪除或提升其他管理員。
選舉與名冊管理
建立選舉與候選人(可鎖定)、匯入選舉人名冊、監看投票與抽籤。
04
技術細節
04.1
commitment:證明知道秘密,而不揭露秘密
註冊時伺服器只取得 Poseidon(學號雜湊, 秘密值) 的結果。投票時瀏覽器產生 Groth16 證明,證明自己知道此 commitment 背後的兩個值;伺服器能驗證證明,卻無從得知秘密值。
瀏覽器(只在你這邊)
學號雜湊秘密值Poseidon(…) = ?Web Worker 產生 Groth16 證明commitment證明伺服器看得到的
- commitment
- 證明…
- 秘密值
04.2
身分與選票分表儲存
驗證通過後,「標記投票金鑰已使用」與「寫入選票」在同一筆交易中完成。選票表僅存放加密內容與證明,不含學號與 commitment。
- 選票於瀏覽器以該場選舉的 RSA-2048 公鑰(OAEP、SHA-256)加密,開票時才以私鑰解密。
- 送出失敗與成功的回應相同,避免從回應推知驗證細節。
同一筆資料庫交易UserVoteKey
學號雜湊 commitment 已投票 9f2c… 1873… false 沒有關聯欄位Vote
加密選票 證明 Qm8x… {…} Zk3p… {…} 欄位與數值為示意。
規格與現行實作的差異
專案文件的設計是在電路中加入 Merkle Tree 成員證明與 nullifier 以防重複投票;目前上線的電路僅證明 commitment 的兩個原始值,資格與是否已投票改由伺服器查表確認。整體為中心化身分驗證結合零知識證明的混合架構,並非去中心化系統。
05
系統畫面


