● Notice //GitHub (@shehzade) is temporarily suspended pending an automated false-positive review. All tools are hosted on this Code Mirror and available on request (contact); GitHub links will return once reinstated.
PUBLIC MIRRORGo v1.0

Multi-protocol network egress assessment and DLP verification framework featuring automated L4 sweeps, corporate proxy traversal, and covert protocol smuggling across 20+ channels.

$ git clone https://shehzade.io/repos/contraband.git Copy
Source ZIP (1.93 MB)
Code 174 Commits 1
contraband / pkg / client / traffic / cadence.go
← Back to directory
180 lines · 4.9 KB
Raw
1package traffic
2
3import (
4 "context"
5 "crypto/rand"
6 "encoding/binary"
7 "fmt"
8 "math"
9 "sync"
10 "time"
11
12 "github.com/shehzade/contraband/pkg/common"
13)
14
15// DistributionType identifies probability distribution for jitter calculations.
16type DistributionType string
17
18const (
19 DistributionUniform DistributionType = "uniform"
20 DistributionGaussian DistributionType = "gaussian"
21)
22
23// CadenceEngine generates delays and schedules pacing between chunks, probes, or sweeps.
24type CadenceEngine struct {
25 mu sync.Mutex
26 profile string // burst, steady, jittered, office-hours, low-and-slow
27 intervalMin time.Duration // Minimum interval boundary
28 intervalMax time.Duration // Maximum interval boundary
29 jitterFactor float64 // 0.0 to 1.0 (or percentage 0 to 100)
30 distribution DistributionType // uniform or gaussian
31 packetGap time.Duration // Microsecond packet gap
32
33 // Office-hours state machine
34 burstCounter int // Current consecutive chunk count
35 burstThreshold int // Chunks before next idle pause (e.g. 5–15)
36 inOfficeIdle bool
37}
38
39// NewCadenceEngine initializes a CadenceEngine with given parameters.
40func NewCadenceEngine(profile string, minDur, maxDur time.Duration, jitter float64, dist DistributionType) (*CadenceEngine, error) {
41 if profile == "" {
42 profile = common.CadenceSteady
43 }
44
45 // Normalize jitter factor
46 if jitter > 1.0 {
47 jitter = jitter / 100.0
48 }
49 if jitter < 0.0 {
50 jitter = 0.0
51 } else if jitter > 1.0 {
52 jitter = 1.0
53 }
54
55 if minDur < 0 {
56 minDur = 0
57 }
58 if maxDur < 0 {
59 maxDur = 0
60 }
61 if maxDur > 0 && minDur > maxDur {
62 return nil, fmt.Errorf("interval_min (%v) cannot be greater than interval_max (%v)", minDur, maxDur)
63 }
64
65 if dist == "" {
66 dist = DistributionGaussian
67 }
68
69 return &CadenceEngine{
70 profile: profile,
71 intervalMin: minDur,
72 intervalMax: maxDur,
73 jitterFactor: jitter,
74 distribution: dist,
75 burstThreshold: 8,
76 }, nil
77}
78
79// NextDelay calculates the next pacing delay according to the configured cadence profile.
80func (c *CadenceEngine) NextDelay() time.Duration {
81 c.mu.Lock()
82 defer c.mu.Unlock()
83
84 switch c.profile {
85 case common.CadenceBurst:
86 return 0
87
88 case common.CadenceSteady:
89 return c.intervalMin
90
91 case common.CadenceJittered:
92 return c.sampleInterval(c.intervalMin, c.intervalMax)
93
94 case common.CadenceOfficeHours:
95 c.burstCounter++
96 if c.burstCounter >= c.burstThreshold {
97 // Trigger realistic user idle think time (10s to 60s)
98 c.burstCounter = 0
99 c.burstThreshold = 5 + int(c.randFloat64()*10.0) // 5 to 14 chunks
100 idleMin := 10 * time.Second
101 idleMax := 60 * time.Second
102 return c.sampleInterval(idleMin, idleMax)
103 }
104 // Active work interval: short inter-packet delay (50ms to 200ms)
105 return c.sampleInterval(50*time.Millisecond, 200*time.Millisecond)
106
107 case common.CadenceLowAndSlow:
108 // Enforce long drip delays (e.g. 30s to 120s if not specified)
109 minD := c.intervalMin
110 maxD := c.intervalMax
111 if minD == 0 && maxD == 0 {
112 minD = 30 * time.Second
113 maxD = 120 * time.Second
114 }
115 return c.sampleInterval(minD, maxD)
116
117 default:
118 return c.intervalMin
119 }
120}
121
122// sampleInterval calculates a random delay between min and max using configured distribution.
123func (c *CadenceEngine) sampleInterval(minD, maxD time.Duration) time.Duration {
124 if minD == maxD || maxD == 0 {
125 if c.jitterFactor > 0 {
126 // Apply jitter factor around base interval: [base*(1-J), base*(1+J)]
127 base := float64(minD)
128 halfRange := base * c.jitterFactor
129 low := math.Max(0, base-halfRange)
130 high := base + halfRange
131 return time.Duration(low + c.randFloat64()*(high-low))
132 }
133 return minD
134 }
135
136 if c.distribution == DistributionUniform {
137 delta := float64(maxD - minD)
138 return minD + time.Duration(c.randFloat64()*delta)
139 }
140
141 // Gaussian distribution: Box-Muller transform
142 // Midpoint mu, standard deviation sigma = (max - min) / 6
143 mu := float64(minD+maxD) / 2.0
144 sigma := float64(maxD-minD) / 6.0
145
146 // Box-Muller transform
147 u1 := c.randFloat64()
148 if u1 <= 0.0 {
149 u1 = 1e-9
150 }
151 u2 := c.randFloat64()
152 z0 := math.Sqrt(-2.0*math.Log(u1)) * math.Cos(2.0*math.Pi*u2)
153
154 sampled := mu + z0*sigma
155 // Clamp to [minD, maxD]
156 clamped := math.Max(float64(minD), math.Min(float64(maxD), sampled))
157 return time.Duration(clamped)
158}
159
160// randFloat64 generates a cryptographically secure pseudo-random float in [0.0, 1.0).
161func (c *CadenceEngine) randFloat64() float64 {
162 var buf [8]byte
163 if _, err := rand.Read(buf[:]); err != nil {
164 // Fallback to nanosecond epoch
165 return float64(time.Now().UnixNano()%1000000) / 1000000.0
166 }
167 val := binary.BigEndian.Uint64(buf[:])
168 return float64(val>>11) / float64(1<<53)
169}
170
171// Sleep calculates NextDelay and blocks with context cancellation.
172func (c *CadenceEngine) Sleep(ctx context.Context) error {
173 d := c.NextDelay()
174 return sleepContext(ctx, d)
175}
176
177// SleepDuration executes context-aware sleep for an explicit duration.
178func (c *CadenceEngine) SleepDuration(ctx context.Context, d time.Duration) error {
179 return sleepContext(ctx, d)
180}