Skip to content

Commit d489140

Browse files
authored
Removes orchestrator in favor of an e2e test (#9)
1 parent b540989 commit d489140

File tree

16 files changed

+1631
-689
lines changed

16 files changed

+1631
-689
lines changed

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ test:
2929
test-race:
3030
go test -race ./...
3131

32+
FUZZTIME ?= 10s
33+
34+
.PHONY: test-fuzz
35+
test-fuzz:
36+
@for fuzz in $$(grep -r "^func Fuzz" ./crypto/*_test.go | sed 's/.*\(Fuzz[a-zA-Z]*\).*/\1/'); do \
37+
echo "Running $$fuzz..."; \
38+
go test -fuzz=$$fuzz -fuzztime=$(FUZZTIME) ./crypto || exit 1; \
39+
done
40+
3241
.PHONY: lint
3342
lint:
3443
gofmt -d -s .

README.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,25 @@ protocol:
101101

102102
### Running the Demo
103103

104-
The demo orchestrator runs a complete local deployment:
104+
The local deployment script runs a complete network using multiservice:
105105

106106
```bash
107-
go run ./services/demo \
107+
./deploy_local.sh \
108108
--clients=10 \
109109
--aggregators=2 \
110110
--servers=5 \
111111
--round=10s \
112-
--msg-length=512000 \
113-
--admin-token="admin:secret"
112+
--msg-length=512000
113+
```
114+
115+
Use the CLI to interact with the deployed network:
116+
117+
```bash
118+
# Send a message
119+
go run ./cmd/demo-cli send -r http://localhost:7999 -m "Hello" -b 100 --skip-verification
120+
121+
# Monitor rounds
122+
go run ./cmd/demo-cli monitor -r http://localhost:7999 --follow --skip-verification
114123
```
115124

116125
## Running Standalone Services

cmd/demo-gateway/main.go

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"net/http"
1111
"os"
1212
"os/signal"
13+
"path/filepath"
14+
"strings"
1315
"sync"
1416
"syscall"
1517
"time"
@@ -30,6 +32,7 @@ func main() {
3032
staticDir = flag.String("static", "", "Directory for static files")
3133
skipVerification = flag.Bool("skip-verification", false, "Skip attestation verification")
3234
measurementsURL = flag.String("measurements-url", "", "URL for allowed TEE measurements")
35+
allowedOrigins = flag.String("allowed-origins", "http://localhost:*", "Comma-separated list of allowed CORS origins (use * for all)")
3336
)
3437
flag.Parse()
3538

@@ -43,11 +46,18 @@ func main() {
4346
cancel()
4447
}()
4548

49+
// Parse allowed origins
50+
origins := strings.Split(*allowedOrigins, ",")
51+
for i := range origins {
52+
origins[i] = strings.TrimSpace(origins[i])
53+
}
54+
4655
gateway := NewGateway(&GatewayConfig{
4756
RegistryURL: *registryURL,
4857
SkipVerification: *skipVerification,
4958
MeasurementsURL: *measurementsURL,
5059
StaticDir: *staticDir,
60+
AllowedOrigins: origins,
5161
})
5262

5363
go gateway.Start(ctx)
@@ -57,7 +67,7 @@ func main() {
5767
r.Use(middleware.Recoverer)
5868
r.Use(middleware.Timeout(30 * time.Second))
5969
r.Use(cors.Handler(cors.Options{
60-
AllowedOrigins: []string{"*"},
70+
AllowedOrigins: gateway.config.AllowedOrigins,
6171
AllowedMethods: []string{"GET", "POST", "OPTIONS"},
6272
AllowedHeaders: []string{"Accept", "Content-Type"},
6373
AllowCredentials: false,
@@ -96,6 +106,7 @@ type GatewayConfig struct {
96106
SkipVerification bool
97107
MeasurementsURL string
98108
StaticDir string
109+
AllowedOrigins []string
99110
}
100111

101112
// Gateway serves the demo API and website.
@@ -143,18 +154,34 @@ func (g *Gateway) RegisterRoutes(r chi.Router) {
143154

144155
func (g *Gateway) registerStaticRoutes(r chi.Router) {
145156
if g.config.StaticDir != "" {
146-
fileServer := http.FileServer(http.Dir(g.config.StaticDir))
157+
// Get absolute path of static directory for security validation
158+
absStaticDir, err := filepath.Abs(g.config.StaticDir)
159+
if err != nil {
160+
fmt.Printf("Warning: could not resolve static dir: %v\n", err)
161+
return
162+
}
163+
164+
fileServer := http.FileServer(http.Dir(absStaticDir))
147165
r.Handle("/*", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
148-
path := g.config.StaticDir + req.URL.Path
149-
if _, err := os.Stat(path); os.IsNotExist(err) && req.URL.Path != "/" {
150-
http.ServeFile(w, req, g.config.StaticDir+"/index.html")
166+
// Clean and validate the path to prevent directory traversal
167+
cleanPath := filepath.Clean(req.URL.Path)
168+
fullPath := filepath.Join(absStaticDir, cleanPath)
169+
170+
// Ensure the resolved path is within the static directory
171+
if !strings.HasPrefix(fullPath, absStaticDir) {
172+
http.Error(w, "forbidden", http.StatusForbidden)
173+
return
174+
}
175+
176+
if _, err := os.Stat(fullPath); os.IsNotExist(err) && req.URL.Path != "/" {
177+
http.ServeFile(w, req, filepath.Join(absStaticDir, "index.html"))
151178
return
152179
}
153180
fileServer.ServeHTTP(w, req)
154181
}))
155182
} else {
156183
r.Get("/", func(w http.ResponseWriter, req *http.Request) {
157-
http.Error(w, "nothing to serve", 500)
184+
http.Error(w, "nothing to serve", http.StatusInternalServerError)
158185
})
159186
r.Get("/favicon.svg", g.handleFavicon)
160187
}

cmd/doc.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
//
2020
// go run ./cmd/registry --addr=:8080 --admin-token=admin:secret
2121
//
22-
// demo: Local orchestrator that runs a complete ADCNet deployment in a single
23-
// process for testing and development.
22+
// demo-cli: CLI for interacting with a deployed ADCNet network.
2423
//
25-
// go run ./services/demo --clients=10 --servers=3 --round=5s
24+
// go run ./cmd/demo-cli send -r http://localhost:7999 -m "Hello" -b 100
25+
// go run ./cmd/demo-cli monitor -r http://localhost:7999 --follow
2626
//
2727
// # HTTP Configuration Mode
2828
//

crypto/blinding_fuzz_test.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package crypto
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
)
7+
8+
func FuzzDeriveBlindingVector(f *testing.F) {
9+
// Add seed corpus with various parameters
10+
f.Add([]byte("shared-secret-1"), uint32(1), int32(10))
11+
f.Add([]byte("shared-secret-2"), uint32(0), int32(1))
12+
f.Add([]byte("shared-secret-3"), uint32(100), int32(50))
13+
14+
f.Fuzz(func(t *testing.T, secret []byte, round uint32, nEls int32) {
15+
// Skip invalid inputs
16+
if len(secret) == 0 || nEls <= 0 || nEls > 1000 {
17+
t.Skip()
18+
}
19+
20+
sharedSecrets := []SharedKey{secret}
21+
22+
result := DeriveBlindingVector(sharedSecrets, round, nEls, AuctionFieldOrder)
23+
24+
// Invariant 1: Output length matches nEls
25+
if len(result) != int(nEls) {
26+
t.Errorf("output length mismatch: got %d, want %d", len(result), nEls)
27+
}
28+
29+
// Invariant 2: All elements are in valid field range
30+
for i, el := range result {
31+
if el == nil {
32+
t.Errorf("element %d is nil", i)
33+
continue
34+
}
35+
if el.Sign() < 0 {
36+
t.Errorf("element %d is negative: %v", i, el)
37+
}
38+
if el.Cmp(AuctionFieldOrder) >= 0 {
39+
t.Errorf("element %d >= fieldOrder: %v", i, el)
40+
}
41+
}
42+
43+
// Invariant 3: Determinism - same inputs produce same outputs
44+
result2 := DeriveBlindingVector(sharedSecrets, round, nEls, AuctionFieldOrder)
45+
for i := range result {
46+
if result[i].Cmp(result2[i]) != 0 {
47+
t.Errorf("non-deterministic: element %d differs on second call", i)
48+
}
49+
}
50+
51+
// Invariant 4: Different round produces different output (with high probability)
52+
if round < ^uint32(0) {
53+
result3 := DeriveBlindingVector(sharedSecrets, round+1, nEls, AuctionFieldOrder)
54+
allSame := true
55+
for i := range result {
56+
if result[i].Cmp(result3[i]) != 0 {
57+
allSame = false
58+
break
59+
}
60+
}
61+
if allSame && nEls > 0 {
62+
t.Errorf("different rounds produced identical output")
63+
}
64+
}
65+
})
66+
}
67+
68+
func FuzzDeriveXorBlindingVector(f *testing.F) {
69+
// Add seed corpus
70+
f.Add([]byte("shared-secret-1"), uint32(1), 100)
71+
f.Add([]byte("shared-secret-2"), uint32(0), 1)
72+
f.Add([]byte("shared-secret-3"), uint32(100), 16) // AES block size
73+
f.Add([]byte("shared-secret-4"), uint32(50), 17) // Non-aligned
74+
75+
f.Fuzz(func(t *testing.T, secret []byte, round uint32, nBytes int) {
76+
// Skip invalid inputs
77+
if len(secret) == 0 || nBytes < 0 || nBytes > 10000 {
78+
t.Skip()
79+
}
80+
81+
sharedSecrets := []SharedKey{secret}
82+
83+
result := DeriveXorBlindingVector(sharedSecrets, round, nBytes)
84+
85+
// Invariant 1: Output length matches nBytes
86+
if len(result) != nBytes {
87+
t.Errorf("output length mismatch: got %d, want %d", len(result), nBytes)
88+
}
89+
90+
// Invariant 2: Determinism
91+
result2 := DeriveXorBlindingVector(sharedSecrets, round, nBytes)
92+
if !bytes.Equal(result, result2) {
93+
t.Errorf("non-deterministic output")
94+
}
95+
96+
// Invariant 3: Different round produces different output
97+
if nBytes > 0 && round < ^uint32(0) {
98+
result3 := DeriveXorBlindingVector(sharedSecrets, round+1, nBytes)
99+
if bytes.Equal(result, result3) {
100+
t.Errorf("different rounds produced identical output")
101+
}
102+
}
103+
104+
// Invariant 4: Zero bytes returns empty slice
105+
if nBytes == 0 && len(result) != 0 {
106+
t.Errorf("zero nBytes should return empty slice")
107+
}
108+
})
109+
}
110+
111+
func FuzzXorBlindingRoundTrip(f *testing.F) {
112+
f.Add([]byte("secret1"), []byte("secret2"), uint32(5), 100)
113+
114+
f.Fuzz(func(t *testing.T, secret1, secret2 []byte, round uint32, nBytes int) {
115+
if len(secret1) == 0 || len(secret2) == 0 || nBytes <= 0 || nBytes > 1000 {
116+
t.Skip()
117+
}
118+
119+
secrets := []SharedKey{secret1, secret2}
120+
121+
// XOR blinding should be self-inverse when applied twice
122+
blinding := DeriveXorBlindingVector(secrets, round, nBytes)
123+
data := make([]byte, nBytes)
124+
for i := range data {
125+
data[i] = byte(i % 256)
126+
}
127+
original := make([]byte, nBytes)
128+
copy(original, data)
129+
130+
// Apply blinding
131+
XorInplace(data, blinding)
132+
133+
// Apply again (should restore original)
134+
XorInplace(data, blinding)
135+
136+
if !bytes.Equal(data, original) {
137+
t.Errorf("XOR round trip failed")
138+
}
139+
})
140+
}

0 commit comments

Comments
 (0)