Skip to content
This repository has been archived by the owner on Feb 9, 2021. It is now read-only.

Commit

Permalink
Refactor with more test coverage
Browse files Browse the repository at this point in the history
Refactor of some of the key file functions to be more generic
and ensure that all types of keys and output formats are covered.

More unit tests to cover added functionalities.

Updated tlsdemo to match current interface.
  • Loading branch information
Josh Hawn committed Aug 21, 2014
1 parent f58e0d5 commit 96be7e4
Show file tree
Hide file tree
Showing 13 changed files with 374 additions and 101 deletions.
2 changes: 2 additions & 0 deletions ec_key.go
Expand Up @@ -151,6 +151,7 @@ func (k *ecPublicKey) PEMBlock() (*pem.Block, error) {
if err != nil {
return nil, fmt.Errorf("unable to serialize EC PublicKey to DER-encoded PKIX format: %s", err)
}
k.extended["keyID"] = k.KeyID()
return createPemBlock("PUBLIC KEY", derBytes, k.extended)
}

Expand Down Expand Up @@ -345,6 +346,7 @@ func (k *ecPrivateKey) PEMBlock() (*pem.Block, error) {
if err != nil {
return nil, fmt.Errorf("unable to serialize EC PrivateKey to DER-encoded PKIX format: %s", err)
}
k.extended["keyID"] = k.KeyID()
return createPemBlock("EC PRIVATE KEY", derBytes, k.extended)
}

Expand Down
18 changes: 18 additions & 0 deletions ec_key_test.go
Expand Up @@ -137,3 +137,21 @@ func TestFromCryptoECKeys(t *testing.T) {
}
}
}

func TestExtendedFields(t *testing.T) {
key, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatal(err)
}

key.AddExtendedField("test", "foobar")
val := key.GetExtendedField("test")

gotVal, ok := val.(string)
if !ok {
t.Fatalf("value is not a string")
} else if gotVal != val {
t.Fatalf("value %q is not equal to %q", gotVal, val)
}

}
39 changes: 16 additions & 23 deletions filter.go
Expand Up @@ -4,41 +4,34 @@ import (
"path/filepath"
)

// FilterByHosts does something.
func FilterByHosts(keys []PublicKey, host string, includeEmpty bool) ([]PublicKey, error) {
filtered := make([]PublicKey, 0, len(keys))
for i := range keys {
var hosts interface{}
switch k := keys[i].(type) {
case *ecPublicKey:
hosts = k.GetExtendedField("hosts")
case *rsaPublicKey:
hosts = k.GetExtendedField("hosts")
default:
continue
}
hostList, ok := hosts.([]interface{})
if !ok || (ok && len(hostList) == 0) {

for _, pubKey := range keys {
hosts, ok := pubKey.GetExtendedField("hosts").([]string)

if !ok || (ok && len(hosts) == 0) {
if includeEmpty {
filtered = append(filtered, keys[i])
filtered = append(filtered, pubKey)
}
continue
}
// Check if any hostList match pattern
for _, h := range hostList {
hString, ok := h.(string)
if !ok {
continue
}
match, matchErr := filepath.Match(hString, host)
if matchErr != nil {
return nil, matchErr

// Check if any hosts match pattern
for _, hostPattern := range hosts {
match, err := filepath.Match(hostPattern, host)
if err != nil {
return nil, err
}

if match {
filtered = append(filtered, keys[i])
filtered = append(filtered, pubKey)
continue
}
}

}

return filtered, nil
}
79 changes: 79 additions & 0 deletions filter_test.go
@@ -0,0 +1,79 @@
package libtrust

import (
"testing"
)

func compareKeySlices(t *testing.T, sliceA, sliceB []PublicKey) {
if len(sliceA) != len(sliceB) {
t.Fatalf("slice size %d, expected %d", len(sliceA), len(sliceB))
}

for i, itemA := range sliceA {
itemB := sliceB[i]
if itemA != itemB {
t.Fatalf("slice index %d not equal: %#v != %#v", i, itemA, itemB)
}
}
}

func TestFilter(t *testing.T) {
keys := make([]PublicKey, 0, 8)

// Create 8 keys and add host entries.
for i := 0; i < cap(keys); i++ {
key, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatal(err)
}

switch {
case i == 0:
// Don't add entries for this key, key 0.
break
case i%2 == 0:
// Should catch keys 2, 4, and 6.
key.AddExtendedField("hosts", []string{"*.even.example.com"})
case i == 7:
// Should catch only the last key, and make it match any hostname.
key.AddExtendedField("hosts", []string{"*"})
default:
// should catch keys 1, 3, 5.
key.AddExtendedField("hosts", []string{"*.example.com"})
}

keys = append(keys, key)
}

// Should match 2 keys, the empty one, and the one that matches all hosts.
matchedKeys, err := FilterByHosts(keys, "foo.bar.com", true)
if err != nil {
t.Fatal(err)
}
expectedMatch := []PublicKey{keys[0], keys[7]}
compareKeySlices(t, expectedMatch, matchedKeys)

// Should match 1 key, the one that matches any host.
matchedKeys, err = FilterByHosts(keys, "foo.bar.com", false)
if err != nil {
t.Fatal(err)
}
expectedMatch = []PublicKey{keys[7]}
compareKeySlices(t, expectedMatch, matchedKeys)

// Should match keys that end in "example.com", and the key that matches anything.
matchedKeys, err = FilterByHosts(keys, "foo.example.com", false)
if err != nil {
t.Fatal(err)
}
expectedMatch = []PublicKey{keys[1], keys[3], keys[5], keys[7]}
compareKeySlices(t, expectedMatch, matchedKeys)

// Should match all of the keys except the empty key.
matchedKeys, err = FilterByHosts(keys, "foo.even.example.com", false)
if err != nil {
t.Fatal(err)
}
expectedMatch = keys[1:]
compareKeySlices(t, expectedMatch, matchedKeys)
}
63 changes: 57 additions & 6 deletions key.go
Expand Up @@ -103,12 +103,33 @@ func UnmarshalPublicKeyPEM(data []byte) (PublicKey, error) {
return nil, fmt.Errorf("unable to get PublicKey from PEM type: %s", pemBlock.Type)
}

cryptoPublicKey, err := x509.ParsePKIXPublicKey(pemBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("unable to decode Public Key PEM data: %s", err)
return pubKeyFromPEMBlock(pemBlock)
}

// UnmarshalPublicKeyPEMBundle parses the PEM encoded data as a bundle of
// PEM blocks appended one after the other and returns a slice of PublicKey
// objects that it finds.
func UnmarshalPublicKeyPEMBundle(data []byte) ([]PublicKey, error) {
pubKeys := []PublicKey{}

for {
var pemBlock *pem.Block
pemBlock, data = pem.Decode(data)
if pemBlock == nil {
break
} else if pemBlock.Type != "PUBLIC KEY" {
return nil, fmt.Errorf("unable to get PublicKey from PEM type: %s", pemBlock.Type)
}

pubKey, err := pubKeyFromPEMBlock(pemBlock)
if err != nil {
return nil, err
}

pubKeys = append(pubKeys, pubKey)
}

return FromCryptoPublicKey(cryptoPublicKey)
return pubKeys, nil
}

// UnmarshalPrivateKeyPEM parses the PEM encoded data and returns a libtrust
Expand All @@ -119,22 +140,31 @@ func UnmarshalPrivateKeyPEM(data []byte) (PrivateKey, error) {
return nil, errors.New("unable to find PEM encoded data")
}

var key PrivateKey

switch {
case pemBlock.Type == "RSA PRIVATE KEY":
rsaPrivateKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("unable to decode RSA Private Key PEM data: %s", err)
}
return fromRSAPrivateKey(rsaPrivateKey), nil
key = fromRSAPrivateKey(rsaPrivateKey)
case pemBlock.Type == "EC PRIVATE KEY":
ecPrivateKey, err := x509.ParseECPrivateKey(pemBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("unable to decode EC Private Key PEM data: %s", err)
}
return fromECPrivateKey(ecPrivateKey)
key, err = fromECPrivateKey(ecPrivateKey)
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unable to get PrivateKey from PEM type: %s", pemBlock.Type)
}

addPEMHeadersToKey(pemBlock, key.PublicKey())

return key, nil
}

// UnmarshalPublicKeyJWK unmarshals the given JSON Web Key into a generic
Expand Down Expand Up @@ -169,6 +199,27 @@ func UnmarshalPublicKeyJWK(data []byte) (PublicKey, error) {
}
}

// UnmarshalPublicKeyJWKSet parses the JSON encoded data as a JSON Web Key Set
// and returns a slice of Public Key objects.
func UnmarshalPublicKeyJWKSet(data []byte) ([]PublicKey, error) {
rawKeys, err := loadJSONKeySetRaw(data)
if err != nil {
return nil, err
}

pubKeys := make([]PublicKey, 0, len(rawKeys))

for _, rawKey := range rawKeys {
pubKey, err := UnmarshalPublicKeyJWK(rawKey)
if err != nil {
return nil, err
}
pubKeys = append(pubKeys, pubKey)
}

return pubKeys, nil
}

// UnmarshalPrivateKeyJWK unmarshals the given JSON Web Key into a generic
// Private Key to be used with libtrust.
func UnmarshalPrivateKeyJWK(data []byte) (PrivateKey, error) {
Expand Down

0 comments on commit 96be7e4

Please sign in to comment.