main.go raw

   1  package main
   2  
   3  import (
   4  	"os"
   5  	"runtime"
   6  	"unsafe"
   7  )
   8  
   9  // Milestone-3.2 verification: SecureRotate.
  10  //
  11  // Allocates a secret, rotates it, verifies:
  12  //   - the rotated slice holds the same bytes
  13  //   - the rotated slice has a different base address (fresh mapping)
  14  //   - the old base is unmapped — reading it faults, which triggers the
  15  //     standard wipe-and-die path
  16  //
  17  // Layout:
  18  //   parent stdout  — normal progress markers
  19  //   parent stderr  — MOXIE_SECALLOC_LOCKDOWN when the old-base read faults
  20  //
  21  // Harness verifies: process dies with a SIGSEGV after printing
  22  // ROTATED_OK, and the raw secret pattern never appears anywhere.
  23  
  24  func main() {
  25  	secret := []byte{:32, secure}
  26  	pattern := []byte("MOXIE_SECRET_PAYLOAD_32_BYTES_AA")
  27  	copy(secret, pattern)
  28  
  29  	oldBase := unsafe.Pointer(&secret[0])
  30  	os.Stdout.Write([]byte("BEFORE_ROTATE\n"))
  31  
  32  	rotated := runtime.SecureRotate(secret)
  33  	newBase := unsafe.Pointer(&rotated[0])
  34  
  35  	if newBase == oldBase {
  36  		os.Stderr.Write([]byte("FAIL: rotated slice reused old base\n"))
  37  		os.Exit(1)
  38  	}
  39  
  40  	// Verify contents survived the copy.
  41  	for i := 0; i < 32; i++ {
  42  		if rotated[i] != pattern[i] {
  43  			os.Stderr.Write([]byte("FAIL: rotated contents differ\n"))
  44  			os.Exit(1)
  45  		}
  46  	}
  47  	os.Stdout.Write([]byte("ROTATED_OK\n"))
  48  
  49  	// Rotate several more times to exercise the C-side slot reuse path.
  50  	// Without slot reuse the fixed 64-slot registry would fill up quickly.
  51  	live := rotated
  52  	for i := 0; i < 10; i++ {
  53  		live = runtime.SecureRotate(live)
  54  		if live[0] != pattern[0] || live[31] != pattern[31] {
  55  			os.Stderr.Write([]byte("FAIL: multi-rotate corrupted contents\n"))
  56  			os.Exit(1)
  57  		}
  58  	}
  59  	os.Stdout.Write([]byte("MULTI_ROTATED_OK\n"))
  60  
  61  	// Touch the old mapping. It should be unmapped — reading it faults.
  62  	// The signal handler wipes the (new) arena and prints the lockdown
  63  	// marker, then the process dies.
  64  	orphan := (*byte)(oldBase)
  65  	sink := *orphan
  66  	os.Stdout.Write([]byte{sink})
  67  
  68  	os.Stdout.Write([]byte("UNREACHABLE\n"))
  69  }
  70