Files
Xray-core/transport/internet/finalmask/sudoku/codec.go
T

164 lines
2.9 KiB
Go

package sudoku
import (
"fmt"
"math/rand"
)
var perm4 = [24][4]byte{
{0, 1, 2, 3},
{0, 1, 3, 2},
{0, 2, 1, 3},
{0, 2, 3, 1},
{0, 3, 1, 2},
{0, 3, 2, 1},
{1, 0, 2, 3},
{1, 0, 3, 2},
{1, 2, 0, 3},
{1, 2, 3, 0},
{1, 3, 0, 2},
{1, 3, 2, 0},
{2, 0, 1, 3},
{2, 0, 3, 1},
{2, 1, 0, 3},
{2, 1, 3, 0},
{2, 3, 0, 1},
{2, 3, 1, 0},
{3, 0, 1, 2},
{3, 0, 2, 1},
{3, 1, 0, 2},
{3, 1, 2, 0},
{3, 2, 0, 1},
{3, 2, 1, 0},
}
type codec struct {
tables []*table
rng *rand.Rand
paddingChance int
tableIndex int
}
func newCodec(tables []*table, pMin, pMax int) *codec {
if len(tables) == 0 {
tables = nil
}
rng := newSeededRand()
return &codec{
tables: tables,
rng: rng,
paddingChance: pickPaddingChance(rng, pMin, pMax),
}
}
func pickPaddingChance(rng *rand.Rand, pMin, pMax int) int {
if pMin < 0 {
pMin = 0
}
if pMax < pMin {
pMax = pMin
}
if pMin > 100 {
pMin = 100
}
if pMax > 100 {
pMax = 100
}
if pMax == pMin {
return pMin
}
return pMin + rng.Intn(pMax-pMin+1)
}
func (c *codec) shouldPad() bool {
if c.paddingChance <= 0 {
return false
}
if c.paddingChance >= 100 {
return true
}
return c.rng.Intn(100) < c.paddingChance
}
func (c *codec) currentTable() *table {
if len(c.tables) == 0 {
return nil
}
return c.tables[c.tableIndex%len(c.tables)]
}
func (c *codec) randomPadding(t *table) byte {
pool := t.layout.paddingPool
return pool[c.rng.Intn(len(pool))]
}
func (c *codec) encode(in []byte) ([]byte, error) {
if len(in) == 0 {
return nil, nil
}
out := make([]byte, 0, len(in)*6+8)
for _, b := range in {
t := c.currentTable()
if t == nil {
return nil, fmt.Errorf("sudoku table set missing")
}
if c.shouldPad() {
out = append(out, c.randomPadding(t))
}
enc := t.encode[b]
if len(enc) == 0 {
return nil, fmt.Errorf("sudoku encode table missing for byte %d", b)
}
hints := enc[c.rng.Intn(len(enc))]
perm := perm4[c.rng.Intn(len(perm4))]
for _, idx := range perm {
if c.shouldPad() {
out = append(out, c.randomPadding(t))
}
out = append(out, hints[idx])
}
c.tableIndex++
}
if c.shouldPad() {
if t := c.currentTable(); t != nil {
out = append(out, c.randomPadding(t))
}
}
return out, nil
}
func decodeBytes(tables []*table, tableIndex *int, in []byte, hintBuf []byte, out []byte) ([]byte, []byte, error) {
if len(tables) == 0 {
return hintBuf, out, fmt.Errorf("sudoku table set missing")
}
for _, b := range in {
t := tables[*tableIndex%len(tables)]
if !t.layout.isHint(b) {
continue
}
hintBuf = append(hintBuf, b)
if len(hintBuf) < 4 {
continue
}
keyBytes := sort4([4]byte{hintBuf[0], hintBuf[1], hintBuf[2], hintBuf[3]})
key := packKey(keyBytes)
decoded, ok := t.decode[key]
if !ok {
return hintBuf[:0], out, fmt.Errorf("invalid sudoku hint tuple")
}
out = append(out, decoded)
hintBuf = hintBuf[:0]
*tableIndex++
}
return hintBuf, out, nil
}