Browse Source

Add signer tool for signing server entries

Rod Hynes 6 years ago
parent
commit
88e4510a71
2 changed files with 171 additions and 8 deletions
  1. 63 8
      psiphon/common/protocol/serverEntry.go
  2. 108 0
      psiphon/common/protocol/signer/main.go

+ 63 - 8
psiphon/common/protocol/serverEntry.go

@@ -167,6 +167,18 @@ func (fields ServerEntryFields) GetIPAddress() string {
 	return ipAddressStr
 }
 
+func (fields ServerEntryFields) GetWebServerPort() string {
+	webServerPort, ok := fields["webServerPort"]
+	if !ok {
+		return ""
+	}
+	webServerPortStr, ok := webServerPort.(string)
+	if !ok {
+		return ""
+	}
+	return webServerPortStr
+}
+
 func (fields ServerEntryFields) GetWebServerSecret() string {
 	webServerSecret, ok := fields["webServerSecret"]
 	if !ok {
@@ -179,6 +191,18 @@ func (fields ServerEntryFields) GetWebServerSecret() string {
 	return webServerSecretStr
 }
 
+func (fields ServerEntryFields) GetWebServerCertificate() string {
+	webServerCertificate, ok := fields["webServerCertificate"]
+	if !ok {
+		return ""
+	}
+	webServerCertificateStr, ok := webServerCertificate.(string)
+	if !ok {
+		return ""
+	}
+	return webServerCertificateStr
+}
+
 func (fields ServerEntryFields) GetConfigurationVersion() int {
 	configurationVersion, ok := fields["configurationVersion"]
 	if !ok {
@@ -500,18 +524,42 @@ func GenerateServerEntryTag(ipAddress, webServerSecret string) string {
 // EncodeServerEntry returns a string containing the encoding of
 // a ServerEntry following Psiphon conventions.
 func EncodeServerEntry(serverEntry *ServerEntry) (string, error) {
-	serverEntryContents, err := json.Marshal(serverEntry)
+	return encodeServerEntry(
+		serverEntry.IpAddress,
+		serverEntry.WebServerPort,
+		serverEntry.WebServerSecret,
+		serverEntry.WebServerCertificate,
+		serverEntry)
+}
+
+// EncodeServerEntryFields returns a string containing the encoding of
+// ServerEntryFields following Psiphon conventions.
+func EncodeServerEntryFields(serverEntryFields ServerEntryFields) (string, error) {
+	return encodeServerEntry(
+		serverEntryFields.GetIPAddress(),
+		serverEntryFields.GetWebServerPort(),
+		serverEntryFields.GetWebServerSecret(),
+		serverEntryFields.GetWebServerCertificate(),
+		serverEntryFields)
+}
+
+func encodeServerEntry(
+	IPAddress, webServerPort, webServerSecret, webServerCertificate string,
+	serverEntry interface{}) (string, error) {
+
+	serverEntryJSON, err := json.Marshal(serverEntry)
 	if err != nil {
 		return "", common.ContextError(err)
 	}
 
+	// Legacy clients expect the space-delimited fields.
 	return hex.EncodeToString([]byte(fmt.Sprintf(
 		"%s %s %s %s %s",
-		serverEntry.IpAddress,
-		serverEntry.WebServerPort,
-		serverEntry.WebServerSecret,
-		serverEntry.WebServerCertificate,
-		serverEntryContents))), nil
+		IPAddress,
+		webServerPort,
+		webServerSecret,
+		webServerCertificate,
+		serverEntryJSON))), nil
 }
 
 // DecodeServerEntry extracts a server entry from the encoding
@@ -544,6 +592,9 @@ func DecodeServerEntry(
 // DecodeServerEntryFields extracts an encoded server entry into a
 // ServerEntryFields type, much like DecodeServerEntry. Unrecognized fields
 // not in ServerEntry are retained in the ServerEntryFields.
+//
+// LocalSource/LocalTimestamp map entries are set only when the corresponding
+// inputs are non-blank.
 func DecodeServerEntryFields(
 	encodedServerEntry, timestamp, serverEntrySource string) (ServerEntryFields, error) {
 
@@ -554,8 +605,12 @@ func DecodeServerEntryFields(
 	}
 
 	// NOTE: if the source JSON happens to have values in these fields, they get clobbered.
-	serverEntryFields.SetLocalSource(serverEntrySource)
-	serverEntryFields.SetLocalTimestamp(timestamp)
+	if serverEntrySource != "" {
+		serverEntryFields.SetLocalSource(serverEntrySource)
+	}
+	if timestamp != "" {
+		serverEntryFields.SetLocalTimestamp(timestamp)
+	}
 
 	return serverEntryFields, nil
 }

+ 108 - 0
psiphon/common/protocol/signer/main.go

@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2019, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
+)
+
+func main() {
+
+	var publicKey string
+	flag.StringVar(&publicKey, "public-key", "", "server entry signing public key")
+
+	var privateKey string
+	flag.StringVar(&privateKey, "private-key", "", "server entry signing private key")
+
+	var encodedServerEntry string
+	flag.StringVar(&encodedServerEntry, "server-entry", "", "encoded server entry")
+
+	flag.Usage = func() {
+		fmt.Fprintf(os.Stderr,
+			"Usage:\n\n"+
+				"%s <flags> generate    generates and outputs a signing key pair\n"+
+				"%s <flags> sign        signs a specified server entry with a specified key pair\n\n",
+			os.Args[0], os.Args[0])
+		flag.PrintDefaults()
+	}
+
+	flag.Parse()
+
+	args := flag.Args()
+
+	var command string
+	if len(args) >= 1 {
+		command = args[0]
+	}
+
+	var err error
+	switch command {
+	case "generate":
+		err = generate()
+	case "sign":
+		err = sign(publicKey, privateKey, encodedServerEntry)
+	default:
+		flag.Usage()
+		os.Exit(1)
+	}
+
+	if err != nil {
+		fmt.Printf("%s\n", err)
+		os.Exit(1)
+	}
+}
+
+func generate() error {
+
+	publicKey, privateKey, err := protocol.NewServerEntrySignatureKeyPair()
+	if err != nil {
+		return fmt.Errorf("generate key pair failed: %s", err)
+	}
+
+	fmt.Printf("public-key:    %s\nprivate-key:   %s\n\n", publicKey, privateKey)
+
+	return nil
+}
+
+func sign(publicKey, privateKey, encodedServerEntry string) error {
+
+	serverEntryFields, err := protocol.DecodeServerEntryFields(encodedServerEntry, "", "")
+	if err != nil {
+		return fmt.Errorf("decode server entry failed: %s", err)
+	}
+
+	err = serverEntryFields.AddSignature(publicKey, privateKey)
+	if err != nil {
+		return fmt.Errorf("add signature failed: %s", err)
+	}
+
+	encodedSignedServerEntry, err := protocol.EncodeServerEntryFields(serverEntryFields)
+	if err != nil {
+		return fmt.Errorf("encode server entry failed: %s", err)
+	}
+
+	fmt.Printf("%s\n\n", encodedSignedServerEntry)
+
+	return nil
+}