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