-privkey-file and -pubkey-file options.

This commit is contained in:
David Fifield
2020-04-18 17:56:45 -06:00
parent 505db3ec23
commit b1bf9164a7
4 changed files with 211 additions and 27 deletions
+26 -11
View File
@@ -4,7 +4,6 @@ import (
"bytes"
"encoding/base32"
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
"io"
@@ -152,6 +151,15 @@ func dnsNameCapacity(domain dns.Name) int {
return capacity
}
func readKeyFromFile(filename string) ([]byte, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
return noise.ReadKey(f)
}
func run(pubkey []byte, domain dns.Name, localAddr *net.TCPAddr, remoteAddr net.Addr, pconn net.PacketConn) error {
defer pconn.Close()
@@ -228,19 +236,19 @@ func main() {
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), `Usage:
%[1]s [-doh URL|-dot ADDR|-udp ADDR] -pubkey PUBKEY DOMAIN LOCALADDR
%[1]s [-doh URL|-dot ADDR|-udp ADDR] -pubkey-file PUBKEYFILE DOMAIN LOCALADDR
Examples:
%[1]s -doh https://resolver.example/dns-query -pubkey 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff t.example.com 127.0.0.1:7000
%[1]s -dot resolver.example:853 -pubkey 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff t.example.com 127.0.0.1:7000
%[1]s -doh https://resolver.example/dns-query -pubkey-file server.pub t.example.com 127.0.0.1:7000
%[1]s -dot resolver.example:853 -pubkey-file server.pub t.example.com 127.0.0.1:7000
`, os.Args[0])
flag.PrintDefaults()
}
flag.StringVar(&dohURL, "doh", "", "URL of DoH resolver")
flag.StringVar(&dotAddr, "dot", "", "address of DoT resolver")
flag.StringVar(&pubkeyString, "pubkey", "", fmt.Sprintf("server public key (%d hex digits)", hex.EncodedLen(noise.KeyLen)))
flag.StringVar(&pubkeyString, "pubkey-file", "", "read server public key from file")
flag.StringVar(&pubkeyString, "pubkey", "", fmt.Sprintf("server public key (%d hex digits)", noise.KeyLen*2))
flag.StringVar(&pubkeyFilename, "pubkey-file", "", "read server public key from file")
flag.StringVar(&udpAddr, "udp", "", "address of UDP DNS resolver")
flag.Parse()
@@ -262,19 +270,26 @@ Examples:
}
var pubkey []byte
if pubkeyString != "" {
if pubkeyFilename != "" && pubkeyString != "" {
fmt.Fprintf(os.Stderr, "only one of -pubkey and -pubkey-file may be used\n")
os.Exit(1)
} else if pubkeyFilename != "" {
var err error
pubkey, err = hex.DecodeString(pubkeyString)
if err == nil && len(pubkey) != noise.KeyLen {
err = fmt.Errorf("length is %d, expected %d", len(pubkey), noise.KeyLen)
pubkey, err = readKeyFromFile(pubkeyFilename)
if err != nil {
fmt.Fprintf(os.Stderr, "cannot read pubkey from file: %v\n", err)
os.Exit(1)
}
} else if pubkeyString != "" {
var err error
pubkey, err = noise.DecodeKey(pubkeyString)
if err != nil {
fmt.Fprintf(os.Stderr, "pubkey format error: %v\n", err)
os.Exit(1)
}
}
if len(pubkey) == 0 {
fmt.Fprintf(os.Stderr, "the -pubkey option is required\n")
fmt.Fprintf(os.Stderr, "the -pubkey or -pubkey-file option is required\n")
os.Exit(1)
}
+100 -16
View File
@@ -4,7 +4,6 @@ import (
"bytes"
"encoding/base32"
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
"io"
@@ -491,16 +490,85 @@ func sendLoop(dnsConn net.PacketConn, ttConn *turbotunnel.QueuePacketConn, ch <-
return nil
}
func generateKeypair() error {
func generateKeypair(privkeyFilename, pubkeyFilename string) (err error) {
// Filenames to delete in case of error (avoid leaving partially written
// files).
var toDelete []string
defer func() {
for _, filename := range toDelete {
fmt.Fprintf(os.Stderr, "deleting partially written file %s\n", filename)
if closeErr := os.Remove(filename); closeErr != nil {
fmt.Fprintf(os.Stderr, "cannot remove %s: %v\n", filename, closeErr)
if err == nil {
err = closeErr
}
}
}
}()
privkey, pubkey, err := noise.GenerateKeypair()
if err != nil {
return err
}
fmt.Printf("privkey: %x\n", privkey)
fmt.Printf("pubkey: %x\n", pubkey)
if privkeyFilename != "" {
// Save the privkey to a file.
f, err := os.Create(privkeyFilename)
if err != nil {
return err
}
toDelete = append(toDelete, privkeyFilename)
err = noise.WriteKey(f, privkey)
if err2 := f.Close(); err == nil {
err = err2
}
if err != nil {
return err
}
}
if pubkeyFilename != "" {
// Save the pubkey to a file.
f, err := os.Create(pubkeyFilename)
if err != nil {
return err
}
toDelete = append(toDelete, pubkeyFilename)
err = noise.WriteKey(f, pubkey)
if err2 := f.Close(); err == nil {
err = err2
}
if err != nil {
return err
}
}
// All good, allow the written files to remain.
toDelete = nil
if privkeyFilename != "" {
fmt.Printf("privkey written to %s\n", privkeyFilename)
} else {
fmt.Printf("privkey %x\n", privkey)
}
if pubkeyFilename != "" {
fmt.Printf("pubkey written to %s\n", pubkeyFilename)
} else {
fmt.Printf("pubkey %x\n", pubkey)
}
return nil
}
func readKeyFromFile(filename string) ([]byte, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
return noise.ReadKey(f)
}
func run(privkey, pubkey []byte, domain dns.Name, upstream net.Addr, udpAddr string) error {
log.Printf("pubkey %x", pubkey)
@@ -542,23 +610,27 @@ func run(privkey, pubkey []byte, domain dns.Name, upstream net.Addr, udpAddr str
func main() {
var genKey bool
var privkeyFilename string
var privkeyString string
var pubkeyFilename string
var udpAddr string
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), `Usage:
%[1]s -gen-key
%[1]s -udp ADDR -privkey PRIVKEY DOMAIN UPSTREAMADDR
%[1]s -gen-key -privkey-file PRIVKEYFILE -pubkey-file PUBKEYFILE
%[1]s -udp ADDR -privkey-file PRIVKEYFILE DOMAIN UPSTREAMADDR
Example:
%[1]s -gen-key
%[1]s -udp 127.0.0.1:5300 -privkey 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef server.key t.example.com 127.0.0.1:8000
%[1]s -gen-key -privkey-file server.key -pubkey-file server.pub
%[1]s -udp 127.0.0.1:5300 -privkey-file server.key t.example.com 127.0.0.1:8000
`, os.Args[0])
flag.PrintDefaults()
}
flag.BoolVar(&genKey, "gen-key", false, "generate a server keypair; print to stdout")
flag.StringVar(&privkeyString, "privkey", "", fmt.Sprintf("server private key (%d hex digits)", hex.EncodedLen(noise.KeyLen)))
flag.BoolVar(&genKey, "gen-key", false, "generate a server keypair; print to stdout or save to files")
flag.StringVar(&privkeyString, "privkey", "", fmt.Sprintf("server private key (%d hex digits)", noise.KeyLen*2))
flag.StringVar(&privkeyFilename, "privkey-file", "", "read server private key from file (with -gen-key, write to file)")
flag.StringVar(&pubkeyFilename, "pubkey-file", "", "with -gen-key, write server public key to file")
flag.StringVar(&udpAddr, "udp", "", "UDP address to listen on (required)")
flag.Parse()
@@ -570,7 +642,7 @@ Example:
flag.Usage()
os.Exit(1)
}
if err := generateKeypair(); err != nil {
if err := generateKeypair(privkeyFilename, pubkeyFilename); err != nil {
fmt.Fprintf(os.Stderr, "cannot generate keypair: %v\n", err)
os.Exit(1)
}
@@ -591,13 +663,25 @@ Example:
os.Exit(1)
}
if pubkeyFilename != "" {
fmt.Fprintf(os.Stderr, "-pubkey-file may only be used with -gen-key\n")
os.Exit(1)
}
var privkey []byte
if privkeyString != "" {
if privkeyFilename != "" && privkeyString != "" {
fmt.Fprintf(os.Stderr, "only one of -privkey and -privkey-file may be used\n")
os.Exit(1)
} else if privkeyFilename != "" {
var err error
privkey, err = hex.DecodeString(privkeyString)
if err == nil && len(privkey) != noise.KeyLen {
err = fmt.Errorf("length is %d, expected %d", len(privkey), noise.KeyLen)
privkey, err = readKeyFromFile(privkeyFilename)
if err != nil {
fmt.Fprintf(os.Stderr, "cannot read privkey from file: %v\n", err)
os.Exit(1)
}
} else if privkeyString != "" {
var err error
privkey, err = noise.DecodeKey(privkeyString)
if err != nil {
fmt.Fprintf(os.Stderr, "privkey format error: %v\n", err)
os.Exit(1)
@@ -605,7 +689,7 @@ Example:
}
if len(privkey) == 0 {
log.Println("generating a temporary one-time keypair")
log.Println("use the -privkey option for a persistent server keypair")
log.Println("use the -privkey or -privkey-file option for a persistent server keypair")
var err error
privkey, _, err = noise.GenerateKeypair()
if err != nil {
+48
View File
@@ -1,12 +1,15 @@
package noise
import (
"bufio"
"bytes"
"crypto/rand"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
"github.com/flynn/noise"
)
@@ -205,3 +208,48 @@ func PubkeyFromPrivkey(privkey []byte) []byte {
}
return pair.Public
}
// ReadKey reads a hex-encoded key from r. r must consist of a single line, with
// or without a '\n' line terminator. The line must consist of KeyLen
// hex-encoded bytes.
func ReadKey(r io.Reader) ([]byte, error) {
br := bufio.NewReader(io.LimitReader(r, 100))
line, err := br.ReadString('\n')
if err == io.EOF {
err = nil
}
if err == nil {
// Check that we're at EOF.
_, err = br.ReadByte()
if err == io.EOF {
err = nil
} else if err == nil {
err = fmt.Errorf("file contains more than one line")
}
}
if err != nil {
return nil, err
}
line = strings.TrimSuffix(line, "\n")
return DecodeKey(line)
}
// WriteKey writes the hex-encoded key in a single line to w.
func WriteKey(w io.Writer, key []byte) error {
_, err := fmt.Fprintf(w, "%x\n", key)
return err
}
// DecodeKey decodes a hex-encoded private or public key.
func DecodeKey(s string) ([]byte, error) {
key, err := hex.DecodeString(s)
if err == nil && len(key) != KeyLen {
err = fmt.Errorf("length is %d, expected %d", len(key), KeyLen)
}
return key, err
}
// DecodeKey decodes a hex-encoded private or public key.
func EncodeKey(key []byte) string {
return hex.EncodeToString(key)
}
+37
View File
@@ -0,0 +1,37 @@
package noise
import (
"bytes"
"testing"
)
func TestReadKey(t *testing.T) {
for _, test := range []struct {
input string
output []byte
}{
{"", nil},
{"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde", nil},
{"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", []byte("\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef")},
{"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n", []byte("\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef")},
{"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0", nil},
{"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\nX", nil},
{"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n\n", nil},
{"\n0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", nil},
{"X123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", nil},
{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", nil},
} {
output, err := ReadKey(bytes.NewReader([]byte(test.input)))
if test.output == nil {
if err == nil {
t.Errorf("%+q expected error", test.input)
}
} else {
if err != nil {
t.Errorf("%+q returned error %v", test.input, err)
} else if !bytes.Equal(output, test.output) {
t.Errorf("%+q got %x, expected %x", test.input, output, test.output)
}
}
}
}