Files
aaaamoooo 3951134940
CI / test (push) Canceled after 0s
CI / anytls-smoke (push) Canceled after 0s
feat: Go port of the panel in panel-go/ — drop-in replacement for server.js
Pure-stdlib Go implementation of the whole control plane: same
nexusgate.json schema (unknown fields preserved verbatim, explicit
null/false tri-states modeled), same HTTP API and error strings,
embedded frontend (NG_PUBLIC_DIR override), self-implemented scrypt
auth, X25519 Reality keys, panel-hosted keyStore and heartbeat
reconciliation included. Version stays 0.6.8.

Verified: Go tests 33/33 (scrypt + Reality vectors generated from
the Node implementation, subscription goldens byte-identical),
Node suite 52/52 untouched, differential E2E against the Node
panel 50/50 including two-way DB swap. See panel-go/PARITY.md
and panel-go/INSTALL-GO.md.
2026-10-20 08:21:54 +00:00

121 lines
3.0 KiB
Go

package main
import (
"encoding/json"
"os"
"testing"
)
type scryptVector struct {
Password string `json:"password"`
Salt string `json:"salt"`
Encoded string `json:"encoded"`
}
func loadScryptVectors(t *testing.T) []scryptVector {
t.Helper()
raw, err := os.ReadFile("testdata/scrypt_vectors.json")
if err != nil {
t.Fatalf("read vectors: %v", err)
}
var vectors []scryptVector
if err := json.Unmarshal(raw, &vectors); err != nil {
t.Fatalf("parse vectors: %v", err)
}
if len(vectors) == 0 {
t.Fatal("no scrypt vectors")
}
return vectors
}
func TestHashSecretWithSaltMatchesVectors(t *testing.T) {
for i, v := range loadScryptVectors(t) {
got := HashSecretWithSalt(v.Password, v.Salt)
if got != v.Encoded {
t.Errorf("vector %d: HashSecretWithSalt(%q, %q)\n got %s\nwant %s", i, v.Password, v.Salt, got, v.Encoded)
}
}
}
func TestVerifySecretAgainstVectors(t *testing.T) {
for i, v := range loadScryptVectors(t) {
if !VerifySecret(v.Password, v.Encoded) {
t.Errorf("vector %d: VerifySecret(correct) = false, want true", i)
}
if VerifySecret(v.Password+"wrong", v.Encoded) {
t.Errorf("vector %d: VerifySecret(wrong password) = true, want false", i)
}
}
}
func TestVerifySecretMalformed(t *testing.T) {
cases := []string{
"",
"scrypt",
"scrypt:",
"scrypt::",
"scrypt:salt:",
"scrypt::abcd",
"bcrypt:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:abcd",
"scrypt:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:not-hex!!",
"scrypt:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:abcd", // wrong length
"scrypt:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:zz",
}
for _, c := range cases {
if VerifySecret("password", c) {
t.Errorf("VerifySecret(%q) = true, want false", c)
}
}
}
func TestHashSecretRoundTrip(t *testing.T) {
encoded := HashSecret("s3cret")
parts := len(encoded)
_ = parts
if !VerifySecret("s3cret", encoded) {
t.Errorf("VerifySecret with fresh HashSecret = false")
}
if VerifySecret("other", encoded) {
t.Errorf("VerifySecret wrong secret = true")
}
// Format: scrypt:<32 hex salt chars>:<128 hex hash chars>
var kind, salt, hash string
n, err := parseEncoded(encoded, &kind, &salt, &hash)
if err != nil || n != 3 {
t.Fatalf("encoded format: n=%d err=%v encoded=%q", n, err, encoded)
}
if kind != "scrypt" || len(salt) != 32 || len(hash) != 128 {
t.Errorf("encoded parts: kind=%q salt len=%d hash len=%d", kind, len(salt), len(hash))
}
// Two hashes of the same secret differ (random salt).
if HashSecret("s3cret") == encoded {
t.Errorf("HashSecret produced identical output twice; salt is not random")
}
}
func parseEncoded(encoded string, kind, salt, hash *string) (int, error) {
parts := splitColon(encoded)
if len(parts) > 0 {
*kind = parts[0]
}
if len(parts) > 1 {
*salt = parts[1]
}
if len(parts) > 2 {
*hash = parts[2]
}
return len(parts), nil
}
func splitColon(s string) []string {
var out []string
start := 0
for i := 0; i < len(s); i++ {
if s[i] == ':' {
out = append(out, s[start:i])
start = i + 1
}
}
return append(out, s[start:])
}