| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990 |
- // Copyright (c) Tailscale Inc & AUTHORS
- // SPDX-License-Identifier: BSD-3-Clause
- package key
- import (
- "crypto/subtle"
- "encoding/hex"
- "encoding/json"
- "errors"
- "fmt"
- "strings"
- "go4.org/mem"
- "tailscale.com/types/structs"
- )
- var ErrInvalidMeshKey = errors.New("invalid mesh key")
- // DERPMesh is a mesh key, used for inter-DERP-node communication and for
- // privileged DERP clients.
- type DERPMesh struct {
- _ structs.Incomparable // == isn't constant-time
- k [32]byte // 64-digit hexadecimal numbers fit in 32 bytes
- }
- // MarshalJSON implements the [encoding/json.Marshaler] interface.
- func (k DERPMesh) MarshalJSON() ([]byte, error) {
- return json.Marshal(k.String())
- }
- // UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
- func (k *DERPMesh) UnmarshalJSON(data []byte) error {
- var s string
- json.Unmarshal(data, &s)
- if hex.DecodedLen(len(s)) != len(k.k) {
- return fmt.Errorf("types/key/derp: cannot unmarshal, incorrect size mesh key len: %d, must be %d, %w", hex.DecodedLen(len(s)), len(k.k), ErrInvalidMeshKey)
- }
- _, err := hex.Decode(k.k[:], []byte(s))
- if err != nil {
- return fmt.Errorf("types/key/derp: cannot unmarshal, invalid mesh key: %w", err)
- }
- return nil
- }
- // DERPMeshFromRaw32 parses a 32-byte raw value as a DERP mesh key.
- func DERPMeshFromRaw32(raw mem.RO) DERPMesh {
- if raw.Len() != 32 {
- panic("input has wrong size")
- }
- var ret DERPMesh
- raw.Copy(ret.k[:])
- return ret
- }
- // ParseDERPMesh parses a DERP mesh key from a string.
- // This function trims whitespace around the string.
- // If the key is not a 64-digit hexadecimal number, ErrInvalidMeshKey is returned.
- func ParseDERPMesh(key string) (DERPMesh, error) {
- key = strings.TrimSpace(key)
- if len(key) != 64 {
- return DERPMesh{}, fmt.Errorf("%w: must be 64-digit hexadecimal number", ErrInvalidMeshKey)
- }
- decoded, err := hex.DecodeString(key)
- if err != nil {
- return DERPMesh{}, fmt.Errorf("%w: %v", ErrInvalidMeshKey, err)
- }
- return DERPMeshFromRaw32(mem.B(decoded)), nil
- }
- // IsZero reports whether k is the zero value.
- func (k DERPMesh) IsZero() bool {
- return k.Equal(DERPMesh{})
- }
- // Equal reports whether k and other are the same key.
- func (k DERPMesh) Equal(other DERPMesh) bool {
- // Compare mesh keys in constant time to prevent timing attacks.
- // Since mesh keys are a fixed length, we don’t need to be concerned
- // about timing attacks on client mesh keys that are the wrong length.
- // See https://github.com/tailscale/corp/issues/28720
- return subtle.ConstantTimeCompare(k.k[:], other.k[:]) == 1
- }
- // String returns k as a hex-encoded 64-digit number.
- func (k DERPMesh) String() string {
- return hex.EncodeToString(k.k[:])
- }
|