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. Choose a random obfuscator on literal obfuscation, useful when multiple obfuscators are implemented. Fixes #62pull/69/head
parent
9c4b7d5a44
commit
d48bdbadae
@ -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,207 @@
|
|||||||
|
package literals
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
mathrand "math/rand"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func callExpr(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 randObfuscator() obfuscator {
|
||||||
|
randPos := mathrand.Intn(len(obfuscators))
|
||||||
|
return obfuscators[randPos]
|
||||||
|
}
|
||||||
|
|
||||||
|
func returnStmt(result ast.Expr) *ast.ReturnStmt {
|
||||||
|
return &ast.ReturnStmt{
|
||||||
|
Results: []ast.Expr{result},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obfuscate replace literals with obfuscated lambda functions
|
||||||
|
func Obfuscate(files []*ast.File, info *types.Info, fset *token.FileSet, blacklist map[types.Object]struct{}) []*ast.File {
|
||||||
|
pre := func(cursor *astutil.Cursor) bool {
|
||||||
|
switch x := cursor.Node().(type) {
|
||||||
|
|
||||||
|
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.CompositeLit:
|
||||||
|
byteType := types.Universe.Lookup("byte").Type()
|
||||||
|
|
||||||
|
switch x := info.TypeOf(x.Type).(type) {
|
||||||
|
case *types.Array:
|
||||||
|
if x.Elem() != byteType {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case *types.Slice:
|
||||||
|
if x.Elem() != byteType {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
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(obfuscateBytes(data))
|
||||||
|
case *ast.BasicLit:
|
||||||
|
switch cursor.Name() {
|
||||||
|
case "Values", "Rhs", "Value", "Args", "X", "Y", "Results":
|
||||||
|
default:
|
||||||
|
return true // we don't want to obfuscate imports etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
switch x.Kind {
|
||||||
|
case token.STRING:
|
||||||
|
typeInfo := info.TypeOf(x)
|
||||||
|
if typeInfo != types.Typ[types.String] && typeInfo != types.Typ[types.UntypedString] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
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 obfuscateString(data string) *ast.CallExpr {
|
||||||
|
obfuscator := randObfuscator()
|
||||||
|
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 callExpr(&ast.Ident{Name: "string"}, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
func obfuscateBytes(data []byte) *ast.CallExpr {
|
||||||
|
obfuscator := randObfuscator()
|
||||||
|
block := obfuscator.obfuscate(data)
|
||||||
|
block.List = append(block.List, &ast.ReturnStmt{
|
||||||
|
Results: []ast.Expr{&ast.Ident{Name: "data"}},
|
||||||
|
})
|
||||||
|
return callExpr(&ast.ArrayType{Elt: &ast.Ident{Name: "byte"}}, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,60 @@
|
|||||||
|
package literals
|
||||||
|
|
||||||
|
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,56 @@
|
|||||||
|
package literals
|
||||||
|
|
||||||
|
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"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
}
|
@ -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