Skip to content

Commit 7382a1b

Browse files
committed
Test compatibility with FIPS-enabled OpenSSL 1.0.2
Some of the standard library crypto/cipher AES-GCM tests fail when using OpenSSL 1.0.2 in FIPS mode, but pass with the same library when FIPS mode is disabled. Extend the AES-GCM tests to cover all the failure cases caught by the standard library tests and build the OpenSSL 1.0.2 library used in CI to be FIPS enabled. Link the OpenSSL 1.0.2 CI build against FIPS Object Module 2.0.1 rather than the latest version (2.0.16) as at least one commercially-supported FIPS validated build of OpenSSL 1.0.2 is known to use that version of the FIPS Object Module and some of the failures seen with 2.0.1 do not reproduce with 2.0.16. Signed-off-by: Cory Snider <[email protected]>
1 parent 1ee02b5 commit 7382a1b

File tree

4 files changed

+875
-20
lines changed

4 files changed

+875
-20
lines changed

aes_test.go

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,52 @@ func TestNewGCMNonce(t *testing.T) {
5656
}
5757

5858
func TestSealAndOpen(t *testing.T) {
59-
key := []byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D")
60-
ci, err := openssl.NewAESCipher(key)
61-
if err != nil {
62-
t.Fatal(err)
63-
}
64-
gcm, err := cipher.NewGCM(ci)
65-
if err != nil {
66-
t.Fatal(err)
67-
}
68-
nonce := []byte{0x91, 0xc7, 0xa7, 0x54, 0x52, 0xef, 0x10, 0xdb, 0x91, 0xa8, 0x6c, 0xf9}
69-
plainText := []byte{0x01, 0x02, 0x03}
70-
additionalData := []byte{0x05, 0x05, 0x07}
71-
sealed := gcm.Seal(nil, nonce, plainText, additionalData)
72-
decrypted, err := gcm.Open(nil, nonce, sealed, additionalData)
73-
if err != nil {
74-
t.Error(err)
75-
}
76-
if !bytes.Equal(decrypted, plainText) {
77-
t.Errorf("unexpected decrypted result\ngot: %#v\nexp: %#v", decrypted, plainText)
59+
for _, tt := range aesGCMTests {
60+
t.Run(tt.description, func(t *testing.T) {
61+
ci, err := openssl.NewAESCipher(tt.key)
62+
if err != nil {
63+
t.Fatalf("NewAESCipher() err = %v", err)
64+
}
65+
gcm, err := cipher.NewGCM(ci)
66+
if err != nil {
67+
t.Fatalf("cipher.NewGCM() err = %v", err)
68+
}
69+
70+
sealed := gcm.Seal(nil, tt.nonce, tt.plaintext, tt.aad)
71+
if !bytes.Equal(sealed, tt.ciphertext) {
72+
t.Errorf("unexpected sealed result\ngot: %#v\nexp: %#v", sealed, tt.ciphertext)
73+
}
74+
75+
decrypted, err := gcm.Open(nil, tt.nonce, tt.ciphertext, tt.aad)
76+
if err != nil {
77+
t.Errorf("gcm.Open() err = %v", err)
78+
}
79+
if !bytes.Equal(decrypted, tt.plaintext) {
80+
t.Errorf("unexpected decrypted result\ngot: %#v\nexp: %#v", decrypted, tt.plaintext)
81+
}
82+
83+
// Test that open fails if the ciphertext is modified.
84+
tt.ciphertext[0] ^= 0x80
85+
_, err = gcm.Open(nil, tt.nonce, tt.ciphertext, tt.aad)
86+
if err != openssl.ErrOpen {
87+
t.Errorf("expected authentication error for tampered message\ngot: %#v", err)
88+
}
89+
tt.ciphertext[0] ^= 0x80
90+
91+
// Test that the ciphertext can be opened using a fresh context
92+
// which was not previously used to seal the same message.
93+
gcm, err = cipher.NewGCM(ci)
94+
if err != nil {
95+
t.Fatalf("cipher.NewGCM() err = %v", err)
96+
}
97+
decrypted, err = gcm.Open(nil, tt.nonce, tt.ciphertext, tt.aad)
98+
if err != nil {
99+
t.Errorf("fresh GCM instance: gcm.Open() err = %v", err)
100+
}
101+
if !bytes.Equal(decrypted, tt.plaintext) {
102+
t.Errorf("fresh GCM instance: unexpected decrypted result\ngot: %#v\nexp: %#v", decrypted, tt.plaintext)
103+
}
104+
})
78105
}
79106
}
80107

cmd/gentestvectors/main.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// gentestvectors emits cryptographic test vectors using the Go standard library
2+
// cryptographic routines to test the OpenSSL bindings.
3+
package main
4+
5+
import (
6+
"bytes"
7+
"crypto/aes"
8+
"crypto/cipher"
9+
"flag"
10+
"fmt"
11+
"go/format"
12+
"io"
13+
"log"
14+
"math/rand"
15+
"os"
16+
"path/filepath"
17+
)
18+
19+
var outputPath = flag.String("out", "", "output path (default stdout)")
20+
21+
func init() {
22+
log.SetFlags(log.Llongfile)
23+
log.SetOutput(os.Stderr)
24+
}
25+
26+
func main() {
27+
flag.Parse()
28+
29+
var b bytes.Buffer
30+
fmt.Fprint(&b, "// Code generated by cmd/gentestvectors. DO NOT EDIT.\n\n")
31+
if *outputPath != "" {
32+
fmt.Fprintf(&b, "//go"+":generate go run github.com/golang-fips/openssl/v2/cmd/gentestvectors -out %s\n\n", filepath.Base(*outputPath))
33+
}
34+
35+
pkg := "openssl_test"
36+
if gopackage := os.Getenv("GOPACKAGE"); gopackage != "" {
37+
pkg = gopackage + "_test"
38+
}
39+
fmt.Fprintf(&b, "package %s\n\n", pkg)
40+
41+
aesGCM(&b)
42+
43+
generated, err := format.Source(b.Bytes())
44+
if err != nil {
45+
log.Fatalf("failed to format generated code: %v", err)
46+
}
47+
48+
if *outputPath != "" {
49+
err := os.WriteFile(*outputPath, generated, 0o644)
50+
if err != nil {
51+
log.Fatalf("failed to write output file: %v\n", err)
52+
}
53+
} else {
54+
_, _ = os.Stdout.Write(generated)
55+
}
56+
}
57+
58+
func aesGCM(w io.Writer) {
59+
r := rand.New(rand.NewSource(0))
60+
61+
fmt.Fprintln(w, `var aesGCMTests = []struct {
62+
description string
63+
key, nonce, plaintext, aad, ciphertext []byte
64+
}{`)
65+
66+
for _, keyLen := range []int{16, 24, 32} {
67+
for _, aadLen := range []int{0, 1, 3, 13, 30} {
68+
for _, plaintextLen := range []int{0, 1, 3, 13, 16, 51} {
69+
if aadLen == 0 && plaintextLen == 0 {
70+
continue
71+
}
72+
73+
key := randbytes(r, keyLen)
74+
nonce := randbytes(r, 12)
75+
plaintext := randbytes(r, plaintextLen)
76+
aad := randbytes(r, aadLen)
77+
78+
c, err := aes.NewCipher(key)
79+
if err != nil {
80+
panic(err)
81+
}
82+
aead, err := cipher.NewGCM(c)
83+
if err != nil {
84+
panic(err)
85+
}
86+
ciphertext := aead.Seal(nil, nonce, plaintext, aad)
87+
88+
fmt.Fprint(w, "\t{\n")
89+
fmt.Fprintf(w, "\t\tdescription: \"AES-%d/AAD=%d/Plaintext=%d\",\n", keyLen*8, aadLen, plaintextLen)
90+
printBytesField(w, "key", key)
91+
printBytesField(w, "nonce", nonce)
92+
printBytesField(w, "plaintext", plaintext)
93+
printBytesField(w, "aad", aad)
94+
printBytesField(w, "ciphertext", ciphertext)
95+
fmt.Fprint(w, "\t},\n")
96+
}
97+
}
98+
}
99+
fmt.Fprintln(w, "}")
100+
}
101+
102+
func randbytes(r *rand.Rand, n int) []byte {
103+
if n == 0 {
104+
return nil
105+
}
106+
b := make([]byte, n)
107+
r.Read(b)
108+
return b
109+
}
110+
111+
func printBytesField(w io.Writer, name string, b []byte) {
112+
if len(b) == 0 {
113+
return
114+
}
115+
fmt.Fprintf(w, "\t\t%s: %#v,\n", name, b)
116+
}

scripts/openssl.sh

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,41 @@ case "$version" in
1212
"1.0.2")
1313
tag="OpenSSL_1_0_2u"
1414
sha256="82fa58e3f273c53128c6fe7e3635ec8cda1319a10ce1ad50a987c3df0deeef05"
15-
config="shared"
15+
fipsmodule_version="2.0.1"
16+
fipsmodule_tag="OpenSSL-fips-2_0_1"
17+
fipsmodule_sha256="6645895f43a0229dd4b89d27874fdd91fee70d9671fff954d3da448d5fc1d331"
18+
config="shared fips --with-fipsdir=/usr/local/src/openssl-fips-$fipsmodule_version/dist"
1619
make="build_libs"
1720
install=""
1821
;;
1922
"1.1.0")
2023
tag="OpenSSL_1_1_0l"
2124
sha256="e2acf0cf58d9bff2b42f2dc0aee79340c8ffe2c5e45d3ca4533dd5d4f5775b1d"
25+
fipsmodule_version=""
2226
config="shared"
2327
make="build_libs"
2428
install=""
2529
;;
2630
"1.1.1")
2731
tag="OpenSSL_1_1_1m"
2832
sha256="36ae24ad7cf0a824d0b76ac08861262e47ec541e5d0f20e6d94bab90b2dab360"
33+
fipsmodule_version=""
2934
config="shared"
3035
make="build_libs"
3136
install=""
3237
;;
3338
"3.0.1")
3439
tag="openssl-3.0.1";
3540
sha256="2a9dcf05531e8be96c296259e817edc41619017a4bf3e229b4618a70103251d5"
41+
fipsmodule_version=""
3642
config="enable-fips"
3743
make="build_libs"
3844
install="install_fips"
3945
;;
4046
"3.0.9")
4147
tag="openssl-3.0.9";
4248
sha256="2eec31f2ac0e126ff68d8107891ef534159c4fcfb095365d4cd4dc57d82616ee"
49+
fipsmodule_version=""
4350
config="enable-fips"
4451
make="build_libs"
4552
install="install_fips"
@@ -58,10 +65,35 @@ tar -xzf "$tag.tar.gz"
5865
rm -rf "openssl-$version"
5966
mv "openssl-$tag" "openssl-$version"
6067

68+
if [ -n "$fipsmodule_version" ]; then
69+
wget -O "$fipsmodule_tag.tar.gz" "https://github.com/openssl/openssl/archive/refs/tags/$fipsmodule_tag.tar.gz"
70+
echo "$fipsmodule_sha256 $fipsmodule_tag.tar.gz" | sha256sum -c -
71+
rm -rf "openssl-$fipsmodule_tag"
72+
tar -xzf "$fipsmodule_tag.tar.gz"
73+
74+
rm -rf "openssl-fips-$fipsmodule_version"
75+
mv "openssl-$fipsmodule_tag" "openssl-fips-$fipsmodule_version"
76+
(
77+
cd "openssl-fips-$fipsmodule_version"
78+
mkdir dist
79+
./config -d shared fipscanisteronly --prefix=$(pwd)/dist
80+
make
81+
make install
82+
)
83+
fi
84+
6185
cd "openssl-$version"
6286
# -d makes a debug build which helps with debugging memory issues and
6387
# other problems. It's not necessary for normal use.
6488
./config -d $config
89+
90+
# OpenSSL 1.0.2 ./config prompts the user to run `make depend` before `make`
91+
# when configuring in debug mode. OpenSSL 1.1.0 and above handle this
92+
# automatically.
93+
if [ "$version" == "1.0.2" ]; then
94+
make depend
95+
fi
96+
6597
make -j$(nproc) $make
6698
if [ -n "$install" ]; then
6799
make $install

0 commit comments

Comments
 (0)