Implement a reasonable default seed

Fixes #156
defaultSeed
Paul Scheduikat 4 months ago
parent d47e0761eb
commit 596f4b666f

@ -204,10 +204,10 @@ func toUpper(b byte) byte { return b - ('a' - 'A') }
func runtimeHashWithCustomSalt(salt []byte) uint32 {
hasher.Reset()
if !flagSeed.present() {
hasher.Write(sharedCache.ListedPackages["runtime"].GarbleActionID[:])
} else {
hasher.Write(flagSeed.bytes)
panic("runtimeHashWithCustomSalt: no seed")
}
hasher.Write(flagSeed.bytes)
hasher.Write(salt)
sum := hasher.Sum(sumBuffer[:0])
return binary.LittleEndian.Uint32(sum)
@ -226,12 +226,6 @@ func entryOffKey() uint32 {
}
func hashWithPackage(pkg *listedPackage, name string) string {
// If the user provided us with an obfuscation seed,
// we use that with the package import path directly..
// Otherwise, we use GarbleActionID as a fallback salt.
if !flagSeed.present() {
return hashWithCustomSalt(pkg.GarbleActionID[:], name)
}
// Use a separator at the end of ImportPath as a salt,
// to ensure that "pkgfoo.bar" and "pkg.foobar" don't both hash
// as the same string "pkgfoobar".
@ -256,13 +250,6 @@ func hashWithStruct(strct *types.Struct, field *types.Var) string {
// TODO: rethink once the proposed go/types.Hash API in https://go.dev/issue/69420 is merged.
salt := strconv.AppendUint(nil, uint64(typeutil_hash(strct)), 32)
// If the user provided us with an obfuscation seed,
// we only use the identity struct type as a salt.
// Otherwise, we add garble's own inputs to the salt as a fallback.
if !flagSeed.present() {
withGarbleHash := addGarbleToHash(salt)
salt = withGarbleHash[:]
}
return hashWithCustomSalt(salt, field.Name())
}
@ -333,6 +320,9 @@ func hashWithCustomSalt(salt []byte, name string) string {
if name == "" {
panic("hashWithCustomSalt: empty name")
}
if !flagSeed.present() {
panic("hashWithCustomSalt:no seed")
}
hasher.Reset()
hasher.Write(salt)

@ -9,6 +9,7 @@ import (
"bytes"
"cmp"
cryptorand "crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/gob"
@ -35,6 +36,7 @@ import (
"runtime/debug"
"runtime/pprof"
"slices"
"sort"
"strconv"
"strings"
"time"
@ -562,6 +564,11 @@ This command wraps "go %s". Below is its help:
return nil, err
}
// Compute a deterministic default seed if the user did not provide one.
if err := computeDefaultSeed(); err != nil {
return nil, err
}
sharedTempDir, err = saveSharedCache()
if err != nil {
return nil, err
@ -969,10 +976,10 @@ func (tf *transformer) transformCompile(args []string) ([]string, error) {
}
// Literal and control flow obfuscation uses math/rand, so seed it deterministically.
randSeed := tf.curPkg.GarbleActionID[:]
if flagSeed.present() {
randSeed = flagSeed.bytes
if !flagSeed.present() {
panic("transformCompile: no seed")
}
randSeed := flagSeed.bytes
// log.Printf("seeding math/rand with %x\n", randSeed)
tf.obfRand = mathrand.New(mathrand.NewSource(int64(binary.BigEndian.Uint64(randSeed))))
@ -2336,3 +2343,63 @@ To install Go, see: https://go.dev/doc/install
sharedCache.GOGARBLE = cmp.Or(os.Getenv("GOGARBLE"), "*") // we default to obfuscating everything
return nil
}
func computeDefaultSeed() error {
// If the user has provided a seed explicitly (or random), keep it.
if flagSeed.present() {
return nil
}
// Step 1: hash module information from `go list -m`.
args := []string{"list", "-m", "-json=Path,Version,GoVersion,Sum,GoModSum,Indirect,Time,Main", "all"}
cmd := exec.Command(sharedCache.GoCmd, args...)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("go list modules failed: %v\n%s", err, stderr.String())
}
hasher := sha256.New()
hasher.Write(stdout.Bytes())
// Parse the module list to determine the main module path.
dec := json.NewDecoder(bytes.NewReader(stdout.Bytes()))
mainModulePath := ""
for dec.More() {
var mod struct {
Path string
Main bool
}
if err := dec.Decode(&mod); err != nil {
return fmt.Errorf("decode module json: %w", err)
}
if mod.Main {
mainModulePath = mod.Path
}
}
if mainModulePath == "" {
return errors.New("could not determine main module path for seed derivation")
}
// Step 2: include BuildIDs of all packages inside the main module.
var pkgs []string
for path := range sharedCache.ListedPackages {
if strings.HasPrefix(path, mainModulePath) {
pkgs = append(pkgs, path)
}
}
// Sort for deterministic order.
sort.Strings(pkgs)
for _, path := range pkgs {
if buildID := sharedCache.ListedPackages[path].BuildID; buildID != "" {
hasher.Write([]byte(buildID))
}
}
seed := hasher.Sum(nil)
flagSeed.bytes = seed
return nil
}

Loading…
Cancel
Save