func TestCAIntermediate(t *testing.T) {
	cc := tls.NewCertCreator()
	cc.KeySize = 512
	prefix := "testcerts/intermediatetest"

	defer func() {
		for _, f := range []string{
			"-ca-key.pem",
			"-ca-cert.pem",
			"-intermediate-key.pem",
			"-intermediate-cert.pem",
			"-server-key.pem",
			"-server-cert.pem",
		} {
			os.Remove(prefix + f)
		}
	}()

	root, err := cc.GenerateRootKeyPair(prefix+"-ca", "Test CA")
	if err != nil {
		t.Fatalf("Couldn't generate CA cert: %s", err)
	}

	intermediate, err := cc.GenerateKeyPair(tls.CA, root, prefix+"-intermediate", "Test Intermediate")
	if err != nil {
		t.Fatalf("Couldn't generate intermediate cert: %s", err)
	}

	_, err = cc.GenerateKeyPair(tls.SERVER, intermediate, prefix+"-server", "example.com", "*.example.com")
	if err != nil {
		t.Fatalf("Couldn't generate server cert: %s", err)
	}
}
// test that CertCreator makes client/server certs that 1) work when used
// correctly and 2) don't work when used with different CAs
func TestCertCreator(t *testing.T) {
	cc := tls.NewCertCreator()
	cc.KeySize = 512
	cc.Country = "US"
	cc.State = "CA"
	cc.City = "San Francisco"
	cc.Organization = "Fastly Testing"

	var wg sync.WaitGroup
	done := false

	type group struct {
		name         string
		clientConfig *gotls.Config
		serverConfig *gotls.Config
		listener     net.Listener
		errors       chan error
		cleanup      func()
	}

	// generate ca, client, and server keypairs with the given name, and stand
	// up a TLS listener
	setup := func(name string) (g *group, err error) {
		g = &group{
			name:   name,
			errors: make(chan error),
			cleanup: func() {
				for _, f := range []string{
					"-ca-key.pem",
					"-ca-cert.pem",
					"client-key.pem",
					"client-cert.pem",
					"server-key.pem",
					"server-cert.pem",
				} {
					file := "testcerts/" + name + f
					if err := os.Remove(file); err != nil {
						t.Errorf("couldn't remove %q: %s", err)
					}
				}
			},
		}

		// put certs in testcerts/ since LocatePackagedPEMDir looks there
		root, err := cc.GenerateRootKeyPair("testcerts/"+name+"-ca", name+" CA")
		if err != nil {
			t.Error(err)
			return
		}

		host := "0.0.0.0"

		_, err = cc.GenerateKeyPair(tls.CLIENT, root, "testcerts/"+name+"client", host, host)
		if err != nil {
			t.Error(err)
			return
		}

		_, err = cc.GenerateKeyPair(tls.SERVER, root, "testcerts/"+name+"server", host, host)
		if err != nil {
			t.Error(err)
			return
		}

		g.clientConfig, err = tls.ConfigureClient(name+"client", name+"-ca")
		if err != nil {
			t.Errorf("%s client: %s", name+"client", err)
			return
		}

		g.serverConfig, err = tls.ConfigureServer(name+"server", name+"-ca")
		if err != nil {
			t.Errorf("%s server: %s", name+"server", err)
			return
		}

		listener, err := gotls.Listen("tcp4", host+":0", g.serverConfig)
		if err != nil {
			t.Errorf("%s listen: %s", name, err)
			return
		}
		g.listener = listener
		g.clientConfig.ServerName = g.listener.Addr().(*net.TCPAddr).IP.String() // required for client to validate server cert
		wg.Add(1)
		go func() {
			defer wg.Done()
			for {
				conn, err := listener.Accept()
				if done {
					return
				}
				if err != nil {
					g.errors <- err
					return
				}
				if conn == nil {
					g.errors <- fmt.Errorf("no conn or err")
					return
				}
				wg.Add(1)
				go func(conn net.Conn) {
					defer wg.Done()
					defer conn.Close()

					conn.SetDeadline(time.Now().Add(time.Second))
					err = conn.(*gotls.Conn).Handshake()
					if err != nil {
						g.errors <- err
						return
					}

					conn.SetDeadline(time.Now().Add(time.Second))
					b := make([]byte, 3) // len("foo")
					n, err := conn.Read(b)
					b = b[:n]
					if err != nil {
						g.errors <- err
						return
					}
					if string(b) != "foo" {
						g.errors <- fmt.Errorf("%q != %q", b, "foo")
						return
					}
					conn.SetDeadline(time.Now().Add(time.Second))
					n, err = io.WriteString(conn, "bar")
					if err != nil {
						g.errors <- err
						return
					}
					g.errors <- nil
				}(conn)
			}
		}()
		return
	}

	// try to create a TLS connection from client to server and send a bit of
	// data back and forth
	check := func(client, server *group, expectedClientError, expectedServerError string) {
		addr := server.listener.Addr().String()
		conn, err := net.DialTimeout("tcp4", addr, time.Second)
		if err != nil {
			t.Errorf("%s->%s: dial: %s", client.name, server.name, err)
			return
		}

		tlsConn := gotls.Client(conn, client.clientConfig)
		clientErr := tlsConn.Handshake()
		if clientErr == nil {
			tlsConn.SetDeadline(time.Now().Add(time.Second))
			if _, err := io.WriteString(tlsConn, "foo"); err == nil {
				tlsConn.SetDeadline(time.Now().Add(time.Second))
				b := make([]byte, 3) // len("bar")
				n, err := tlsConn.Read(b)
				b = b[:n]
				if err != nil {
					clientErr = err
				}
				if string(b) != "bar" {
					clientErr = fmt.Errorf("%q != %q", b, "bar")
				}
			} else {
				clientErr = err
			}
			tlsConn.Close()
		}

		var serverErr error
		select {
		case serverErr = <-server.errors:
		case <-time.After(time.Second):
			t.Errorf("timed out on serverErr")
			return
		}

		if expectedClientError == "" && clientErr != nil {
			t.Errorf("%s->%s client: should've worked but saw err=`%s`", client.name, server.name, clientErr)
		}
		if expectedClientError != "" {
			if clientErr == nil {
				t.Errorf("%s->%s client: should not have worked", client.name, server.name)
			} else if clientErr.Error() != expectedClientError {
				t.Errorf("%s->%s client: expected error %q but got %q", client.name, server.name, expectedClientError, clientErr)
			}
		}
		if expectedServerError == "" && serverErr != nil {
			t.Errorf("%s->%s server: should've worked but saw err=`%s`", client.name, server.name, serverErr)
		}
		if expectedServerError != "" {
			if serverErr == nil {
				t.Errorf("%s->%s server: should not have worked", client.name, server.name)
			} else if serverErr.Error() != expectedServerError {
				t.Errorf("%s->%s server: expected error %q but got %q", client.name, server.name, expectedServerError, serverErr)
			}
		}
	}

	// test that friend client talks to friend server, but friend client does
	// not talk to foe server or foe client to friend server
	friend, err := setup("friend")
	if err != nil {
		t.Errorf("setup: %s", err)
		return
	}
	defer friend.cleanup()

	foe, err := setup("foe")
	if err != nil {
		t.Errorf("setup: %s", err)
		return
	}
	defer foe.cleanup()

	// friend to friend: should work
	check(friend, friend, "", "")

	// friend to foe: friend client should reject foe server
	check(friend, foe, "x509: certificate signed by unknown authority", "remote error: bad certificate")

	// foe to friend: foe client accepts friend server but server should reject
	// foe client.

	// prevent client from rejecting the server's "bad" cert so we can test
	// that the server rejects the client's bad cert.
	foe.clientConfig.InsecureSkipVerify = true

	// this expects no certificate from the client because
	// tls/handshake_server.go sends a list of acceptable CA certs to the
	// client when requesting a client cert and tls/handshake_client.go avoids
	// sending any cert if no eligible one is in its Config.Certificates.
	//
	// a more complete test would be to force the client to send a cert it
	// knows is bad, but that's a hassle in a pure test so I verified it
	// manually with a Go 1.2.1 patched to not send the list of CAs from the
	// server and got:
	/*
			--- FAIL: TestCertCreator (0.20 seconds)
		        cert_creator_test.go:224: foe->friend server: expected error "tls: client didn't provide a certificate" but got "tls: failed to verify client's certificate: x509: certificate signed by unknown authority"
	*/
	check(foe, friend, "remote error: bad certificate", "tls: client didn't provide a certificate")
	foe.clientConfig.InsecureSkipVerify = false

	done = true
	friend.listener.Close()
	foe.listener.Close()

	c := make(chan struct{})
	go func() {
		wg.Wait()
		c <- struct{}{}
	}()

	select {
	case <-c:
	case <-time.After(time.Second):
		t.Errorf("timed out on wg.Wait")
	}
}