diff --git a/fips.go b/fips.go index 01ed2fb57..3f3bc68ef 100644 --- a/fips.go +++ b/fips.go @@ -1,13 +1,14 @@ package notary -import "os" +import ( + "crypto" + // Need to import md5 so can test availability. + _ "crypto/md5" +) -// FIPSEnvVar is the name of the environment variable that is being used to switch -// between FIPS and non-FIPS mode -const FIPSEnvVar = "GOFIPS" - -// FIPSEnabled returns true if environment variable `GOFIPS` has been set to enable -// FIPS mode +// FIPSEnabled returns true if running in FIPS mode. +// If compiled in FIPS mode the md5 hash function is never available +// even when imported. This seems to be the best test we have for it. func FIPSEnabled() bool { - return os.Getenv(FIPSEnvVar) != "" + return !crypto.MD5.Available() } diff --git a/tuf/utils/x509.go b/tuf/utils/x509.go index 7738418ac..4ebf83c2e 100644 --- a/tuf/utils/x509.go +++ b/tuf/utils/x509.go @@ -86,10 +86,6 @@ func X509PublicKeyID(certPubKey data.PublicKey) (string, error) { } func parseLegacyPrivateKey(block *pem.Block, passphrase string) (data.PrivateKey, error) { - if notary.FIPSEnabled() { - return nil, fmt.Errorf("%s not supported in FIPS mode", block.Type) - } - var privKeyBytes []byte var err error if x509.IsEncryptedPEMBlock(block) { @@ -146,6 +142,10 @@ func parseLegacyPrivateKey(block *pem.Block, passphrase string) (data.PrivateKey // supports PKCS#8 as well as RSA/ECDSA (PKCS#1) only in non-FIPS mode and // attempts to decrypt using the passphrase, if encrypted. func ParsePEMPrivateKey(pemBytes []byte, passphrase string) (data.PrivateKey, error) { + return parsePEMPrivateKey(pemBytes, passphrase, notary.FIPSEnabled()) +} + +func parsePEMPrivateKey(pemBytes []byte, passphrase string, fips bool) (data.PrivateKey, error) { block, _ := pem.Decode(pemBytes) if block == nil { return nil, errors.New("no valid private key found") @@ -153,6 +153,9 @@ func ParsePEMPrivateKey(pemBytes []byte, passphrase string) (data.PrivateKey, er switch block.Type { case "RSA PRIVATE KEY", "EC PRIVATE KEY", "ED25519 PRIVATE KEY": + if fips { + return nil, fmt.Errorf("%s not supported in FIPS mode", block.Type) + } return parseLegacyPrivateKey(block, passphrase) case "ENCRYPTED PRIVATE KEY", "PRIVATE KEY": if passphrase == "" { @@ -433,6 +436,10 @@ func ED25519ToPrivateKey(privKeyBytes []byte) (data.PrivateKey, error) { // ExtractPrivateKeyAttributes extracts role and gun values from private key bytes func ExtractPrivateKeyAttributes(pemBytes []byte) (data.RoleName, data.GUN, error) { + return extractPrivateKeyAttributes(pemBytes, notary.FIPSEnabled()) +} + +func extractPrivateKeyAttributes(pemBytes []byte, fips bool) (data.RoleName, data.GUN, error) { block, _ := pem.Decode(pemBytes) if block == nil { return "", "", errors.New("PEM block is empty") @@ -440,7 +447,7 @@ func ExtractPrivateKeyAttributes(pemBytes []byte) (data.RoleName, data.GUN, erro switch block.Type { case "RSA PRIVATE KEY", "EC PRIVATE KEY", "ED25519 PRIVATE KEY": - if notary.FIPSEnabled() { + if fips { return "", "", fmt.Errorf("%s not supported in FIPS mode", block.Type) } case "PRIVATE KEY", "ENCRYPTED PRIVATE KEY": diff --git a/tuf/utils/x509_test.go b/tuf/utils/x509_test.go index c8c5351b2..07fe36600 100644 --- a/tuf/utils/x509_test.go +++ b/tuf/utils/x509_test.go @@ -9,13 +9,11 @@ import ( "encoding/pem" "fmt" "io/ioutil" - "os" "strings" "testing" "time" "github.com/stretchr/testify/require" - "github.com/theupdateframework/notary" "github.com/theupdateframework/notary/tuf/data" ) @@ -289,27 +287,13 @@ func TestECDSAX509PublickeyID(t *testing.T) { require.Equal(t, tufPrivKey.ID(), tufID) } -func preserveEnv(name string) func() { - if env, has := os.LookupEnv(name); has { - os.Unsetenv(name) - return func() { - os.Setenv(name, env) - } - } - - return func() {} -} - func TestExtractPrivateKeyAttributes(t *testing.T) { testExtractPrivateKeyAttributes(t) testExtractPrivateKeyAttributesWithFIPS(t) } func testExtractPrivateKeyAttributes(t *testing.T) { - defer preserveEnv(notary.FIPSEnvVar)() - - err := os.Unsetenv(notary.FIPSEnvVar) - require.NoError(t, err) + fips := false testPKCS1PEM1 := getPKCS1KeyWithRole(t, "unicorn", "rainbow") testPKCS1PEM2 := getPKCS1KeyWithRole(t, "docker", "") @@ -317,61 +301,58 @@ func testExtractPrivateKeyAttributes(t *testing.T) { testPKCS8PEM2 := getPKCS8KeyWithRole(t, "dagger", "") // Try garbage bytes - _, _, err = ExtractPrivateKeyAttributes([]byte("Knock knock; it's Bob.")) + _, _, err := extractPrivateKeyAttributes([]byte("Knock knock; it's Bob."), fips) require.Error(t, err) // PKCS#8 - role, gun, err := ExtractPrivateKeyAttributes(testPKCS8PEM1) + role, gun, err := extractPrivateKeyAttributes(testPKCS8PEM1, fips) require.NoError(t, err) require.EqualValues(t, data.RoleName("fat"), role) require.EqualValues(t, data.GUN("panda"), gun) - role, gun, err = ExtractPrivateKeyAttributes(testPKCS8PEM2) + role, gun, err = extractPrivateKeyAttributes(testPKCS8PEM2, fips) require.NoError(t, err) require.EqualValues(t, data.RoleName("dagger"), role) require.EqualValues(t, data.GUN(""), gun) // PKCS#1 - role, gun, err = ExtractPrivateKeyAttributes(testPKCS1PEM1) + role, gun, err = extractPrivateKeyAttributes(testPKCS1PEM1, fips) require.NoError(t, err) require.EqualValues(t, data.RoleName("unicorn"), role) require.EqualValues(t, data.GUN("rainbow"), gun) - role, gun, err = ExtractPrivateKeyAttributes(testPKCS1PEM2) + role, gun, err = extractPrivateKeyAttributes(testPKCS1PEM2, fips) require.NoError(t, err) require.EqualValues(t, data.RoleName("docker"), role) require.EqualValues(t, data.GUN(""), gun) } func testExtractPrivateKeyAttributesWithFIPS(t *testing.T) { - defer preserveEnv(notary.FIPSEnvVar)() - - err := os.Setenv(notary.FIPSEnvVar, "1") - require.NoError(t, err) + fips := true testPKCS1PEM1 := getPKCS1KeyWithRole(t, "unicorn", "rainbow") testPKCS1PEM2 := getPKCS1KeyWithRole(t, "docker", "") // PKCS#1 - _, _, err = ExtractPrivateKeyAttributes(testPKCS1PEM1) + _, _, err := extractPrivateKeyAttributes(testPKCS1PEM1, fips) require.Error(t, err) - _, _, err = ExtractPrivateKeyAttributes(testPKCS1PEM2) + _, _, err = extractPrivateKeyAttributes(testPKCS1PEM2, fips) require.Error(t, err) testPKCS8PEM1 := getPKCS8KeyWithRole(t, "fat", "panda") testPKCS8PEM2 := getPKCS8KeyWithRole(t, "dagger", "") // Try garbage bytes - _, _, err = ExtractPrivateKeyAttributes([]byte("Knock knock; it's Bob.")) + _, _, err = extractPrivateKeyAttributes([]byte("Knock knock; it's Bob."), fips) require.Error(t, err) // PKCS#8 - role, gun, err := ExtractPrivateKeyAttributes(testPKCS8PEM1) + role, gun, err := extractPrivateKeyAttributes(testPKCS8PEM1, fips) require.NoError(t, err) require.EqualValues(t, data.RoleName("fat"), role) require.EqualValues(t, data.GUN("panda"), gun) - role, gun, err = ExtractPrivateKeyAttributes(testPKCS8PEM2) + role, gun, err = extractPrivateKeyAttributes(testPKCS8PEM2, fips) require.NoError(t, err) require.EqualValues(t, data.RoleName("dagger"), role) require.EqualValues(t, data.GUN(""), gun) @@ -433,25 +414,19 @@ PBV11bfmoHzDVeeuz1ztFUb3WjR7xlQe09izY3o3N6yZlTFIsqawIg== } func testParsePEMPrivateKeyLegacy(t *testing.T, raw []byte) { - defer preserveEnv(notary.FIPSEnvVar)() - - err := os.Unsetenv(notary.FIPSEnvVar) - require.NoError(t, err) + fips := false - key, err := ParsePEMPrivateKey(raw, "") + key, err := parsePEMPrivateKey(raw, "", fips) require.NoError(t, err) require.NotNil(t, key.Public()) require.NotNil(t, key.Private()) } func testParsePEMPrivateKeyLegacyWithFIPS(t *testing.T, raw []byte) { - defer preserveEnv(notary.FIPSEnvVar)() - - err := os.Setenv(notary.FIPSEnvVar, "1") - require.NoError(t, err) + fips := true // No legacy key must be accepted in FIPS mode - _, err = ParsePEMPrivateKey(raw, "") + _, err := parsePEMPrivateKey(raw, "", fips) require.Error(t, err) }