Source file src/crypto/internal/fips140test/cast_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 fipstest
     6  
     7  import (
     8  	"crypto/rand"
     9  	"crypto/x509"
    10  	"encoding/pem"
    11  	"fmt"
    12  	"internal/testenv"
    13  	"io/fs"
    14  	"os"
    15  	"regexp"
    16  	"slices"
    17  	"strings"
    18  	"testing"
    19  
    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 PCT",
    54  	"ML-KEM PCT",
    55  	"ML-KEM-768",
    56  	"PBKDF2",
    57  	"RSA sign and verify PCT",
    58  	"RSASSA-PKCS-v1.5 2048-bit sign and verify",
    59  	"SHA2-256",
    60  	"SHA2-512",
    61  	"TLSv1.2-SHA2-256",
    62  	"TLSv1.3-SHA2-256",
    63  	"cSHAKE128",
    64  }
    65  
    66  func TestAllCASTs(t *testing.T) {
    67  	testenv.MustHaveSource(t)
    68  
    69  	// Ask "go list" for the location of the crypto/internal/fips140 tree, as it
    70  	// might be the unpacked frozen tree selected with GOFIPS140.
    71  	cmd := testenv.Command(t, testenv.GoToolPath(t), "list", "-f", `{{.Dir}}`, "crypto/internal/fips140")
    72  	out, err := cmd.CombinedOutput()
    73  	if err != nil {
    74  		t.Fatalf("go list: %v\n%s", err, out)
    75  	}
    76  	fipsDir := strings.TrimSpace(string(out))
    77  	t.Logf("FIPS module directory: %s", fipsDir)
    78  
    79  	// Find all invocations of fips140.CAST or fips140.PCT.
    80  	var foundCASTs []string
    81  	castRe := regexp.MustCompile(`fips140\.(CAST|PCT)\("([^"]+)"`)
    82  	if err := fs.WalkDir(os.DirFS(fipsDir), ".", func(path string, d fs.DirEntry, err error) error {
    83  		if err != nil {
    84  			return err
    85  		}
    86  		if d.IsDir() || !strings.HasSuffix(path, ".go") {
    87  			return nil
    88  		}
    89  		data, err := os.ReadFile(fipsDir + "/" + path)
    90  		if err != nil {
    91  			return err
    92  		}
    93  		for _, m := range castRe.FindAllSubmatch(data, -1) {
    94  			foundCASTs = append(foundCASTs, string(m[2]))
    95  		}
    96  		return nil
    97  	}); err != nil {
    98  		t.Fatalf("WalkDir: %v", err)
    99  	}
   100  
   101  	slices.Sort(foundCASTs)
   102  	if !slices.Equal(foundCASTs, allCASTs) {
   103  		t.Errorf("AllCASTs is out of date. Found CASTs: %#v", foundCASTs)
   104  	}
   105  }
   106  
   107  // TestConditionals causes the conditional CASTs and PCTs to be invoked.
   108  func TestConditionals(t *testing.T) {
   109  	mlkem.GenerateKey768()
   110  	k, err := ecdh.GenerateKey(ecdh.P256(), rand.Reader)
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  	ecdh.ECDH(ecdh.P256(), k, k.PublicKey())
   115  	kDSA, err := ecdsa.GenerateKey(ecdsa.P256(), rand.Reader)
   116  	if err != nil {
   117  		t.Fatal(err)
   118  	}
   119  	ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32))
   120  	k25519, err := ed25519.GenerateKey()
   121  	if err != nil {
   122  		t.Fatal(err)
   123  	}
   124  	ed25519.Sign(k25519, make([]byte, 32))
   125  	rsa.VerifyPKCS1v15(&rsa.PublicKey{}, "", nil, nil)
   126  	// Parse an RSA key to hit the PCT rather than generating one (which is slow).
   127  	block, _ := pem.Decode([]byte(strings.ReplaceAll(
   128  		`-----BEGIN RSA TESTING KEY-----
   129  MIIEowIBAAKCAQEAsPnoGUOnrpiSqt4XynxA+HRP7S+BSObI6qJ7fQAVSPtRkqso
   130  tWxQYLEYzNEx5ZSHTGypibVsJylvCfuToDTfMul8b/CZjP2Ob0LdpYrNH6l5hvFE
   131  89FU1nZQF15oVLOpUgA7wGiHuEVawrGfey92UE68mOyUVXGweJIVDdxqdMoPvNNU
   132  l86BU02vlBiESxOuox+dWmuVV7vfYZ79Toh/LUK43YvJh+rhv4nKuF7iHjVjBd9s
   133  B6iDjj70HFldzOQ9r8SRI+9NirupPTkF5AKNe6kUhKJ1luB7S27ZkvB3tSTT3P59
   134  3VVJvnzOjaA1z6Cz+4+eRvcysqhrRgFlwI9TEwIDAQABAoIBAEEYiyDP29vCzx/+
   135  dS3LqnI5BjUuJhXUnc6AWX/PCgVAO+8A+gZRgvct7PtZb0sM6P9ZcLrweomlGezI
   136  FrL0/6xQaa8bBr/ve/a8155OgcjFo6fZEw3Dz7ra5fbSiPmu4/b/kvrg+Br1l77J
   137  aun6uUAs1f5B9wW+vbR7tzbT/mxaUeDiBzKpe15GwcvbJtdIVMa2YErtRjc1/5B2
   138  BGVXyvlJv0SIlcIEMsHgnAFOp1ZgQ08aDzvilLq8XVMOahAhP1O2A3X8hKdXPyrx
   139  IVWE9bS9ptTo+eF6eNl+d7htpKGEZHUxinoQpWEBTv+iOoHsVunkEJ3vjLP3lyI/
   140  fY0NQ1ECgYEA3RBXAjgvIys2gfU3keImF8e/TprLge1I2vbWmV2j6rZCg5r/AS0u
   141  pii5CvJ5/T5vfJPNgPBy8B/yRDs+6PJO1GmnlhOkG9JAIPkv0RBZvR0PMBtbp6nT
   142  Y3yo1lwamBVBfY6rc0sLTzosZh2aGoLzrHNMQFMGaauORzBFpY5lU50CgYEAzPHl
   143  u5DI6Xgep1vr8QvCUuEesCOgJg8Yh1UqVoY/SmQh6MYAv1I9bLGwrb3WW/7kqIoD
   144  fj0aQV5buVZI2loMomtU9KY5SFIsPV+JuUpy7/+VE01ZQM5FdY8wiYCQiVZYju9X
   145  Wz5LxMNoz+gT7pwlLCsC4N+R8aoBk404aF1gum8CgYAJ7VTq7Zj4TFV7Soa/T1eE
   146  k9y8a+kdoYk3BASpCHJ29M5R2KEA7YV9wrBklHTz8VzSTFTbKHEQ5W5csAhoL5Fo
   147  qoHzFFi3Qx7MHESQb9qHyolHEMNx6QdsHUn7rlEnaTTyrXh3ifQtD6C0yTmFXUIS
   148  CW9wKApOrnyKJ9nI0HcuZQKBgQCMtoV6e9VGX4AEfpuHvAAnMYQFgeBiYTkBKltQ
   149  XwozhH63uMMomUmtSG87Sz1TmrXadjAhy8gsG6I0pWaN7QgBuFnzQ/HOkwTm+qKw
   150  AsrZt4zeXNwsH7QXHEJCFnCmqw9QzEoZTrNtHJHpNboBuVnYcoueZEJrP8OnUG3r
   151  UjmopwKBgAqB2KYYMUqAOvYcBnEfLDmyZv9BTVNHbR2lKkMYqv5LlvDaBxVfilE0
   152  2riO4p6BaAdvzXjKeRrGNEKoHNBpOSfYCOM16NjL8hIZB1CaV3WbT5oY+jp7Mzd5
   153  7d56RZOE+ERK2uz/7JX9VSsM/LbH9pJibd4e8mikDS9ntciqOH/3
   154  -----END RSA TESTING KEY-----`, "TESTING KEY", "PRIVATE KEY")))
   155  	if _, err := x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
   156  		t.Fatal(err)
   157  	}
   158  	t.Log("completed successfully")
   159  }
   160  
   161  func TestCASTFailures(t *testing.T) {
   162  	moduleStatus(t)
   163  	testenv.MustHaveExec(t)
   164  
   165  	for _, name := range allCASTs {
   166  		t.Run(name, func(t *testing.T) {
   167  			// Don't parallelize if running in verbose mode, to produce a less
   168  			// confusing recoding for the validation lab.
   169  			if !testing.Verbose() {
   170  				t.Parallel()
   171  			}
   172  			t.Logf("CAST/PCT succeeded: %s", name)
   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 {
   184  				t.Logf("CAST/PCT %s failed as expected and caused the program to exit", name)
   185  			}
   186  		})
   187  	}
   188  }
   189  

View as plain text