Fix godeps

Signed-off-by: Olivier Gambier <olivier@docker.com>
This commit is contained in:
Olivier Gambier 2016-03-21 12:08:47 -07:00
parent 77e69b9cf3
commit 53e3c1d7b2
806 changed files with 431 additions and 1075412 deletions

View file

@ -23,7 +23,7 @@ func EncodeSorted(values url.Values) string {
// preallocate the arrays for perfomance
keys := make([]string, 0, len(values))
sarray := make([]string, 0, len(values))
for k, _ := range values {
for k := range values {
keys = append(keys, k)
}
sort.Strings(keys)
@ -65,7 +65,7 @@ func (s *V2Signer) Sign(method, path string, params map[string]string) {
// from the natural order of the encoded value of key=value.
// Percent and gocheck.Equals affect the sorting order.
var keys, sarray []string
for k, _ := range params {
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
@ -372,7 +372,7 @@ func (s *V4Signer) canonicalHeaders(h http.Header) string {
func (s *V4Signer) signedHeaders(h http.Header) string {
i, a := 0, make([]string, len(h))
for k, _ := range h {
for k := range h {
a[i] = strings.ToLower(k)
i++
}

File diff suppressed because it is too large Load diff

View file

@ -1,111 +0,0 @@
package libtrust
import (
"encoding/pem"
"io/ioutil"
"net"
"os"
"path"
"testing"
)
func TestGenerateCertificates(t *testing.T) {
key, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatal(err)
}
_, err = GenerateSelfSignedServerCert(key, []string{"localhost"}, []net.IP{net.ParseIP("127.0.0.1")})
if err != nil {
t.Fatal(err)
}
_, err = GenerateSelfSignedClientCert(key)
if err != nil {
t.Fatal(err)
}
}
func TestGenerateCACertPool(t *testing.T) {
key, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatal(err)
}
caKey1, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatal(err)
}
caKey2, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatal(err)
}
_, err = GenerateCACertPool(key, []PublicKey{caKey1.PublicKey(), caKey2.PublicKey()})
if err != nil {
t.Fatal(err)
}
}
func TestLoadCertificates(t *testing.T) {
key, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatal(err)
}
caKey1, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatal(err)
}
caKey2, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatal(err)
}
cert1, err := GenerateCACert(caKey1, key)
if err != nil {
t.Fatal(err)
}
cert2, err := GenerateCACert(caKey2, key)
if err != nil {
t.Fatal(err)
}
d, err := ioutil.TempDir("/tmp", "cert-test")
if err != nil {
t.Fatal(err)
}
caFile := path.Join(d, "ca.pem")
f, err := os.OpenFile(caFile, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
t.Fatal(err)
}
err = pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert1.Raw})
if err != nil {
t.Fatal(err)
}
err = pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert2.Raw})
if err != nil {
t.Fatal(err)
}
f.Close()
certs, err := LoadCertificateBundle(caFile)
if err != nil {
t.Fatal(err)
}
if len(certs) != 2 {
t.Fatalf("Wrong number of certs received, expected: %d, received %d", 2, len(certs))
}
pool, err := LoadCertificatePool(caFile)
if err != nil {
t.Fatal(err)
}
if len(pool.Subjects()) != 2 {
t.Fatalf("Invalid certificate pool")
}
}

View file

@ -1,157 +0,0 @@
package libtrust
import (
"bytes"
"encoding/json"
"testing"
)
func generateECTestKeys(t *testing.T) []PrivateKey {
p256Key, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatal(err)
}
p384Key, err := GenerateECP384PrivateKey()
if err != nil {
t.Fatal(err)
}
p521Key, err := GenerateECP521PrivateKey()
if err != nil {
t.Fatal(err)
}
return []PrivateKey{p256Key, p384Key, p521Key}
}
func TestECKeys(t *testing.T) {
ecKeys := generateECTestKeys(t)
for _, ecKey := range ecKeys {
if ecKey.KeyType() != "EC" {
t.Fatalf("key type must be %q, instead got %q", "EC", ecKey.KeyType())
}
}
}
func TestECSignVerify(t *testing.T) {
ecKeys := generateECTestKeys(t)
message := "Hello, World!"
data := bytes.NewReader([]byte(message))
sigAlgs := []*signatureAlgorithm{es256, es384, es512}
for i, ecKey := range ecKeys {
sigAlg := sigAlgs[i]
t.Logf("%s signature of %q with kid: %s\n", sigAlg.HeaderParam(), message, ecKey.KeyID())
data.Seek(0, 0) // Reset the byte reader
// Sign
sig, alg, err := ecKey.Sign(data, sigAlg.HashID())
if err != nil {
t.Fatal(err)
}
data.Seek(0, 0) // Reset the byte reader
// Verify
err = ecKey.Verify(data, alg, sig)
if err != nil {
t.Fatal(err)
}
}
}
func TestMarshalUnmarshalECKeys(t *testing.T) {
ecKeys := generateECTestKeys(t)
data := bytes.NewReader([]byte("This is a test. I repeat: this is only a test."))
sigAlgs := []*signatureAlgorithm{es256, es384, es512}
for i, ecKey := range ecKeys {
sigAlg := sigAlgs[i]
privateJWKJSON, err := json.MarshalIndent(ecKey, "", " ")
if err != nil {
t.Fatal(err)
}
publicJWKJSON, err := json.MarshalIndent(ecKey.PublicKey(), "", " ")
if err != nil {
t.Fatal(err)
}
t.Logf("JWK Private Key: %s", string(privateJWKJSON))
t.Logf("JWK Public Key: %s", string(publicJWKJSON))
privKey2, err := UnmarshalPrivateKeyJWK(privateJWKJSON)
if err != nil {
t.Fatal(err)
}
pubKey2, err := UnmarshalPublicKeyJWK(publicJWKJSON)
if err != nil {
t.Fatal(err)
}
// Ensure we can sign/verify a message with the unmarshalled keys.
data.Seek(0, 0) // Reset the byte reader
signature, alg, err := privKey2.Sign(data, sigAlg.HashID())
if err != nil {
t.Fatal(err)
}
data.Seek(0, 0) // Reset the byte reader
err = pubKey2.Verify(data, alg, signature)
if err != nil {
t.Fatal(err)
}
}
}
func TestFromCryptoECKeys(t *testing.T) {
ecKeys := generateECTestKeys(t)
for _, ecKey := range ecKeys {
cryptoPrivateKey := ecKey.CryptoPrivateKey()
cryptoPublicKey := ecKey.CryptoPublicKey()
pubKey, err := FromCryptoPublicKey(cryptoPublicKey)
if err != nil {
t.Fatal(err)
}
if pubKey.KeyID() != ecKey.KeyID() {
t.Fatal("public key key ID mismatch")
}
privKey, err := FromCryptoPrivateKey(cryptoPrivateKey)
if err != nil {
t.Fatal(err)
}
if privKey.KeyID() != ecKey.KeyID() {
t.Fatal("public key key ID mismatch")
}
}
}
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)
}
}

View file

@ -1,81 +0,0 @@
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)
}
// we use both []interface{} and []string here because jwt uses
// []interface{} format, while PEM uses []string
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", []interface{}{"*.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)
}

View file

@ -1,380 +0,0 @@
package libtrust
import (
"bytes"
"crypto/rand"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"testing"
"github.com/docker/libtrust/testutil"
)
func createTestJSON(sigKey string, indent string) (map[string]interface{}, []byte) {
testMap := map[string]interface{}{
"name": "dmcgowan/mycontainer",
"config": map[string]interface{}{
"ports": []int{9101, 9102},
"run": "/bin/echo \"Hello\"",
},
"layers": []string{
"2893c080-27f5-11e4-8c21-0800200c9a66",
"c54bc25b-fbb2-497b-a899-a8bc1b5b9d55",
"4d5d7e03-f908-49f3-a7f6-9ba28dfe0fb4",
"0b6da891-7f7f-4abf-9c97-7887549e696c",
"1d960389-ae4f-4011-85fd-18d0f96a67ad",
},
}
formattedSection := `{"config":{"ports":[9101,9102],"run":"/bin/echo \"Hello\""},"layers":["2893c080-27f5-11e4-8c21-0800200c9a66","c54bc25b-fbb2-497b-a899-a8bc1b5b9d55","4d5d7e03-f908-49f3-a7f6-9ba28dfe0fb4","0b6da891-7f7f-4abf-9c97-7887549e696c","1d960389-ae4f-4011-85fd-18d0f96a67ad"],"name":"dmcgowan/mycontainer","%s":[{"header":{`
formattedSection = fmt.Sprintf(formattedSection, sigKey)
if indent != "" {
buf := bytes.NewBuffer(nil)
json.Indent(buf, []byte(formattedSection), "", indent)
return testMap, buf.Bytes()
}
return testMap, []byte(formattedSection)
}
func TestSignJSON(t *testing.T) {
key, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatalf("Error generating EC key: %s", err)
}
testMap, _ := createTestJSON("buildSignatures", " ")
indented, err := json.MarshalIndent(testMap, "", " ")
if err != nil {
t.Fatalf("Marshall error: %s", err)
}
js, err := NewJSONSignature(indented)
if err != nil {
t.Fatalf("Error creating JSON signature: %s", err)
}
err = js.Sign(key)
if err != nil {
t.Fatalf("Error signing content: %s", err)
}
keys, err := js.Verify()
if err != nil {
t.Fatalf("Error verifying signature: %s", err)
}
if len(keys) != 1 {
t.Fatalf("Error wrong number of keys returned")
}
if keys[0].KeyID() != key.KeyID() {
t.Fatalf("Unexpected public key returned")
}
}
func TestSignMap(t *testing.T) {
key, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatalf("Error generating EC key: %s", err)
}
testMap, _ := createTestJSON("buildSignatures", " ")
js, err := NewJSONSignatureFromMap(testMap)
if err != nil {
t.Fatalf("Error creating JSON signature: %s", err)
}
err = js.Sign(key)
if err != nil {
t.Fatalf("Error signing JSON signature: %s", err)
}
keys, err := js.Verify()
if err != nil {
t.Fatalf("Error verifying signature: %s", err)
}
if len(keys) != 1 {
t.Fatalf("Error wrong number of keys returned")
}
if keys[0].KeyID() != key.KeyID() {
t.Fatalf("Unexpected public key returned")
}
}
func TestFormattedJson(t *testing.T) {
key, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatalf("Error generating EC key: %s", err)
}
testMap, firstSection := createTestJSON("buildSignatures", " ")
indented, err := json.MarshalIndent(testMap, "", " ")
if err != nil {
t.Fatalf("Marshall error: %s", err)
}
js, err := NewJSONSignature(indented)
if err != nil {
t.Fatalf("Error creating JSON signature: %s", err)
}
err = js.Sign(key)
if err != nil {
t.Fatalf("Error signing content: %s", err)
}
b, err := js.PrettySignature("buildSignatures")
if err != nil {
t.Fatalf("Error signing map: %s", err)
}
if bytes.Compare(b[:len(firstSection)], firstSection) != 0 {
t.Fatalf("Wrong signed value\nExpected:\n%s\nActual:\n%s", firstSection, b[:len(firstSection)])
}
parsed, err := ParsePrettySignature(b, "buildSignatures")
if err != nil {
t.Fatalf("Error parsing formatted signature: %s", err)
}
keys, err := parsed.Verify()
if err != nil {
t.Fatalf("Error verifying signature: %s", err)
}
if len(keys) != 1 {
t.Fatalf("Error wrong number of keys returned")
}
if keys[0].KeyID() != key.KeyID() {
t.Fatalf("Unexpected public key returned")
}
var unmarshalled map[string]interface{}
err = json.Unmarshal(b, &unmarshalled)
if err != nil {
t.Fatalf("Could not unmarshall after parse: %s", err)
}
}
func TestFormattedFlatJson(t *testing.T) {
key, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatalf("Error generating EC key: %s", err)
}
testMap, firstSection := createTestJSON("buildSignatures", "")
unindented, err := json.Marshal(testMap)
if err != nil {
t.Fatalf("Marshall error: %s", err)
}
js, err := NewJSONSignature(unindented)
if err != nil {
t.Fatalf("Error creating JSON signature: %s", err)
}
err = js.Sign(key)
if err != nil {
t.Fatalf("Error signing JSON signature: %s", err)
}
b, err := js.PrettySignature("buildSignatures")
if err != nil {
t.Fatalf("Error signing map: %s", err)
}
if bytes.Compare(b[:len(firstSection)], firstSection) != 0 {
t.Fatalf("Wrong signed value\nExpected:\n%s\nActual:\n%s", firstSection, b[:len(firstSection)])
}
parsed, err := ParsePrettySignature(b, "buildSignatures")
if err != nil {
t.Fatalf("Error parsing formatted signature: %s", err)
}
keys, err := parsed.Verify()
if err != nil {
t.Fatalf("Error verifying signature: %s", err)
}
if len(keys) != 1 {
t.Fatalf("Error wrong number of keys returned")
}
if keys[0].KeyID() != key.KeyID() {
t.Fatalf("Unexpected public key returned")
}
}
func generateTrustChain(t *testing.T, key PrivateKey, ca *x509.Certificate) (PrivateKey, []*x509.Certificate) {
parent := ca
parentKey := key
chain := make([]*x509.Certificate, 6)
for i := 5; i > 0; i-- {
intermediatekey, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatalf("Error generate key: %s", err)
}
chain[i], err = testutil.GenerateIntermediate(intermediatekey.CryptoPublicKey(), parentKey.CryptoPrivateKey(), parent)
if err != nil {
t.Fatalf("Error generating intermdiate certificate: %s", err)
}
parent = chain[i]
parentKey = intermediatekey
}
trustKey, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatalf("Error generate key: %s", err)
}
chain[0], err = testutil.GenerateTrustCert(trustKey.CryptoPublicKey(), parentKey.CryptoPrivateKey(), parent)
if err != nil {
t.Fatalf("Error generate trust cert: %s", err)
}
return trustKey, chain
}
func TestChainVerify(t *testing.T) {
caKey, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatalf("Error generating key: %s", err)
}
ca, err := testutil.GenerateTrustCA(caKey.CryptoPublicKey(), caKey.CryptoPrivateKey())
if err != nil {
t.Fatalf("Error generating ca: %s", err)
}
trustKey, chain := generateTrustChain(t, caKey, ca)
testMap, _ := createTestJSON("verifySignatures", " ")
js, err := NewJSONSignatureFromMap(testMap)
if err != nil {
t.Fatalf("Error creating JSONSignature from map: %s", err)
}
err = js.SignWithChain(trustKey, chain)
if err != nil {
t.Fatalf("Error signing with chain: %s", err)
}
pool := x509.NewCertPool()
pool.AddCert(ca)
chains, err := js.VerifyChains(pool)
if err != nil {
t.Fatalf("Error verifying content: %s", err)
}
if len(chains) != 1 {
t.Fatalf("Unexpected chains length: %d", len(chains))
}
if len(chains[0]) != 7 {
t.Fatalf("Unexpected chain length: %d", len(chains[0]))
}
}
func TestInvalidChain(t *testing.T) {
caKey, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatalf("Error generating key: %s", err)
}
ca, err := testutil.GenerateTrustCA(caKey.CryptoPublicKey(), caKey.CryptoPrivateKey())
if err != nil {
t.Fatalf("Error generating ca: %s", err)
}
trustKey, chain := generateTrustChain(t, caKey, ca)
testMap, _ := createTestJSON("verifySignatures", " ")
js, err := NewJSONSignatureFromMap(testMap)
if err != nil {
t.Fatalf("Error creating JSONSignature from map: %s", err)
}
err = js.SignWithChain(trustKey, chain[:5])
if err != nil {
t.Fatalf("Error signing with chain: %s", err)
}
pool := x509.NewCertPool()
pool.AddCert(ca)
chains, err := js.VerifyChains(pool)
if err == nil {
t.Fatalf("Expected error verifying with bad chain")
}
if len(chains) != 0 {
t.Fatalf("Unexpected chains returned from invalid verify")
}
}
func TestMergeSignatures(t *testing.T) {
pk1, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatalf("unexpected error generating private key 1: %v", err)
}
pk2, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatalf("unexpected error generating private key 2: %v", err)
}
payload := make([]byte, 1<<10)
if _, err = io.ReadFull(rand.Reader, payload); err != nil {
t.Fatalf("error generating payload: %v", err)
}
payload, _ = json.Marshal(map[string]interface{}{"data": payload})
sig1, err := NewJSONSignature(payload)
if err != nil {
t.Fatalf("unexpected error creating signature 1: %v", err)
}
if err := sig1.Sign(pk1); err != nil {
t.Fatalf("unexpected error signing with pk1: %v", err)
}
sig2, err := NewJSONSignature(payload)
if err != nil {
t.Fatalf("unexpected error creating signature 2: %v", err)
}
if err := sig2.Sign(pk2); err != nil {
t.Fatalf("unexpected error signing with pk2: %v", err)
}
// Now, we actually merge into sig1
if err := sig1.Merge(sig2); err != nil {
t.Fatalf("unexpected error merging: %v", err)
}
// Verify the new signature package
pubkeys, err := sig1.Verify()
if err != nil {
t.Fatalf("unexpected error during verify: %v", err)
}
// Make sure the pubkeys match the two private keys from before
privkeys := map[string]PrivateKey{
pk1.KeyID(): pk1,
pk2.KeyID(): pk2,
}
found := map[string]struct{}{}
for _, pubkey := range pubkeys {
if _, ok := privkeys[pubkey.KeyID()]; !ok {
t.Fatalf("unexpected public key found during verification: %v", pubkey)
}
found[pubkey.KeyID()] = struct{}{}
}
// Make sure we've found all the private keys from verification
for keyid, _ := range privkeys {
if _, ok := found[keyid]; !ok {
t.Fatalf("public key %v not found during verification", keyid)
}
}
// Create another signature, with a different payload, and ensure we get an error.
sig3, err := NewJSONSignature([]byte("{}"))
if err != nil {
t.Fatalf("unexpected error making signature for sig3: %v", err)
}
if err := sig1.Merge(sig3); err == nil {
t.Fatalf("error expected during invalid merge with different payload")
}
}

View file

@ -1,220 +0,0 @@
package libtrust
import (
"errors"
"io/ioutil"
"os"
"testing"
)
func makeTempFile(t *testing.T, prefix string) (filename string) {
file, err := ioutil.TempFile("", prefix)
if err != nil {
t.Fatal(err)
}
filename = file.Name()
file.Close()
return
}
func TestKeyFiles(t *testing.T) {
key, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatal(err)
}
testKeyFiles(t, key)
key, err = GenerateRSA2048PrivateKey()
if err != nil {
t.Fatal(err)
}
testKeyFiles(t, key)
}
func testKeyFiles(t *testing.T, key PrivateKey) {
var err error
privateKeyFilename := makeTempFile(t, "private_key")
privateKeyFilenamePEM := privateKeyFilename + ".pem"
privateKeyFilenameJWK := privateKeyFilename + ".jwk"
publicKeyFilename := makeTempFile(t, "public_key")
publicKeyFilenamePEM := publicKeyFilename + ".pem"
publicKeyFilenameJWK := publicKeyFilename + ".jwk"
if err = SaveKey(privateKeyFilenamePEM, key); err != nil {
t.Fatal(err)
}
if err = SaveKey(privateKeyFilenameJWK, key); err != nil {
t.Fatal(err)
}
if err = SavePublicKey(publicKeyFilenamePEM, key.PublicKey()); err != nil {
t.Fatal(err)
}
if err = SavePublicKey(publicKeyFilenameJWK, key.PublicKey()); err != nil {
t.Fatal(err)
}
loadedPEMKey, err := LoadKeyFile(privateKeyFilenamePEM)
if err != nil {
t.Fatal(err)
}
loadedJWKKey, err := LoadKeyFile(privateKeyFilenameJWK)
if err != nil {
t.Fatal(err)
}
loadedPEMPublicKey, err := LoadPublicKeyFile(publicKeyFilenamePEM)
if err != nil {
t.Fatal(err)
}
loadedJWKPublicKey, err := LoadPublicKeyFile(publicKeyFilenameJWK)
if err != nil {
t.Fatal(err)
}
if key.KeyID() != loadedPEMKey.KeyID() {
t.Fatal(errors.New("key IDs do not match"))
}
if key.KeyID() != loadedJWKKey.KeyID() {
t.Fatal(errors.New("key IDs do not match"))
}
if key.KeyID() != loadedPEMPublicKey.KeyID() {
t.Fatal(errors.New("key IDs do not match"))
}
if key.KeyID() != loadedJWKPublicKey.KeyID() {
t.Fatal(errors.New("key IDs do not match"))
}
os.Remove(privateKeyFilename)
os.Remove(privateKeyFilenamePEM)
os.Remove(privateKeyFilenameJWK)
os.Remove(publicKeyFilename)
os.Remove(publicKeyFilenamePEM)
os.Remove(publicKeyFilenameJWK)
}
func TestTrustedHostKeysFile(t *testing.T) {
trustedHostKeysFilename := makeTempFile(t, "trusted_host_keys")
trustedHostKeysFilenamePEM := trustedHostKeysFilename + ".pem"
trustedHostKeysFilenameJWK := trustedHostKeysFilename + ".json"
testTrustedHostKeysFile(t, trustedHostKeysFilenamePEM)
testTrustedHostKeysFile(t, trustedHostKeysFilenameJWK)
os.Remove(trustedHostKeysFilename)
os.Remove(trustedHostKeysFilenamePEM)
os.Remove(trustedHostKeysFilenameJWK)
}
func testTrustedHostKeysFile(t *testing.T, trustedHostKeysFilename string) {
hostAddress1 := "docker.example.com:2376"
hostKey1, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatal(err)
}
hostKey1.AddExtendedField("hosts", []string{hostAddress1})
err = AddKeySetFile(trustedHostKeysFilename, hostKey1.PublicKey())
if err != nil {
t.Fatal(err)
}
trustedHostKeysMapping, err := LoadKeySetFile(trustedHostKeysFilename)
if err != nil {
t.Fatal(err)
}
for addr, hostKey := range trustedHostKeysMapping {
t.Logf("Host Address: %d\n", addr)
t.Logf("Host Key: %s\n\n", hostKey)
}
hostAddress2 := "192.168.59.103:2376"
hostKey2, err := GenerateRSA2048PrivateKey()
if err != nil {
t.Fatal(err)
}
hostKey2.AddExtendedField("hosts", hostAddress2)
err = AddKeySetFile(trustedHostKeysFilename, hostKey2.PublicKey())
if err != nil {
t.Fatal(err)
}
trustedHostKeysMapping, err = LoadKeySetFile(trustedHostKeysFilename)
if err != nil {
t.Fatal(err)
}
for addr, hostKey := range trustedHostKeysMapping {
t.Logf("Host Address: %d\n", addr)
t.Logf("Host Key: %s\n\n", hostKey)
}
}
func TestTrustedClientKeysFile(t *testing.T) {
trustedClientKeysFilename := makeTempFile(t, "trusted_client_keys")
trustedClientKeysFilenamePEM := trustedClientKeysFilename + ".pem"
trustedClientKeysFilenameJWK := trustedClientKeysFilename + ".json"
testTrustedClientKeysFile(t, trustedClientKeysFilenamePEM)
testTrustedClientKeysFile(t, trustedClientKeysFilenameJWK)
os.Remove(trustedClientKeysFilename)
os.Remove(trustedClientKeysFilenamePEM)
os.Remove(trustedClientKeysFilenameJWK)
}
func testTrustedClientKeysFile(t *testing.T, trustedClientKeysFilename string) {
clientKey1, err := GenerateECP256PrivateKey()
if err != nil {
t.Fatal(err)
}
err = AddKeySetFile(trustedClientKeysFilename, clientKey1.PublicKey())
if err != nil {
t.Fatal(err)
}
trustedClientKeys, err := LoadKeySetFile(trustedClientKeysFilename)
if err != nil {
t.Fatal(err)
}
for _, clientKey := range trustedClientKeys {
t.Logf("Client Key: %s\n", clientKey)
}
clientKey2, err := GenerateRSA2048PrivateKey()
if err != nil {
t.Fatal(err)
}
err = AddKeySetFile(trustedClientKeysFilename, clientKey2.PublicKey())
if err != nil {
t.Fatal(err)
}
trustedClientKeys, err = LoadKeySetFile(trustedClientKeysFilename)
if err != nil {
t.Fatal(err)
}
for _, clientKey := range trustedClientKeys {
t.Logf("Client Key: %s\n", clientKey)
}
}

View file

@ -1,80 +0,0 @@
package libtrust
import (
"testing"
)
type generateFunc func() (PrivateKey, error)
func runGenerateBench(b *testing.B, f generateFunc, name string) {
for i := 0; i < b.N; i++ {
_, err := f()
if err != nil {
b.Fatalf("Error generating %s: %s", name, err)
}
}
}
func runFingerprintBench(b *testing.B, f generateFunc, name string) {
b.StopTimer()
// Don't count this relatively slow generation call.
key, err := f()
if err != nil {
b.Fatalf("Error generating %s: %s", name, err)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
if key.KeyID() == "" {
b.Fatalf("Error generating key ID for %s", name)
}
}
}
func BenchmarkECP256Generate(b *testing.B) {
runGenerateBench(b, GenerateECP256PrivateKey, "P256")
}
func BenchmarkECP384Generate(b *testing.B) {
runGenerateBench(b, GenerateECP384PrivateKey, "P384")
}
func BenchmarkECP521Generate(b *testing.B) {
runGenerateBench(b, GenerateECP521PrivateKey, "P521")
}
func BenchmarkRSA2048Generate(b *testing.B) {
runGenerateBench(b, GenerateRSA2048PrivateKey, "RSA2048")
}
func BenchmarkRSA3072Generate(b *testing.B) {
runGenerateBench(b, GenerateRSA3072PrivateKey, "RSA3072")
}
func BenchmarkRSA4096Generate(b *testing.B) {
runGenerateBench(b, GenerateRSA4096PrivateKey, "RSA4096")
}
func BenchmarkECP256Fingerprint(b *testing.B) {
runFingerprintBench(b, GenerateECP256PrivateKey, "P256")
}
func BenchmarkECP384Fingerprint(b *testing.B) {
runFingerprintBench(b, GenerateECP384PrivateKey, "P384")
}
func BenchmarkECP521Fingerprint(b *testing.B) {
runFingerprintBench(b, GenerateECP521PrivateKey, "P521")
}
func BenchmarkRSA2048Fingerprint(b *testing.B) {
runFingerprintBench(b, GenerateRSA2048PrivateKey, "RSA2048")
}
func BenchmarkRSA3072Fingerprint(b *testing.B) {
runFingerprintBench(b, GenerateRSA3072PrivateKey, "RSA3072")
}
func BenchmarkRSA4096Fingerprint(b *testing.B) {
runFingerprintBench(b, GenerateRSA4096PrivateKey, "RSA4096")
}

View file

@ -1,157 +0,0 @@
package libtrust
import (
"bytes"
"encoding/json"
"log"
"testing"
)
var rsaKeys []PrivateKey
func init() {
var err error
rsaKeys, err = generateRSATestKeys()
if err != nil {
log.Fatal(err)
}
}
func generateRSATestKeys() (keys []PrivateKey, err error) {
log.Println("Generating RSA 2048-bit Test Key")
rsa2048Key, err := GenerateRSA2048PrivateKey()
if err != nil {
return
}
log.Println("Generating RSA 3072-bit Test Key")
rsa3072Key, err := GenerateRSA3072PrivateKey()
if err != nil {
return
}
log.Println("Generating RSA 4096-bit Test Key")
rsa4096Key, err := GenerateRSA4096PrivateKey()
if err != nil {
return
}
log.Println("Done generating RSA Test Keys!")
keys = []PrivateKey{rsa2048Key, rsa3072Key, rsa4096Key}
return
}
func TestRSAKeys(t *testing.T) {
for _, rsaKey := range rsaKeys {
if rsaKey.KeyType() != "RSA" {
t.Fatalf("key type must be %q, instead got %q", "RSA", rsaKey.KeyType())
}
}
}
func TestRSASignVerify(t *testing.T) {
message := "Hello, World!"
data := bytes.NewReader([]byte(message))
sigAlgs := []*signatureAlgorithm{rs256, rs384, rs512}
for i, rsaKey := range rsaKeys {
sigAlg := sigAlgs[i]
t.Logf("%s signature of %q with kid: %s\n", sigAlg.HeaderParam(), message, rsaKey.KeyID())
data.Seek(0, 0) // Reset the byte reader
// Sign
sig, alg, err := rsaKey.Sign(data, sigAlg.HashID())
if err != nil {
t.Fatal(err)
}
data.Seek(0, 0) // Reset the byte reader
// Verify
err = rsaKey.Verify(data, alg, sig)
if err != nil {
t.Fatal(err)
}
}
}
func TestMarshalUnmarshalRSAKeys(t *testing.T) {
data := bytes.NewReader([]byte("This is a test. I repeat: this is only a test."))
sigAlgs := []*signatureAlgorithm{rs256, rs384, rs512}
for i, rsaKey := range rsaKeys {
sigAlg := sigAlgs[i]
privateJWKJSON, err := json.MarshalIndent(rsaKey, "", " ")
if err != nil {
t.Fatal(err)
}
publicJWKJSON, err := json.MarshalIndent(rsaKey.PublicKey(), "", " ")
if err != nil {
t.Fatal(err)
}
t.Logf("JWK Private Key: %s", string(privateJWKJSON))
t.Logf("JWK Public Key: %s", string(publicJWKJSON))
privKey2, err := UnmarshalPrivateKeyJWK(privateJWKJSON)
if err != nil {
t.Fatal(err)
}
pubKey2, err := UnmarshalPublicKeyJWK(publicJWKJSON)
if err != nil {
t.Fatal(err)
}
// Ensure we can sign/verify a message with the unmarshalled keys.
data.Seek(0, 0) // Reset the byte reader
signature, alg, err := privKey2.Sign(data, sigAlg.HashID())
if err != nil {
t.Fatal(err)
}
data.Seek(0, 0) // Reset the byte reader
err = pubKey2.Verify(data, alg, signature)
if err != nil {
t.Fatal(err)
}
// It's a good idea to validate the Private Key to make sure our
// (un)marshal process didn't corrupt the extra parameters.
k := privKey2.(*rsaPrivateKey)
err = k.PrivateKey.Validate()
if err != nil {
t.Fatal(err)
}
}
}
func TestFromCryptoRSAKeys(t *testing.T) {
for _, rsaKey := range rsaKeys {
cryptoPrivateKey := rsaKey.CryptoPrivateKey()
cryptoPublicKey := rsaKey.CryptoPublicKey()
pubKey, err := FromCryptoPublicKey(cryptoPublicKey)
if err != nil {
t.Fatal(err)
}
if pubKey.KeyID() != rsaKey.KeyID() {
t.Fatal("public key key ID mismatch")
}
privKey, err := FromCryptoPrivateKey(cryptoPrivateKey)
if err != nil {
t.Fatal(err)
}
if privKey.KeyID() != rsaKey.KeyID() {
t.Fatal("public key key ID mismatch")
}
}
}

View file

@ -1,94 +0,0 @@
package testutil
import (
"crypto"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"math/big"
"time"
)
// GenerateTrustCA generates a new certificate authority for testing.
func GenerateTrustCA(pub crypto.PublicKey, priv crypto.PrivateKey) (*x509.Certificate, error) {
cert := &x509.Certificate{
SerialNumber: big.NewInt(0),
Subject: pkix.Name{
CommonName: "CA Root",
},
NotBefore: time.Now().Add(-time.Second),
NotAfter: time.Now().Add(time.Hour),
IsCA: true,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
}
certDER, err := x509.CreateCertificate(rand.Reader, cert, cert, pub, priv)
if err != nil {
return nil, err
}
cert, err = x509.ParseCertificate(certDER)
if err != nil {
return nil, err
}
return cert, nil
}
// GenerateIntermediate generates an intermediate certificate for testing using
// the parent certificate (likely a CA) and the provided keys.
func GenerateIntermediate(key crypto.PublicKey, parentKey crypto.PrivateKey, parent *x509.Certificate) (*x509.Certificate, error) {
cert := &x509.Certificate{
SerialNumber: big.NewInt(0),
Subject: pkix.Name{
CommonName: "Intermediate",
},
NotBefore: time.Now().Add(-time.Second),
NotAfter: time.Now().Add(time.Hour),
IsCA: true,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
}
certDER, err := x509.CreateCertificate(rand.Reader, cert, parent, key, parentKey)
if err != nil {
return nil, err
}
cert, err = x509.ParseCertificate(certDER)
if err != nil {
return nil, err
}
return cert, nil
}
// GenerateTrustCert generates a new trust certificate for testing. Unlike the
// intermediate certificates, this certificate should be used for signature
// only, not creating certificates.
func GenerateTrustCert(key crypto.PublicKey, parentKey crypto.PrivateKey, parent *x509.Certificate) (*x509.Certificate, error) {
cert := &x509.Certificate{
SerialNumber: big.NewInt(0),
Subject: pkix.Name{
CommonName: "Trust Cert",
},
NotBefore: time.Now().Add(-time.Second),
NotAfter: time.Now().Add(time.Hour),
IsCA: true,
KeyUsage: x509.KeyUsageDigitalSignature,
BasicConstraintsValid: true,
}
certDER, err := x509.CreateCertificate(rand.Reader, cert, parent, key, parentKey)
if err != nil {
return nil, err
}
cert, err = x509.ParseCertificate(certDER)
if err != nil {
return nil, err
}
return cert, nil
}

View file

@ -1,50 +0,0 @@
## Libtrust TLS Config Demo
This program generates key pairs and trust files for a TLS client and server.
To generate the keys, run:
```
$ go run genkeys.go
```
The generated files are:
```
$ ls -l client_data/ server_data/
client_data/:
total 24
-rw------- 1 jlhawn staff 281 Aug 8 16:21 private_key.json
-rw-r--r-- 1 jlhawn staff 225 Aug 8 16:21 public_key.json
-rw-r--r-- 1 jlhawn staff 275 Aug 8 16:21 trusted_hosts.json
server_data/:
total 24
-rw-r--r-- 1 jlhawn staff 348 Aug 8 16:21 trusted_clients.json
-rw------- 1 jlhawn staff 281 Aug 8 16:21 private_key.json
-rw-r--r-- 1 jlhawn staff 225 Aug 8 16:21 public_key.json
```
The private key and public key for the client and server are stored in `private_key.json` and `public_key.json`, respectively, and in their respective directories. They are represented as JSON Web Keys: JSON objects which represent either an ECDSA or RSA private key. The host keys trusted by the client are stored in `trusted_hosts.json` and contain a mapping of an internet address, `<HOSTNAME_OR_IP>:<PORT>`, to a JSON Web Key which is a JSON object representing either an ECDSA or RSA public key of the trusted server. The client keys trusted by the server are stored in `trusted_clients.json` and contain an array of JSON objects which contain a comment field which can be used describe the key and a JSON Web Key which is a JSON object representing either an ECDSA or RSA public key of the trusted client.
To start the server, run:
```
$ go run server.go
```
This starts an HTTPS server which listens on `localhost:8888`. The server configures itself with a certificate which is valid for both `localhost` and `127.0.0.1` and uses the key from `server_data/private_key.json`. It accepts connections from clients which present a certificate for a key that it is configured to trust from the `trusted_clients.json` file and returns a simple 'hello' message.
To make a request using the client, run:
```
$ go run client.go
```
This command creates an HTTPS client which makes a GET request to `https://localhost:8888`. The client configures itself with a certificate using the key from `client_data/private_key.json`. It only connects to a server which presents a certificate signed by the key specified for the `localhost:8888` address from `client_data/trusted_hosts.json` and made to be used for the `localhost` hostname. If the connection succeeds, it prints the response from the server.
The file `gencert.go` can be used to generate PEM encoded version of the client key and certificate. If you save them to `key.pem` and `cert.pem` respectively, you can use them with `curl` to test out the server (if it is still running).
```
curl --cert cert.pem --key key.pem -k https://localhost:8888
```

View file

@ -1,89 +0,0 @@
package main
import (
"crypto/tls"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"github.com/docker/libtrust"
)
var (
serverAddress = "localhost:8888"
privateKeyFilename = "client_data/private_key.pem"
trustedHostsFilename = "client_data/trusted_hosts.pem"
)
func main() {
// Load Client Key.
clientKey, err := libtrust.LoadKeyFile(privateKeyFilename)
if err != nil {
log.Fatal(err)
}
// Generate Client Certificate.
selfSignedClientCert, err := libtrust.GenerateSelfSignedClientCert(clientKey)
if err != nil {
log.Fatal(err)
}
// Load trusted host keys.
hostKeys, err := libtrust.LoadKeySetFile(trustedHostsFilename)
if err != nil {
log.Fatal(err)
}
// Ensure the host we want to connect to is trusted!
host, _, err := net.SplitHostPort(serverAddress)
if err != nil {
log.Fatal(err)
}
serverKeys, err := libtrust.FilterByHosts(hostKeys, host, false)
if err != nil {
log.Fatalf("%q is not a known and trusted host", host)
}
// Generate a CA pool with the trusted host's key.
caPool, err := libtrust.GenerateCACertPool(clientKey, serverKeys)
if err != nil {
log.Fatal(err)
}
// Create HTTP Client.
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
Certificates: []tls.Certificate{
tls.Certificate{
Certificate: [][]byte{selfSignedClientCert.Raw},
PrivateKey: clientKey.CryptoPrivateKey(),
Leaf: selfSignedClientCert,
},
},
RootCAs: caPool,
},
},
}
var makeRequest = func(url string) {
resp, err := client.Get(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
log.Println(resp.Status)
log.Println(string(body))
}
// Make the request to the trusted server!
makeRequest(fmt.Sprintf("https://%s", serverAddress))
}

View file

@ -1,62 +0,0 @@
package main
import (
"encoding/pem"
"fmt"
"log"
"net"
"github.com/docker/libtrust"
)
var (
serverAddress = "localhost:8888"
clientPrivateKeyFilename = "client_data/private_key.pem"
trustedHostsFilename = "client_data/trusted_hosts.pem"
)
func main() {
key, err := libtrust.LoadKeyFile(clientPrivateKeyFilename)
if err != nil {
log.Fatal(err)
}
keyPEMBlock, err := key.PEMBlock()
if err != nil {
log.Fatal(err)
}
encodedPrivKey := pem.EncodeToMemory(keyPEMBlock)
fmt.Printf("Client Key:\n\n%s\n", string(encodedPrivKey))
cert, err := libtrust.GenerateSelfSignedClientCert(key)
if err != nil {
log.Fatal(err)
}
encodedCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
fmt.Printf("Client Cert:\n\n%s\n", string(encodedCert))
trustedServerKeys, err := libtrust.LoadKeySetFile(trustedHostsFilename)
if err != nil {
log.Fatal(err)
}
hostname, _, err := net.SplitHostPort(serverAddress)
if err != nil {
log.Fatal(err)
}
trustedServerKeys, err = libtrust.FilterByHosts(trustedServerKeys, hostname, false)
if err != nil {
log.Fatal(err)
}
caCert, err := libtrust.GenerateCACert(key, trustedServerKeys[0])
if err != nil {
log.Fatal(err)
}
encodedCert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: caCert.Raw})
fmt.Printf("CA Cert:\n\n%s\n", string(encodedCert))
}

View file

@ -1,61 +0,0 @@
package main
import (
"log"
"github.com/docker/libtrust"
)
func main() {
// Generate client key.
clientKey, err := libtrust.GenerateECP256PrivateKey()
if err != nil {
log.Fatal(err)
}
// Add a comment for the client key.
clientKey.AddExtendedField("comment", "TLS Demo Client")
// Save the client key, public and private versions.
err = libtrust.SaveKey("client_data/private_key.pem", clientKey)
if err != nil {
log.Fatal(err)
}
err = libtrust.SavePublicKey("client_data/public_key.pem", clientKey.PublicKey())
if err != nil {
log.Fatal(err)
}
// Generate server key.
serverKey, err := libtrust.GenerateECP256PrivateKey()
if err != nil {
log.Fatal(err)
}
// Set the list of addresses to use for the server.
serverKey.AddExtendedField("hosts", []string{"localhost", "docker.example.com"})
// Save the server key, public and private versions.
err = libtrust.SaveKey("server_data/private_key.pem", serverKey)
if err != nil {
log.Fatal(err)
}
err = libtrust.SavePublicKey("server_data/public_key.pem", serverKey.PublicKey())
if err != nil {
log.Fatal(err)
}
// Generate Authorized Keys file for server.
err = libtrust.AddKeySetFile("server_data/trusted_clients.pem", clientKey.PublicKey())
if err != nil {
log.Fatal(err)
}
// Generate Known Host Keys file for client.
err = libtrust.AddKeySetFile("client_data/trusted_hosts.pem", serverKey.PublicKey())
if err != nil {
log.Fatal(err)
}
}

View file

@ -1,80 +0,0 @@
package main
import (
"crypto/tls"
"fmt"
"html"
"log"
"net"
"net/http"
"github.com/docker/libtrust"
)
var (
serverAddress = "localhost:8888"
privateKeyFilename = "server_data/private_key.pem"
authorizedClientsFilename = "server_data/trusted_clients.pem"
)
func requestHandler(w http.ResponseWriter, r *http.Request) {
clientCert := r.TLS.PeerCertificates[0]
keyID := clientCert.Subject.CommonName
log.Printf("Request from keyID: %s\n", keyID)
fmt.Fprintf(w, "Hello, client! I'm a server! And you are %T: %s.\n", clientCert.PublicKey, html.EscapeString(keyID))
}
func main() {
// Load server key.
serverKey, err := libtrust.LoadKeyFile(privateKeyFilename)
if err != nil {
log.Fatal(err)
}
// Generate server certificate.
selfSignedServerCert, err := libtrust.GenerateSelfSignedServerCert(
serverKey, []string{"localhost"}, []net.IP{net.ParseIP("127.0.0.1")},
)
if err != nil {
log.Fatal(err)
}
// Load authorized client keys.
authorizedClients, err := libtrust.LoadKeySetFile(authorizedClientsFilename)
if err != nil {
log.Fatal(err)
}
// Create CA pool using trusted client keys.
caPool, err := libtrust.GenerateCACertPool(serverKey, authorizedClients)
if err != nil {
log.Fatal(err)
}
// Create TLS config, requiring client certificates.
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{
tls.Certificate{
Certificate: [][]byte{selfSignedServerCert.Raw},
PrivateKey: serverKey.CryptoPrivateKey(),
Leaf: selfSignedServerCert,
},
},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: caPool,
}
// Create HTTP server with simple request handler.
server := &http.Server{
Addr: serverAddress,
Handler: http.HandlerFunc(requestHandler),
}
// Listen and server HTTPS using the libtrust TLS config.
listener, err := net.Listen("tcp", server.Addr)
if err != nil {
log.Fatal(err)
}
tlsListener := tls.NewListener(listener, tlsConfig)
server.Serve(tlsListener)
}

View file

@ -1,50 +0,0 @@
package trustgraph
import "github.com/docker/libtrust"
// TrustGraph represents a graph of authorization mapping
// public keys to nodes and grants between nodes.
type TrustGraph interface {
// Verifies that the given public key is allowed to perform
// the given action on the given node according to the trust
// graph.
Verify(libtrust.PublicKey, string, uint16) (bool, error)
// GetGrants returns an array of all grant chains which are used to
// allow the requested permission.
GetGrants(libtrust.PublicKey, string, uint16) ([][]*Grant, error)
}
// Grant represents a transfer of permission from one part of the
// trust graph to another. This is the only way to delegate
// permission between two different sub trees in the graph.
type Grant struct {
// Subject is the namespace being granted
Subject string
// Permissions is a bit map of permissions
Permission uint16
// Grantee represents the node being granted
// a permission scope. The grantee can be
// either a namespace item or a key id where namespace
// items will always start with a '/'.
Grantee string
// statement represents the statement used to create
// this object.
statement *Statement
}
// Permissions
// Read node 0x01 (can read node, no sub nodes)
// Write node 0x02 (can write to node object, cannot create subnodes)
// Read subtree 0x04 (delegates read to each sub node)
// Write subtree 0x08 (delegates write to each sub node, included create on the subject)
//
// Permission shortcuts
// ReadItem = 0x01
// WriteItem = 0x03
// ReadAccess = 0x07
// WriteAccess = 0x0F
// Delegate = 0x0F

View file

@ -1,133 +0,0 @@
package trustgraph
import (
"strings"
"github.com/docker/libtrust"
)
type grantNode struct {
grants []*Grant
children map[string]*grantNode
}
type memoryGraph struct {
roots map[string]*grantNode
}
func newGrantNode() *grantNode {
return &grantNode{
grants: []*Grant{},
children: map[string]*grantNode{},
}
}
// NewMemoryGraph returns a new in memory trust graph created from
// a static list of grants. This graph is immutable after creation
// and any alterations should create a new instance.
func NewMemoryGraph(grants []*Grant) TrustGraph {
roots := map[string]*grantNode{}
for _, grant := range grants {
parts := strings.Split(grant.Grantee, "/")
nodes := roots
var node *grantNode
var nodeOk bool
for _, part := range parts {
node, nodeOk = nodes[part]
if !nodeOk {
node = newGrantNode()
nodes[part] = node
}
if part != "" {
node.grants = append(node.grants, grant)
}
nodes = node.children
}
}
return &memoryGraph{roots}
}
func (g *memoryGraph) getGrants(name string) []*Grant {
nameParts := strings.Split(name, "/")
nodes := g.roots
var node *grantNode
var nodeOk bool
for _, part := range nameParts {
node, nodeOk = nodes[part]
if !nodeOk {
return nil
}
nodes = node.children
}
return node.grants
}
func isSubName(name, sub string) bool {
if strings.HasPrefix(name, sub) {
if len(name) == len(sub) || name[len(sub)] == '/' {
return true
}
}
return false
}
type walkFunc func(*Grant, []*Grant) bool
func foundWalkFunc(*Grant, []*Grant) bool {
return true
}
func (g *memoryGraph) walkGrants(start, target string, permission uint16, f walkFunc, chain []*Grant, visited map[*Grant]bool, collect bool) bool {
if visited == nil {
visited = map[*Grant]bool{}
}
grants := g.getGrants(start)
subGrants := make([]*Grant, 0, len(grants))
for _, grant := range grants {
if visited[grant] {
continue
}
visited[grant] = true
if grant.Permission&permission == permission {
if isSubName(target, grant.Subject) {
if f(grant, chain) {
return true
}
} else {
subGrants = append(subGrants, grant)
}
}
}
for _, grant := range subGrants {
var chainCopy []*Grant
if collect {
chainCopy = make([]*Grant, len(chain)+1)
copy(chainCopy, chain)
chainCopy[len(chainCopy)-1] = grant
} else {
chainCopy = nil
}
if g.walkGrants(grant.Subject, target, permission, f, chainCopy, visited, collect) {
return true
}
}
return false
}
func (g *memoryGraph) Verify(key libtrust.PublicKey, node string, permission uint16) (bool, error) {
return g.walkGrants(key.KeyID(), node, permission, foundWalkFunc, nil, nil, false), nil
}
func (g *memoryGraph) GetGrants(key libtrust.PublicKey, node string, permission uint16) ([][]*Grant, error) {
grants := [][]*Grant{}
collect := func(grant *Grant, chain []*Grant) bool {
grantChain := make([]*Grant, len(chain)+1)
copy(grantChain, chain)
grantChain[len(grantChain)-1] = grant
grants = append(grants, grantChain)
return false
}
g.walkGrants(key.KeyID(), node, permission, collect, nil, nil, true)
return grants, nil
}

View file

@ -1,174 +0,0 @@
package trustgraph
import (
"fmt"
"testing"
"github.com/docker/libtrust"
)
func createTestKeysAndGrants(count int) ([]*Grant, []libtrust.PrivateKey) {
grants := make([]*Grant, count)
keys := make([]libtrust.PrivateKey, count)
for i := 0; i < count; i++ {
pk, err := libtrust.GenerateECP256PrivateKey()
if err != nil {
panic(err)
}
grant := &Grant{
Subject: fmt.Sprintf("/user-%d", i+1),
Permission: 0x0f,
Grantee: pk.KeyID(),
}
keys[i] = pk
grants[i] = grant
}
return grants, keys
}
func testVerified(t *testing.T, g TrustGraph, k libtrust.PublicKey, keyName, target string, permission uint16) {
if ok, err := g.Verify(k, target, permission); err != nil {
t.Fatalf("Unexpected error during verification: %s", err)
} else if !ok {
t.Errorf("key failed verification\n\tKey: %s(%s)\n\tNamespace: %s", keyName, k.KeyID(), target)
}
}
func testNotVerified(t *testing.T, g TrustGraph, k libtrust.PublicKey, keyName, target string, permission uint16) {
if ok, err := g.Verify(k, target, permission); err != nil {
t.Fatalf("Unexpected error during verification: %s", err)
} else if ok {
t.Errorf("key should have failed verification\n\tKey: %s(%s)\n\tNamespace: %s", keyName, k.KeyID(), target)
}
}
func TestVerify(t *testing.T) {
grants, keys := createTestKeysAndGrants(4)
extraGrants := make([]*Grant, 3)
extraGrants[0] = &Grant{
Subject: "/user-3",
Permission: 0x0f,
Grantee: "/user-2",
}
extraGrants[1] = &Grant{
Subject: "/user-3/sub-project",
Permission: 0x0f,
Grantee: "/user-4",
}
extraGrants[2] = &Grant{
Subject: "/user-4",
Permission: 0x07,
Grantee: "/user-1",
}
grants = append(grants, extraGrants...)
g := NewMemoryGraph(grants)
testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1", 0x0f)
testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1/some-project/sub-value", 0x0f)
testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-4", 0x07)
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2/", 0x0f)
testVerified(t, g, keys[2].PublicKey(), "user-key-3", "/user-3/sub-value", 0x0f)
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3/sub-value", 0x0f)
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3", 0x0f)
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3/", 0x0f)
testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3/sub-project", 0x0f)
testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3/sub-project/app", 0x0f)
testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-4", 0x0f)
testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-2", 0x0f)
testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-3/sub-value", 0x0f)
testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-4", 0x0f)
testNotVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-1/", 0x0f)
testNotVerified(t, g, keys[2].PublicKey(), "user-key-3", "/user-2", 0x0f)
testNotVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-4", 0x0f)
testNotVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3", 0x0f)
}
func TestCircularWalk(t *testing.T) {
grants, keys := createTestKeysAndGrants(3)
user1Grant := &Grant{
Subject: "/user-2",
Permission: 0x0f,
Grantee: "/user-1",
}
user2Grant := &Grant{
Subject: "/user-1",
Permission: 0x0f,
Grantee: "/user-2",
}
grants = append(grants, user1Grant, user2Grant)
g := NewMemoryGraph(grants)
testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1", 0x0f)
testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-2", 0x0f)
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2", 0x0f)
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-1", 0x0f)
testVerified(t, g, keys[2].PublicKey(), "user-key-3", "/user-3", 0x0f)
testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-3", 0x0f)
testNotVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3", 0x0f)
}
func assertGrantSame(t *testing.T, actual, expected *Grant) {
if actual != expected {
t.Fatalf("Unexpected grant retrieved\n\tExpected: %v\n\tActual: %v", expected, actual)
}
}
func TestGetGrants(t *testing.T) {
grants, keys := createTestKeysAndGrants(5)
extraGrants := make([]*Grant, 4)
extraGrants[0] = &Grant{
Subject: "/user-3/friend-project",
Permission: 0x0f,
Grantee: "/user-2/friends",
}
extraGrants[1] = &Grant{
Subject: "/user-3/sub-project",
Permission: 0x0f,
Grantee: "/user-4",
}
extraGrants[2] = &Grant{
Subject: "/user-2/friends",
Permission: 0x0f,
Grantee: "/user-5/fun-project",
}
extraGrants[3] = &Grant{
Subject: "/user-5/fun-project",
Permission: 0x0f,
Grantee: "/user-1",
}
grants = append(grants, extraGrants...)
g := NewMemoryGraph(grants)
grantChains, err := g.GetGrants(keys[3], "/user-3/sub-project/specific-app", 0x0f)
if err != nil {
t.Fatalf("Error getting grants: %s", err)
}
if len(grantChains) != 1 {
t.Fatalf("Expected number of grant chains returned, expected %d, received %d", 1, len(grantChains))
}
if len(grantChains[0]) != 2 {
t.Fatalf("Unexpected number of grants retrieved\n\tExpected: %d\n\tActual: %d", 2, len(grantChains[0]))
}
assertGrantSame(t, grantChains[0][0], grants[3])
assertGrantSame(t, grantChains[0][1], extraGrants[1])
grantChains, err = g.GetGrants(keys[0], "/user-3/friend-project/fun-app", 0x0f)
if err != nil {
t.Fatalf("Error getting grants: %s", err)
}
if len(grantChains) != 1 {
t.Fatalf("Expected number of grant chains returned, expected %d, received %d", 1, len(grantChains))
}
if len(grantChains[0]) != 4 {
t.Fatalf("Unexpected number of grants retrieved\n\tExpected: %d\n\tActual: %d", 2, len(grantChains[0]))
}
assertGrantSame(t, grantChains[0][0], grants[0])
assertGrantSame(t, grantChains[0][1], extraGrants[3])
assertGrantSame(t, grantChains[0][2], extraGrants[2])
assertGrantSame(t, grantChains[0][3], extraGrants[0])
}

View file

@ -1,227 +0,0 @@
package trustgraph
import (
"crypto/x509"
"encoding/json"
"io"
"io/ioutil"
"sort"
"strings"
"time"
"github.com/docker/libtrust"
)
type jsonGrant struct {
Subject string `json:"subject"`
Permission uint16 `json:"permission"`
Grantee string `json:"grantee"`
}
type jsonRevocation struct {
Subject string `json:"subject"`
Revocation uint16 `json:"revocation"`
Grantee string `json:"grantee"`
}
type jsonStatement struct {
Revocations []*jsonRevocation `json:"revocations"`
Grants []*jsonGrant `json:"grants"`
Expiration time.Time `json:"expiration"`
IssuedAt time.Time `json:"issuedAt"`
}
func (g *jsonGrant) Grant(statement *Statement) *Grant {
return &Grant{
Subject: g.Subject,
Permission: g.Permission,
Grantee: g.Grantee,
statement: statement,
}
}
// Statement represents a set of grants made from a verifiable
// authority. A statement has an expiration associated with it
// set by the authority.
type Statement struct {
jsonStatement
signature *libtrust.JSONSignature
}
// IsExpired returns whether the statement has expired
func (s *Statement) IsExpired() bool {
return s.Expiration.Before(time.Now().Add(-10 * time.Second))
}
// Bytes returns an indented json representation of the statement
// in a byte array. This value can be written to a file or stream
// without alteration.
func (s *Statement) Bytes() ([]byte, error) {
return s.signature.PrettySignature("signatures")
}
// LoadStatement loads and verifies a statement from an input stream.
func LoadStatement(r io.Reader, authority *x509.CertPool) (*Statement, error) {
b, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
js, err := libtrust.ParsePrettySignature(b, "signatures")
if err != nil {
return nil, err
}
payload, err := js.Payload()
if err != nil {
return nil, err
}
var statement Statement
err = json.Unmarshal(payload, &statement.jsonStatement)
if err != nil {
return nil, err
}
if authority == nil {
_, err = js.Verify()
if err != nil {
return nil, err
}
} else {
_, err = js.VerifyChains(authority)
if err != nil {
return nil, err
}
}
statement.signature = js
return &statement, nil
}
// CreateStatements creates and signs a statement from a stream of grants
// and revocations in a JSON array.
func CreateStatement(grants, revocations io.Reader, expiration time.Duration, key libtrust.PrivateKey, chain []*x509.Certificate) (*Statement, error) {
var statement Statement
err := json.NewDecoder(grants).Decode(&statement.jsonStatement.Grants)
if err != nil {
return nil, err
}
err = json.NewDecoder(revocations).Decode(&statement.jsonStatement.Revocations)
if err != nil {
return nil, err
}
statement.jsonStatement.Expiration = time.Now().UTC().Add(expiration)
statement.jsonStatement.IssuedAt = time.Now().UTC()
b, err := json.MarshalIndent(&statement.jsonStatement, "", " ")
if err != nil {
return nil, err
}
statement.signature, err = libtrust.NewJSONSignature(b)
if err != nil {
return nil, err
}
err = statement.signature.SignWithChain(key, chain)
if err != nil {
return nil, err
}
return &statement, nil
}
type statementList []*Statement
func (s statementList) Len() int {
return len(s)
}
func (s statementList) Less(i, j int) bool {
return s[i].IssuedAt.Before(s[j].IssuedAt)
}
func (s statementList) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// CollapseStatements returns a single list of the valid statements as well as the
// time when the next grant will expire.
func CollapseStatements(statements []*Statement, useExpired bool) ([]*Grant, time.Time, error) {
sorted := make(statementList, 0, len(statements))
for _, statement := range statements {
if useExpired || !statement.IsExpired() {
sorted = append(sorted, statement)
}
}
sort.Sort(sorted)
var minExpired time.Time
var grantCount int
roots := map[string]*grantNode{}
for i, statement := range sorted {
if statement.Expiration.Before(minExpired) || i == 0 {
minExpired = statement.Expiration
}
for _, grant := range statement.Grants {
parts := strings.Split(grant.Grantee, "/")
nodes := roots
g := grant.Grant(statement)
grantCount = grantCount + 1
for _, part := range parts {
node, nodeOk := nodes[part]
if !nodeOk {
node = newGrantNode()
nodes[part] = node
}
node.grants = append(node.grants, g)
nodes = node.children
}
}
for _, revocation := range statement.Revocations {
parts := strings.Split(revocation.Grantee, "/")
nodes := roots
var node *grantNode
var nodeOk bool
for _, part := range parts {
node, nodeOk = nodes[part]
if !nodeOk {
break
}
nodes = node.children
}
if node != nil {
for _, grant := range node.grants {
if isSubName(grant.Subject, revocation.Subject) {
grant.Permission = grant.Permission &^ revocation.Revocation
}
}
}
}
}
retGrants := make([]*Grant, 0, grantCount)
for _, rootNodes := range roots {
retGrants = append(retGrants, rootNodes.grants...)
}
return retGrants, minExpired, nil
}
// FilterStatements filters the statements to statements including the given grants.
func FilterStatements(grants []*Grant) ([]*Statement, error) {
statements := map[*Statement]bool{}
for _, grant := range grants {
if grant.statement != nil {
statements[grant.statement] = true
}
}
retStatements := make([]*Statement, len(statements))
var i int
for statement := range statements {
retStatements[i] = statement
i++
}
return retStatements, nil
}

View file

@ -1,417 +0,0 @@
package trustgraph
import (
"bytes"
"crypto/x509"
"encoding/json"
"testing"
"time"
"github.com/docker/libtrust"
"github.com/docker/libtrust/testutil"
)
const testStatementExpiration = time.Hour * 5
func generateStatement(grants []*Grant, key libtrust.PrivateKey, chain []*x509.Certificate) (*Statement, error) {
var statement Statement
statement.Grants = make([]*jsonGrant, len(grants))
for i, grant := range grants {
statement.Grants[i] = &jsonGrant{
Subject: grant.Subject,
Permission: grant.Permission,
Grantee: grant.Grantee,
}
}
statement.IssuedAt = time.Now()
statement.Expiration = time.Now().Add(testStatementExpiration)
statement.Revocations = make([]*jsonRevocation, 0)
marshalled, err := json.MarshalIndent(statement.jsonStatement, "", " ")
if err != nil {
return nil, err
}
sig, err := libtrust.NewJSONSignature(marshalled)
if err != nil {
return nil, err
}
err = sig.SignWithChain(key, chain)
if err != nil {
return nil, err
}
statement.signature = sig
return &statement, nil
}
func generateTrustChain(t *testing.T, chainLen int) (libtrust.PrivateKey, *x509.CertPool, []*x509.Certificate) {
caKey, err := libtrust.GenerateECP256PrivateKey()
if err != nil {
t.Fatalf("Error generating key: %s", err)
}
ca, err := testutil.GenerateTrustCA(caKey.CryptoPublicKey(), caKey.CryptoPrivateKey())
if err != nil {
t.Fatalf("Error generating ca: %s", err)
}
parent := ca
parentKey := caKey
chain := make([]*x509.Certificate, chainLen)
for i := chainLen - 1; i > 0; i-- {
intermediatekey, err := libtrust.GenerateECP256PrivateKey()
if err != nil {
t.Fatalf("Error generate key: %s", err)
}
chain[i], err = testutil.GenerateIntermediate(intermediatekey.CryptoPublicKey(), parentKey.CryptoPrivateKey(), parent)
if err != nil {
t.Fatalf("Error generating intermdiate certificate: %s", err)
}
parent = chain[i]
parentKey = intermediatekey
}
trustKey, err := libtrust.GenerateECP256PrivateKey()
if err != nil {
t.Fatalf("Error generate key: %s", err)
}
chain[0], err = testutil.GenerateTrustCert(trustKey.CryptoPublicKey(), parentKey.CryptoPrivateKey(), parent)
if err != nil {
t.Fatalf("Error generate trust cert: %s", err)
}
caPool := x509.NewCertPool()
caPool.AddCert(ca)
return trustKey, caPool, chain
}
func TestLoadStatement(t *testing.T) {
grantCount := 4
grants, _ := createTestKeysAndGrants(grantCount)
trustKey, caPool, chain := generateTrustChain(t, 6)
statement, err := generateStatement(grants, trustKey, chain)
if err != nil {
t.Fatalf("Error generating statement: %s", err)
}
statementBytes, err := statement.Bytes()
if err != nil {
t.Fatalf("Error getting statement bytes: %s", err)
}
s2, err := LoadStatement(bytes.NewReader(statementBytes), caPool)
if err != nil {
t.Fatalf("Error loading statement: %s", err)
}
if len(s2.Grants) != grantCount {
t.Fatalf("Unexpected grant length\n\tExpected: %d\n\tActual: %d", grantCount, len(s2.Grants))
}
pool := x509.NewCertPool()
_, err = LoadStatement(bytes.NewReader(statementBytes), pool)
if err == nil {
t.Fatalf("No error thrown verifying without an authority")
} else if _, ok := err.(x509.UnknownAuthorityError); !ok {
t.Fatalf("Unexpected error verifying without authority: %s", err)
}
s2, err = LoadStatement(bytes.NewReader(statementBytes), nil)
if err != nil {
t.Fatalf("Error loading statement: %s", err)
}
if len(s2.Grants) != grantCount {
t.Fatalf("Unexpected grant length\n\tExpected: %d\n\tActual: %d", grantCount, len(s2.Grants))
}
badData := make([]byte, len(statementBytes))
copy(badData, statementBytes)
badData[0] = '['
_, err = LoadStatement(bytes.NewReader(badData), nil)
if err == nil {
t.Fatalf("No error thrown parsing bad json")
}
alteredData := make([]byte, len(statementBytes))
copy(alteredData, statementBytes)
alteredData[30] = '0'
_, err = LoadStatement(bytes.NewReader(alteredData), nil)
if err == nil {
t.Fatalf("No error thrown from bad data")
}
}
func TestCollapseGrants(t *testing.T) {
grantCount := 8
grants, keys := createTestKeysAndGrants(grantCount)
linkGrants := make([]*Grant, 4)
linkGrants[0] = &Grant{
Subject: "/user-3",
Permission: 0x0f,
Grantee: "/user-2",
}
linkGrants[1] = &Grant{
Subject: "/user-3/sub-project",
Permission: 0x0f,
Grantee: "/user-4",
}
linkGrants[2] = &Grant{
Subject: "/user-6",
Permission: 0x0f,
Grantee: "/user-7",
}
linkGrants[3] = &Grant{
Subject: "/user-6/sub-project/specific-app",
Permission: 0x0f,
Grantee: "/user-5",
}
trustKey, pool, chain := generateTrustChain(t, 3)
statements := make([]*Statement, 3)
var err error
statements[0], err = generateStatement(grants[0:4], trustKey, chain)
if err != nil {
t.Fatalf("Error generating statement: %s", err)
}
statements[1], err = generateStatement(grants[4:], trustKey, chain)
if err != nil {
t.Fatalf("Error generating statement: %s", err)
}
statements[2], err = generateStatement(linkGrants, trustKey, chain)
if err != nil {
t.Fatalf("Error generating statement: %s", err)
}
statementsCopy := make([]*Statement, len(statements))
for i, statement := range statements {
b, err := statement.Bytes()
if err != nil {
t.Fatalf("Error getting statement bytes: %s", err)
}
verifiedStatement, err := LoadStatement(bytes.NewReader(b), pool)
if err != nil {
t.Fatalf("Error loading statement: %s", err)
}
// Force sort by reversing order
statementsCopy[len(statementsCopy)-i-1] = verifiedStatement
}
statements = statementsCopy
collapsedGrants, expiration, err := CollapseStatements(statements, false)
if len(collapsedGrants) != 12 {
t.Fatalf("Unexpected number of grants\n\tExpected: %d\n\tActual: %d", 12, len(collapsedGrants))
}
if expiration.After(time.Now().Add(time.Hour*5)) || expiration.Before(time.Now()) {
t.Fatalf("Unexpected expiration time: %s", expiration.String())
}
g := NewMemoryGraph(collapsedGrants)
testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1", 0x0f)
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2", 0x0f)
testVerified(t, g, keys[2].PublicKey(), "user-key-3", "/user-3", 0x0f)
testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-4", 0x0f)
testVerified(t, g, keys[4].PublicKey(), "user-key-5", "/user-5", 0x0f)
testVerified(t, g, keys[5].PublicKey(), "user-key-6", "/user-6", 0x0f)
testVerified(t, g, keys[6].PublicKey(), "user-key-7", "/user-7", 0x0f)
testVerified(t, g, keys[7].PublicKey(), "user-key-8", "/user-8", 0x0f)
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3", 0x0f)
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3/sub-project/specific-app", 0x0f)
testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3/sub-project", 0x0f)
testVerified(t, g, keys[6].PublicKey(), "user-key-7", "/user-6", 0x0f)
testVerified(t, g, keys[6].PublicKey(), "user-key-7", "/user-6/sub-project/specific-app", 0x0f)
testVerified(t, g, keys[4].PublicKey(), "user-key-5", "/user-6/sub-project/specific-app", 0x0f)
testNotVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3", 0x0f)
testNotVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-6/sub-project", 0x0f)
testNotVerified(t, g, keys[4].PublicKey(), "user-key-5", "/user-6/sub-project", 0x0f)
// Add revocation grant
statements = append(statements, &Statement{
jsonStatement{
IssuedAt: time.Now(),
Expiration: time.Now().Add(testStatementExpiration),
Grants: []*jsonGrant{},
Revocations: []*jsonRevocation{
&jsonRevocation{
Subject: "/user-1",
Revocation: 0x0f,
Grantee: keys[0].KeyID(),
},
&jsonRevocation{
Subject: "/user-2",
Revocation: 0x08,
Grantee: keys[1].KeyID(),
},
&jsonRevocation{
Subject: "/user-6",
Revocation: 0x0f,
Grantee: "/user-7",
},
&jsonRevocation{
Subject: "/user-9",
Revocation: 0x0f,
Grantee: "/user-10",
},
},
},
nil,
})
collapsedGrants, expiration, err = CollapseStatements(statements, false)
if len(collapsedGrants) != 12 {
t.Fatalf("Unexpected number of grants\n\tExpected: %d\n\tActual: %d", 12, len(collapsedGrants))
}
if expiration.After(time.Now().Add(time.Hour*5)) || expiration.Before(time.Now()) {
t.Fatalf("Unexpected expiration time: %s", expiration.String())
}
g = NewMemoryGraph(collapsedGrants)
testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1", 0x0f)
testNotVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2", 0x0f)
testNotVerified(t, g, keys[6].PublicKey(), "user-key-7", "/user-6/sub-project/specific-app", 0x0f)
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2", 0x07)
}
func TestFilterStatements(t *testing.T) {
grantCount := 8
grants, keys := createTestKeysAndGrants(grantCount)
linkGrants := make([]*Grant, 3)
linkGrants[0] = &Grant{
Subject: "/user-3",
Permission: 0x0f,
Grantee: "/user-2",
}
linkGrants[1] = &Grant{
Subject: "/user-5",
Permission: 0x0f,
Grantee: "/user-4",
}
linkGrants[2] = &Grant{
Subject: "/user-7",
Permission: 0x0f,
Grantee: "/user-6",
}
trustKey, _, chain := generateTrustChain(t, 3)
statements := make([]*Statement, 5)
var err error
statements[0], err = generateStatement(grants[0:2], trustKey, chain)
if err != nil {
t.Fatalf("Error generating statement: %s", err)
}
statements[1], err = generateStatement(grants[2:4], trustKey, chain)
if err != nil {
t.Fatalf("Error generating statement: %s", err)
}
statements[2], err = generateStatement(grants[4:6], trustKey, chain)
if err != nil {
t.Fatalf("Error generating statement: %s", err)
}
statements[3], err = generateStatement(grants[6:], trustKey, chain)
if err != nil {
t.Fatalf("Error generating statement: %s", err)
}
statements[4], err = generateStatement(linkGrants, trustKey, chain)
if err != nil {
t.Fatalf("Error generating statement: %s", err)
}
collapsed, _, err := CollapseStatements(statements, false)
if err != nil {
t.Fatalf("Error collapsing grants: %s", err)
}
// Filter 1, all 5 statements
filter1, err := FilterStatements(collapsed)
if err != nil {
t.Fatalf("Error filtering statements: %s", err)
}
if len(filter1) != 5 {
t.Fatalf("Wrong number of statements, expected %d, received %d", 5, len(filter1))
}
// Filter 2, one statement
filter2, err := FilterStatements([]*Grant{collapsed[0]})
if err != nil {
t.Fatalf("Error filtering statements: %s", err)
}
if len(filter2) != 1 {
t.Fatalf("Wrong number of statements, expected %d, received %d", 1, len(filter2))
}
// Filter 3, 2 statements, from graph lookup
g := NewMemoryGraph(collapsed)
lookupGrants, err := g.GetGrants(keys[1], "/user-3", 0x0f)
if err != nil {
t.Fatalf("Error looking up grants: %s", err)
}
if len(lookupGrants) != 1 {
t.Fatalf("Wrong numberof grant chains returned from lookup, expected %d, received %d", 1, len(lookupGrants))
}
if len(lookupGrants[0]) != 2 {
t.Fatalf("Wrong number of grants looked up, expected %d, received %d", 2, len(lookupGrants))
}
filter3, err := FilterStatements(lookupGrants[0])
if err != nil {
t.Fatalf("Error filtering statements: %s", err)
}
if len(filter3) != 2 {
t.Fatalf("Wrong number of statements, expected %d, received %d", 2, len(filter3))
}
}
func TestCreateStatement(t *testing.T) {
grantJSON := bytes.NewReader([]byte(`[
{
"subject": "/user-2",
"permission": 15,
"grantee": "/user-1"
},
{
"subject": "/user-7",
"permission": 1,
"grantee": "/user-9"
},
{
"subject": "/user-3",
"permission": 15,
"grantee": "/user-2"
}
]`))
revocationJSON := bytes.NewReader([]byte(`[
{
"subject": "user-8",
"revocation": 12,
"grantee": "user-9"
}
]`))
trustKey, pool, chain := generateTrustChain(t, 3)
statement, err := CreateStatement(grantJSON, revocationJSON, testStatementExpiration, trustKey, chain)
if err != nil {
t.Fatalf("Error creating statement: %s", err)
}
b, err := statement.Bytes()
if err != nil {
t.Fatalf("Error retrieving bytes: %s", err)
}
verified, err := LoadStatement(bytes.NewReader(b), pool)
if err != nil {
t.Fatalf("Error loading statement: %s", err)
}
if len(verified.Grants) != 3 {
t.Errorf("Unexpected number of grants, expected %d, received %d", 3, len(verified.Grants))
}
if len(verified.Revocations) != 1 {
t.Errorf("Unexpected number of revocations, expected %d, received %d", 1, len(verified.Revocations))
}
}

View file

@ -1,23 +0,0 @@
package libtrust
import (
"encoding/pem"
"reflect"
"testing"
)
func TestAddPEMHeadersToKey(t *testing.T) {
pk := &rsaPublicKey{nil, map[string]interface{}{}}
blk := &pem.Block{Headers: map[string]string{"hosts": "localhost,127.0.0.1"}}
addPEMHeadersToKey(blk, pk)
val := pk.GetExtendedField("hosts")
hosts, ok := val.([]string)
if !ok {
t.Fatalf("hosts type(%v), expected []string", reflect.TypeOf(val))
}
expected := []string{"localhost", "127.0.0.1"}
if !reflect.DeepEqual(hosts, expected) {
t.Errorf("hosts(%v), expected %v", hosts, expected)
}
}