...

Source file src/crypto/internal/fips140test/cast_test.go

Documentation: crypto/internal/fips140test

     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 fipstest
     6  
     7  import (
     8  	"crypto"
     9  	"crypto/rand"
    10  	"fmt"
    11  	"internal/testenv"
    12  	"io/fs"
    13  	"os"
    14  	"regexp"
    15  	"slices"
    16  	"strings"
    17  	"testing"
    18  
    19  	"crypto/internal/fips140"
    20  	// Import packages that define CASTs to test them.
    21  	_ "crypto/internal/fips140/aes"
    22  	_ "crypto/internal/fips140/aes/gcm"
    23  	_ "crypto/internal/fips140/drbg"
    24  	"crypto/internal/fips140/ecdh"
    25  	"crypto/internal/fips140/ecdsa"
    26  	"crypto/internal/fips140/ed25519"
    27  	_ "crypto/internal/fips140/hkdf"
    28  	_ "crypto/internal/fips140/hmac"
    29  	"crypto/internal/fips140/mlkem"
    30  	"crypto/internal/fips140/rsa"
    31  	"crypto/internal/fips140/sha256"
    32  	_ "crypto/internal/fips140/sha3"
    33  	_ "crypto/internal/fips140/sha512"
    34  	_ "crypto/internal/fips140/tls12"
    35  	_ "crypto/internal/fips140/tls13"
    36  )
    37  
    38  var allCASTs = []string{
    39  	"AES-CBC",
    40  	"CTR_DRBG",
    41  	"CounterKDF",
    42  	"DetECDSA P-256 SHA2-512 sign",
    43  	"ECDH PCT",
    44  	"ECDSA P-256 SHA2-512 sign and verify",
    45  	"ECDSA PCT",
    46  	"Ed25519 sign and verify",
    47  	"Ed25519 sign and verify PCT",
    48  	"HKDF-SHA2-256",
    49  	"HMAC-SHA2-256",
    50  	"KAS-ECC-SSC P-256",
    51  	"ML-KEM PCT",
    52  	"ML-KEM PCT",
    53  	"ML-KEM-768",
    54  	"PBKDF2",
    55  	"RSA sign and verify PCT",
    56  	"RSASSA-PKCS-v1.5 2048-bit sign and verify",
    57  	"SHA2-256",
    58  	"SHA2-512",
    59  	"TLSv1.2-SHA2-256",
    60  	"TLSv1.3-SHA2-256",
    61  	"cSHAKE128",
    62  }
    63  
    64  func TestAllCASTs(t *testing.T) {
    65  	testenv.MustHaveSource(t)
    66  
    67  	// Ask "go list" for the location of the crypto/internal/fips140 tree, as it
    68  	// might be the unpacked frozen tree selected with GOFIPS140.
    69  	cmd := testenv.Command(t, testenv.GoToolPath(t), "list", "-f", `{{.Dir}}`, "crypto/internal/fips140")
    70  	out, err := cmd.CombinedOutput()
    71  	if err != nil {
    72  		t.Fatalf("go list: %v\n%s", err, out)
    73  	}
    74  	fipsDir := strings.TrimSpace(string(out))
    75  	t.Logf("FIPS module directory: %s", fipsDir)
    76  
    77  	// Find all invocations of fips140.CAST or fips140.PCT.
    78  	var foundCASTs []string
    79  	castRe := regexp.MustCompile(`fips140\.(CAST|PCT)\("([^"]+)"`)
    80  	if err := fs.WalkDir(os.DirFS(fipsDir), ".", func(path string, d fs.DirEntry, err error) error {
    81  		if err != nil {
    82  			return err
    83  		}
    84  		if d.IsDir() || !strings.HasSuffix(path, ".go") {
    85  			return nil
    86  		}
    87  		data, err := os.ReadFile(fipsDir + "/" + path)
    88  		if err != nil {
    89  			return err
    90  		}
    91  		for _, m := range castRe.FindAllSubmatch(data, -1) {
    92  			foundCASTs = append(foundCASTs, string(m[2]))
    93  		}
    94  		return nil
    95  	}); err != nil {
    96  		t.Fatalf("WalkDir: %v", err)
    97  	}
    98  
    99  	slices.Sort(foundCASTs)
   100  	if !slices.Equal(foundCASTs, allCASTs) {
   101  		t.Errorf("AllCASTs is out of date. Found CASTs: %#v", foundCASTs)
   102  	}
   103  }
   104  
   105  // TestConditionals causes the conditional CASTs and PCTs to be invoked.
   106  func TestConditionals(t *testing.T) {
   107  	mlkem.GenerateKey768()
   108  	kDH, err := ecdh.GenerateKey(ecdh.P256(), rand.Reader)
   109  	if err != nil {
   110  		t.Error(err)
   111  	} else {
   112  		ecdh.ECDH(ecdh.P256(), kDH, kDH.PublicKey())
   113  	}
   114  	kDSA, err := ecdsa.GenerateKey(ecdsa.P256(), rand.Reader)
   115  	if err != nil {
   116  		t.Error(err)
   117  	} else {
   118  		ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32))
   119  	}
   120  	k25519, err := ed25519.GenerateKey()
   121  	if err != nil {
   122  		t.Error(err)
   123  	} else {
   124  		ed25519.Sign(k25519, make([]byte, 32))
   125  	}
   126  	kRSA, err := rsa.GenerateKey(rand.Reader, 2048)
   127  	if err != nil {
   128  		t.Error(err)
   129  	} else {
   130  		rsa.SignPKCS1v15(kRSA, crypto.SHA256.String(), make([]byte, 32))
   131  	}
   132  	t.Log("completed successfully")
   133  }
   134  
   135  func TestCASTPasses(t *testing.T) {
   136  	testenv.MustHaveExec(t)
   137  	if err := fips140.Supported(); err != nil {
   138  		t.Skipf("FIPS140 not supported: %v", err)
   139  	}
   140  
   141  	cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^TestConditionals$", "-test.v")
   142  	cmd.Env = append(cmd.Env, "GODEBUG=fips140=debug")
   143  	out, err := cmd.CombinedOutput()
   144  	t.Logf("%s", out)
   145  	if err != nil || !strings.Contains(string(out), "completed successfully") {
   146  		t.Errorf("TestConditionals did not complete successfully")
   147  	}
   148  
   149  	for _, name := range allCASTs {
   150  		t.Run(name, func(t *testing.T) {
   151  			if !strings.Contains(string(out), fmt.Sprintf("passed: %s\n", name)) {
   152  				t.Errorf("CAST/PCT %s success was not logged", name)
   153  			} else {
   154  				t.Logf("CAST/PCT succeeded: %s", name)
   155  			}
   156  		})
   157  	}
   158  }
   159  
   160  func TestCASTFailures(t *testing.T) {
   161  	testenv.MustHaveExec(t)
   162  	if err := fips140.Supported(); err != nil {
   163  		t.Skipf("FIPS140 not supported: %v", err)
   164  	}
   165  
   166  	for _, name := range allCASTs {
   167  		t.Run(name, func(t *testing.T) {
   168  			// Don't parallelize if running in verbose mode, to produce a less
   169  			// confusing recoding for the validation lab.
   170  			if !testing.Verbose() {
   171  				t.Parallel()
   172  			}
   173  			t.Logf("Testing CAST/PCT failure...")
   174  			cmd := testenv.Command(t, testenv.Executable(t), "-test.run=TestConditionals", "-test.v")
   175  			cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=failfipscast=%s,fips140=on", name))
   176  			out, err := cmd.CombinedOutput()
   177  			t.Logf("%s", out)
   178  			if err == nil {
   179  				t.Fatal("Test did not fail as expected")
   180  			}
   181  			if strings.Contains(string(out), "completed successfully") {
   182  				t.Errorf("CAST/PCT %s failure did not stop the program", name)
   183  			} else if !strings.Contains(string(out), "self-test failed: "+name) {
   184  				t.Errorf("CAST/PCT %s failure did not log the expected message", name)
   185  			} else {
   186  				t.Logf("CAST/PCT %s failed as expected and caused the program to exit", name)
   187  			}
   188  		})
   189  	}
   190  }
   191  

View as plain text