|
|
|
// Copyright (c) 2020, The Garble Authors.
|
|
|
|
// See LICENSE for licensing information.
|
|
|
|
|
|
|
|
package literals
|
|
|
|
|
|
|
|
import (
|
|
|
|
"go/ast"
|
|
|
|
"go/token"
|
avoid using math/rand's global funcs like Seed and Intn
Go 1.20 is starting to deprecate the use of math/rand's global state,
per https://go.dev/issue/56319 and https://go.dev/issue/20661.
The reasoning is sound:
Deprecated: Programs that call Seed and then expect a specific sequence
of results from the global random source (using functions such as Int)
can be broken when a dependency changes how much it consumes from the
global random source. To avoid such breakages, programs that need a
specific result sequence should use NewRand(NewSource(seed)) to obtain a
random generator that other packages cannot access.
Aside from the tests, we used math/rand only for obfuscating literals,
which caused a deterministic series of calls like Intn. Our call to Seed
was also deterministic, per either GarbleActionID or the -seed flag.
However, our determinism was fragile. If any of our dependencies or
other packages made any calls to math/rand's global funcs, then our
determinism could be broken entirely, and it's hard to notice.
Start using separate math/rand.Rand objects for each use case.
Also make uses of crypto/rand use "cryptorand" for consistency.
Note that this requires a bit of a refactor in internal/literals
to start passing around Rand objects. We also do away with unnecessary
short funcs, especially since math/rand's Read never errors,
and we can obtain a byte via math/rand's Uint32.
2 years ago
|
|
|
mathrand "math/rand"
|
|
|
|
|
|
|
|
ah "mvdan.cc/garble/internal/asthelper"
|
|
|
|
)
|
|
|
|
|
|
|
|
type seed struct{}
|
|
|
|
|
|
|
|
// check that the obfuscator interface is implemented
|
|
|
|
var _ obfuscator = seed{}
|
|
|
|
|
|
|
|
func (seed) obfuscate(obfRand *mathrand.Rand, data []byte, extKeys []*extKey) *ast.BlockStmt {
|
avoid using math/rand's global funcs like Seed and Intn
Go 1.20 is starting to deprecate the use of math/rand's global state,
per https://go.dev/issue/56319 and https://go.dev/issue/20661.
The reasoning is sound:
Deprecated: Programs that call Seed and then expect a specific sequence
of results from the global random source (using functions such as Int)
can be broken when a dependency changes how much it consumes from the
global random source. To avoid such breakages, programs that need a
specific result sequence should use NewRand(NewSource(seed)) to obtain a
random generator that other packages cannot access.
Aside from the tests, we used math/rand only for obfuscating literals,
which caused a deterministic series of calls like Intn. Our call to Seed
was also deterministic, per either GarbleActionID or the -seed flag.
However, our determinism was fragile. If any of our dependencies or
other packages made any calls to math/rand's global funcs, then our
determinism could be broken entirely, and it's hard to notice.
Start using separate math/rand.Rand objects for each use case.
Also make uses of crypto/rand use "cryptorand" for consistency.
Note that this requires a bit of a refactor in internal/literals
to start passing around Rand objects. We also do away with unnecessary
short funcs, especially since math/rand's Read never errors,
and we can obtain a byte via math/rand's Uint32.
2 years ago
|
|
|
seed := byte(obfRand.Uint32())
|
|
|
|
originalSeed := seed
|
|
|
|
|
avoid using math/rand's global funcs like Seed and Intn
Go 1.20 is starting to deprecate the use of math/rand's global state,
per https://go.dev/issue/56319 and https://go.dev/issue/20661.
The reasoning is sound:
Deprecated: Programs that call Seed and then expect a specific sequence
of results from the global random source (using functions such as Int)
can be broken when a dependency changes how much it consumes from the
global random source. To avoid such breakages, programs that need a
specific result sequence should use NewRand(NewSource(seed)) to obtain a
random generator that other packages cannot access.
Aside from the tests, we used math/rand only for obfuscating literals,
which caused a deterministic series of calls like Intn. Our call to Seed
was also deterministic, per either GarbleActionID or the -seed flag.
However, our determinism was fragile. If any of our dependencies or
other packages made any calls to math/rand's global funcs, then our
determinism could be broken entirely, and it's hard to notice.
Start using separate math/rand.Rand objects for each use case.
Also make uses of crypto/rand use "cryptorand" for consistency.
Note that this requires a bit of a refactor in internal/literals
to start passing around Rand objects. We also do away with unnecessary
short funcs, especially since math/rand's Read never errors,
and we can obtain a byte via math/rand's Uint32.
2 years ago
|
|
|
op := randOperator(obfRand)
|
|
|
|
var callExpr *ast.CallExpr
|
|
|
|
for i, b := range data {
|
|
|
|
encB := evalOperator(op, b, seed)
|
|
|
|
seed += encB
|
|
|
|
|
|
|
|
if i == 0 {
|
|
|
|
callExpr = ah.CallExpr(ast.NewIdent("fnc"), byteLitWithExtKey(obfRand, encB, extKeys, commonRarity))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
callExpr = ah.CallExpr(callExpr, byteLitWithExtKey(obfRand, encB, extKeys, rareRarity))
|
|
|
|
}
|
|
|
|
|
|
|
|
return ah.BlockStmt(
|
|
|
|
&ast.AssignStmt{
|
|
|
|
Lhs: []ast.Expr{ast.NewIdent("seed")},
|
|
|
|
Tok: token.DEFINE,
|
|
|
|
Rhs: []ast.Expr{ah.CallExprByName("byte", byteLitWithExtKey(obfRand, originalSeed, extKeys, commonRarity))},
|
|
|
|
},
|
|
|
|
&ast.DeclStmt{
|
|
|
|
Decl: &ast.GenDecl{
|
|
|
|
Tok: token.VAR,
|
|
|
|
Specs: []ast.Spec{&ast.ValueSpec{
|
|
|
|
Names: []*ast.Ident{ast.NewIdent("data")},
|
|
|
|
Type: &ast.ArrayType{Elt: ast.NewIdent("byte")},
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&ast.DeclStmt{
|
|
|
|
Decl: &ast.GenDecl{
|
|
|
|
Tok: token.TYPE,
|
|
|
|
Specs: []ast.Spec{&ast.TypeSpec{
|
|
|
|
Name: ast.NewIdent("decFunc"),
|
|
|
|
Type: &ast.FuncType{
|
|
|
|
Params: &ast.FieldList{List: []*ast.Field{
|
|
|
|
{Type: ast.NewIdent("byte")},
|
|
|
|
}},
|
|
|
|
Results: &ast.FieldList{List: []*ast.Field{
|
|
|
|
{Type: ast.NewIdent("decFunc")},
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&ast.DeclStmt{
|
|
|
|
Decl: &ast.GenDecl{
|
|
|
|
Tok: token.VAR,
|
|
|
|
Specs: []ast.Spec{&ast.ValueSpec{
|
|
|
|
Names: []*ast.Ident{ast.NewIdent("fnc")},
|
|
|
|
Type: ast.NewIdent("decFunc"),
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&ast.AssignStmt{
|
|
|
|
Lhs: []ast.Expr{ast.NewIdent("fnc")},
|
|
|
|
Tok: token.ASSIGN,
|
|
|
|
Rhs: []ast.Expr{
|
|
|
|
&ast.FuncLit{
|
|
|
|
Type: &ast.FuncType{
|
|
|
|
Params: &ast.FieldList{
|
|
|
|
List: []*ast.Field{{
|
|
|
|
Names: []*ast.Ident{ast.NewIdent("x")},
|
|
|
|
Type: ast.NewIdent("byte"),
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
Results: &ast.FieldList{
|
|
|
|
List: []*ast.Field{{
|
|
|
|
Type: ast.NewIdent("decFunc"),
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Body: ah.BlockStmt(
|
|
|
|
&ast.AssignStmt{
|
|
|
|
Lhs: []ast.Expr{ast.NewIdent("data")},
|
|
|
|
Tok: token.ASSIGN,
|
|
|
|
Rhs: []ast.Expr{
|
|
|
|
ah.CallExpr(ast.NewIdent("append"), ast.NewIdent("data"), operatorToReversedBinaryExpr(op, ast.NewIdent("x"), ast.NewIdent("seed"))),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&ast.AssignStmt{
|
|
|
|
Lhs: []ast.Expr{ast.NewIdent("seed")},
|
|
|
|
Tok: token.ADD_ASSIGN,
|
|
|
|
Rhs: []ast.Expr{ast.NewIdent("x")},
|
|
|
|
},
|
|
|
|
ah.ReturnStmt(ast.NewIdent("fnc")),
|
|
|
|
),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ah.ExprStmt(callExpr),
|
|
|
|
)
|
|
|
|
}
|