diff --git a/cmd/derper/derper.go b/cmd/derper/derper.go index f328932a2..43c0e84da 100644 --- a/cmd/derper/derper.go +++ b/cmd/derper/derper.go @@ -25,7 +25,6 @@ import ( "strings" "time" - "github.com/tailscale/wireguard-go/wgcfg" "golang.org/x/crypto/acme/autocert" "tailscale.com/atomicfile" "tailscale.com/derp" @@ -35,6 +34,7 @@ import ( "tailscale.com/net/stun" "tailscale.com/tsweb" "tailscale.com/types/key" + "tailscale.com/types/wgkey" "tailscale.com/version" ) @@ -51,7 +51,7 @@ var ( ) type config struct { - PrivateKey wgcfg.PrivateKey + PrivateKey wgkey.Private } func loadConfig() config { @@ -77,8 +77,8 @@ func loadConfig() config { } } -func mustNewKey() wgcfg.PrivateKey { - key, err := wgcfg.NewPrivateKey() +func mustNewKey() wgkey.Private { + key, err := wgkey.NewPrivate() if err != nil { log.Fatal(err) } diff --git a/cmd/tailscale/depaware.txt b/cmd/tailscale/depaware.txt index 4313d4892..63e51cbc4 100644 --- a/cmd/tailscale/depaware.txt +++ b/cmd/tailscale/depaware.txt @@ -73,6 +73,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep tailscale.com/types/opt from tailscale.com/control/controlclient+ tailscale.com/types/strbuilder from tailscale.com/net/packet tailscale.com/types/structs from tailscale.com/control/controlclient+ + tailscale.com/types/wgkey from tailscale.com/control/controlclient+ LW tailscale.com/util/endian from tailscale.com/net/netns+ tailscale.com/util/lineread from tailscale.com/control/controlclient+ tailscale.com/util/systemd from tailscale.com/control/controlclient+ diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index a09909bf8..8c963e0d7 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -81,6 +81,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/types/opt from tailscale.com/control/controlclient+ tailscale.com/types/strbuilder from tailscale.com/net/packet tailscale.com/types/structs from tailscale.com/control/controlclient+ + tailscale.com/types/wgkey from tailscale.com/control/controlclient+ LW tailscale.com/util/endian from tailscale.com/net/netns+ tailscale.com/util/lineread from tailscale.com/control/controlclient+ tailscale.com/util/pidowner from tailscale.com/ipn/ipnserver diff --git a/control/controlclient/auto.go b/control/controlclient/auto.go index 75a9835eb..7acb5be63 100644 --- a/control/controlclient/auto.go +++ b/control/controlclient/auto.go @@ -17,13 +17,13 @@ import ( "sync" "time" - "github.com/tailscale/wireguard-go/wgcfg" "golang.org/x/oauth2" "tailscale.com/logtail/backoff" "tailscale.com/tailcfg" "tailscale.com/types/empty" "tailscale.com/types/logger" "tailscale.com/types/structs" + "tailscale.com/types/wgkey" ) // State is the high-level state of the client. It is used only in @@ -665,7 +665,7 @@ func (c *Client) Shutdown() { // NodePublicKey returns the node public key currently in use. This is // used exclusively in tests. -func (c *Client) TestOnlyNodePublicKey() wgcfg.Key { +func (c *Client) TestOnlyNodePublicKey() wgkey.Key { priv := c.direct.GetPersist() return priv.PrivateNodeKey.Public() } diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go index 911d2f1e5..4ab7ea03f 100644 --- a/control/controlclient/direct.go +++ b/control/controlclient/direct.go @@ -31,7 +31,6 @@ import ( "sync/atomic" "time" - "github.com/tailscale/wireguard-go/wgcfg" "golang.org/x/crypto/nacl/box" "golang.org/x/oauth2" "inet.af/netaddr" @@ -44,6 +43,7 @@ import ( "tailscale.com/types/logger" "tailscale.com/types/opt" "tailscale.com/types/structs" + "tailscale.com/types/wgkey" "tailscale.com/util/systemd" "tailscale.com/version" "tailscale.com/wgengine/filter" @@ -61,10 +61,10 @@ type Persist struct { // needed. This field should be considered read-only from GUI // frontends. The real value should not be written back in // this field, lest the frontend persist it to disk. - LegacyFrontendPrivateMachineKey wgcfg.PrivateKey `json:"PrivateMachineKey"` + LegacyFrontendPrivateMachineKey wgkey.Private `json:"PrivateMachineKey"` - PrivateNodeKey wgcfg.PrivateKey - OldPrivateNodeKey wgcfg.PrivateKey // needed to request key rotation + PrivateNodeKey wgkey.Private + OldPrivateNodeKey wgkey.Private // needed to request key rotation Provider string LoginName string } @@ -85,7 +85,7 @@ func (p *Persist) Equals(p2 *Persist) bool { } func (p *Persist) Pretty() string { - var mk, ok, nk wgcfg.Key + var mk, ok, nk wgkey.Key if !p.LegacyFrontendPrivateMachineKey.IsZero() { mk = p.LegacyFrontendPrivateMachineKey.Public() } @@ -95,7 +95,7 @@ func (p *Persist) Pretty() string { if !p.PrivateNodeKey.IsZero() { nk = p.PrivateNodeKey.Public() } - ss := func(k wgcfg.Key) string { + ss := func(k wgkey.Key) string { if k.IsZero() { return "" } @@ -115,14 +115,14 @@ type Direct struct { keepAlive bool logf logger.Logf discoPubKey tailcfg.DiscoKey - machinePrivKey wgcfg.PrivateKey + machinePrivKey wgkey.Private debugFlags []string mu sync.Mutex // mutex guards the following fields - serverKey wgcfg.Key + serverKey wgkey.Key persist Persist authKey string - tryingNewKey wgcfg.PrivateKey + tryingNewKey wgkey.Private expiry *time.Time // hostinfo is mutated in-place while mu is held. hostinfo *tailcfg.Hostinfo // always non-nil @@ -133,7 +133,7 @@ type Direct struct { type Options struct { Persist Persist // initial persistent data - MachinePrivateKey wgcfg.PrivateKey // the machine key to use + MachinePrivateKey wgkey.Private // the machine key to use ServerURL string // URL of the tailcontrol server AuthKey string // optional node auth key for auto registration TimeNow func() time.Time // time.Now implementation used by Client @@ -340,7 +340,7 @@ func (c *Direct) doLogin(ctx context.Context, t *oauth2.Token, flags LoginFlags, } c.logf("doLogin(regen=%v, hasUrl=%v)", regen, url != "") - if serverKey == (wgcfg.Key{}) { + if serverKey.IsZero() { var err error serverKey, err = loadServerKey(ctx, c.httpc, c.serverURL) if err != nil { @@ -352,12 +352,12 @@ func (c *Direct) doLogin(ctx context.Context, t *oauth2.Token, flags LoginFlags, c.mu.Unlock() } - var oldNodeKey wgcfg.Key + var oldNodeKey wgkey.Key if url != "" { } else if regen || persist.PrivateNodeKey.IsZero() { c.logf("Generating a new nodekey.") persist.OldPrivateNodeKey = persist.PrivateNodeKey - key, err := wgcfg.NewPrivateKey() + key, err := wgkey.NewPrivate() if err != nil { c.logf("login keygen: %v", err) return regen, url, err @@ -793,7 +793,7 @@ func (c *Direct) PollNetMap(ctx context.Context, maxPolls int, cb func(*NetworkM return nil } -func decode(res *http.Response, v interface{}, serverKey *wgcfg.Key, mkey *wgcfg.PrivateKey) error { +func decode(res *http.Response, v interface{}, serverKey *wgkey.Key, mkey *wgkey.Private) error { defer res.Body.Close() msg, err := ioutil.ReadAll(io.LimitReader(res.Body, 1<<20)) if err != nil { @@ -848,7 +848,7 @@ func (c *Direct) decodeMsg(msg []byte, v interface{}) error { } -func decodeMsg(msg []byte, v interface{}, serverKey *wgcfg.Key, mkey *wgcfg.PrivateKey) error { +func decodeMsg(msg []byte, v interface{}, serverKey *wgkey.Key, mkey *wgkey.Private) error { decrypted, err := decryptMsg(msg, serverKey, mkey) if err != nil { return err @@ -862,7 +862,7 @@ func decodeMsg(msg []byte, v interface{}, serverKey *wgcfg.Key, mkey *wgcfg.Priv return nil } -func decryptMsg(msg []byte, serverKey *wgcfg.Key, mkey *wgcfg.PrivateKey) ([]byte, error) { +func decryptMsg(msg []byte, serverKey *wgkey.Key, mkey *wgkey.Private) ([]byte, error) { var nonce [24]byte if len(msg) < len(nonce)+1 { return nil, fmt.Errorf("response missing nonce, len=%d", len(msg)) @@ -878,7 +878,7 @@ func decryptMsg(msg []byte, serverKey *wgcfg.Key, mkey *wgcfg.PrivateKey) ([]byt return decrypted, nil } -func encode(v interface{}, serverKey *wgcfg.Key, mkey *wgcfg.PrivateKey) ([]byte, error) { +func encode(v interface{}, serverKey *wgkey.Key, mkey *wgkey.Private) ([]byte, error) { b, err := json.Marshal(v) if err != nil { return nil, err @@ -897,27 +897,27 @@ func encode(v interface{}, serverKey *wgcfg.Key, mkey *wgcfg.PrivateKey) ([]byte return msg, nil } -func loadServerKey(ctx context.Context, httpc *http.Client, serverURL string) (wgcfg.Key, error) { +func loadServerKey(ctx context.Context, httpc *http.Client, serverURL string) (wgkey.Key, error) { req, err := http.NewRequest("GET", serverURL+"/key", nil) if err != nil { - return wgcfg.Key{}, fmt.Errorf("create control key request: %v", err) + return wgkey.Key{}, fmt.Errorf("create control key request: %v", err) } req = req.WithContext(ctx) res, err := httpc.Do(req) if err != nil { - return wgcfg.Key{}, fmt.Errorf("fetch control key: %v", err) + return wgkey.Key{}, fmt.Errorf("fetch control key: %v", err) } defer res.Body.Close() b, err := ioutil.ReadAll(io.LimitReader(res.Body, 1<<16)) if err != nil { - return wgcfg.Key{}, fmt.Errorf("fetch control key response: %v", err) + return wgkey.Key{}, fmt.Errorf("fetch control key response: %v", err) } if res.StatusCode != 200 { - return wgcfg.Key{}, fmt.Errorf("fetch control key: %d: %s", res.StatusCode, string(b)) + return wgkey.Key{}, fmt.Errorf("fetch control key: %d: %s", res.StatusCode, string(b)) } - key, err := wgcfg.ParseHexKey(string(b)) + key, err := wgkey.ParseHex(string(b)) if err != nil { - return wgcfg.Key{}, fmt.Errorf("fetch control key: %v", err) + return wgkey.Key{}, fmt.Errorf("fetch control key: %v", err) } return key, nil } diff --git a/control/controlclient/netmap.go b/control/controlclient/netmap.go index cda3239e4..c9d053449 100644 --- a/control/controlclient/netmap.go +++ b/control/controlclient/netmap.go @@ -17,6 +17,7 @@ import ( "inet.af/netaddr" "tailscale.com/tailcfg" "tailscale.com/types/logger" + "tailscale.com/types/wgkey" "tailscale.com/wgengine/filter" ) @@ -24,7 +25,7 @@ type NetworkMap struct { // Core networking NodeKey tailcfg.NodeKey - PrivateKey wgcfg.PrivateKey + PrivateKey wgkey.Private Expiry time.Time // Name is the DNS name assigned to this node. Name string @@ -241,7 +242,7 @@ const EndpointDiscoSuffix = ".disco.tailscale:12345" func (nm *NetworkMap) WGCfg(logf logger.Logf, flags WGConfigFlags) (*wgcfg.Config, error) { cfg := &wgcfg.Config{ Name: "tailscale", - PrivateKey: nm.PrivateKey, + PrivateKey: wgcfg.PrivateKey(nm.PrivateKey), Addresses: nm.Addresses, ListenPort: nm.LocalPort, Peers: make([]wgcfg.Peer, 0, len(nm.Peers)), diff --git a/control/controlclient/persist_test.go b/control/controlclient/persist_test.go index c769b5e48..efee06273 100644 --- a/control/controlclient/persist_test.go +++ b/control/controlclient/persist_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/tailscale/wireguard-go/wgcfg" + "tailscale.com/types/wgkey" ) func TestPersistEqual(t *testing.T) { @@ -18,8 +18,8 @@ func TestPersistEqual(t *testing.T) { have, persistHandles) } - newPrivate := func() wgcfg.PrivateKey { - k, err := wgcfg.NewPrivateKey() + newPrivate := func() wgkey.Private { + k, err := wgkey.NewPrivate() if err != nil { panic(err) } diff --git a/go.sum b/go.sum index bff9913b7..4d963c8d0 100644 --- a/go.sum +++ b/go.sum @@ -68,7 +68,9 @@ github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lxn/walk v0.0.0-20191128110447-55ccb3a9f5c1/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= github.com/lxn/walk v0.0.0-20201110160827-18ea5e372cdb/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= +github.com/lxn/win v0.0.0-20191128105842-2da648fda5b4/go.mod h1:ouWl4wViUNh8tPSIwxTVMuS014WakR1hqvBc2I0bMoA= github.com/lxn/win v0.0.0-20201111105847-2a20daff6a55/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= github.com/mattn/go-zglob v0.0.1 h1:xsEx/XUoVlI6yXjqBK062zYhRTZltCNmYPx6v+8DNaY= github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= @@ -104,6 +106,7 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/tailscale/depaware v0.0.0-20201003033024-5d95aab075be/go.mod h1:jissDaJNHiyV2tFdr3QyNEfsZrax/i2yQiSO+CljThI= github.com/tailscale/depaware v0.0.0-20201214215404-77d1e9757027 h1:lK99QQdH3yBWY6aGilF+IRlQIdmhzLrsEmF6JgN+Ryw= github.com/tailscale/depaware v0.0.0-20201214215404-77d1e9757027/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= github.com/tailscale/wireguard-go v0.0.0-20201220011020-db78fad0bebf h1:HuBwLWbDNIh/G72KSImSEx+dnd7FPGFI1e60LMJtLjU= @@ -124,6 +127,7 @@ go4.org/intern v0.0.0-20201223054237-ef8cbcb8edd7 h1:yeDrXaQ3VRXbTN7lHj70DxW4LdP go4.org/intern v0.0.0-20201223054237-ef8cbcb8edd7/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc= go4.org/intern v0.0.0-20201223061701-969c7e87e7cb h1:yuqO0E4bHRsTPUocDpRKXfLE40lwWplVxENQ2WOV7Gc= go4.org/intern v0.0.0-20201223061701-969c7e87e7cb/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc= +go4.org/mem v0.0.0-20200706164138-185c595c3ecc/go.mod h1:NEYvpHWemiG/E5UWfaN5QAIGZeT1sa0Z2UNk6oeMb/k= go4.org/mem v0.0.0-20201119185036-c04c5a6ff174 h1:vSug/WNOi2+4jrKdivxayTN/zd8EA1UrStjpWvvo1jk= go4.org/mem v0.0.0-20201119185036-c04c5a6ff174/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e h1:ExUmGi0ZsQmiVo9giDQqXkr7vreeXPMkOGIusfsfbzI= @@ -131,13 +135,17 @@ go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1: go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 h1:1tk03FUNpulq2cuWpXZWj649rwJpk0d20rxWiopKRmc= go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 h1:xYJJ3S178yv++9zXV/hnr29plCAGO9vAFG9dorqaFQc= golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -153,6 +161,9 @@ golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -165,6 +176,7 @@ golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -173,10 +185,15 @@ golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -192,6 +209,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b h1:a0ErnNnPKmhDyIXQvdZr+Lq8dc8xpMeqkF8y5PgQU4Q= golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -199,8 +217,11 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqG golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200609164405-eb789aa7ce50/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201001230009-b5b87423c93b/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 h1:1Bs6RVeBFtLZ8Yi1Hk07DiOqzvwLD/4hln4iahvFlag= golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -208,7 +229,11 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wireguard v0.0.20200321-0.20200715051853-507f148e1c42/go.mod h1:GJvYs5O24/ASlwPiRklVnjMx2xQzrOic0DuU6GvYJL4= +golang.zx2c4.com/wireguard v0.0.20200321-0.20201111175144-60b3766b89b9 h1:qowcZ56hhpeoESmWzI4Exhx4Y78TpCyXUJur4/c0CoE= golang.zx2c4.com/wireguard v0.0.20200321-0.20201111175144-60b3766b89b9/go.mod h1:LMeNfjlcPZTrBC1juwgbQyA4Zy2XVcsrdO/fIJxwyuA= +golang.zx2c4.com/wireguard/windows v0.1.2-0.20201004085714-dd60d0447f81/go.mod h1:GaK5zcgr5XE98WaRzIDilumDBp5/yP8j2kG/LCDnvAM= +golang.zx2c4.com/wireguard/windows v0.1.2-0.20201113162609-9b85be97fdf8 h1:nlXPqGA98n+qcq1pwZ28KjM5EsFQvamKS00A+VUeVjs= golang.zx2c4.com/wireguard/windows v0.1.2-0.20201113162609-9b85be97fdf8/go.mod h1:psva4yDnAHLuh7lUzOK7J7bLYxNFfo0iKWz+mi9gzkA= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -219,8 +244,10 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.0 h1:AWNL1W1i7f0wNZ8VwOKNJ0sliKvOF/adn0EHenfUh+c= honnef.co/go/tools v0.1.0/go.mod h1:XtegFAyX/PfluP4921rXU5IkjkqBCDnUq4W8VCIoKvM= +inet.af/netaddr v0.0.0-20200810144936-56928fe48a98/go.mod h1:qqYzz/2whtrbWJvt+DNWQyvekNN4ePQZcg2xc2/Yjww= inet.af/netaddr v0.0.0-20201218162718-658fec415e52/go.mod h1:qqYzz/2whtrbWJvt+DNWQyvekNN4ePQZcg2xc2/Yjww= inet.af/netaddr v0.0.0-20201223185330-97d366981fac h1:aqMW8vft7VmOIhtQhsTWhAuZzOBGYBv+Otyvwj+VGSU= inet.af/netaddr v0.0.0-20201223185330-97d366981fac/go.mod h1:9NdhtHLglxJliAZB6aC5ws3mfnUArdAzHG/iJq7cB/o= @@ -232,3 +259,4 @@ inet.af/netaddr v0.0.0-20201228234250-33d0a924ebbf h1:0eHZ8v6j5wIiOVyoYPd70ueZ/R inet.af/netaddr v0.0.0-20201228234250-33d0a924ebbf/go.mod h1:9NdhtHLglxJliAZB6aC5ws3mfnUArdAzHG/iJq7cB/o= rsc.io/goversion v1.2.0 h1:SPn+NLTiAG7w30IRK/DKp1BjvpWabYgxlLp/+kx5J8w= rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo= +tailscale.com v1.2.10/go.mod h1:JEJiCce3MHtPCTdX2ahLc4tcnxZ7b5etish1Yt0B6+w= diff --git a/ipn/local.go b/ipn/local.go index d2b2f3c78..5b781542c 100644 --- a/ipn/local.go +++ b/ipn/local.go @@ -29,6 +29,7 @@ import ( "tailscale.com/types/empty" "tailscale.com/types/key" "tailscale.com/types/logger" + "tailscale.com/types/wgkey" "tailscale.com/util/systemd" "tailscale.com/version" "tailscale.com/wgengine" @@ -83,7 +84,7 @@ type LocalBackend struct { userID string // current controlling user ID (for Windows, primarily) prefs *Prefs inServerMode bool - machinePrivKey wgcfg.PrivateKey + machinePrivKey wgkey.Private state State // hostinfo is mutated in-place while mu is held. hostinfo *tailcfg.Hostinfo @@ -737,7 +738,7 @@ func (b *LocalBackend) initMachineKeyLocked() (err error) { return nil } - var legacyMachineKey wgcfg.PrivateKey + var legacyMachineKey wgkey.Private if b.prefs.Persist != nil { legacyMachineKey = b.prefs.Persist.LegacyFrontendPrivateMachineKey } @@ -772,7 +773,7 @@ func (b *LocalBackend) initMachineKeyLocked() (err error) { } else { b.logf("generating new machine key") var err error - b.machinePrivKey, err = wgcfg.NewPrivateKey() + b.machinePrivKey, err = wgkey.NewPrivate() if err != nil { return fmt.Errorf("initializing new machine key: %w", err) } diff --git a/ipn/prefs_test.go b/ipn/prefs_test.go index 47fc02f6c..9715d7c89 100644 --- a/ipn/prefs_test.go +++ b/ipn/prefs_test.go @@ -13,10 +13,10 @@ import ( "testing" "time" - "github.com/tailscale/wireguard-go/wgcfg" "inet.af/netaddr" "tailscale.com/control/controlclient" "tailscale.com/tstest" + "tailscale.com/types/wgkey" "tailscale.com/wgengine/router" ) @@ -348,7 +348,7 @@ func TestPrefsPretty(t *testing.T) { { Prefs{ Persist: &controlclient.Persist{ - PrivateNodeKey: wgcfg.PrivateKey{1: 1}, + PrivateNodeKey: wgkey.Private{1: 1}, }, }, "linux", diff --git a/ipn/store.go b/ipn/store.go index 117c68ca7..acce235d2 100644 --- a/ipn/store.go +++ b/ipn/store.go @@ -24,7 +24,7 @@ var ErrStateNotExist = errors.New("no state with given ID") const ( // MachineKeyStateKey is the key under which we store the machine key, - // in its wgcfg.PrivateKey.MarshalText representation. + // in its wgkey.Private.MarshalText representation. MachineKeyStateKey = StateKey("_machinekey") // GlobalDaemonStateKey is the ipn.StateKey that tailscaled diff --git a/tailcfg/tailcfg_test.go b/tailcfg/tailcfg_test.go index d0e2d6dce..93d3a6fae 100644 --- a/tailcfg/tailcfg_test.go +++ b/tailcfg/tailcfg_test.go @@ -11,8 +11,8 @@ import ( "testing" "time" - "github.com/tailscale/wireguard-go/wgcfg" "inet.af/netaddr" + "tailscale.com/types/wgkey" ) func fieldsOf(t reflect.Type) (fields []string) { @@ -194,9 +194,9 @@ func TestNodeEqual(t *testing.T) { have, nodeHandles) } - newPublicKey := func(t *testing.T) wgcfg.Key { + newPublicKey := func(t *testing.T) wgkey.Key { t.Helper() - k, err := wgcfg.NewPrivateKey() + k, err := wgkey.NewPrivate() if err != nil { t.Fatal(err) } diff --git a/types/key/key_test.go b/types/key/key_test.go index 4c5c97625..7a2155ef0 100644 --- a/types/key/key_test.go +++ b/types/key/key_test.go @@ -7,7 +7,7 @@ package key import ( "testing" - "github.com/tailscale/wireguard-go/wgcfg" + "tailscale.com/types/wgkey" ) func TestTextUnmarshal(t *testing.T) { @@ -28,10 +28,10 @@ func TestTextUnmarshal(t *testing.T) { func TestClamping(t *testing.T) { t.Run("NewPrivate", func(t *testing.T) { testClamping(t, NewPrivate) }) - // Also test the wgcfg package, as their behavior should match. - t.Run("wgcfg", func(t *testing.T) { + // Also test the wgkey package, as their behavior should match. + t.Run("wgkey", func(t *testing.T) { testClamping(t, func() Private { - k, err := wgcfg.NewPrivateKey() + k, err := wgkey.NewPrivate() if err != nil { t.Fatal(err) } diff --git a/types/wgkey/key.go b/types/wgkey/key.go new file mode 100644 index 000000000..b9ba6deed --- /dev/null +++ b/types/wgkey/key.go @@ -0,0 +1,241 @@ +// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package wgkey contains types and helpers for WireGuard keys. +// It is very similar to package tailscale.com/types/key, +// which is also used for curve25519 keys. +// These keys are used for WireGuard clients; +// those keys are used in other curve25519 clients. +package wgkey + +import ( + "bytes" + "crypto/rand" + "crypto/subtle" + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "strings" + + "golang.org/x/crypto/chacha20poly1305" + "golang.org/x/crypto/curve25519" +) + +// Size is the number of bytes in a curve25519 key. +const Size = 32 + +// A Key is a curve25519 key. +// It is used by WireGuard to represent public keys. +type Key [Size]byte + +// NewPreshared generates a new random Key. +func NewPreshared() (*Key, error) { + var k [Size]byte + _, err := rand.Read(k[:]) + if err != nil { + return nil, err + } + return (*Key)(&k), nil +} + +func Parse(b64 string) (*Key, error) { return parseBase64(base64.StdEncoding, b64) } + +func ParseHex(s string) (Key, error) { + b, err := hex.DecodeString(s) + if err != nil { + return Key{}, fmt.Errorf("invalid hex key (%q): %w", s, err) + } + if len(b) != Size { + return Key{}, fmt.Errorf("invalid hex key (%q): length=%d, want %d", s, len(b), Size) + } + + var key Key + copy(key[:], b) + return key, nil +} + +func ParsePrivateHex(v string) (Private, error) { + k, err := ParseHex(v) + if err != nil { + return Private{}, err + } + pk := Private(k) + if pk.IsZero() { + // Do not clamp a zero key, pass the zero through + // (much like NaN propagation) so that IsZero reports + // a useful result. + return pk, nil + } + pk.clamp() + return pk, nil +} + +func (k Key) Base64() string { return base64.StdEncoding.EncodeToString(k[:]) } +func (k Key) String() string { return k.ShortString() } +func (k Key) HexString() string { return hex.EncodeToString(k[:]) } +func (k Key) Equal(k2 Key) bool { return subtle.ConstantTimeCompare(k[:], k2[:]) == 1 } + +func (k *Key) ShortString() string { + long := k.Base64() + return "[" + long[0:5] + "]" +} + +func (k *Key) IsZero() bool { + if k == nil { + return true + } + var zeros Key + return subtle.ConstantTimeCompare(zeros[:], k[:]) == 1 +} + +func (k *Key) MarshalJSON() ([]byte, error) { + if k == nil { + return []byte("null"), nil + } + // TODO(josharian): use encoding/hex instead? + buf := new(bytes.Buffer) + fmt.Fprintf(buf, `"%x"`, k[:]) + return buf.Bytes(), nil +} + +func (k *Key) UnmarshalJSON(b []byte) error { + if k == nil { + return errors.New("wgkey.Key: UnmarshalJSON on nil pointer") + } + if len(b) < 3 || b[0] != '"' || b[len(b)-1] != '"' { + return errors.New("wgkey.Key: UnmarshalJSON not given a string") + } + b = b[1 : len(b)-1] + key, err := ParseHex(string(b)) + if err != nil { + return fmt.Errorf("wgkey.Key: UnmarshalJSON: %v", err) + } + copy(k[:], key[:]) + return nil +} + +func (a *Key) LessThan(b *Key) bool { + for i := range a { + if a[i] < b[i] { + return true + } else if a[i] > b[i] { + return false + } + } + return false +} + +// A Private is a curve25519 key. +// It is used by WireGuard to represent private keys. +type Private [Size]byte + +// NewPrivate generates a new curve25519 secret key. +// It conforms to the format described on https://cr.yp.to/ecdh.html. +func NewPrivate() (Private, error) { + k, err := NewPreshared() + if err != nil { + return Private{}, err + } + k[0] &= 248 + k[31] = (k[31] & 127) | 64 + return (Private)(*k), nil +} + +func ParsePrivate(b64 string) (*Private, error) { + k, err := parseBase64(base64.StdEncoding, b64) + return (*Private)(k), err +} + +func (k *Private) String() string { return base64.StdEncoding.EncodeToString(k[:]) } +func (k *Private) HexString() string { return hex.EncodeToString(k[:]) } +func (k *Private) Equal(k2 Private) bool { return subtle.ConstantTimeCompare(k[:], k2[:]) == 1 } + +func (k *Private) IsZero() bool { + pk := Key(*k) + return pk.IsZero() +} + +func (k *Private) clamp() { + k[0] &= 248 + k[31] = (k[31] & 127) | 64 +} + +// Public computes the public key matching this curve25519 secret key. +func (k *Private) Public() Key { + pk := Key(*k) + if pk.IsZero() { + panic("Tried to generate emptyPrivate.Public()") + } + var p [Size]byte + curve25519.ScalarBaseMult(&p, (*[Size]byte)(k)) + return (Key)(p) +} + +func (k Private) MarshalText() ([]byte, error) { + // TODO(josharian): use encoding/hex instead? + buf := new(bytes.Buffer) + fmt.Fprintf(buf, `privkey:%x`, k[:]) + return buf.Bytes(), nil +} + +func (k *Private) UnmarshalText(b []byte) error { + s := string(b) + if !strings.HasPrefix(s, `privkey:`) { + return errors.New("wgkey.Private: UnmarshalText not given a private-key string") + } + s = strings.TrimPrefix(s, `privkey:`) + key, err := ParseHex(s) + if err != nil { + return fmt.Errorf("wgkey.Private: UnmarshalText: %v", err) + } + copy(k[:], key[:]) + return nil +} + +func parseBase64(enc *base64.Encoding, s string) (*Key, error) { + k, err := enc.DecodeString(s) + if err != nil { + return nil, fmt.Errorf("invalid key (%q): %w", s, err) + } + if len(k) != Size { + return nil, fmt.Errorf("invalid key (%q): length=%d, want %d", s, len(k), Size) + } + var key Key + copy(key[:], k) + return &key, nil +} + +func ParseSymmetric(b64 string) (Symmetric, error) { + k, err := parseBase64(base64.StdEncoding, b64) + if err != nil { + return Symmetric{}, err + } + return Symmetric(*k), nil +} + +func ParseSymmetricHex(s string) (Symmetric, error) { + b, err := hex.DecodeString(s) + if err != nil { + return Symmetric{}, fmt.Errorf("invalid symmetric hex key (%q): %w", s, err) + } + if len(b) != chacha20poly1305.KeySize { + return Symmetric{}, fmt.Errorf("invalid symmetric hex key length (%q): length=%d, want %d", s, len(b), chacha20poly1305.KeySize) + } + var key Symmetric + copy(key[:], b) + return key, nil +} + +// Symmetric is a chacha20poly1305 key. +// It is used by WireGuard to represent pre-shared symmetric keys. +type Symmetric [chacha20poly1305.KeySize]byte + +func (k Symmetric) Base64() string { return base64.StdEncoding.EncodeToString(k[:]) } +func (k Symmetric) String() string { return "sym:" + k.Base64()[:8] } +func (k Symmetric) HexString() string { return hex.EncodeToString(k[:]) } +func (k Symmetric) IsZero() bool { return k.Equal(Symmetric{}) } +func (k Symmetric) Equal(k2 Symmetric) bool { + return subtle.ConstantTimeCompare(k[:], k2[:]) == 1 +} diff --git a/types/wgkey/key_test.go b/types/wgkey/key_test.go new file mode 100644 index 000000000..9b8632a3b --- /dev/null +++ b/types/wgkey/key_test.go @@ -0,0 +1,111 @@ +// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package wgkey + +import ( + "bytes" + "testing" +) + +func TestKeyBasics(t *testing.T) { + k1, err := NewPreshared() + if err != nil { + t.Fatal(err) + } + + b, err := k1.MarshalJSON() + if err != nil { + t.Fatal(err) + } + + t.Run("JSON round-trip", func(t *testing.T) { + // should preserve the keys + k2 := new(Key) + if err := k2.UnmarshalJSON(b); err != nil { + t.Fatal(err) + } + if !bytes.Equal(k1[:], k2[:]) { + t.Fatalf("k1 %v != k2 %v", k1[:], k2[:]) + } + if b1, b2 := k1.String(), k2.String(); b1 != b2 { + t.Fatalf("base64-encoded keys do not match: %s, %s", b1, b2) + } + }) + + t.Run("JSON incompatible with PrivateKey", func(t *testing.T) { + k2 := new(Private) + if err := k2.UnmarshalText(b); err == nil { + t.Fatalf("successfully decoded key as private key") + } + }) + + t.Run("second key", func(t *testing.T) { + // A second call to NewPreshared should make a new key. + k3, err := NewPreshared() + if err != nil { + t.Fatal(err) + } + if bytes.Equal(k1[:], k3[:]) { + t.Fatalf("k1 %v == k3 %v", k1[:], k3[:]) + } + // Check for obvious comparables to make sure we are not generating bad strings somewhere. + if b1, b2 := k1.String(), k3.String(); b1 == b2 { + t.Fatalf("base64-encoded keys match: %s, %s", b1, b2) + } + }) +} +func TestPrivateKeyBasics(t *testing.T) { + pri, err := NewPrivate() + if err != nil { + t.Fatal(err) + } + + b, err := pri.MarshalText() + if err != nil { + t.Fatal(err) + } + + t.Run("JSON round-trip", func(t *testing.T) { + // should preserve the keys + pri2 := new(Private) + if err := pri2.UnmarshalText(b); err != nil { + t.Fatal(err) + } + if !bytes.Equal(pri[:], pri2[:]) { + t.Fatalf("pri %v != pri2 %v", pri[:], pri2[:]) + } + if b1, b2 := pri.String(), pri2.String(); b1 != b2 { + t.Fatalf("base64-encoded keys do not match: %s, %s", b1, b2) + } + if pub1, pub2 := pri.Public().String(), pri2.Public().String(); pub1 != pub2 { + t.Fatalf("base64-encoded public keys do not match: %s, %s", pub1, pub2) + } + }) + + t.Run("JSON incompatible with Key", func(t *testing.T) { + k2 := new(Key) + if err := k2.UnmarshalJSON(b); err == nil { + t.Fatalf("successfully decoded private key as key") + } + }) + + t.Run("second key", func(t *testing.T) { + // A second call to New should make a new key. + pri3, err := NewPrivate() + if err != nil { + t.Fatal(err) + } + if bytes.Equal(pri[:], pri3[:]) { + t.Fatalf("pri %v == pri3 %v", pri[:], pri3[:]) + } + // Check for obvious comparables to make sure we are not generating bad strings somewhere. + if b1, b2 := pri.String(), pri3.String(); b1 == b2 { + t.Fatalf("base64-encoded keys match: %s, %s", b1, b2) + } + if pub1, pub2 := pri.Public().String(), pri3.Public().String(); pub1 == pub2 { + t.Fatalf("base64-encoded public keys match: %s, %s", pub1, pub2) + } + }) +} diff --git a/wgengine/magicsock/legacy.go b/wgengine/magicsock/legacy.go index cb61a0a2d..083f313db 100644 --- a/wgengine/magicsock/legacy.go +++ b/wgengine/magicsock/legacy.go @@ -20,6 +20,7 @@ import ( "tailscale.com/ipn/ipnstate" "tailscale.com/types/key" "tailscale.com/types/logger" + "tailscale.com/types/wgkey" ) var errNoDestinations = errors.New("magicsock: no destinations") @@ -387,7 +388,7 @@ func (a *addrSet) UpdateDst(new *net.UDPAddr) error { } } - publicKey := wgcfg.Key(a.publicKey) + publicKey := wgkey.Key(a.publicKey) pk := publicKey.ShortString() old := "" if a.curAddr >= 0 { diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index 8073d01e0..c162da9b0 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -52,6 +52,7 @@ import ( "tailscale.com/types/nettype" "tailscale.com/types/opt" "tailscale.com/types/structs" + "tailscale.com/types/wgkey" "tailscale.com/version" ) @@ -1562,7 +1563,7 @@ Top: } else if asEp != nil { ep = asEp } else { - key := wgcfg.Key(dm.src) + key := wgkey.Key(dm.src) c.logf("magicsock: DERP packet from unknown key: %s", key.ShortString()) // TODO(danderson): after we fail to find a DERP endpoint, we // seem to be falling through to passing the packet to @@ -1952,7 +1953,7 @@ func (c *Conn) SetNetworkUp(up bool) { // // If the private key changes, any DERP connections are torn down & // recreated when needed. -func (c *Conn) SetPrivateKey(privateKey wgcfg.PrivateKey) error { +func (c *Conn) SetPrivateKey(privateKey wgkey.Private) error { c.mu.Lock() defer c.mu.Unlock() @@ -2660,7 +2661,7 @@ func simpleDur(d time.Duration) time.Duration { } func peerShort(k key.Public) string { - k2 := wgcfg.Key(k) + k2 := wgkey.Key(k) return k2.ShortString() } diff --git a/wgengine/magicsock/magicsock_test.go b/wgengine/magicsock/magicsock_test.go index 0ca3816c4..7aac0572a 100644 --- a/wgengine/magicsock/magicsock_test.go +++ b/wgengine/magicsock/magicsock_test.go @@ -42,6 +42,7 @@ import ( "tailscale.com/types/key" "tailscale.com/types/logger" "tailscale.com/types/nettype" + "tailscale.com/types/wgkey" "tailscale.com/wgengine/filter" "tailscale.com/wgengine/tstun" ) @@ -119,7 +120,7 @@ func runDERPAndStun(t *testing.T, logf logger.Logf, l nettype.PacketListener, st // necessary to send and receive packets to test e2e wireguard // happiness. type magicStack struct { - privateKey wgcfg.PrivateKey + privateKey wgkey.Private epCh chan []string // endpoint updates produced by this peer conn *Conn // the magicsock itself tun *tuntest.ChannelTUN // TUN device to send/receive packets @@ -133,7 +134,7 @@ type magicStack struct { func newMagicStack(t testing.TB, logf logger.Logf, l nettype.PacketListener, derpMap *tailcfg.DERPMap) *magicStack { t.Helper() - privateKey, err := wgcfg.NewPrivateKey() + privateKey, err := wgkey.NewPrivate() if err != nil { t.Fatalf("generating private key: %v", err) } @@ -347,7 +348,7 @@ func TestNewConn(t *testing.T) { } defer conn.Close() conn.SetDERPMap(stuntest.DERPMapOf(stunAddr.String())) - conn.SetPrivateKey(wgcfg.PrivateKey(key.NewPrivate())) + conn.SetPrivateKey(wgkey.Private(key.NewPrivate())) conn.Start() go func() { @@ -457,11 +458,11 @@ func makeConfigs(t *testing.T, addrs []netaddr.IPPort) []wgcfg.Config { var addresses [][]netaddr.IPPrefix for i := range addrs { - privKey, err := wgcfg.NewPrivateKey() + privKey, err := wgkey.NewPrivate() if err != nil { t.Fatal(err) } - privKeys = append(privKeys, privKey) + privKeys = append(privKeys, wgcfg.PrivateKey(privKey)) addresses = append(addresses, []netaddr.IPPrefix{ parseCIDR(t, fmt.Sprintf("1.0.0.%d/32", i+1)), diff --git a/wgengine/userspace.go b/wgengine/userspace.go index 13397c60f..f7b403ce1 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -37,6 +37,7 @@ import ( "tailscale.com/tailcfg" "tailscale.com/types/key" "tailscale.com/types/logger" + "tailscale.com/types/wgkey" "tailscale.com/version" "tailscale.com/version/distro" "tailscale.com/wgengine/filter" @@ -113,9 +114,9 @@ type userspaceEngine struct { closing bool // Close was called (even if we're still closing) statusCallback StatusCallback linkChangeCallback func(major bool, newState *interfaces.State) - peerSequence []wgcfg.Key + peerSequence []wgkey.Key endpoints []string - pingers map[wgcfg.Key]*pinger // legacy pingers for pre-discovery peers + pingers map[wgkey.Key]*pinger // legacy pingers for pre-discovery peers linkState *interfaces.State // Lock ordering: magicsock.Conn.mu, wgLock, then mu. @@ -202,7 +203,7 @@ func newUserspaceEngineAdvanced(conf EngineConfig) (_ Engine, reterr error) { waitCh: make(chan struct{}), tundev: tstun.WrapTUN(logf, conf.TUN), resolver: tsdns.NewResolver(rconf), - pingers: make(map[wgcfg.Key]*pinger), + pingers: make(map[wgkey.Key]*pinger), } e.localAddrs.Store(map[netaddr.IP]bool{}) e.linkState, _ = getLinkState() @@ -290,7 +291,7 @@ func newUserspaceEngineAdvanced(conf EngineConfig) (_ Engine, reterr error) { } } if len(ips) > 0 { - go e.pinger(peerKey, ips) + go e.pinger(wgkey.Key(peerKey), ips) } else { logf("[unexpected] peer %s has no single-IP routes: %v", peerKey.ShortString(), allowedIPs) } @@ -487,7 +488,7 @@ func (p *pinger) close() { <-p.done } -func (p *pinger) run(ctx context.Context, peerKey wgcfg.Key, ips []netaddr.IP, srcIP netaddr.IP) { +func (p *pinger) run(ctx context.Context, peerKey wgkey.Key, ips []netaddr.IP, srcIP netaddr.IP) { defer func() { p.e.mu.Lock() if p.e.pingers[peerKey] == p { @@ -556,7 +557,7 @@ func (p *pinger) run(ctx context.Context, peerKey wgcfg.Key, ips []netaddr.IP, s // // This is only used with legacy peers (before 0.100.0) that don't // have advertised discovery keys. -func (e *userspaceEngine) pinger(peerKey wgcfg.Key, ips []netaddr.IP) { +func (e *userspaceEngine) pinger(peerKey wgkey.Key, ips []netaddr.IP) { e.logf("[v1] generating initial ping traffic to %s (%v)", peerKey.ShortString(), ips) var srcIP netaddr.IP @@ -890,7 +891,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config) e.mu.Lock() e.peerSequence = e.peerSequence[:0] for _, p := range cfg.Peers { - e.peerSequence = append(e.peerSequence, p.PublicKey) + e.peerSequence = append(e.peerSequence, wgkey.Key(p.PublicKey)) peerSet[key.Public(p.PublicKey)] = struct{}{} } e.mu.Unlock() @@ -932,7 +933,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config) // (which is needed by DERP) before wgdev gets it, as wgdev // will start trying to handshake, which we want to be able to // go over DERP. - if err := e.magicConn.SetPrivateKey(cfg.PrivateKey); err != nil { + if err := e.magicConn.SetPrivateKey(wgkey.Private(cfg.PrivateKey)); err != nil { e.logf("wgengine: Reconfig: SetPrivateKey: %v", err) } e.magicConn.UpdatePeers(peerSet) @@ -1039,7 +1040,7 @@ func (e *userspaceEngine) getStatus() (*Status, error) { errc <- bw.Flush() }() - pp := make(map[wgcfg.Key]*PeerStatus) + pp := make(map[wgkey.Key]*PeerStatus) p := &PeerStatus{} var hst1, hst2, n int64 @@ -1062,7 +1063,7 @@ func (e *userspaceEngine) getStatus() (*Status, error) { log.Fatalf("IpcGetOperation: invalid key %#v", v) } p = &PeerStatus{} - pp[wgcfg.Key(pk)] = p + pp[wgkey.Key(pk)] = p key := tailcfg.NodeKey(pk) p.NodeKey = key