Skip to content

Commit

Permalink
Merge pull request notaryproject#1320 from justincormack/fips-test
Browse files Browse the repository at this point in the history
Rework the FIPS mode detection
  • Loading branch information
HuKeping authored Mar 28, 2018
2 parents 30aebf8 + 1710178 commit ab322b6
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 54 deletions.
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

0 comments on commit ab322b6

Please sign in to comment.