Example #1
0
// setupKeyservers initializes everything needed to start a set of keyserver
// replicas, but does not actually start them yet
func setupKeyservers(t *testing.T, nReplicas int) (
	cfgs []*proto.ReplicaConfig, serverKeyGetters []func(string) (crypto.PrivateKey, error),
	clientKeyGetter func(string) (crypto.PrivateKey, error),
	clientConfig *proto.Config, caCert *x509.Certificate, caPool *x509.CertPool, caKey *ecdsa.PrivateKey, teardown func(),
) {
	caCert, caPool, caKey = tlstestutil.CA(t, nil)

	vrfPublic, vrfSecret, err := vrf.GenerateKey(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}

	teardown = func() {}

	clientCert := tlstestutil.Cert(t, caCert, caKey, "client", nil)
	clientKeyGetter = func(id string) (crypto.PrivateKey, error) {
		switch id {
		case "client":
			return clientCert.PrivateKey, nil
		default:
			t.Fatalf("unknown key %s", id)
			return nil, nil
		}
	}

	pks := make(map[uint64]*proto.PublicKey)
	replicaIDs := []uint64{}
	pol := &proto.AuthorizationPolicy{}
	realmConfig := &proto.RealmConfig{
		RealmName:          testingRealm,
		Domains:            []string{realmDomain},
		VRFPublic:          vrfPublic,
		VerificationPolicy: pol,
		EpochTimeToLive:    proto.DurationStamp(time.Hour),
		ClientTLS: &proto.TLSConfig{
			RootCAs:      [][]byte{caCert.Raw},
			Certificates: []*proto.CertificateAndKeyID{{clientCert.Certificate, "client", nil}},
		},
	}
	clientConfig = &proto.Config{
		Realms: []*proto.RealmConfig{realmConfig},
	}
	for n := 0; n < nReplicas; n++ {
		pk, sk, err := ed25519.GenerateKey(rand.Reader)
		if err != nil {
			teardown()
			t.Fatal(err)
		}
		pked := &proto.PublicKey{PubkeyType: &proto.PublicKey_Ed25519{Ed25519: pk[:]}}
		replicaID := proto.KeyID(pked)
		pks[replicaID] = pked
		replicaIDs = append(replicaIDs, replicaID)

		cert := tlstestutil.Cert(t, caCert, caKey, "127.0.0.1", nil)
		pcerts := []*proto.CertificateAndKeyID{{cert.Certificate, "tls", nil}}
		cfgs = append(cfgs, &proto.ReplicaConfig{
			KeyserverConfig: proto.KeyserverConfig{
				Realm:    testingRealm,
				ServerID: replicaID,
				VRFKeyID: "vrf",

				MinEpochInterval:      proto.DurationStamp(tick),
				MaxEpochInterval:      proto.DurationStamp(tick),
				ProposalRetryInterval: proto.DurationStamp(poll),
			},

			SigningKeyID:        "signing",
			ReplicaID:           replicaID,
			PublicAddr:          "localhost:0",
			VerifierAddr:        "localhost:0",
			HKPAddr:             "localhost:0",
			PublicTLS:           proto.TLSConfig{Certificates: pcerts, RootCAs: [][]byte{caCert.Raw}, ClientCAs: [][]byte{caCert.Raw}, ClientAuth: proto.REQUIRE_AND_VERIFY_CLIENT_CERT},
			VerifierTLS:         proto.TLSConfig{Certificates: pcerts, RootCAs: [][]byte{caCert.Raw}, ClientCAs: [][]byte{caCert.Raw}, ClientAuth: proto.REQUIRE_AND_VERIFY_CLIENT_CERT},
			HKPTLS:              proto.TLSConfig{Certificates: pcerts, RootCAs: [][]byte{caCert.Raw}, ClientCAs: [][]byte{caCert.Raw}, ClientAuth: proto.REQUIRE_AND_VERIFY_CLIENT_CERT},
			ClientTimeout:       proto.DurationStamp(time.Hour),
			LaggingVerifierScan: 1000 * 1000 * 1000,
		})
		serverKeyGetters = append(serverKeyGetters, func(keyid string) (crypto.PrivateKey, error) {
			switch keyid {
			case "vrf":
				return vrfSecret, nil
			case "signing":
				return sk, nil
			case "tls":
				return cert.PrivateKey, nil
			default:
				panic("unknown key requested in test")
			}
		})
	}
	pol.PublicKeys = pks
	pol.PolicyType = &proto.AuthorizationPolicy_Quorum{Quorum: majorityQuorum(replicaIDs)}
	return
}
Example #2
0
func main() {
	if len(os.Args) < 4 {
		fmt.Printf("usage: %s cacert cakey host0 host1 host2...\n", os.Args[0])
		return
	}

	caCertFile := os.Args[1]
	caKeyFile := os.Args[2]
	caCertPem, err := ioutil.ReadFile(caCertFile)
	if err != nil {
		log.Fatalf("Failed to read from %v: %v", caCertFile, err)
	}
	caCertBlock, _ := pem.Decode(caCertPem)
	if caCertBlock == nil {
		log.Fatalf("Failed to parse PEM: %v", string(caCertPem))
	}
	caCert, err := x509.ParseCertificate(caCertBlock.Bytes)
	if err != nil {
		log.Fatalf("Failed to parse X.509 cert: %v", err)
	}

	caKeyPem, err := ioutil.ReadFile(caKeyFile)
	if err != nil {
		log.Fatalf("Failed to read from %v: %v", caKeyFile, err)
	}
	caKeyBlock, _ := pem.Decode(caKeyPem)
	if caKeyBlock == nil {
		log.Fatalf("Failed to parse PEM: %v", string(caKeyPem))
	}
	caKey, err := x509.ParseECPrivateKey(caKeyBlock.Bytes)
	if err != nil {
		log.Fatalf("Failed to parse EC private key: %v", err)
	}

	hosts := os.Args[3:]

	vrfPublic, vrfSecret, err := vrf.GenerateKey(rand.Reader)
	if err != nil {
		log.Panic(err)
	}

	var pks []*[ed25519.PublicKeySize]byte
	var sks []*[ed25519.PrivateKeySize]byte
	var replicas []*proto.Replica
	for _, host := range hosts {
		pk, sk, err := ed25519.GenerateKey(rand.Reader)
		if err != nil {
			log.Panic(err)
		}
		pks = append(pks, pk)
		sks = append(sks, sk)
		ppk := &proto.PublicKey{
			PubkeyType: &proto.PublicKey_Ed25519{Ed25519: pk[:]},
		}
		replicas = append(replicas, &proto.Replica{
			ID:         proto.KeyID(ppk),
			PublicKeys: []*proto.PublicKey{ppk},
			RaftAddr:   fmt.Sprintf("%s:%d", host, raftPort),
		})
	}

	// TODO: non-silly way of generating ID
	var serverID [8]byte
	sha3.ShakeSum128(serverID[:], vrfPublic)
	ksConfig := proto.KeyserverConfig{
		Realm:    realm,
		ServerID: binary.LittleEndian.Uint64(serverID[:]),
		VRFKeyID: "vrf.vrfsecret",

		MinEpochInterval:      proto.DurationStamp(1 * time.Second),
		MaxEpochInterval:      proto.DurationStamp(1 * time.Minute),
		ProposalRetryInterval: proto.DurationStamp(1 * time.Second),

		InitialReplicas: replicas,
		RegistrationPolicy: []*proto.RegistrationPolicy{
			&proto.RegistrationPolicy{PolicyType: &proto.RegistrationPolicy_EmailProofByDKIM{
				EmailProofByDKIM: &proto.EmailProofByDKIM{
					AllowedDomains: []string{dkimProofDomain},
					ToAddr:         "*****@*****.**",
					SubjectPrefix:  dkimProofPrefix},
			},
			},
			&proto.RegistrationPolicy{PolicyType: &proto.RegistrationPolicy_EmailProofByOIDC{
				EmailProofByOIDC: &proto.EmailProofByOIDC{
					OIDCConfig: []*proto.OIDCConfig{
						&proto.OIDCConfig{AllowedDomains: []string{"yahoo.com"},
							DiscoveryURL: "https://login.yahoo.com",
							Issuer:       "https://api.login.yahoo.com",
							ClientID:     "foobar"},
					},
				},
			},
			},
		},
	}

	var cfgs []*proto.ReplicaConfig
	for i, host := range hosts {
		pk, sk := pks[i], sks[i]
		pked := &proto.PublicKey{PubkeyType: &proto.PublicKey_Ed25519{Ed25519: pk[:]}}
		replicaID := proto.KeyID(pked)

		cert, err := make_cert(caCert, caKey, host)
		if err != nil {
			log.Fatal(err)
		}
		pcerts := []*proto.CertificateAndKeyID{{cert.Certificate, "tls.key.pem", nil}}
		heartbeat := proto.DurationStamp(1 * time.Second)
		cfg := &proto.ReplicaConfig{
			KeyserverConfig:     ksConfig,
			SigningKeyID:        "signing.ed25519secret",
			ReplicaID:           replicaID,
			PublicAddr:          fmt.Sprintf("%s:%d", host, publicPort),
			VerifierAddr:        fmt.Sprintf("%s:%d", host, verifierPort),
			HKPAddr:             fmt.Sprintf("%s:%d", host, hkpPort),
			HTTPFrontAddr:       fmt.Sprintf("%s:%d", host, httpFrontPort),
			RaftAddr:            fmt.Sprintf("%s:%d", host, raftPort),
			PublicTLS:           proto.TLSConfig{Certificates: pcerts, RootCAs: [][]byte{caCert.Raw}, ClientCAs: [][]byte{caCert.Raw}, ClientAuth: proto.REQUIRE_AND_VERIFY_CLIENT_CERT},
			VerifierTLS:         proto.TLSConfig{Certificates: pcerts, RootCAs: [][]byte{caCert.Raw}, ClientCAs: [][]byte{caCert.Raw}, ClientAuth: proto.REQUIRE_AND_VERIFY_CLIENT_CERT},
			HKPTLS:              proto.TLSConfig{Certificates: pcerts, RootCAs: [][]byte{caCert.Raw}, ClientCAs: [][]byte{caCert.Raw}, ClientAuth: proto.REQUIRE_AND_VERIFY_CLIENT_CERT},
			HTTPFrontTLS:        proto.TLSConfig{Certificates: pcerts, RootCAs: [][]byte{caCert.Raw}, ClientCAs: [][]byte{caCert.Raw}, ClientAuth: proto.REQUIRE_AND_VERIFY_CLIENT_CERT},
			RaftTLS:             proto.TLSConfig{Certificates: pcerts, RootCAs: [][]byte{caCert.Raw}},
			LevelDBPath:         "db", // TODO
			RaftHeartbeat:       heartbeat,
			ClientTimeout:       proto.DurationStamp(1 * time.Minute),
			LaggingVerifierScan: 1000,
		}
		cfgs = append(cfgs, cfg)

		if _, err := os.Stat(host + "/"); os.IsNotExist(err) {
			os.Mkdir(host, 0700)
		}
		tlsKeyF, err := os.OpenFile(path.Join(host, "tls.key.pem"), os.O_WRONLY|os.O_CREATE, 0600)
		defer tlsKeyF.Close()
		if err != nil {
			log.Panic(err)
		}
		pkDer, err := x509.MarshalECPrivateKey(cert.PrivateKey.(*ecdsa.PrivateKey))
		if err != nil {
			log.Panic(err)
		}
		err = pem.Encode(tlsKeyF, &pem.Block{
			Type:    "EC PRIVATE KEY",
			Headers: make(map[string]string),
			Bytes:   pkDer,
		})
		if err != nil {
			log.Panic(err)
		}

		err = ioutil.WriteFile(path.Join(host, "vrf.vrfsecret"), vrfSecret[:], 0600)
		if err != nil {
			log.Panic(err)
		}

		err = ioutil.WriteFile(path.Join(host, "signing.ed25519secret"), sk[:], 0600)
		if err != nil {
			log.Panic(err)
		}

		configF, err := os.OpenFile(path.Join(host, "config.json"), os.O_WRONLY|os.O_CREATE, 0600)
		defer configF.Close()
		if err != nil {
			log.Panic(err)
		}
		err = new(jsonpb.Marshaler).Marshal(configF, cfg)
		if err != nil {
			log.Panic(err)
		}
	}

	err = ioutil.WriteFile("vrf.vrfpublic", vrfPublic[:], 0644)
	if err != nil {
		log.Panic(err)
	}

	clientCert, err := make_cert(caCert, caKey, "client")
	if err != nil {
		log.Fatal(err)
	}

	verificationPolicy := &proto.AuthorizationPolicy{
		PublicKeys: make(map[uint64]*proto.PublicKey),
		PolicyType: &proto.AuthorizationPolicy_Quorum{
			Quorum: &proto.QuorumExpr{Threshold: uint32(majority(len(hosts)))},
		},
	}
	replicaIDs := []uint64{}
	for i, cfg := range cfgs {
		replicaIDs = append(replicaIDs, cfg.ReplicaID)
		pk := &proto.PublicKey{
			PubkeyType: &proto.PublicKey_Ed25519{Ed25519: pks[i][:]},
		}
		pkid := proto.KeyID(pk)
		verificationPolicy.PublicKeys[pkid] = pk
		replicaExpr := &proto.QuorumExpr{
			Threshold:  1,
			Candidates: []uint64{pkid},
		}
		verificationPolicy.PolicyType.(*proto.AuthorizationPolicy_Quorum).Quorum.Subexpressions = append(verificationPolicy.PolicyType.(*proto.AuthorizationPolicy_Quorum).Quorum.Subexpressions, replicaExpr)
	}
	clientConfig := &proto.Config{
		Realms: []*proto.RealmConfig{
			&proto.RealmConfig{
				Domains:            []string{"yahoo-inc.com"},
				Addr:               cfgs[0].PublicAddr,
				VRFPublic:          vrfPublic[:],
				VerificationPolicy: verificationPolicy,
				EpochTimeToLive:    proto.DurationStamp(3 * time.Minute),
				TreeNonce:          nil,
				ClientTLS: &proto.TLSConfig{
					RootCAs:      [][]byte{caCert.Raw},
					Certificates: []*proto.CertificateAndKeyID{{clientCert.Certificate, "client.key.pem", nil}},
				},
			},
		},
	}
	clientConfigF, err := os.OpenFile("clientconfig.json", os.O_WRONLY|os.O_CREATE, 0644)
	defer clientConfigF.Close()
	if err != nil {
		log.Panic(err)
	}
	err = new(jsonpb.Marshaler).Marshal(clientConfigF, clientConfig)
	if err != nil {
		log.Panic(err)
	}

	clientKeyF, err := os.OpenFile("client.key.pem", os.O_WRONLY|os.O_CREATE, 0644)
	defer clientKeyF.Close()
	if err != nil {
		log.Panic(err)
	}
	skDer, err := x509.MarshalECPrivateKey(clientCert.PrivateKey.(*ecdsa.PrivateKey))
	if err != nil {
		log.Panic(err)
	}
	err = pem.Encode(clientKeyF, &pem.Block{
		Type:    "EC PRIVATE KEY",
		Headers: make(map[string]string),
		Bytes:   skDer,
	})
	if err != nil {
		log.Panic(err)
	}
}