|  |  |  | // Copyright (c) 2020, The Garble Authors.
 | 
					
						
							|  |  |  | // See LICENSE for licensing information.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package literals | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"go/ast" | 
					
						
							|  |  |  | 	"go/token" | 
					
						
							|  |  |  | 	mathrand "math/rand" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ah "mvdan.cc/garble/internal/asthelper" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type shuffle struct{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // check that the obfuscator interface is implemented
 | 
					
						
							|  |  |  | var _ obfuscator = shuffle{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												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.
											
										 
											3 years ago
										 |  |  | func (shuffle) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt { | 
					
						
							|  |  |  | 	key := make([]byte, len(data)) | 
					
						
							| 
									
										
											  
											
												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.
											
										 
											3 years ago
										 |  |  | 	obfRand.Read(key) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const ( | 
					
						
							|  |  |  | 		minIdxKeySize = 2 | 
					
						
							|  |  |  | 		maxIdxKeySize = 16 | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	idxKeySize := minIdxKeySize | 
					
						
							|  |  |  | 	if tmp := obfRand.Intn(len(data)); tmp > idxKeySize { | 
					
						
							|  |  |  | 		idxKeySize = tmp | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if idxKeySize > maxIdxKeySize { | 
					
						
							|  |  |  | 		idxKeySize = maxIdxKeySize | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	idxKey := make([]byte, idxKeySize) | 
					
						
							|  |  |  | 	obfRand.Read(idxKey) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fullData := make([]byte, len(data)+len(key)) | 
					
						
							|  |  |  | 	operators := make([]token.Token, len(fullData)) | 
					
						
							|  |  |  | 	for i := range operators { | 
					
						
							| 
									
										
											  
											
												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.
											
										 
											3 years ago
										 |  |  | 		operators[i] = randOperator(obfRand) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, b := range key { | 
					
						
							|  |  |  | 		fullData[i], fullData[i+len(data)] = evalOperator(operators[i], data[i], b), b | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												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.
											
										 
											3 years ago
										 |  |  | 	shuffledIdxs := obfRand.Perm(len(fullData)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	shuffledFullData := make([]byte, len(fullData)) | 
					
						
							|  |  |  | 	for i, b := range fullData { | 
					
						
							|  |  |  | 		shuffledFullData[shuffledIdxs[i]] = b | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	args := []ast.Expr{ast.NewIdent("data")} | 
					
						
							|  |  |  | 	for i := range data { | 
					
						
							|  |  |  | 		keyIdx := obfRand.Intn(idxKeySize) | 
					
						
							|  |  |  | 		k := int(idxKey[keyIdx]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		args = append(args, operatorToReversedBinaryExpr( | 
					
						
							|  |  |  | 			operators[i], | 
					
						
							|  |  |  | 			ah.IndexExpr("fullData", &ast.BinaryExpr{X: ah.IntLit(shuffledIdxs[i] ^ k), Op: token.XOR, Y: ah.CallExprByName("int", ah.IndexExpr("idxKey", ah.IntLit(keyIdx)))}), | 
					
						
							|  |  |  | 			ah.IndexExpr("fullData", &ast.BinaryExpr{X: ah.IntLit(shuffledIdxs[len(data)+i] ^ k), Op: token.XOR, Y: ah.CallExprByName("int", ah.IndexExpr("idxKey", ah.IntLit(keyIdx)))}), | 
					
						
							|  |  |  | 		)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ah.BlockStmt( | 
					
						
							|  |  |  | 		&ast.AssignStmt{ | 
					
						
							|  |  |  | 			Lhs: []ast.Expr{ast.NewIdent("fullData")}, | 
					
						
							|  |  |  | 			Tok: token.DEFINE, | 
					
						
							|  |  |  | 			Rhs: []ast.Expr{ah.DataToByteSlice(shuffledFullData)}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		&ast.AssignStmt{ | 
					
						
							|  |  |  | 			Lhs: []ast.Expr{ast.NewIdent("idxKey")}, | 
					
						
							|  |  |  | 			Tok: token.DEFINE, | 
					
						
							|  |  |  | 			Rhs: []ast.Expr{ah.DataToByteSlice(idxKey)}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		&ast.AssignStmt{ | 
					
						
							|  |  |  | 			Lhs: []ast.Expr{ast.NewIdent("data")}, | 
					
						
							|  |  |  | 			Tok: token.DEFINE, | 
					
						
							|  |  |  | 			Rhs: []ast.Expr{ah.CallExpr(ast.NewIdent("make"), &ast.ArrayType{Elt: ast.NewIdent("byte")}, ah.IntLit(0), ah.IntLit(len(data)+1))}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		&ast.AssignStmt{ | 
					
						
							|  |  |  | 			Lhs: []ast.Expr{ast.NewIdent("data")}, | 
					
						
							|  |  |  | 			Tok: token.ASSIGN, | 
					
						
							|  |  |  | 			Rhs: []ast.Expr{ah.CallExpr(ast.NewIdent("append"), args...)}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | } |