381 lines
9.9 KiB
Go
381 lines
9.9 KiB
Go
|
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")
|
||
|
}
|
||
|
}
|