Source file src/crypto/tls/fips140_test.go

     1  // Copyright 2017 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 tls
     6  
     7  import (
     8  	"crypto/ecdsa"
     9  	"crypto/elliptic"
    10  	"crypto/fips140"
    11  	"crypto/internal/boring"
    12  	"crypto/internal/cryptotest"
    13  	"crypto/mldsa"
    14  	"crypto/rand"
    15  	"crypto/rsa"
    16  	"crypto/x509"
    17  	"crypto/x509/pkix"
    18  	"fmt"
    19  	"internal/testenv"
    20  	"math/big"
    21  	"net"
    22  	"runtime"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  )
    27  
    28  var testConfigFIPS140 = &Config{
    29  	Time:         testTime,
    30  	Certificates: []Certificate{testECDSAP256Cert, testRSAPSSCert, testEd25519Cert, testMLDSA44Cert, testMLDSA65Cert, testMLDSA87Cert},
    31  	RootCAs:      testRootCertPool,
    32  	ServerName:   "test.golang.example",
    33  }
    34  
    35  func allCipherSuitesIncludingTLS13() []uint16 {
    36  	s := make([]uint16, 0, len(cipherSuites))
    37  	for _, suite := range cipherSuites {
    38  		s = append(s, suite.id)
    39  	}
    40  	for _, suite := range cipherSuitesTLS13 {
    41  		s = append(s, suite.id)
    42  	}
    43  	return s
    44  }
    45  
    46  func isTLS13CipherSuite(id uint16) bool {
    47  	for _, suite := range cipherSuitesTLS13 {
    48  		if id == suite.id {
    49  			return true
    50  		}
    51  	}
    52  	return false
    53  }
    54  
    55  func generateKeyShare(group CurveID) keyShare {
    56  	ke, err := keyExchangeForCurveID(group)
    57  	if err != nil {
    58  		panic(err)
    59  	}
    60  	_, shares, err := ke.keyShares(rand.Reader)
    61  	if err != nil {
    62  		panic(err)
    63  	}
    64  	return shares[0]
    65  }
    66  
    67  func TestFIPSServerProtocolVersion(t *testing.T) {
    68  	test := func(t *testing.T, name string, v uint16, msg string) {
    69  		t.Run(name, func(t *testing.T) {
    70  			serverConfig := testConfigFIPS140.Clone()
    71  			serverConfig.MinVersion = VersionSSL30
    72  			serverConfig.MaxVersion = VersionTLS13
    73  			clientConfig := testConfigFIPS140.Clone()
    74  			clientConfig.MinVersion = v
    75  			clientConfig.MaxVersion = v
    76  			_, _, err := testHandshake(t, clientConfig, serverConfig)
    77  			if msg == "" {
    78  				if err != nil {
    79  					t.Fatalf("got error: %v, expected success", err)
    80  				}
    81  			} else {
    82  				if err == nil {
    83  					t.Fatalf("got success, expected error")
    84  				}
    85  				if !strings.Contains(err.Error(), msg) {
    86  					t.Fatalf("got error %v, expected %q", err, msg)
    87  				}
    88  			}
    89  		})
    90  	}
    91  
    92  	runWithFIPSDisabled(t, func(t *testing.T) {
    93  		test(t, "VersionTLS10", VersionTLS10, "")
    94  		test(t, "VersionTLS11", VersionTLS11, "")
    95  		test(t, "VersionTLS12", VersionTLS12, "")
    96  		test(t, "VersionTLS13", VersionTLS13, "")
    97  	})
    98  
    99  	runWithFIPSEnabled(t, func(t *testing.T) {
   100  		test(t, "VersionTLS10", VersionTLS10, "supported versions")
   101  		test(t, "VersionTLS11", VersionTLS11, "supported versions")
   102  		test(t, "VersionTLS12", VersionTLS12, "")
   103  		test(t, "VersionTLS13", VersionTLS13, "")
   104  	})
   105  
   106  	if !fips140.Enforced() {
   107  		cryptotest.RerunWithFIPS140Enforced(t)
   108  	}
   109  }
   110  
   111  func isFIPSVersion(v uint16) bool {
   112  	return v == VersionTLS12 || v == VersionTLS13
   113  }
   114  
   115  func isFIPSCipherSuite(id uint16) bool {
   116  	name := CipherSuiteName(id)
   117  	if isTLS13CipherSuite(id) {
   118  		switch id {
   119  		case TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384:
   120  			return true
   121  		case TLS_CHACHA20_POLY1305_SHA256:
   122  			return false
   123  		default:
   124  			panic("unknown TLS 1.3 cipher suite: " + name)
   125  		}
   126  	}
   127  	switch id {
   128  	case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
   129  		TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
   130  		TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
   131  		TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
   132  		return true
   133  	case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
   134  		TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
   135  		// Only for the native module.
   136  		return !boring.Enabled
   137  	}
   138  	switch {
   139  	case strings.Contains(name, "CHACHA20"):
   140  		return false
   141  	case strings.HasSuffix(name, "_SHA"): // SHA-1
   142  		return false
   143  	case strings.HasPrefix(name, "TLS_RSA"): // RSA kex
   144  		return false
   145  	default:
   146  		panic("unknown cipher suite: " + name)
   147  	}
   148  }
   149  
   150  func isFIPSCurve(id CurveID) bool {
   151  	switch id {
   152  	case CurveP256, CurveP384, CurveP521:
   153  		return true
   154  	case X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, MLKEM1024:
   155  		// Only for the native module.
   156  		return !boring.Enabled
   157  	case X25519:
   158  		return false
   159  	default:
   160  		panic("unknown curve: " + id.String())
   161  	}
   162  }
   163  
   164  func isECDSA(id uint16) bool {
   165  	for _, suite := range cipherSuites {
   166  		if suite.id == id {
   167  			return suite.flags&suiteECSign == suiteECSign
   168  		}
   169  	}
   170  	return false // TLS 1.3 cipher suites are not tied to the signature algorithm.
   171  }
   172  
   173  func isFIPSSignatureScheme(alg SignatureScheme) bool {
   174  	switch alg {
   175  	case PKCS1WithSHA256,
   176  		ECDSAWithP256AndSHA256,
   177  		PKCS1WithSHA384,
   178  		ECDSAWithP384AndSHA384,
   179  		PKCS1WithSHA512,
   180  		ECDSAWithP521AndSHA512,
   181  		PSSWithSHA256,
   182  		PSSWithSHA384,
   183  		PSSWithSHA512:
   184  		return true
   185  	case Ed25519, MLDSA44, MLDSA65, MLDSA87:
   186  		// Only for the native module.
   187  		return !boring.Enabled
   188  	case PKCS1WithSHA1, ECDSAWithSHA1:
   189  		return false
   190  	default:
   191  		panic("unknown signature scheme: " + alg.String())
   192  	}
   193  }
   194  
   195  func TestFIPSServerCipherSuites(t *testing.T) {
   196  	for _, id := range allCipherSuitesIncludingTLS13() {
   197  		t.Run(fmt.Sprintf("suite=%s", CipherSuiteName(id)), func(t *testing.T) {
   198  			serverConfig := testConfigFIPS140.Clone()
   199  			clientHello := &clientHelloMsg{
   200  				vers:                         VersionTLS12,
   201  				random:                       make([]byte, 32),
   202  				cipherSuites:                 []uint16{id},
   203  				compressionMethods:           []uint8{compressionNone},
   204  				supportedCurves:              []CurveID{CurveP256},
   205  				keyShares:                    []keyShare{generateKeyShare(CurveP256)},
   206  				supportedPoints:              []uint8{pointFormatUncompressed},
   207  				supportedVersions:            []uint16{VersionTLS12},
   208  				supportedSignatureAlgorithms: allowedSignatureAlgorithmsFIPS,
   209  			}
   210  			if isTLS13CipherSuite(id) {
   211  				clientHello.supportedVersions = []uint16{VersionTLS13}
   212  			} else {
   213  				serverConfig.CipherSuites = []uint16{id}
   214  			}
   215  
   216  			runWithFIPSDisabled(t, func(t *testing.T) {
   217  				testClientHello(t, serverConfig, clientHello)
   218  			})
   219  
   220  			runWithFIPSEnabled(t, func(t *testing.T) {
   221  				msg := ""
   222  				if !isFIPSCipherSuite(id) {
   223  					msg = "no cipher suite supported by both client and server"
   224  				}
   225  				testClientHelloFailure(t, serverConfig, clientHello, msg)
   226  			})
   227  		})
   228  	}
   229  
   230  	if !fips140.Enforced() {
   231  		cryptotest.RerunWithFIPS140Enforced(t)
   232  	}
   233  }
   234  
   235  func TestFIPSServerCurves(t *testing.T) {
   236  	for _, curveid := range curvePreferenceOrder() {
   237  		t.Run(fmt.Sprintf("curve=%v", curveid), func(t *testing.T) {
   238  			testConfig := testConfigFIPS140.Clone()
   239  			testConfig.CurvePreferences = []CurveID{curveid}
   240  
   241  			runWithFIPSDisabled(t, func(t *testing.T) {
   242  				if _, _, err := testHandshake(t, testConfig, testConfig); err != nil {
   243  					t.Fatalf("got error: %v, expected success", err)
   244  				}
   245  			})
   246  
   247  			// With fipstls forced, bad curves should be rejected.
   248  			runWithFIPSEnabled(t, func(t *testing.T) {
   249  				_, _, err := testHandshake(t, testConfig, testConfig)
   250  				if err != nil && isFIPSCurve(curveid) {
   251  					t.Fatalf("got error: %v, expected success", err)
   252  				} else if err == nil && !isFIPSCurve(curveid) {
   253  					t.Fatalf("got success, expected error")
   254  				}
   255  			})
   256  		})
   257  	}
   258  
   259  	if !fips140.Enforced() {
   260  		cryptotest.RerunWithFIPS140Enforced(t)
   261  	}
   262  }
   263  
   264  func fipsHandshake(t *testing.T, clientConfig, serverConfig *Config) (clientErr, serverErr error) {
   265  	c, s := localPipe(t)
   266  	client := Client(c, clientConfig)
   267  	server := Server(s, serverConfig)
   268  	done := make(chan error, 1)
   269  	go func() {
   270  		done <- client.Handshake()
   271  		c.Close()
   272  	}()
   273  	serverErr = server.Handshake()
   274  	s.Close()
   275  	clientErr = <-done
   276  	return
   277  }
   278  
   279  func TestFIPSServerSignatureAndHash(t *testing.T) {
   280  	defer func() {
   281  		testingOnlySupportedSignatureAlgorithms = nil
   282  	}()
   283  	testenv.SetGODEBUG(t, "tlssha1=1")
   284  
   285  	for _, sigHash := range defaultSupportedSignatureAlgorithms() {
   286  		t.Run(fmt.Sprintf("%v", sigHash), func(t *testing.T) {
   287  			isMLDSA := sigHash == MLDSA44 || sigHash == MLDSA65 || sigHash == MLDSA87
   288  			if isMLDSA {
   289  				cryptotest.MustMinimumFIPS140ModuleVersion(t, "v1.26.0")
   290  			}
   291  			serverConfig := testConfigFIPS140.Clone()
   292  			testingOnlySupportedSignatureAlgorithms = []SignatureScheme{sigHash}
   293  			// PKCS#1 v1.5 signature algorithms can't be used standalone in TLS
   294  			// 1.3, and the ECDSA ones bind to the curve used. However, ML-DSA
   295  			// requires TLS 1.3.
   296  			if !isMLDSA {
   297  				serverConfig.MaxVersion = VersionTLS12
   298  			}
   299  
   300  			runWithFIPSDisabled(t, func(t *testing.T) {
   301  				clientErr, serverErr := fipsHandshake(t, testConfigFIPS140, serverConfig)
   302  				if clientErr != nil {
   303  					t.Fatalf("expected handshake with %v to succeed; client error: %v; server error: %v", sigHash, clientErr, serverErr)
   304  				}
   305  			})
   306  
   307  			// With fipstls forced, bad curves should be rejected.
   308  			runWithFIPSEnabled(t, func(t *testing.T) {
   309  				clientErr, _ := fipsHandshake(t, testConfigFIPS140, serverConfig)
   310  				if isFIPSSignatureScheme(sigHash) {
   311  					if clientErr != nil {
   312  						t.Fatalf("expected handshake with %v to succeed; err=%v", sigHash, clientErr)
   313  					}
   314  				} else {
   315  					if clientErr == nil {
   316  						t.Fatalf("expected handshake with %v to fail, but it succeeded", sigHash)
   317  					}
   318  				}
   319  			})
   320  		})
   321  	}
   322  
   323  	if !fips140.Enforced() {
   324  		cryptotest.RerunWithFIPS140Enforced(t)
   325  	}
   326  }
   327  
   328  func TestFIPSClientHello(t *testing.T) {
   329  	runWithFIPSEnabled(t, testFIPSClientHello)
   330  }
   331  
   332  func testFIPSClientHello(t *testing.T) {
   333  	// Test that no matter what we put in the client config,
   334  	// the client does not offer non-FIPS configurations.
   335  
   336  	c, s := net.Pipe()
   337  	defer c.Close()
   338  	defer s.Close()
   339  
   340  	clientConfig := testConfigFIPS140.Clone()
   341  	// All sorts of traps for the client to avoid.
   342  	clientConfig.MinVersion = VersionSSL30
   343  	clientConfig.MaxVersion = VersionTLS13
   344  	clientConfig.CipherSuites = allCipherSuitesIncludingTLS13()
   345  	clientConfig.CurvePreferences = curvePreferenceOrder()
   346  
   347  	go Client(c, clientConfig).Handshake()
   348  	srv := Server(s, testConfigFIPS140)
   349  	msg, err := srv.readHandshake(nil)
   350  	if err != nil {
   351  		t.Fatal(err)
   352  	}
   353  	hello, ok := msg.(*clientHelloMsg)
   354  	if !ok {
   355  		t.Fatalf("unexpected message type %T", msg)
   356  	}
   357  
   358  	if !isFIPSVersion(hello.vers) {
   359  		t.Errorf("client vers=%#x", hello.vers)
   360  	}
   361  	for _, v := range hello.supportedVersions {
   362  		if !isFIPSVersion(v) {
   363  			t.Errorf("client offered disallowed version %#x", v)
   364  		}
   365  	}
   366  	for _, id := range hello.cipherSuites {
   367  		if !isFIPSCipherSuite(id) {
   368  			t.Errorf("client offered disallowed suite %v", CipherSuiteName(id))
   369  		}
   370  	}
   371  	for _, id := range hello.supportedCurves {
   372  		if !isFIPSCurve(id) {
   373  			t.Errorf("client offered disallowed curve %v", id)
   374  		}
   375  	}
   376  	for _, sigHash := range hello.supportedSignatureAlgorithms {
   377  		if !isFIPSSignatureScheme(sigHash) {
   378  			t.Errorf("client offered disallowed signature-and-hash %v", sigHash)
   379  		}
   380  	}
   381  }
   382  
   383  func TestFIPSCertAlgs(t *testing.T) {
   384  	// arm and wasm time out generating keys. Nothing in this test is
   385  	// architecture-specific, so just don't bother on those.
   386  	if testenv.CPUIsSlow() {
   387  		t.Skipf("skipping on %s/%s because key generation takes too long", runtime.GOOS, runtime.GOARCH)
   388  	}
   389  
   390  	// Set up some roots, intermediate CAs, and leaf certs with various algorithms.
   391  	// X_Y is X signed by Y.
   392  	R1 := fipsCert(t, "R1", fipsRSAKey(t, 2048), nil, fipsCertCA|fipsCertFIPSOK)
   393  	R2 := fipsCert(t, "R2", fipsRSAKey(t, 1024), nil, fipsCertCA)
   394  	R3 := fipsCert(t, "R3", fipsRSAKey(t, 4096), nil, fipsCertCA|fipsCertFIPSOK)
   395  
   396  	M1_R1 := fipsCert(t, "M1_R1", fipsECDSAKey(t, elliptic.P256()), R1, fipsCertCA|fipsCertFIPSOK)
   397  	M2_R1 := fipsCert(t, "M2_R1", fipsECDSAKey(t, elliptic.P224()), R1, fipsCertCA)
   398  
   399  	I_R1 := fipsCert(t, "I_R1", fipsRSAKey(t, 3072), R1, fipsCertCA|fipsCertFIPSOK)
   400  	I_R2 := fipsCert(t, "I_R2", I_R1.key, R2, fipsCertCA|fipsCertFIPSOK)
   401  	I_M1 := fipsCert(t, "I_M1", I_R1.key, M1_R1, fipsCertCA|fipsCertFIPSOK)
   402  	I_M2 := fipsCert(t, "I_M2", I_R1.key, M2_R1, fipsCertCA|fipsCertFIPSOK)
   403  
   404  	I_R3 := fipsCert(t, "I_R3", fipsRSAKey(t, 3072), R3, fipsCertCA|fipsCertFIPSOK)
   405  	fipsCert(t, "I_R3", I_R3.key, R3, fipsCertCA|fipsCertFIPSOK)
   406  
   407  	L1_I := fipsCert(t, "L1_I", fipsECDSAKey(t, elliptic.P384()), I_R1, fipsCertLeaf|fipsCertFIPSOK)
   408  	L2_I := fipsCert(t, "L2_I", fipsRSAKey(t, 1024), I_R1, fipsCertLeaf)
   409  	var L3_I *fipsCertificate
   410  	if fips140.Version() != "v1.0.0" {
   411  		// ML-DSA is not implemented by the Go+BoringCrypto FIPS 140 module.
   412  		mldsaFlags := fipsCertLeaf | fipsCertFIPSOK
   413  		if boring.Enabled {
   414  			mldsaFlags = fipsCertLeaf
   415  		}
   416  		L3_I = fipsCert(t, "L3_I", fipsMLDSAKey(t, mldsa.MLDSA44()), I_R1, mldsaFlags)
   417  	}
   418  
   419  	// client verifying server cert
   420  	testServerCert := func(t *testing.T, desc string, pool *x509.CertPool, key any, list [][]byte, ok bool) {
   421  		clientConfig := testConfigFIPS140.Clone()
   422  		clientConfig.RootCAs = pool
   423  		clientConfig.InsecureSkipVerify = false
   424  		clientConfig.ServerName = "example.com"
   425  		clientConfig.Time = func() time.Time { return time.Unix(0, 0) }
   426  
   427  		serverConfig := testConfigFIPS140.Clone()
   428  		serverConfig.Certificates = []Certificate{{Certificate: list, PrivateKey: key}}
   429  		serverConfig.Time = func() time.Time { return time.Unix(0, 0) }
   430  
   431  		clientErr, _ := fipsHandshake(t, clientConfig, serverConfig)
   432  
   433  		if (clientErr == nil) == ok {
   434  			if ok {
   435  				t.Logf("%s: accept", desc)
   436  			} else {
   437  				t.Logf("%s: reject", desc)
   438  			}
   439  		} else {
   440  			if ok {
   441  				t.Errorf("%s: BAD reject (%v)", desc, clientErr)
   442  			} else {
   443  				t.Errorf("%s: BAD accept", desc)
   444  			}
   445  		}
   446  	}
   447  
   448  	// server verifying client cert
   449  	testClientCert := func(t *testing.T, desc string, pool *x509.CertPool, key any, list [][]byte, ok bool) {
   450  		clientConfig := testConfigFIPS140.Clone()
   451  		clientConfig.InsecureSkipVerify = true
   452  		clientConfig.Certificates = []Certificate{{Certificate: list, PrivateKey: key}}
   453  		clientConfig.Time = func() time.Time { return time.Unix(0, 0) }
   454  
   455  		serverConfig := testConfigFIPS140.Clone()
   456  		serverConfig.ClientCAs = pool
   457  		serverConfig.ClientAuth = RequireAndVerifyClientCert
   458  		serverConfig.Time = func() time.Time { return time.Unix(0, 0) }
   459  
   460  		_, serverErr := fipsHandshake(t, clientConfig, serverConfig)
   461  
   462  		if (serverErr == nil) == ok {
   463  			if ok {
   464  				t.Logf("%s: accept", desc)
   465  			} else {
   466  				t.Logf("%s: reject", desc)
   467  			}
   468  		} else {
   469  			if ok {
   470  				t.Errorf("%s: BAD reject (%v)", desc, serverErr)
   471  			} else {
   472  				t.Errorf("%s: BAD accept", desc)
   473  			}
   474  		}
   475  	}
   476  
   477  	// Run simple basic test with known answers before proceeding to
   478  	// exhaustive test with computed answers.
   479  	r1pool := x509.NewCertPool()
   480  	r1pool.AddCert(R1.cert)
   481  
   482  	runWithFIPSDisabled(t, func(t *testing.T) {
   483  		testServerCert(t, "basic", r1pool, L2_I.key, [][]byte{L2_I.der, I_R1.der}, true)
   484  		testClientCert(t, "basic (client cert)", r1pool, L2_I.key, [][]byte{L2_I.der, I_R1.der}, true)
   485  		if L3_I != nil {
   486  			testServerCert(t, "basic ML-DSA", r1pool, L3_I.key, [][]byte{L3_I.der, I_R1.der}, true)
   487  			testClientCert(t, "basic ML-DSA (client cert)", r1pool, L3_I.key, [][]byte{L3_I.der, I_R1.der}, true)
   488  		}
   489  	})
   490  
   491  	runWithFIPSEnabled(t, func(t *testing.T) {
   492  		testServerCert(t, "basic (fips)", r1pool, L2_I.key, [][]byte{L2_I.der, I_R1.der}, false)
   493  		testClientCert(t, "basic (fips, client cert)", r1pool, L2_I.key, [][]byte{L2_I.der, I_R1.der}, false)
   494  		if L3_I != nil {
   495  			testServerCert(t, "basic ML-DSA (fips)", r1pool, L3_I.key, [][]byte{L3_I.der, I_R1.der}, L3_I.fipsOK)
   496  			testClientCert(t, "basic ML-DSA (fips, client cert)", r1pool, L3_I.key, [][]byte{L3_I.der, I_R1.der}, L3_I.fipsOK)
   497  		}
   498  	})
   499  
   500  	if t.Failed() {
   501  		t.Fatal("basic test failed, skipping exhaustive test")
   502  	}
   503  
   504  	if testing.Short() {
   505  		t.Logf("basic test passed; skipping exhaustive test in -short mode")
   506  		return
   507  	}
   508  
   509  	for l := 1; l <= 2; l++ {
   510  		leaf := L1_I
   511  		if l == 2 {
   512  			leaf = L2_I
   513  		}
   514  		for i := 0; i < 64; i++ {
   515  			reachable := map[string]bool{leaf.parentOrg: true}
   516  			reachableFIPS := map[string]bool{leaf.parentOrg: leaf.fipsOK}
   517  			list := [][]byte{leaf.der}
   518  			listName := leaf.name
   519  			addList := func(cond int, c *fipsCertificate) {
   520  				if cond != 0 {
   521  					list = append(list, c.der)
   522  					listName += "," + c.name
   523  					if reachable[c.org] {
   524  						reachable[c.parentOrg] = true
   525  					}
   526  					if reachableFIPS[c.org] && c.fipsOK {
   527  						reachableFIPS[c.parentOrg] = true
   528  					}
   529  				}
   530  			}
   531  			addList(i&1, I_R1)
   532  			addList(i&2, I_R2)
   533  			addList(i&4, I_M1)
   534  			addList(i&8, I_M2)
   535  			addList(i&16, M1_R1)
   536  			addList(i&32, M2_R1)
   537  
   538  			for r := 1; r <= 3; r++ {
   539  				pool := x509.NewCertPool()
   540  				rootName := ","
   541  				shouldVerify := false
   542  				shouldVerifyFIPS := false
   543  				addRoot := func(cond int, c *fipsCertificate) {
   544  					if cond != 0 {
   545  						rootName += "," + c.name
   546  						pool.AddCert(c.cert)
   547  						if reachable[c.org] {
   548  							shouldVerify = true
   549  						}
   550  						if reachableFIPS[c.org] && c.fipsOK {
   551  							shouldVerifyFIPS = true
   552  						}
   553  					}
   554  				}
   555  				addRoot(r&1, R1)
   556  				addRoot(r&2, R2)
   557  				rootName = rootName[1:] // strip leading comma
   558  
   559  				runWithFIPSDisabled(t, func(t *testing.T) {
   560  					testServerCert(t, listName+"->"+rootName[1:], pool, leaf.key, list, shouldVerify)
   561  					testClientCert(t, listName+"->"+rootName[1:]+"(client cert)", pool, leaf.key, list, shouldVerify)
   562  				})
   563  
   564  				runWithFIPSEnabled(t, func(t *testing.T) {
   565  					testServerCert(t, listName+"->"+rootName[1:]+" (fips)", pool, leaf.key, list, shouldVerifyFIPS)
   566  					testClientCert(t, listName+"->"+rootName[1:]+" (fips, client cert)", pool, leaf.key, list, shouldVerifyFIPS)
   567  				})
   568  			}
   569  		}
   570  	}
   571  }
   572  
   573  const (
   574  	fipsCertCA = iota
   575  	fipsCertLeaf
   576  	fipsCertFIPSOK = 0x80
   577  )
   578  
   579  func fipsRSAKey(t *testing.T, size int) *rsa.PrivateKey {
   580  	k, err := rsa.GenerateKey(rand.Reader, size)
   581  	if err != nil {
   582  		t.Fatal(err)
   583  	}
   584  	return k
   585  }
   586  
   587  func fipsECDSAKey(t *testing.T, curve elliptic.Curve) *ecdsa.PrivateKey {
   588  	k, err := ecdsa.GenerateKey(curve, rand.Reader)
   589  	if err != nil {
   590  		t.Fatal(err)
   591  	}
   592  	return k
   593  }
   594  
   595  func fipsMLDSAKey(t *testing.T, params mldsa.Parameters) *mldsa.PrivateKey {
   596  	k, err := mldsa.GenerateKey(params)
   597  	if err != nil {
   598  		t.Fatal(err)
   599  	}
   600  	return k
   601  }
   602  
   603  type fipsCertificate struct {
   604  	name      string
   605  	org       string
   606  	parentOrg string
   607  	der       []byte
   608  	cert      *x509.Certificate
   609  	key       any
   610  	fipsOK    bool
   611  }
   612  
   613  func fipsCert(t *testing.T, name string, key any, parent *fipsCertificate, mode int) *fipsCertificate {
   614  	org := name
   615  	parentOrg := ""
   616  	if i := strings.Index(org, "_"); i >= 0 {
   617  		org = org[:i]
   618  		parentOrg = name[i+1:]
   619  	}
   620  	tmpl := &x509.Certificate{
   621  		SerialNumber: big.NewInt(1),
   622  		Subject: pkix.Name{
   623  			Organization: []string{org},
   624  		},
   625  		NotBefore: time.Unix(0, 0),
   626  		NotAfter:  time.Unix(0, 0),
   627  
   628  		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
   629  		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
   630  		BasicConstraintsValid: true,
   631  	}
   632  	if mode&^fipsCertFIPSOK == fipsCertLeaf {
   633  		tmpl.DNSNames = []string{"example.com"}
   634  	} else {
   635  		tmpl.IsCA = true
   636  		tmpl.KeyUsage |= x509.KeyUsageCertSign
   637  	}
   638  
   639  	var pcert *x509.Certificate
   640  	var pkey any
   641  	if parent != nil {
   642  		pcert = parent.cert
   643  		pkey = parent.key
   644  	} else {
   645  		pcert = tmpl
   646  		pkey = key
   647  	}
   648  
   649  	var pub any
   650  	var desc string
   651  	switch k := key.(type) {
   652  	case *rsa.PrivateKey:
   653  		pub = &k.PublicKey
   654  		desc = fmt.Sprintf("RSA-%d", k.N.BitLen())
   655  	case *ecdsa.PrivateKey:
   656  		pub = &k.PublicKey
   657  		desc = "ECDSA-" + k.Curve.Params().Name
   658  	case *mldsa.PrivateKey:
   659  		pub = k.PublicKey()
   660  		desc = k.PublicKey().Parameters().String()
   661  	default:
   662  		t.Fatalf("invalid key %T", key)
   663  	}
   664  
   665  	der, err := x509.CreateCertificate(rand.Reader, tmpl, pcert, pub, pkey)
   666  	if err != nil {
   667  		t.Fatal(err)
   668  	}
   669  	cert, err := x509.ParseCertificate(der)
   670  	if err != nil {
   671  		t.Fatal(err)
   672  	}
   673  
   674  	fipsOK := mode&fipsCertFIPSOK != 0
   675  	runWithFIPSEnabled(t, func(t *testing.T) {
   676  		if isCertificateAllowedFIPS(cert) != fipsOK {
   677  			t.Errorf("fipsAllowCert(cert with %s key) = %v, want %v", desc, !fipsOK, fipsOK)
   678  		}
   679  	})
   680  
   681  	return &fipsCertificate{name, org, parentOrg, der, cert, key, fipsOK}
   682  }
   683  

View as plain text