You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
garble/internal/literals/swap.go

157 lines
3.8 KiB
Go

// Copyright (c) 2020, The Garble Authors.
// See LICENSE for licensing information.
package literals
import (
"go/ast"
"go/token"
"math"
mathrand "math/rand"
ah "mvdan.cc/garble/internal/asthelper"
)
type swap struct{}
// check that the obfuscator interface is implemented
var _ obfuscator = swap{}
func getIndexType(dataLen int) string {
switch {
case dataLen <= math.MaxUint8:
return "byte"
case dataLen <= math.MaxUint16:
return "uint16"
case dataLen <= math.MaxUint32:
return "uint32"
default:
return "uint64"
}
}
func positionsToSlice(data []int) *ast.CompositeLit {
arr := &ast.CompositeLit{
Type: &ast.ArrayType{
Len: &ast.Ellipsis{}, // Performance optimization
Elt: ah.Ident(getIndexType(len(data))),
},
Elts: []ast.Expr{},
}
for _, data := range data {
arr.Elts = append(arr.Elts, ah.IntLit(data))
}
return arr
}
// Generates a random even swap count based on the length of data
func generateSwapCount(dataLen int) int {
swapCount := dataLen
maxExtraPositions := dataLen / 2 // Limit the number of extra positions to half the data length
if maxExtraPositions > 1 {
swapCount += mathrand.Intn(maxExtraPositions)
}
if swapCount%2 != 0 { // Swap count must be even
swapCount++
}
return swapCount
}
func (swap) obfuscate(data []byte) *ast.BlockStmt {
swapCount := generateSwapCount(len(data))
shiftKey := genRandByte()
op := randOperator()
positions := genRandIntSlice(len(data), swapCount)
for i := len(positions) - 2; i >= 0; i -= 2 {
// Generate local key for xor based on random key and byte position
localKey := byte(i) + byte(positions[i]^positions[i+1]) + shiftKey
// Swap bytes from i+1 to i and encrypt using operator and local key
data[positions[i]], data[positions[i+1]] = evalOperator(op, data[positions[i+1]], localKey), evalOperator(op, data[positions[i]], localKey)
}
return ah.BlockStmt(
&ast.AssignStmt{
Lhs: []ast.Expr{ah.Ident("data")},
Tok: token.DEFINE,
Rhs: []ast.Expr{ah.DataToByteSlice(data)},
},
&ast.AssignStmt{
Lhs: []ast.Expr{ah.Ident("positions")},
Tok: token.DEFINE,
Rhs: []ast.Expr{positionsToSlice(positions)},
},
&ast.ForStmt{
Init: &ast.AssignStmt{
Lhs: []ast.Expr{ah.Ident("i")},
Tok: token.DEFINE,
Rhs: []ast.Expr{ah.IntLit(0)},
},
Cond: &ast.BinaryExpr{
X: ah.Ident("i"),
Op: token.LSS,
Y: ah.IntLit(len(positions)),
},
Post: &ast.AssignStmt{
Lhs: []ast.Expr{ah.Ident("i")},
Tok: token.ADD_ASSIGN,
Rhs: []ast.Expr{ah.IntLit(2)},
},
Body: ah.BlockStmt(
&ast.AssignStmt{
Lhs: []ast.Expr{ah.Ident("localKey")},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.BinaryExpr{
X: &ast.BinaryExpr{
X: ah.CallExpr(ah.Ident("byte"), ah.Ident("i")),
Op: token.ADD,
Y: ah.CallExpr(ah.Ident("byte"), &ast.BinaryExpr{
X: ah.IndexExpr("positions", ah.Ident("i")),
Op: token.XOR,
Y: ah.IndexExpr("positions", &ast.BinaryExpr{
X: ah.Ident("i"),
Op: token.ADD,
Y: ah.IntLit(1),
}),
}),
},
Op: token.ADD,
Y: ah.IntLit(int(shiftKey)),
}},
},
&ast.AssignStmt{
Lhs: []ast.Expr{
ah.IndexExpr("data", ah.IndexExpr("positions", ah.Ident("i"))),
ah.IndexExpr("data", ah.IndexExpr("positions", &ast.BinaryExpr{
X: ah.Ident("i"),
Op: token.ADD,
Y: ah.IntLit(1),
})),
},
Tok: token.ASSIGN,
Rhs: []ast.Expr{
operatorToReversedBinaryExpr(
op,
ah.IndexExpr("data",
ah.IndexExpr("positions", &ast.BinaryExpr{
X: ah.Ident("i"),
Op: token.ADD,
Y: ah.IntLit(1),
}),
),
ah.Ident("localKey"),
),
operatorToReversedBinaryExpr(
op,
ah.IndexExpr("data", ah.IndexExpr("positions", ah.Ident("i"))),
ah.Ident("localKey"),
),
},
},
),
},
)
}