Source file src/cmd/go/internal/fips140/fips_test.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package fips140
     6  
     7  import (
     8  	"crypto/sha256"
     9  	"flag"
    10  	"fmt"
    11  	"internal/testenv"
    12  	"maps"
    13  	"os"
    14  	"path/filepath"
    15  	"slices"
    16  	"strings"
    17  	"testing"
    18  
    19  	"golang.org/x/mod/sumdb/dirhash"
    20  )
    21  
    22  var update = flag.Bool("update", false, "update GOROOT/lib/fips140/fips140.sum")
    23  
    24  func TestSums(t *testing.T) {
    25  	lib := filepath.Join(testenv.GOROOT(t), "lib/fips140")
    26  	file := filepath.Join(lib, "fips140.sum")
    27  	sums, err := os.ReadFile(file)
    28  	if err != nil {
    29  		t.Fatal(err)
    30  	}
    31  	lines := strings.SplitAfter(string(sums), "\n")
    32  
    33  	zips, err := filepath.Glob(filepath.Join(lib, "*.zip"))
    34  	if err != nil {
    35  		t.Fatal(err)
    36  	}
    37  
    38  	format := func(name string, sum [32]byte, ziphash string) string {
    39  		return fmt.Sprintf("%s %x %s\n", name, sum[:], ziphash)
    40  	}
    41  
    42  	want := make(map[string]string)
    43  	for _, zip := range zips {
    44  		data, err := os.ReadFile(zip)
    45  		if err != nil {
    46  			t.Fatal(err)
    47  		}
    48  		ziphash, err := dirhash.HashZip(zip, dirhash.DefaultHash)
    49  		if err != nil {
    50  			t.Fatal(err)
    51  		}
    52  		name := filepath.Base(zip)
    53  		want[name] = format(name, sha256.Sum256(data), ziphash)
    54  	}
    55  
    56  	// Process diff, deleting or correcting stale lines.
    57  	var diff []string
    58  	have := make(map[string]bool)
    59  	for i, line := range lines {
    60  		if line == "" {
    61  			continue
    62  		}
    63  		if strings.HasPrefix(line, "#") || line == "\n" {
    64  			// comment, preserve
    65  			diff = append(diff, " "+line)
    66  			continue
    67  		}
    68  		name, _, _ := strings.Cut(line, " ")
    69  		if want[name] == "" {
    70  			lines[i] = ""
    71  			diff = append(diff, "-"+line)
    72  			continue
    73  		}
    74  		have[name] = true
    75  		fixed := want[name]
    76  		delete(want, name)
    77  		if line == fixed {
    78  			diff = append(diff, " "+line)
    79  		} else {
    80  			// zip hashes should never change once listed
    81  			t.Errorf("policy violation: zip file hash is changing:\n-%s+%s", line, fixed)
    82  			lines[i] = fixed
    83  			diff = append(diff, "-"+line, "+"+fixed)
    84  		}
    85  	}
    86  
    87  	// Add missing lines.
    88  	// Sort keys to avoid non-determinism, but overall file is not sorted.
    89  	// It will end up time-ordered instead.
    90  	for _, name := range slices.Sorted(maps.Keys(want)) {
    91  		line := want[name]
    92  		lines = append(lines, line)
    93  		diff = append(diff, "+"+line)
    94  	}
    95  
    96  	// Show diffs or update file.
    97  	fixed := strings.Join(lines, "")
    98  	if fixed != string(sums) {
    99  		if *update && !t.Failed() {
   100  			t.Logf("updating GOROOT/lib/fips140/fips140.sum:\n%s", strings.Join(diff, ""))
   101  			if err := os.WriteFile(file, []byte(fixed), 0666); err != nil {
   102  				t.Fatal(err)
   103  			}
   104  			return
   105  		}
   106  		t.Errorf("GOROOT/lib/fips140/fips140.sum out of date. changes needed:\n%s", strings.Join(diff, ""))
   107  	}
   108  }
   109  
   110  func TestVerifyZipSum(t *testing.T) {
   111  	dir := t.TempDir()
   112  	zipfile := filepath.Join(dir, "v1.2.3.zip")
   113  	data := []byte("not really a zip, but it does not matter here")
   114  	if err := os.WriteFile(zipfile, data, 0666); err != nil {
   115  		t.Fatal(err)
   116  	}
   117  	sum := sha256.Sum256(data)
   118  
   119  	sumfile := filepath.Join(dir, "fips140.sum")
   120  	write := func(contents string) {
   121  		if err := os.WriteFile(sumfile, []byte(contents), 0666); err != nil {
   122  			t.Fatal(err)
   123  		}
   124  	}
   125  
   126  	const (
   127  		zeroSum = "0000000000000000000000000000000000000000000000000000000000000000"
   128  		ziphash = "h1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
   129  	)
   130  
   131  	// Matching hash with comments and a second entry passes.
   132  	write(fmt.Sprintf("# comment\n\nv1.2.3.zip %x %s\nother.zip %s h1:other=\n", sum[:], ziphash, zeroSum))
   133  	if err := verifyZipSum(zipfile, sumfile); err != nil {
   134  		t.Errorf("unexpected error: %v", err)
   135  	}
   136  
   137  	// Missing entry for this zip fails.
   138  	write("# only comments\n")
   139  	if err := verifyZipSum(zipfile, sumfile); err == nil {
   140  		t.Errorf("expected error when hash entry is missing")
   141  	}
   142  
   143  	// Wrong hash fails.
   144  	write(fmt.Sprintf("v1.2.3.zip %s %s\n", zeroSum, ziphash))
   145  	if err := verifyZipSum(zipfile, sumfile); err == nil {
   146  		t.Errorf("expected error when hash does not match")
   147  	}
   148  
   149  	// Missing sum file fails.
   150  	if err := verifyZipSum(zipfile, filepath.Join(dir, "does-not-exist.sum")); err == nil {
   151  		t.Errorf("expected error when sum file is missing")
   152  	}
   153  }
   154  

View as plain text