Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework the FIPS mode detection #1320

Merged
merged 1 commit into from
Mar 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions fips.go
Original file line number Diff line number Diff line change
@@ -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()
}
17 changes: 12 additions & 5 deletions tuf/utils/x509.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -146,13 +142,20 @@ 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")
}

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 == "" {
Expand Down Expand Up @@ -433,14 +436,18 @@ 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")
}

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":
Expand Down
57 changes: 16 additions & 41 deletions tuf/utils/x509_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -289,89 +287,72 @@ 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", "")
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)

// 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)
Expand Down Expand Up @@ -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)
}

Expand Down