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/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 moduleStatus(t)
137 testenv.MustHaveExec(t)
138 if err := fips140.Supported(); err != nil {
139 t.Skipf("test requires FIPS 140 mode: %v", err)
140 }
141
142 cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^TestConditionals$", "-test.v")
143 cmd.Env = append(cmd.Env, "GODEBUG=fips140=debug")
144 out, err := cmd.CombinedOutput()
145 t.Logf("%s", out)
146 if err != nil || !strings.Contains(string(out), "completed successfully") {
147 t.Errorf("TestConditionals did not complete successfully")
148 }
149
150 for _, name := range allCASTs {
151 t.Run(name, func(t *testing.T) {
152 if !strings.Contains(string(out), fmt.Sprintf("passed: %s\n", name)) {
153 t.Errorf("CAST/PCT %s success was not logged", name)
154 } else {
155 t.Logf("CAST/PCT succeeded: %s", name)
156 }
157 })
158 }
159 }
160
161 func TestCASTFailures(t *testing.T) {
162 moduleStatus(t)
163 testenv.MustHaveExec(t)
164 if err := fips140.Supported(); err != nil {
165 t.Skipf("test requires FIPS 140 mode: %v", err)
166 }
167
168 for _, name := range allCASTs {
169 t.Run(name, func(t *testing.T) {
170
171
172 if !testing.Verbose() {
173 t.Parallel()
174 }
175 t.Logf("Testing CAST/PCT failure...")
176 cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^TestConditionals$", "-test.v")
177 cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=failfipscast=%s,fips140=on", name))
178 out, err := cmd.CombinedOutput()
179 t.Logf("%s", out)
180 if err == nil {
181 t.Fatal("Test did not fail as expected")
182 }
183 if strings.Contains(string(out), "completed successfully") {
184 t.Errorf("CAST/PCT %s failure did not stop the program", name)
185 } else if !strings.Contains(string(out), "self-test failed: "+name) {
186 t.Errorf("CAST/PCT %s failure did not log the expected message", name)
187 } else {
188 t.Logf("CAST/PCT %s failed as expected and caused the program to exit", name)
189 }
190 })
191 }
192 }
193
View as plain text