diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index 515ed200c..ee6af9952 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -267,6 +267,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/types/ipproto from tailscale.com/net/flowtrack+ tailscale.com/types/key from tailscale.com/control/controlbase+ tailscale.com/types/logger from tailscale.com/control/controlclient+ + tailscale.com/types/logid from tailscale.com/logtail+ tailscale.com/types/netlogtype from tailscale.com/net/tstun+ tailscale.com/types/netmap from tailscale.com/control/controlclient+ tailscale.com/types/nettype from tailscale.com/wgengine/magicsock+ diff --git a/logtail/id.go b/logtail/id.go index 8261905f2..3c1470612 100644 --- a/logtail/id.go +++ b/logtail/id.go @@ -4,170 +4,25 @@ package logtail -import ( - "crypto/rand" - "crypto/sha256" - "encoding/binary" - "encoding/hex" - "errors" - "fmt" -) +import "tailscale.com/types/logid" -// PrivateID represents an instance that write logs. -// Private IDs are only shared with the server when writing logs. -type PrivateID [32]byte +// Deprecated: Use "tailscale.com/types/logid".PrivateID instead. +type PrivateID = logid.PrivateID -// Safely generate a new PrivateId for use in Config objects. -// You should persist this across runs of an instance of your app, so that -// it can append to the same log file on each run. -func NewPrivateID() (id PrivateID, err error) { - _, err = rand.Read(id[:]) - if err != nil { - return PrivateID{}, err - } - // Clamping, for future use. - id[0] &= 248 - id[31] = (id[31] & 127) | 64 - return id, nil +// Deprecated: Use "tailscale.com/types/logid".NewPrivateID instead. +func NewPrivateID() (PrivateID, error) { + return logid.NewPrivateID() } -func (id PrivateID) MarshalText() ([]byte, error) { - b := make([]byte, hex.EncodedLen(len(id))) - if i := hex.Encode(b, id[:]); i != len(b) { - return nil, fmt.Errorf("logtail.PrivateID.MarshalText: i=%d", i) - } - return b, nil -} - -// ParsePrivateID returns a PrivateID from its hex (String) representation. +// Deprecated: Use "tailscale.com/types/logid".ParsePrivateID instead. func ParsePrivateID(s string) (PrivateID, error) { - if len(s) != 64 { - return PrivateID{}, errors.New("invalid length") - } - var p PrivateID - for i := range p { - a, ok1 := fromHexChar(s[i*2+0]) - b, ok2 := fromHexChar(s[i*2+1]) - if !ok1 || !ok2 { - return PrivateID{}, errors.New("invalid hex character") - } - p[i] = (a << 4) | b - } - return p, nil -} - -// IsZero reports whether id is the zero value. -func (id PrivateID) IsZero() bool { return id == PrivateID{} } - -func (id *PrivateID) UnmarshalText(s []byte) error { - b, err := hex.DecodeString(string(s)) - if err != nil { - return fmt.Errorf("logtail.PrivateID.UnmarshalText: %v", err) - } - if len(b) != len(id) { - return fmt.Errorf("logtail.PrivateID.UnmarshalText: invalid hex length: %d", len(b)) - } - copy(id[:], b) - return nil -} - -func (id PrivateID) String() string { - b, err := id.MarshalText() - if err != nil { - panic(err) - } - return string(b) -} - -func (id PrivateID) Public() (pub PublicID) { - h := sha256.New() - h.Write(id[:]) - if n := copy(pub[:], h.Sum(pub[:0])); n != len(pub) { - panic(fmt.Sprintf("public id short copy: %d", n)) - } - return pub + return logid.ParsePrivateID(s) } -// PublicID represents an instance in the logs service for reading and adoption. -// The public ID value is a SHA-256 hash of a private ID. -type PublicID [sha256.Size]byte +// Deprecated: Use "tailscale.com/types/logid".PublicID instead. +type PublicID = logid.PublicID -// ParsePublicID returns a PublicID from its hex (String) representation. +// Deprecated: Use "tailscale.com/types/logid".ParsePublicID instead. func ParsePublicID(s string) (PublicID, error) { - if len(s) != sha256.Size*2 { - return PublicID{}, errors.New("invalid length") - } - var p PublicID - for i := range p { - a, ok1 := fromHexChar(s[i*2+0]) - b, ok2 := fromHexChar(s[i*2+1]) - if !ok1 || !ok2 { - return PublicID{}, errors.New("invalid hex character") - } - p[i] = (a << 4) | b - } - return p, nil -} - -func (id PublicID) MarshalText() ([]byte, error) { - b := make([]byte, hex.EncodedLen(len(id))) - if i := hex.Encode(b, id[:]); i != len(b) { - return nil, fmt.Errorf("logtail.PublicID.MarshalText: i=%d", i) - } - return b, nil -} - -func (id *PublicID) UnmarshalText(s []byte) error { - if len(s) != len(id)*2 { - return fmt.Errorf("logtail.PublicID.UnmarshalText: invalid hex length: %d", len(s)) - } - for i := range id { - a, ok1 := fromHexChar(s[i*2+0]) - b, ok2 := fromHexChar(s[i*2+1]) - if !ok1 || !ok2 { - return errors.New("invalid hex character") - } - id[i] = (a << 4) | b - } - return nil -} - -func (id PublicID) String() string { - b, err := id.MarshalText() - if err != nil { - panic(err) - } - return string(b) -} - -// fromHexChar converts a hex character into its value and a success flag. -func fromHexChar(c byte) (byte, bool) { - switch { - case '0' <= c && c <= '9': - return c - '0', true - case 'a' <= c && c <= 'f': - return c - 'a' + 10, true - case 'A' <= c && c <= 'F': - return c - 'A' + 10, true - } - - return 0, false -} - -func (id1 PublicID) Less(id2 PublicID) bool { - for i, c1 := range id1[:] { - c2 := id2[i] - if c1 != c2 { - return c1 < c2 - } - } - return false // equal -} - -func (id PublicID) IsZero() bool { - return id == PublicID{} -} - -func (id PublicID) Prefix64() uint64 { - return binary.BigEndian.Uint64(id[:8]) + return logid.ParsePublicID(s) } diff --git a/types/logid/id.go b/types/logid/id.go new file mode 100644 index 000000000..a55784052 --- /dev/null +++ b/types/logid/id.go @@ -0,0 +1,183 @@ +// 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 logid + +import ( + "crypto/rand" + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "errors" + "fmt" +) + +// PrivateID represents an instance that write logs. +// Private IDs are only shared with the server when writing logs. +type PrivateID [32]byte + +// Safely generate a new PrivateId for use in Config objects. +// You should persist this across runs of an instance of your app, so that +// it can append to the same log file on each run. +func NewPrivateID() (id PrivateID, err error) { + _, err = rand.Read(id[:]) + if err != nil { + return PrivateID{}, err + } + // Clamping, for future use. + id[0] &= 248 + id[31] = (id[31] & 127) | 64 + return id, nil +} + +func (id PrivateID) MarshalText() ([]byte, error) { + b := make([]byte, hex.EncodedLen(len(id))) + if i := hex.Encode(b, id[:]); i != len(b) { + return nil, fmt.Errorf("logtail.PrivateID.MarshalText: i=%d", i) + } + return b, nil +} + +// ParsePrivateID returns a PrivateID from its hex (String) representation. +func ParsePrivateID(s string) (PrivateID, error) { + if len(s) != 64 { + return PrivateID{}, errors.New("invalid length") + } + var p PrivateID + for i := range p { + a, ok1 := fromHexChar(s[i*2+0]) + b, ok2 := fromHexChar(s[i*2+1]) + if !ok1 || !ok2 { + return PrivateID{}, errors.New("invalid hex character") + } + p[i] = (a << 4) | b + } + return p, nil +} + +// IsZero reports whether id is the zero value. +func (id PrivateID) IsZero() bool { return id == PrivateID{} } + +func (id *PrivateID) UnmarshalText(s []byte) error { + b, err := hex.DecodeString(string(s)) + if err != nil { + return fmt.Errorf("logtail.PrivateID.UnmarshalText: %v", err) + } + if len(b) != len(id) { + return fmt.Errorf("logtail.PrivateID.UnmarshalText: invalid hex length: %d", len(b)) + } + copy(id[:], b) + return nil +} + +func (id PrivateID) String() string { + b, err := id.MarshalText() + if err != nil { + panic(err) + } + return string(b) +} + +func (id PrivateID) Public() (pub PublicID) { + h := sha256.New() + h.Write(id[:]) + if n := copy(pub[:], h.Sum(pub[:0])); n != len(pub) { + panic(fmt.Sprintf("public id short copy: %d", n)) + } + return pub +} + +// PublicID represents an instance in the logs service for reading and adoption. +// The public ID value is a SHA-256 hash of a private ID. +type PublicID [sha256.Size]byte + +// ParsePublicID returns a PublicID from its hex (String) representation. +func ParsePublicID(s string) (PublicID, error) { + if len(s) != sha256.Size*2 { + return PublicID{}, errors.New("invalid length") + } + var p PublicID + for i := range p { + a, ok1 := fromHexChar(s[i*2+0]) + b, ok2 := fromHexChar(s[i*2+1]) + if !ok1 || !ok2 { + return PublicID{}, errors.New("invalid hex character") + } + p[i] = (a << 4) | b + } + return p, nil +} + +// MustParsePublicID calls ParsePublicID and panics in case of an error. +// It is intended for use with constant strings, typically in tests. +func MustParsePublicID(s string) PublicID { + id, err := ParsePublicID(s) + if err != nil { + panic(err) + } + return id +} + +func (id PublicID) MarshalText() ([]byte, error) { + b := make([]byte, hex.EncodedLen(len(id))) + if i := hex.Encode(b, id[:]); i != len(b) { + return nil, fmt.Errorf("logtail.PublicID.MarshalText: i=%d", i) + } + return b, nil +} + +func (id *PublicID) UnmarshalText(s []byte) error { + if len(s) != len(id)*2 { + return fmt.Errorf("logtail.PublicID.UnmarshalText: invalid hex length: %d", len(s)) + } + for i := range id { + a, ok1 := fromHexChar(s[i*2+0]) + b, ok2 := fromHexChar(s[i*2+1]) + if !ok1 || !ok2 { + return errors.New("invalid hex character") + } + id[i] = (a << 4) | b + } + return nil +} + +func (id PublicID) String() string { + b, err := id.MarshalText() + if err != nil { + panic(err) + } + return string(b) +} + +// fromHexChar converts a hex character into its value and a success flag. +func fromHexChar(c byte) (byte, bool) { + switch { + case '0' <= c && c <= '9': + return c - '0', true + case 'a' <= c && c <= 'f': + return c - 'a' + 10, true + case 'A' <= c && c <= 'F': + return c - 'A' + 10, true + } + + return 0, false +} + +func (id1 PublicID) Less(id2 PublicID) bool { + for i, c1 := range id1[:] { + c2 := id2[i] + if c1 != c2 { + return c1 < c2 + } + } + return false // equal +} + +func (id PublicID) IsZero() bool { + return id == PublicID{} +} + +func (id PublicID) Prefix64() uint64 { + return binary.BigEndian.Uint64(id[:8]) +} diff --git a/logtail/id_test.go b/types/logid/id_test.go similarity index 99% rename from logtail/id_test.go rename to types/logid/id_test.go index 1a7798dc6..dbe16d95d 100644 --- a/logtail/id_test.go +++ b/types/logid/id_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package logtail +package logid import ( "testing" diff --git a/wgengine/wgcfg/wgcfg_clone.go b/wgengine/wgcfg/wgcfg_clone.go index e556fe5aa..ed040cde4 100644 --- a/wgengine/wgcfg/wgcfg_clone.go +++ b/wgengine/wgcfg/wgcfg_clone.go @@ -9,9 +9,9 @@ package wgcfg import ( "net/netip" - "tailscale.com/logtail" "tailscale.com/tailcfg" "tailscale.com/types/key" + "tailscale.com/types/logid" ) // Clone makes a deep copy of Config. @@ -41,8 +41,8 @@ var _ConfigCloneNeedsRegeneration = Config(struct { DNS []netip.Addr Peers []Peer NetworkLogging struct { - NodeID logtail.PrivateID - DomainID logtail.PrivateID + NodeID logid.PrivateID + DomainID logid.PrivateID } }{})