1
2
3
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
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
68
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
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
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
169
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