Use XOR instead of AES for literal obfuscation.
Implement a literal obfuscator interface, to allow the easy addition of new encodings. Add literal obfuscation for byte literals, integers, booleans, floats. Choose a random obfuscator on literal obfuscation, useful when multiple obfuscators are implemented. Fixes #62, #55.pull/64/head
parent
0c5e0a8944
commit
ce9efab562
@ -1,57 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
mathrand "math/rand"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// If math/rand.Seed() is not called, the generator behaves as if seeded by rand.Seed(1),
|
||||
// so the generator is deterministic.
|
||||
|
||||
// genAesKey generates a 128-bit AES Key.
|
||||
func genAesKey() []byte {
|
||||
return genRandBytes(16)
|
||||
}
|
||||
|
||||
// genAesKey generates a 128-bit nonce.
|
||||
func genNonce() []byte {
|
||||
return genRandBytes(12)
|
||||
}
|
||||
|
||||
// genRandBytes return a random []byte with the length of size.
|
||||
func genRandBytes(size int) []byte {
|
||||
buffer := make([]byte, size)
|
||||
|
||||
if strings.HasPrefix(envGarbleSeed, "random;") {
|
||||
_, err := rand.Read(buffer)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("couldn't generate random key: %v", err))
|
||||
}
|
||||
} else {
|
||||
mathrand.Read(buffer) // error is always nil so save to ignore
|
||||
}
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
// encAES encrypt data with key in AES GCM mode.
|
||||
func encAES(data, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nonce := genNonce()
|
||||
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext := aesgcm.Seal(nil, nonce, data, nil)
|
||||
encData := append(nonce, ciphertext...)
|
||||
return encData, nil
|
||||
}
|
@ -0,0 +1,374 @@
|
||||
package literals
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
mathrand "math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"mvdan.cc/garble/literals/obfuscators"
|
||||
)
|
||||
|
||||
func getCallexpr(resultType ast.Expr, block *ast.BlockStmt) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Fun: &ast.FuncLit{
|
||||
Type: &ast.FuncType{
|
||||
Params: &ast.FieldList{},
|
||||
Results: &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
{Type: resultType},
|
||||
},
|
||||
},
|
||||
},
|
||||
Body: block,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getObfuscator() obfuscators.Obfuscator {
|
||||
randPos := mathrand.Intn(len(obfuscators.Obfuscators))
|
||||
return obfuscators.Obfuscators[randPos]
|
||||
}
|
||||
|
||||
func getReturnStmt(result ast.Expr) *ast.ReturnStmt {
|
||||
return &ast.ReturnStmt{
|
||||
Results: []ast.Expr{result},
|
||||
}
|
||||
}
|
||||
|
||||
func isTypeDefStr(typ types.Type) bool {
|
||||
strType := types.Typ[types.String]
|
||||
|
||||
if named, ok := typ.(*types.Named); ok {
|
||||
return types.Identical(named.Underlying(), strType)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func containsTypeDefStr(expr ast.Expr, info *types.Info) bool {
|
||||
typ := info.TypeOf(expr)
|
||||
// log.Println(expr, typ, reflect.TypeOf(expr), reflect.TypeOf(typ))
|
||||
|
||||
if sig, ok := typ.(*types.Signature); ok {
|
||||
for i := 0; i < sig.Params().Len(); i++ {
|
||||
if isTypeDefStr(sig.Params().At(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mapT, ok := typ.(*types.Map); ok {
|
||||
return isTypeDefStr(mapT.Elem()) || isTypeDefStr(mapT.Key())
|
||||
}
|
||||
|
||||
if named, ok := typ.(*types.Named); ok {
|
||||
return isTypeDefStr(named)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Obfuscate replace literals with obfuscated lambda functions
|
||||
func Obfuscate(files []*ast.File, info *types.Info, blacklist map[types.Object]struct{}) []*ast.File {
|
||||
pre := func(cursor *astutil.Cursor) bool {
|
||||
switch x := cursor.Node().(type) {
|
||||
case *ast.ValueSpec:
|
||||
return !containsTypeDefStr(x.Type, info)
|
||||
|
||||
case *ast.AssignStmt:
|
||||
for _, expr := range x.Lhs {
|
||||
if index, ok := expr.(*ast.IndexExpr); ok {
|
||||
return !containsTypeDefStr(index.X, info)
|
||||
}
|
||||
|
||||
if ident, ok := expr.(*ast.Ident); ok {
|
||||
return !containsTypeDefStr(ident, info)
|
||||
}
|
||||
}
|
||||
case *ast.CallExpr:
|
||||
return !containsTypeDefStr(x.Fun, info)
|
||||
|
||||
case *ast.CompositeLit:
|
||||
if t, ok := x.Type.(*ast.MapType); ok {
|
||||
return !(containsTypeDefStr(t.Key, info) || containsTypeDefStr(t.Value, info))
|
||||
}
|
||||
|
||||
case *ast.FuncDecl:
|
||||
if x.Type.Results == nil {
|
||||
return true
|
||||
}
|
||||
for _, result := range x.Type.Results.List {
|
||||
for _, name := range result.Names {
|
||||
return !containsTypeDefStr(name, info)
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.KeyValueExpr:
|
||||
if ident, ok := x.Key.(*ast.Ident); ok {
|
||||
return !containsTypeDefStr(ident, info)
|
||||
}
|
||||
case *ast.GenDecl:
|
||||
if x.Tok != token.CONST {
|
||||
return true
|
||||
}
|
||||
for _, spec := range x.Specs {
|
||||
spec, ok := spec.(*ast.ValueSpec)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, name := range spec.Names {
|
||||
obj := info.ObjectOf(name)
|
||||
|
||||
// The object itself is blacklisted, e.g. a value that needs to be constant
|
||||
if _, ok := blacklist[obj]; ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, val := range spec.Values {
|
||||
if _, ok := val.(*ast.BasicLit); !ok {
|
||||
return false // skip the block if it contains non basic literals
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
x.Tok = token.VAR
|
||||
// constants are not possible if we want to obfuscate literals, therefore
|
||||
// move all constant blocks which only contain strings to variables
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
post := func(cursor *astutil.Cursor) bool {
|
||||
switch x := cursor.Node().(type) {
|
||||
case *ast.Ident:
|
||||
obj := info.ObjectOf(x)
|
||||
if obj == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if obj.Type() == types.Typ[types.Bool] || obj.Type() == types.Typ[types.UntypedBool] {
|
||||
if obj.Name() == "true" || obj.Name() == "false" {
|
||||
cursor.Replace(obfuscateBool(x.Name == "true"))
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.CompositeLit:
|
||||
if info.TypeOf(x.Type).String() == "[]byte" {
|
||||
var data []byte
|
||||
|
||||
for _, el := range x.Elts {
|
||||
lit, ok := el.(*ast.BasicLit)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
value, err := strconv.Atoi(lit.Value)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
data = append(data, byte(value))
|
||||
}
|
||||
|
||||
cursor.Replace(obfuscateByte(data))
|
||||
}
|
||||
case *ast.UnaryExpr:
|
||||
switch cursor.Name() {
|
||||
case "Values", "Rhs", "Value", "Args", "X":
|
||||
default:
|
||||
return true // we don't want to obfuscate imports etc.
|
||||
}
|
||||
|
||||
obfuscateNumberLiteral(cursor, info)
|
||||
|
||||
case *ast.BasicLit:
|
||||
_, ok := cursor.Parent().(*ast.UnaryExpr)
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
switch cursor.Name() {
|
||||
case "Values", "Rhs", "Value", "Args", "X":
|
||||
default:
|
||||
return true // we don't want to obfuscate imports etc.
|
||||
}
|
||||
|
||||
switch x.Kind {
|
||||
case token.FLOAT, token.INT:
|
||||
obfuscateNumberLiteral(cursor, info)
|
||||
|
||||
case token.STRING:
|
||||
value, err := strconv.Unquote(x.Value)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("cannot unquote string: %v", err))
|
||||
}
|
||||
|
||||
cursor.Replace(obfuscateString(value))
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
for i := range files {
|
||||
files[i] = astutil.Apply(files[i], pre, post).(*ast.File)
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
func obfuscateNumberLiteral(cursor *astutil.Cursor, info *types.Info) error {
|
||||
var (
|
||||
call *ast.CallExpr
|
||||
basic *ast.BasicLit
|
||||
ok bool
|
||||
typeInfo types.Type
|
||||
)
|
||||
|
||||
sign := ""
|
||||
node := cursor.Node()
|
||||
|
||||
switch x := node.(type) {
|
||||
case *ast.UnaryExpr:
|
||||
basic, ok = x.X.(*ast.BasicLit)
|
||||
if !ok {
|
||||
return errors.New("UnaryExpr doesn't contain basic literal")
|
||||
}
|
||||
typeInfo = info.TypeOf(x)
|
||||
|
||||
if x.Op != token.SUB {
|
||||
return errors.New("UnaryExpr has a non SUB token")
|
||||
}
|
||||
sign = "-"
|
||||
|
||||
switch y := cursor.Parent().(type) {
|
||||
case *ast.ValueSpec:
|
||||
tempInfo := info.TypeOf(y.Type)
|
||||
if tempInfo != nil {
|
||||
typeInfo = tempInfo
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.BasicLit:
|
||||
basic = x
|
||||
typeInfo = info.TypeOf(x)
|
||||
|
||||
default:
|
||||
return errors.New("Wrong node Type")
|
||||
}
|
||||
|
||||
strValue := sign + basic.Value
|
||||
|
||||
switch typeInfo {
|
||||
case types.Typ[types.Float32]:
|
||||
fV, err := strconv.ParseFloat(strValue, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
call = obfuscateFloat32(float32(fV))
|
||||
case types.Typ[types.Float64], types.Typ[types.UntypedFloat]:
|
||||
fV, err := strconv.ParseFloat(strValue, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
call = obfuscateFloat64(fV)
|
||||
}
|
||||
|
||||
if call != nil {
|
||||
cursor.Replace(call)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Explicitly typed integers can have a decimal place
|
||||
splitStrValue := strings.Split(strValue, ".")
|
||||
intValue, err := strconv.Atoi(splitStrValue[0])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
switch typeInfo {
|
||||
case types.Typ[types.Int], types.Typ[types.UntypedInt]:
|
||||
call = obfuscateInt(intValue)
|
||||
case types.Typ[types.Int8]:
|
||||
call = obfuscateInt8(int8(intValue))
|
||||
case types.Typ[types.Int16]:
|
||||
call = obfuscateInt16(int16(intValue))
|
||||
case types.Typ[types.Int32]:
|
||||
call = obfuscateInt32(int32(intValue))
|
||||
case types.Typ[types.Int64]:
|
||||
call = obfuscateInt64(int64(intValue))
|
||||
case types.Typ[types.Uint]:
|
||||
call = obfuscateUint(uint(intValue))
|
||||
case types.Typ[types.Uint8]:
|
||||
call = obfuscateUint8(uint8(intValue))
|
||||
case types.Typ[types.Uint16]:
|
||||
call = obfuscateUint16(uint16(intValue))
|
||||
case types.Typ[types.Uint32]:
|
||||
call = obfuscateUint32(uint32(intValue))
|
||||
case types.Typ[types.Uint64]:
|
||||
call = obfuscateUint64(uint64(intValue))
|
||||
case types.Typ[types.Uintptr]:
|
||||
call = obfuscateUintptr(uintptr(intValue))
|
||||
}
|
||||
|
||||
if call == nil {
|
||||
return errors.New("Node is not Integer")
|
||||
}
|
||||
|
||||
cursor.Replace(call)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConstBlacklist blacklist identifieres used in constant expressions
|
||||
func ConstBlacklist(node ast.Node, info *types.Info, blacklist map[types.Object]struct{}) {
|
||||
blacklistObjects := func(node ast.Node) bool {
|
||||
ident, ok := node.(*ast.Ident)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
obj := info.ObjectOf(ident)
|
||||
blacklist[obj] = struct{}{}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
switch x := node.(type) {
|
||||
// in a slice or array composite literal all explicit keys must be constant representable
|
||||
case *ast.CompositeLit:
|
||||
if _, ok := x.Type.(*ast.ArrayType); !ok {
|
||||
break
|
||||
}
|
||||
for _, elt := range x.Elts {
|
||||
if kv, ok := elt.(*ast.KeyValueExpr); ok {
|
||||
ast.Inspect(kv.Key, blacklistObjects)
|
||||
}
|
||||
}
|
||||
// in an array type the length must be a constant representable
|
||||
case *ast.ArrayType:
|
||||
if x.Len != nil {
|
||||
ast.Inspect(x.Len, blacklistObjects)
|
||||
}
|
||||
|
||||
// in a const declaration all values must be constant representable
|
||||
case *ast.GenDecl:
|
||||
if x.Tok != token.CONST {
|
||||
break
|
||||
}
|
||||
for _, spec := range x.Specs {
|
||||
spec := spec.(*ast.ValueSpec)
|
||||
|
||||
for _, val := range spec.Values {
|
||||
ast.Inspect(val, blacklistObjects)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,459 @@
|
||||
package literals
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
func getBoundsCheck(pos string) *ast.AssignStmt {
|
||||
return &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{&ast.Ident{Name: "_"}},
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: pos,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func obfuscateUint8(data uint8) *ast.CallExpr {
|
||||
obfuscator := getObfuscator()
|
||||
block := obfuscator.Obfuscate([]byte{byte(data)})
|
||||
block.List = append(block.List,
|
||||
&ast.ReturnStmt{
|
||||
Results: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint8"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return getCallexpr(&ast.Ident{Name: "uint8"}, block)
|
||||
}
|
||||
|
||||
func obfuscateUint16(data uint16) *ast.CallExpr {
|
||||
obfuscator := getObfuscator()
|
||||
b := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(b, data)
|
||||
block := obfuscator.Obfuscate(b)
|
||||
|
||||
convertExpr := &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint16"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.OR,
|
||||
Y: &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint16"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.SHL,
|
||||
Y: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "8",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
block.List = append(block.List, getBoundsCheck("1"), getReturnStmt(convertExpr))
|
||||
|
||||
return getCallexpr(&ast.Ident{Name: "uint16"}, block)
|
||||
}
|
||||
|
||||
func obfuscateUint32(data uint32) *ast.CallExpr {
|
||||
obfuscator := getObfuscator()
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, data)
|
||||
block := obfuscator.Obfuscate(b)
|
||||
|
||||
convertExpr := &ast.BinaryExpr{
|
||||
X: &ast.BinaryExpr{
|
||||
X: &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint32"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.OR,
|
||||
Y: &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint32"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.SHL,
|
||||
Y: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "8",
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.OR,
|
||||
Y: &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint32"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.SHL,
|
||||
Y: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "16",
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.OR,
|
||||
Y: &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint32"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.SHL,
|
||||
Y: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "24",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
block.List = append(block.List, getBoundsCheck("3"), getReturnStmt(convertExpr))
|
||||
|
||||
return getCallexpr(&ast.Ident{Name: "uint32"}, block)
|
||||
}
|
||||
|
||||
func obfuscateUint64(data uint64) *ast.CallExpr {
|
||||
obfuscator := getObfuscator()
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, data)
|
||||
block := obfuscator.Obfuscate(b)
|
||||
convertExpr := &ast.BinaryExpr{
|
||||
X: &ast.BinaryExpr{
|
||||
X: &ast.BinaryExpr{
|
||||
X: &ast.BinaryExpr{
|
||||
X: &ast.BinaryExpr{
|
||||
X: &ast.BinaryExpr{
|
||||
X: &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint64"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.OR,
|
||||
Y: &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint64"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.SHL,
|
||||
Y: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "8",
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.OR,
|
||||
Y: &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint64"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.SHL,
|
||||
Y: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "16",
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.OR,
|
||||
Y: &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint64"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.SHL,
|
||||
Y: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "24",
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.OR,
|
||||
Y: &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint64"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.SHL,
|
||||
Y: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "32",
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.OR,
|
||||
Y: &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint64"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "5",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.SHL,
|
||||
Y: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "40",
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.OR,
|
||||
Y: &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint64"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "6",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.SHL,
|
||||
Y: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "48",
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.OR,
|
||||
Y: &ast.BinaryExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "uint64"},
|
||||
Args: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "7",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Op: token.SHL,
|
||||
Y: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "56",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
block.List = append(block.List, getBoundsCheck("7"), getReturnStmt(convertExpr))
|
||||
|
||||
return getCallexpr(&ast.Ident{Name: "uint64"}, block)
|
||||
}
|
||||
|
||||
func obfuscateUint(data uint) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Fun: &ast.Ident{
|
||||
Name: "uint",
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
obfuscateUint64(uint64(data)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func obfuscateUintptr(data uintptr) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Fun: &ast.Ident{
|
||||
Name: "uintptr",
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
obfuscateUint64(uint64(data)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func obfuscateInt8(data int8) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Fun: &ast.Ident{
|
||||
Name: "int8",
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
obfuscateUint8(uint8(data)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func obfuscateInt16(data int16) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Fun: &ast.Ident{
|
||||
Name: "int16",
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
obfuscateUint16(uint16(data)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func obfuscateInt32(data int32) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Fun: &ast.Ident{
|
||||
Name: "int32",
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
obfuscateUint32(uint32(data)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func obfuscateInt64(data int64) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Fun: &ast.Ident{
|
||||
Name: "int64",
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
obfuscateUint64(uint64(data)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func obfuscateInt(data int) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Fun: &ast.Ident{
|
||||
Name: "int",
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
obfuscateUint64(uint64(data)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func obfuscateFloat32(data float32) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Fun: &ast.Ident{
|
||||
Name: "float32",
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
obfuscateUint32(uint32(data)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func obfuscateFloat64(data float64) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Fun: &ast.Ident{
|
||||
Name: "float64",
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
obfuscateUint64(uint64(data)),
|
||||
},
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package obfuscators
|
||||
|
||||
import (
|
||||
cryptrand "crypto/rand"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
mathrand "math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Obfuscator takes a byte slice and converts it to a ast.BlockStmt
|
||||
type Obfuscator interface {
|
||||
Obfuscate(data []byte) *ast.BlockStmt
|
||||
}
|
||||
|
||||
var (
|
||||
// Obfuscators contains all types which implement the Obfuscator Interface
|
||||
Obfuscators = []Obfuscator{
|
||||
xor{},
|
||||
}
|
||||
envGarbleSeed = os.Getenv("GARBLE_SEED")
|
||||
)
|
||||
|
||||
// dataToByteSlice turns a byte slice like []byte{1, 2, 3} into an AST
|
||||
// expression
|
||||
func dataToByteSlice(data []byte) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Fun: &ast.ArrayType{
|
||||
Elt: &ast.Ident{Name: "byte"},
|
||||
},
|
||||
Args: []ast.Expr{&ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: fmt.Sprintf("%q", data),
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
// If math/rand.Seed() is not called, the generator behaves as if seeded by rand.Seed(1),
|
||||
// so the generator is deterministic.
|
||||
|
||||
// genRandBytes return a random []byte with the length of size.
|
||||
func genRandBytes(buffer []byte) {
|
||||
if strings.HasPrefix(envGarbleSeed, "random;") {
|
||||
_, err := cryptrand.Read(buffer)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("couldn't generate random key: %v", err))
|
||||
}
|
||||
} else {
|
||||
_, err := mathrand.Read(buffer)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("couldn't generate random key: %v", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func genRandInt() int {
|
||||
return mathrand.Int()
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package obfuscators
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
type xor struct{}
|
||||
|
||||
// check that the obfuscator interface is implemented
|
||||
var _ Obfuscator = xor{}
|
||||
|
||||
func (x xor) Obfuscate(data []byte) *ast.BlockStmt {
|
||||
key := make([]byte, len(data))
|
||||
genRandBytes(key)
|
||||
|
||||
for i, b := range key {
|
||||
data[i] = data[i] ^ b
|
||||
}
|
||||
|
||||
return &ast.BlockStmt{
|
||||
List: []ast.Stmt{
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.Ident{Name: "key"},
|
||||
},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{dataToByteSlice(key)},
|
||||
},
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.Ident{Name: "data"},
|
||||
},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{dataToByteSlice(data)},
|
||||
},
|
||||
&ast.RangeStmt{
|
||||
Key: &ast.Ident{Name: "i"},
|
||||
Value: &ast.Ident{Name: "b"},
|
||||
Tok: token.DEFINE,
|
||||
X: &ast.Ident{Name: "key"},
|
||||
Body: &ast.BlockStmt{
|
||||
List: []ast.Stmt{
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.Ident{Name: "i"},
|
||||
},
|
||||
},
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: []ast.Expr{
|
||||
&ast.BinaryExpr{
|
||||
X: &ast.IndexExpr{
|
||||
X: &ast.Ident{Name: "data"},
|
||||
Index: &ast.Ident{Name: "i"},
|
||||
},
|
||||
Op: token.XOR,
|
||||
Y: &ast.Ident{Name: "b"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package literals
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
func obfuscateString(data string) *ast.CallExpr {
|
||||
obfuscator := getObfuscator()
|
||||
block := obfuscator.Obfuscate([]byte(data))
|
||||
block.List = append(block.List, &ast.ReturnStmt{
|
||||
Results: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "string"},
|
||||
Args: []ast.Expr{
|
||||
&ast.Ident{Name: "data"},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return getCallexpr(&ast.Ident{Name: "string"}, block)
|
||||
}
|
||||
|
||||
func obfuscateByte(data []byte) *ast.CallExpr {
|
||||
obfuscator := getObfuscator()
|
||||
block := obfuscator.Obfuscate(data)
|
||||
block.List = append(block.List, &ast.ReturnStmt{
|
||||
Results: []ast.Expr{
|
||||
&ast.Ident{Name: "data"},
|
||||
},
|
||||
})
|
||||
return getCallexpr(&ast.ArrayType{Elt: &ast.Ident{Name: "byte"}}, block)
|
||||
}
|
||||
|
||||
func obfuscateBool(data bool) *ast.BinaryExpr {
|
||||
var dataUint8 uint8 = 0
|
||||
if data {
|
||||
dataUint8 = 1
|
||||
}
|
||||
|
||||
return &ast.BinaryExpr{
|
||||
X: obfuscateUint8(dataUint8),
|
||||
Op: token.EQL,
|
||||
Y: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "1",
|
||||
},
|
||||
}
|
||||
}
|
@ -1,362 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
func isTypeDefStr(typ types.Type) bool {
|
||||
strType := types.Typ[types.String]
|
||||
|
||||
if named, ok := typ.(*types.Named); ok {
|
||||
return types.Identical(named.Underlying(), strType)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func containsTypeDefStr(expr ast.Expr, info *types.Info) bool {
|
||||
typ := info.TypeOf(expr)
|
||||
// log.Println(expr, typ, reflect.TypeOf(expr), reflect.TypeOf(typ))
|
||||
|
||||
if sig, ok := typ.(*types.Signature); ok {
|
||||
for i := 0; i < sig.Params().Len(); i++ {
|
||||
if isTypeDefStr(sig.Params().At(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mapT, ok := typ.(*types.Map); ok {
|
||||
return isTypeDefStr(mapT.Elem()) || isTypeDefStr(mapT.Key())
|
||||
}
|
||||
|
||||
if named, ok := typ.(*types.Named); ok {
|
||||
return isTypeDefStr(named)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func obfuscateLiterals(files []*ast.File, info *types.Info, blacklist map[types.Object]struct{}) []*ast.File {
|
||||
pre := func(cursor *astutil.Cursor) bool {
|
||||
switch x := cursor.Node().(type) {
|
||||
case *ast.ValueSpec:
|
||||
return !containsTypeDefStr(x.Type, info)
|
||||
|
||||
case *ast.AssignStmt:
|
||||
for _, expr := range x.Lhs {
|
||||
if index, ok := expr.(*ast.IndexExpr); ok {
|
||||
return !containsTypeDefStr(index.X, info)
|
||||
}
|
||||
|
||||
if ident, ok := expr.(*ast.Ident); ok {
|
||||
return !containsTypeDefStr(ident, info)
|
||||
}
|
||||
}
|
||||
case *ast.CallExpr:
|
||||
return !containsTypeDefStr(x.Fun, info)
|
||||
|
||||
case *ast.CompositeLit:
|
||||
if t, ok := x.Type.(*ast.MapType); ok {
|
||||
return !(containsTypeDefStr(t.Key, info) || containsTypeDefStr(t.Value, info))
|
||||
}
|
||||
|
||||
case *ast.FuncDecl:
|
||||
if x.Type.Results == nil {
|
||||
return true
|
||||
}
|
||||
for _, result := range x.Type.Results.List {
|
||||
for _, name := range result.Names {
|
||||
return !containsTypeDefStr(name, info)
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.KeyValueExpr:
|
||||
if ident, ok := x.Key.(*ast.Ident); ok {
|
||||
return !containsTypeDefStr(ident, info)
|
||||
}
|
||||
case *ast.GenDecl:
|
||||
if x.Tok != token.CONST {
|
||||
return true
|
||||
}
|
||||
for _, spec := range x.Specs {
|
||||
spec, ok := spec.(*ast.ValueSpec)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, name := range spec.Names {
|
||||
obj := info.ObjectOf(name)
|
||||
|
||||
// The object itself is blacklisted, e.g. a value that needs to be constant
|
||||
if _, ok := blacklist[obj]; ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, val := range spec.Values {
|
||||
if v, ok := val.(*ast.BasicLit); !ok || v.Kind != token.STRING {
|
||||
return false // skip the block if it contains non basic literals
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
x.Tok = token.VAR
|
||||
// constants are not possible if we want to obfuscate literals, therefore
|
||||
// move all constant blocks which only contain strings to variables
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
key := genAesKey()
|
||||
addedToPkg := false // we only want to inject the code and imports once
|
||||
post := func(cursor *astutil.Cursor) bool {
|
||||
switch x := cursor.Node().(type) {
|
||||
case *ast.File:
|
||||
if addedToPkg {
|
||||
break
|
||||
}
|
||||
x.Decls = append(x.Decls, funcStmt)
|
||||
x.Decls = append(x.Decls, keyStmt(key))
|
||||
astutil.AddImport(fset, x, "crypto/aes")
|
||||
astutil.AddImport(fset, x, "crypto/cipher")
|
||||
|
||||
addedToPkg = true
|
||||
case *ast.BasicLit:
|
||||
switch cursor.Name() {
|
||||
case "Values", "Rhs", "Value", "Args":
|
||||
default:
|
||||
return true // we don't want to obfuscate imports etc.
|
||||
}
|
||||
if x.Kind != token.STRING {
|
||||
return true // TODO: garble literals other than strings
|
||||
}
|
||||
|
||||
value, err := strconv.Unquote(x.Value)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("cannot unquote string: %v", err))
|
||||
}
|
||||
ciphertext, err := encAES([]byte(value), key)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("cannot encrypt string: %v", err))
|
||||
}
|
||||
|
||||
cursor.Replace(ciphertextStmt(ciphertext))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
for i := range files {
|
||||
files[i] = astutil.Apply(files[i], pre, post).(*ast.File)
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
// AST definitions for injection
|
||||
var (
|
||||
aesCipherStmt = &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.Ident{Name: "block"},
|
||||
&ast.Ident{Name: "err"},
|
||||
},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{&ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "aes"},
|
||||
Sel: &ast.Ident{Name: "NewCipher"},
|
||||
},
|
||||
Args: []ast.Expr{&ast.Ident{Name: "garbleKey"}},
|
||||
}},
|
||||
}
|
||||
|
||||
aesGcmCipherStmt = &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.Ident{Name: "aesgcm"},
|
||||
&ast.Ident{Name: "err"},
|
||||
},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{&ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "cipher"},
|
||||
Sel: &ast.Ident{Name: "NewGCM"},
|
||||
},
|
||||
Args: []ast.Expr{&ast.Ident{Name: "block"}},
|
||||
}},
|
||||
}
|
||||
|
||||
plaintextStmt = &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.Ident{Name: "plaintext"},
|
||||
&ast.Ident{Name: "err"},
|
||||
},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{&ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "aesgcm"},
|
||||
Sel: &ast.Ident{Name: "Open"},
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.Ident{Name: "nil"},
|
||||
&ast.SliceExpr{
|
||||
X: &ast.Ident{Name: "ciphertext"},
|
||||
High: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "12",
|
||||
},
|
||||
},
|
||||
&ast.SliceExpr{
|
||||
X: &ast.Ident{Name: "ciphertext"},
|
||||
Low: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: "12",
|
||||
},
|
||||
},
|
||||
&ast.Ident{Name: "nil"},
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
returnStmt = &ast.ReturnStmt{Results: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "string"},
|
||||
Args: []ast.Expr{&ast.Ident{Name: "plaintext"}},
|
||||
},
|
||||
}}
|
||||
)
|
||||
|
||||
func decErrStmt() *ast.IfStmt {
|
||||
return &ast.IfStmt{
|
||||
Cond: &ast.BinaryExpr{
|
||||
X: &ast.Ident{Name: "err"},
|
||||
Op: token.NEQ,
|
||||
Y: &ast.Ident{Name: "nil"},
|
||||
},
|
||||
Body: &ast.BlockStmt{List: []ast.Stmt{
|
||||
&ast.ExprStmt{X: &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "panic"},
|
||||
Args: []ast.Expr{&ast.BinaryExpr{
|
||||
X: &ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: `"garble: literal couldn't be decrypted: "`,
|
||||
},
|
||||
Op: token.ADD,
|
||||
Y: &ast.CallExpr{Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "err"},
|
||||
Sel: &ast.Ident{Name: "Error"},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
var funcStmt = &ast.FuncDecl{
|
||||
Name: &ast.Ident{Name: "garbleDecrypt"},
|
||||
Type: &ast.FuncType{
|
||||
Params: &ast.FieldList{List: []*ast.Field{{
|
||||
Names: []*ast.Ident{{Name: "ciphertext"}},
|
||||
Type: &ast.ArrayType{
|
||||
Elt: &ast.Ident{Name: "byte"},
|
||||
},
|
||||
}}},
|
||||
Results: &ast.FieldList{List: []*ast.Field{{
|
||||
Type: &ast.Ident{Name: "string"},
|
||||
}}},
|
||||
},
|
||||
Body: &ast.BlockStmt{List: []ast.Stmt{
|
||||
aesCipherStmt,
|
||||
decErrStmt(),
|
||||
aesGcmCipherStmt,
|
||||
decErrStmt(),
|
||||
plaintextStmt,
|
||||
decErrStmt(),
|
||||
returnStmt,
|
||||
}},
|
||||
}
|
||||
|
||||
func ciphertextStmt(ciphertext []byte) *ast.CallExpr {
|
||||
ciphertextLit := dataToByteSlice(ciphertext)
|
||||
|
||||
return &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "garbleDecrypt"},
|
||||
Args: []ast.Expr{ciphertextLit},
|
||||
}
|
||||
}
|
||||
|
||||
// dataToByteSlice turns a byte slice like []byte{1, 2, 3} into an AST
|
||||
// expression
|
||||
func dataToByteSlice(data []byte) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Fun: &ast.ArrayType{
|
||||
Elt: &ast.Ident{Name: "byte"},
|
||||
},
|
||||
Args: []ast.Expr{&ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: fmt.Sprintf("%q", data),
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func keyStmt(key []byte) *ast.GenDecl {
|
||||
keyLit := dataToByteSlice(key)
|
||||
return &ast.GenDecl{
|
||||
Tok: token.VAR,
|
||||
Specs: []ast.Spec{&ast.ValueSpec{
|
||||
Names: []*ast.Ident{{Name: "garbleKey"}},
|
||||
Values: []ast.Expr{keyLit},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func constBlacklist(node ast.Node, info *types.Info, blacklist map[types.Object]struct{}) {
|
||||
blacklistObjects := func(node ast.Node) bool {
|
||||
ident, ok := node.(*ast.Ident)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
obj := info.ObjectOf(ident)
|
||||
blacklist[obj] = struct{}{}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
switch x := node.(type) {
|
||||
// in a slice or array composite literal all explicit keys must be constant representable
|
||||
case *ast.CompositeLit:
|
||||
if _, ok := x.Type.(*ast.ArrayType); !ok {
|
||||
break
|
||||
}
|
||||
for _, elt := range x.Elts {
|
||||
if kv, ok := elt.(*ast.KeyValueExpr); ok {
|
||||
ast.Inspect(kv.Key, blacklistObjects)
|
||||
}
|
||||
}
|
||||
// in an array type the length must be a constant representable
|
||||
case *ast.ArrayType:
|
||||
if x.Len != nil {
|
||||
ast.Inspect(x.Len, blacklistObjects)
|
||||
}
|
||||
|
||||
// in a const declaration all values must be constant representable
|
||||
case *ast.GenDecl:
|
||||
if x.Tok != token.CONST {
|
||||
break
|
||||
}
|
||||
for _, spec := range x.Specs {
|
||||
spec := spec.(*ast.ValueSpec)
|
||||
|
||||
for _, val := range spec.Values {
|
||||
ast.Inspect(val, blacklistObjects)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue