mirror of https://github.com/golang/go.git
487 lines
13 KiB
Go
487 lines
13 KiB
Go
// Copyright 2024 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package fipstest collects external tests that would ordinarily live in
|
|
// crypto/internal/fips140/... packages. That tree gets snapshot at each
|
|
// validation, while we want tests to evolve and still apply to all versions of
|
|
// the module. Also, we can't fix failing tests in a module snapshot, so we need
|
|
// to either minimize, skip, or remove them. Finally, the module needs to avoid
|
|
// importing internal packages like testenv and cryptotest to avoid locking in
|
|
// their APIs.
|
|
//
|
|
// Also, this package includes the ACVP and functional testing harnesses.
|
|
package fipstest
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/internal/boring"
|
|
"crypto/internal/fips140"
|
|
"crypto/internal/fips140/aes"
|
|
"crypto/internal/fips140/aes/gcm"
|
|
"crypto/internal/fips140/check"
|
|
"crypto/internal/fips140/drbg"
|
|
"crypto/internal/fips140/ecdh"
|
|
"crypto/internal/fips140/ecdsa"
|
|
"crypto/internal/fips140/ed25519"
|
|
"crypto/internal/fips140/hkdf"
|
|
"crypto/internal/fips140/hmac"
|
|
"crypto/internal/fips140/mlkem"
|
|
"crypto/internal/fips140/pbkdf2"
|
|
"crypto/internal/fips140/rsa"
|
|
"crypto/internal/fips140/sha256"
|
|
"crypto/internal/fips140/sha3"
|
|
"crypto/internal/fips140/sha512"
|
|
"crypto/internal/fips140/tls12"
|
|
"crypto/internal/fips140/tls13"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"runtime/debug"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func moduleStatus(t *testing.T) {
|
|
if fips140.Enabled {
|
|
t.Log("FIPS 140-3 mode enabled")
|
|
} else {
|
|
t.Log("FIPS 140-3 mode not enabled")
|
|
}
|
|
|
|
t.Logf("Module name: %s", fips140.Name())
|
|
t.Logf("Module version: %s", fips140.Version())
|
|
|
|
if noPAAPAI {
|
|
t.Log("PAA/PAI disabled")
|
|
} else {
|
|
t.Log("PAA/PAI enabled")
|
|
}
|
|
|
|
if check.Verified {
|
|
t.Log("FIPS 140-3 integrity self-check succeeded")
|
|
} else {
|
|
t.Log("FIPS 140-3 integrity self-check not succeeded")
|
|
}
|
|
}
|
|
|
|
func TestVersion(t *testing.T) {
|
|
bi, ok := debug.ReadBuildInfo()
|
|
if !ok {
|
|
t.Skip("no build info")
|
|
}
|
|
for _, setting := range bi.Settings {
|
|
if setting.Key != "GOFIPS140" {
|
|
continue
|
|
}
|
|
exp := setting.Value
|
|
if exp == "v1.0.0" {
|
|
// Unfortunately we enshrined the version of the first module as
|
|
// v1.0 before deciding to go for full versions.
|
|
exp = "v1.0"
|
|
}
|
|
if v := fips140.Version(); v != exp {
|
|
t.Errorf("Version is %q, expected %q", v, exp)
|
|
}
|
|
return
|
|
}
|
|
// Without GOFIPS140, the Version should be "latest".
|
|
if v := fips140.Version(); v != "latest" {
|
|
t.Errorf("Version is %q, expected latest", v)
|
|
}
|
|
}
|
|
|
|
func TestFIPS140(t *testing.T) {
|
|
moduleStatus(t)
|
|
if boring.Enabled {
|
|
t.Skip("Go+BoringCrypto shims prevent the service indicator from being set")
|
|
}
|
|
|
|
aesKey := make([]byte, 128/8)
|
|
aesIV := make([]byte, aes.BlockSize)
|
|
plaintext := []byte("Go Cryptographic Module TestFIPS140 plaintext...")
|
|
plaintextSHA256 := decodeHex(t, "06b2614e2ef315832b23f5d0ff70294d8ddd3889527dfbe75707fe41da929325")
|
|
aesBlock, err := aes.New(aesKey)
|
|
fatalIfErr(t, err)
|
|
|
|
t.Run("AES-CTR", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
ctr := aes.NewCTR(aesBlock, aesIV)
|
|
ciphertext := make([]byte, len(plaintext))
|
|
ctr.XORKeyStream(ciphertext, plaintext)
|
|
t.Logf("AES-CTR ciphertext: %x", ciphertext)
|
|
out := make([]byte, len(plaintext))
|
|
ctr = aes.NewCTR(aesBlock, aesIV)
|
|
ctr.XORKeyStream(out, ciphertext)
|
|
t.Logf("AES-CTR decrypted plaintext: %s", out)
|
|
if !bytes.Equal(plaintext, out) {
|
|
t.Errorf("AES-CTR round trip failed")
|
|
}
|
|
})
|
|
|
|
t.Run("AES-CBC", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
cbcEnc := aes.NewCBCEncrypter(aesBlock, [16]byte(aesIV))
|
|
ciphertext := make([]byte, len(plaintext))
|
|
cbcEnc.CryptBlocks(ciphertext, plaintext)
|
|
t.Logf("AES-CBC ciphertext: %x", ciphertext)
|
|
cbcDec := aes.NewCBCDecrypter(aesBlock, [16]byte(aesIV))
|
|
out := make([]byte, len(plaintext))
|
|
cbcDec.CryptBlocks(out, ciphertext)
|
|
t.Logf("AES-CBC decrypted plaintext: %s", out)
|
|
if !bytes.Equal(plaintext, out) {
|
|
t.Errorf("AES-CBC round trip failed")
|
|
}
|
|
})
|
|
|
|
t.Run("AES-GCM", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
g, err := gcm.New(aesBlock, 12, 16)
|
|
fatalIfErr(t, err)
|
|
nonce := make([]byte, 12)
|
|
ciphertext := make([]byte, len(plaintext)+g.Overhead())
|
|
gcm.SealWithRandomNonce(g, nonce, ciphertext, plaintext, nil)
|
|
t.Logf("AES-GCM ciphertext: %x", ciphertext)
|
|
out, err := g.Open(nil, nonce, ciphertext, nil)
|
|
fatalIfErr(t, err)
|
|
t.Logf("AES-GCM decrypted plaintext: %s", out)
|
|
if !bytes.Equal(plaintext, out) {
|
|
t.Errorf("AES-GCM round trip failed")
|
|
}
|
|
})
|
|
|
|
t.Run("Counter KDF", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
k := gcm.NewCounterKDF(aesBlock)
|
|
context := [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
|
|
key := k.DeriveKey(0x01, context)
|
|
t.Logf("Counter KDF key: %x", key)
|
|
})
|
|
|
|
t.Run("KAS-ECC-SSC ephemeralUnified", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
k, err := ecdh.GenerateKey(ecdh.P256(), rand.Reader)
|
|
fatalIfErr(t, err)
|
|
pk := k.PublicKey()
|
|
shared, err := ecdh.ECDH(ecdh.P256(), k, pk)
|
|
fatalIfErr(t, err)
|
|
t.Logf("KAS-ECC-SSC shared secret: %x", shared)
|
|
})
|
|
|
|
t.Run("ECDSA KeyGen, SigGen, SigVer", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
k, err := ecdsa.GenerateKey(ecdsa.P256(), rand.Reader)
|
|
fatalIfErr(t, err)
|
|
|
|
sig, err := ecdsa.Sign(ecdsa.P256(), sha256.New, k, rand.Reader, plaintextSHA256)
|
|
fatalIfErr(t, err)
|
|
t.Logf("ECDSA signature: %x", sig)
|
|
err = ecdsa.Verify(ecdsa.P256(), k.PublicKey(), plaintextSHA256, sig)
|
|
if err != nil {
|
|
t.Errorf("ECDSA signature verification failed")
|
|
}
|
|
|
|
sig, err = ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, k, plaintextSHA256)
|
|
fatalIfErr(t, err)
|
|
t.Logf("ECDSA deterministic signature: %x", sig)
|
|
err = ecdsa.Verify(ecdsa.P256(), k.PublicKey(), plaintextSHA256, sig)
|
|
if err != nil {
|
|
t.Errorf("ECDSA deterministic signature verification failed")
|
|
}
|
|
})
|
|
|
|
t.Run("EDDSA KeyGen, SigGen, SigVer", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
k, err := ed25519.GenerateKey()
|
|
fatalIfErr(t, err)
|
|
|
|
sig := ed25519.Sign(k, plaintext)
|
|
t.Logf("EDDSA signature: %x", sig)
|
|
|
|
pk, err := ed25519.NewPublicKey(k.PublicKey())
|
|
fatalIfErr(t, err)
|
|
err = ed25519.Verify(pk, plaintext, sig)
|
|
if err != nil {
|
|
t.Errorf("EDDSA signature verification failed")
|
|
}
|
|
})
|
|
|
|
t.Run("ctrDRBG", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
r := drbg.NewCounter((*[48]byte)(plaintext))
|
|
r.Reseed((*[48]byte)(plaintext), (*[48]byte)(plaintext))
|
|
out := make([]byte, 16)
|
|
r.Generate(out, (*[48]byte)(plaintext))
|
|
t.Logf("ctrDRBG output: %x", out)
|
|
})
|
|
|
|
t.Run("HMAC", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := hmac.New(sha256.New, plaintext)
|
|
h.Write(plaintext)
|
|
out := h.Sum(nil)
|
|
t.Logf("HMAC output: %x", out)
|
|
})
|
|
|
|
t.Run("ML-KEM KeyGen, Encap, Decap", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
k, err := mlkem.GenerateKey768()
|
|
fatalIfErr(t, err)
|
|
|
|
ss, c := k.EncapsulationKey().Encapsulate()
|
|
t.Logf("ML-KEM encapsulation: %x", c)
|
|
|
|
ss2, err := k.Decapsulate(c)
|
|
fatalIfErr(t, err)
|
|
t.Logf("ML-KEM shared secret: %x", ss)
|
|
if !bytes.Equal(ss, ss2) {
|
|
t.Errorf("ML-KEM round trip failed")
|
|
}
|
|
})
|
|
|
|
var rsaKey *rsa.PrivateKey
|
|
t.Run("RSA KeyGen", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
var err error
|
|
rsaKey, err = rsa.GenerateKey(rand.Reader, 2048)
|
|
fatalIfErr(t, err)
|
|
t.Log("RSA key generated")
|
|
})
|
|
|
|
t.Run("RSA SigGen, SigVer PKCS 1.5", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
sig, err := rsa.SignPKCS1v15(rsaKey, "SHA-256", plaintextSHA256)
|
|
fatalIfErr(t, err)
|
|
t.Logf("RSA PKCS1v15 signature: %x", sig)
|
|
|
|
err = rsa.VerifyPKCS1v15(rsaKey.PublicKey(), "SHA-256", plaintextSHA256, sig)
|
|
fatalIfErr(t, err)
|
|
})
|
|
|
|
t.Run("RSA SigGen, SigVer PSS", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
sig, err := rsa.SignPSS(rand.Reader, rsaKey, sha256.New(), plaintextSHA256, 16)
|
|
fatalIfErr(t, err)
|
|
t.Logf("RSA PSS signature: %x", sig)
|
|
|
|
err = rsa.VerifyPSS(rsaKey.PublicKey(), sha256.New(), plaintextSHA256, sig)
|
|
fatalIfErr(t, err)
|
|
})
|
|
|
|
t.Run("RSA KeyGen w/ small key [NOT APPROVED]", func(t *testing.T) {
|
|
ensureServiceIndicatorFalse(t)
|
|
_, err := rsa.GenerateKey(rand.Reader, 512)
|
|
fatalIfErr(t, err)
|
|
t.Log("RSA key generated")
|
|
})
|
|
|
|
t.Run("KTS IFC OAEP", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
c, err := rsa.EncryptOAEP(sha256.New(), sha256.New(), rand.Reader, rsaKey.PublicKey(), plaintextSHA256, nil)
|
|
fatalIfErr(t, err)
|
|
t.Logf("RSA OAEP ciphertext: %x", c)
|
|
|
|
out, err := rsa.DecryptOAEP(sha256.New(), sha256.New(), rsaKey, c, nil)
|
|
fatalIfErr(t, err)
|
|
t.Logf("RSA OAEP decrypted plaintext: %x", out)
|
|
if !bytes.Equal(plaintextSHA256, out) {
|
|
t.Errorf("RSA OAEP round trip failed")
|
|
}
|
|
})
|
|
|
|
t.Run("SHA2-224", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha256.New224()
|
|
h.Write(plaintext)
|
|
out := h.Sum(nil)
|
|
t.Logf("SHA2-224 output: %x", out)
|
|
})
|
|
|
|
t.Run("SHA2-256", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha256.New()
|
|
h.Write(plaintext)
|
|
out := h.Sum(nil)
|
|
t.Logf("SHA2-256 output: %x", out)
|
|
})
|
|
|
|
t.Run("SHA2-384", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha512.New384()
|
|
h.Write(plaintext)
|
|
out := h.Sum(nil)
|
|
t.Logf("SHA2-384 output: %x", out)
|
|
})
|
|
|
|
t.Run("SHA2-512", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha512.New()
|
|
h.Write(plaintext)
|
|
out := h.Sum(nil)
|
|
t.Logf("SHA2-512 output: %x", out)
|
|
})
|
|
|
|
t.Run("SHA2-512/224", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha512.New512_224()
|
|
h.Write(plaintext)
|
|
out := h.Sum(nil)
|
|
t.Logf("SHA2-512/224 output: %x", out)
|
|
})
|
|
|
|
t.Run("SHA2-512/256", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha512.New512_256()
|
|
h.Write(plaintext)
|
|
out := h.Sum(nil)
|
|
t.Logf("SHA2-512/256 output: %x", out)
|
|
})
|
|
|
|
t.Run("SHA3-224", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha3.New224()
|
|
h.Write(plaintext)
|
|
out := h.Sum(nil)
|
|
t.Logf("SHA3-224 output: %x", out)
|
|
})
|
|
|
|
t.Run("SHA3-256", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha3.New256()
|
|
h.Write(plaintext)
|
|
out := h.Sum(nil)
|
|
t.Logf("SHA3-256 output: %x", out)
|
|
})
|
|
|
|
t.Run("SHA3-384", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha3.New384()
|
|
h.Write(plaintext)
|
|
out := h.Sum(nil)
|
|
t.Logf("SHA3-384 output: %x", out)
|
|
})
|
|
|
|
t.Run("SHA3-512", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha3.New512()
|
|
h.Write(plaintext)
|
|
out := h.Sum(nil)
|
|
t.Logf("SHA3-512 output: %x", out)
|
|
})
|
|
|
|
t.Run("SHAKE-128", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha3.NewShake128()
|
|
h.Write(plaintext)
|
|
out := make([]byte, 16)
|
|
h.Read(out)
|
|
t.Logf("SHAKE-128 output: %x", out)
|
|
})
|
|
|
|
t.Run("SHAKE-256", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha3.NewShake256()
|
|
h.Write(plaintext)
|
|
out := make([]byte, 16)
|
|
h.Read(out)
|
|
t.Logf("SHAKE-256 output: %x", out)
|
|
})
|
|
|
|
t.Run("cSHAKE-128", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha3.NewCShake128(nil, []byte("test"))
|
|
h.Write(plaintext)
|
|
out := make([]byte, 16)
|
|
h.Read(out)
|
|
t.Logf("cSHAKE-128 output: %x", out)
|
|
})
|
|
|
|
t.Run("cSHAKE-256", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
h := sha3.NewCShake256(nil, []byte("test"))
|
|
h.Write(plaintext)
|
|
out := make([]byte, 16)
|
|
h.Read(out)
|
|
t.Logf("cSHAKE-256 output: %x", out)
|
|
})
|
|
|
|
t.Run("KDA HKDF", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
key := hkdf.Key(sha256.New, plaintextSHA256, []byte("salt"), "info", 16)
|
|
t.Logf("HKDF key: %x", key)
|
|
})
|
|
|
|
t.Run("KDA OneStepNoCounter", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
key := hkdf.Extract(sha256.New, plaintextSHA256, []byte("salt"))
|
|
t.Logf("KDA OneStepNoCounter key: %x", key)
|
|
})
|
|
|
|
t.Run("Feedback KDF", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
key := hkdf.Expand(sha256.New, plaintextSHA256, "info", 16)
|
|
t.Logf("Feedback KDF key: %x", key)
|
|
})
|
|
|
|
t.Run("PBKDF", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
key, err := pbkdf2.Key(sha256.New, "password", plaintextSHA256, 2, 16)
|
|
fatalIfErr(t, err)
|
|
t.Logf("PBKDF key: %x", key)
|
|
})
|
|
|
|
t.Run("KDF TLS v1.2 CVL", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
key := tls12.MasterSecret(sha256.New, plaintextSHA256, []byte("test"))
|
|
t.Logf("TLS v1.2 CVL Master Secret: %x", key)
|
|
})
|
|
|
|
t.Run("KDF TLS v1.3 CVL", func(t *testing.T) {
|
|
ensureServiceIndicator(t)
|
|
es := tls13.NewEarlySecret(sha256.New, plaintextSHA256)
|
|
hs := es.HandshakeSecret(plaintextSHA256)
|
|
ms := hs.MasterSecret()
|
|
client := ms.ClientApplicationTrafficSecret(sha256.New())
|
|
server := ms.ServerApplicationTrafficSecret(sha256.New())
|
|
t.Logf("TLS v1.3 CVL Application Traffic Secrets: client %x, server %x", client, server)
|
|
})
|
|
}
|
|
|
|
func ensureServiceIndicator(t *testing.T) {
|
|
fips140.ResetServiceIndicator()
|
|
t.Cleanup(func() {
|
|
if fips140.ServiceIndicator() {
|
|
t.Logf("Service indicator is set")
|
|
} else {
|
|
t.Errorf("Service indicator is not set")
|
|
}
|
|
})
|
|
}
|
|
|
|
func ensureServiceIndicatorFalse(t *testing.T) {
|
|
fips140.ResetServiceIndicator()
|
|
t.Cleanup(func() {
|
|
if !fips140.ServiceIndicator() {
|
|
t.Logf("Service indicator is not set")
|
|
} else {
|
|
t.Errorf("Service indicator is set")
|
|
}
|
|
})
|
|
}
|
|
|
|
func fatalIfErr(t *testing.T, err error) {
|
|
t.Helper()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func decodeHex(t *testing.T, s string) []byte {
|
|
t.Helper()
|
|
s = strings.ReplaceAll(s, " ", "")
|
|
b, err := hex.DecodeString(s)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return b
|
|
}
|