Something went wrong. Try again.
forked from tailscale/tailscale
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265// Copyright (c) Tailscale Inc & contributors// SPDX-License-Identifier: BSD-3-Clause
package key
import ( "bytes" "crypto/subtle" "encoding/hex"
"go4.org/mem" "golang.org/x/crypto/curve25519" "golang.org/x/crypto/nacl/box" "tailscale.com/types/structs")
const ( // machinePrivateHexPrefix is the prefix used to identify a // hex-encoded machine private key. // // This prefix name is a little unfortunate, in that it comes from // WireGuard's own key types. Unfortunately we're stuck with it for // machine keys, because we serialize them to disk with this prefix. machinePrivateHexPrefix = "privkey:"
// machinePublicHexPrefix is the prefix used to identify a // hex-encoded machine public key. // // This prefix is used in the control protocol, so cannot be // changed. machinePublicHexPrefix = "mkey:")
// MachinePrivate is a machine key, used for communication with the// Tailscale coordination server.type MachinePrivate struct { _ structs.Incomparable // == isn't constant-time k [32]byte}
// NewMachine creates and returns a new machine private key.func NewMachine() MachinePrivate { var ret MachinePrivate rand(ret.k[:]) clamp25519Private(ret.k[:]) return ret}
// IsZero reports whether k is the zero value.func (k MachinePrivate) IsZero() bool { return k.Equal(MachinePrivate{})}
// Equal reports whether k and other are the same key.func (k MachinePrivate) Equal(other MachinePrivate) bool { return subtle.ConstantTimeCompare(k.k[:], other.k[:]) == 1}
// Public returns the MachinePublic for k.// Panics if MachinePrivate is zero.func (k MachinePrivate) Public() MachinePublic { if k.IsZero() { panic("can't take the public key of a zero MachinePrivate") } var ret MachinePublic curve25519.ScalarBaseMult(&ret.k, &k.k) return ret}
// AppendText implements encoding.TextAppender.func (k MachinePrivate) AppendText(b []byte) ([]byte, error) { return appendHexKey(b, machinePrivateHexPrefix, k.k[:]), nil}
// MarshalText implements encoding.TextMarshaler.func (k MachinePrivate) MarshalText() ([]byte, error) { return k.AppendText(nil)}
// MarshalText implements encoding.TextUnmarshaler.func (k *MachinePrivate) UnmarshalText(b []byte) error { return parseHex(k.k[:], mem.B(b), mem.S(machinePrivateHexPrefix))}
// UntypedBytes returns k, encoded as an untyped 64-character hex// string.//// Deprecated: this function is risky to use, because it produces// serialized values that do not identify themselves as a// MachinePrivate, allowing other code to potentially parse it back in// as the wrong key type. For new uses that don't require this// specific raw byte serialization, please use// MarshalText/UnmarshalText.func (k MachinePrivate) UntypedBytes() []byte { return bytes.Clone(k.k[:])}
// SealTo wraps cleartext into a NaCl box (see// golang.org/x/crypto/nacl) to p, authenticated from k, using a// random nonce.//// The returned ciphertext is a 24-byte nonce concatenated with the// box value.func (k MachinePrivate) SealTo(p MachinePublic, cleartext []byte) (ciphertext []byte) { if k.IsZero() || p.IsZero() { panic("can't seal with zero keys") } var nonce [24]byte rand(nonce[:]) return box.Seal(nonce[:], cleartext, &nonce, &p.k, &k.k)}
// SharedKey returns the precomputed Nacl box shared key between k and p.func (k MachinePrivate) SharedKey(p MachinePublic) MachinePrecomputedSharedKey { var shared MachinePrecomputedSharedKey box.Precompute(&shared.k, &p.k, &k.k) return shared}
// MachinePrecomputedSharedKey is a precomputed shared NaCl box shared key.type MachinePrecomputedSharedKey struct { k [32]byte}
// Seal wraps cleartext into a NaCl box (see// golang.org/x/crypto/nacl) using the shared key k as generated// by MachinePrivate.SharedKey.//// The returned ciphertext is a 24-byte nonce concatenated with the// box value.func (k MachinePrecomputedSharedKey) Seal(cleartext []byte) (ciphertext []byte) { if k == (MachinePrecomputedSharedKey{}) { panic("can't seal with zero keys") } var nonce [24]byte rand(nonce[:]) return box.SealAfterPrecomputation(nonce[:], cleartext, &nonce, &k.k)}
// Open opens the NaCl box ciphertext, which must be a value created by// MachinePrecomputedSharedKey.Seal or MachinePrivate.SealTo, and returns the// inner cleartext if ciphertext is a valid box for the shared key k.func (k MachinePrecomputedSharedKey) Open(ciphertext []byte) (cleartext []byte, ok bool) { if k == (MachinePrecomputedSharedKey{}) { panic("can't open with zero keys") } if len(ciphertext) < 24 { return nil, false } var nonce [24]byte copy(nonce[:], ciphertext) return box.OpenAfterPrecomputation(nil, ciphertext[len(nonce):], &nonce, &k.k)}
// OpenFrom opens the NaCl box ciphertext, which must be a value// created by SealTo, and returns the inner cleartext if ciphertext is// a valid box from p to k.func (k MachinePrivate) OpenFrom(p MachinePublic, ciphertext []byte) (cleartext []byte, ok bool) { if k.IsZero() || p.IsZero() { panic("can't open with zero keys") } if len(ciphertext) < 24 { return nil, false } var nonce [24]byte copy(nonce[:], ciphertext) return box.Open(nil, ciphertext[len(nonce):], &nonce, &p.k, &k.k)}
// MachinePublic is the public portion of a MachinePrivate.type MachinePublic struct { k [32]byte}
// MachinePublicFromRaw32 parses a 32-byte raw value as a MachinePublic.//// This should be used only when deserializing a MachinePublic from a// binary protocol.func MachinePublicFromRaw32(raw mem.RO) MachinePublic { if raw.Len() != 32 { panic("input has wrong size") } var ret MachinePublic raw.Copy(ret.k[:]) return ret}
// ParseMachinePublicUntyped parses an untyped 64-character hex value// as a MachinePublic.//// Deprecated: this function is risky to use, because it cannot verify// that the hex string was intended to be a MachinePublic. This can// lead to accidentally decoding one type of key as another. For new// uses that don't require backwards compatibility with the untyped// string format, please use MarshalText/UnmarshalText.func ParseMachinePublicUntyped(raw mem.RO) (MachinePublic, error) { var ret MachinePublic if err := parseHex(ret.k[:], raw, mem.B(nil)); err != nil { return MachinePublic{}, err } return ret, nil}
// IsZero reports whether k is the zero value.func (k MachinePublic) IsZero() bool { return k == MachinePublic{}}
// ShortString returns the Tailscale conventional debug representation// of a public key: the first five base64 digits of the key, in square// brackets.func (k MachinePublic) ShortString() string { return debug32(k.k)}
// UntypedHexString returns k, encoded as an untyped 64-character hex// string.//// Warning: this function is risky to use, because it produces// serialized values that do not identify themselves as a// MachinePublic, allowing other code to potentially parse it back in// as the wrong key type. For new uses that don't require backwards// compatibility with the untyped string format, please use// MarshalText/UnmarshalText.func (k MachinePublic) UntypedHexString() string { return hex.EncodeToString(k.k[:])}
// UntypedBytes returns k, encoded as an untyped 64-character hex// string.//// Deprecated: this function is risky to use, because it produces// serialized values that do not identify themselves as a// MachinePublic, allowing other code to potentially parse it back in// as the wrong key type. For new uses that don't require this// specific raw byte serialization, please use// MarshalText/UnmarshalText.func (k MachinePublic) UntypedBytes() []byte { return bytes.Clone(k.k[:])}
// String returns the output of MarshalText as a string.func (k MachinePublic) String() string { bs, err := k.MarshalText() if err != nil { panic(err) } return string(bs)}
// AppendText implements encoding.TextAppender.func (k MachinePublic) AppendText(b []byte) ([]byte, error) { return appendHexKey(b, machinePublicHexPrefix, k.k[:]), nil}
// MarshalText implements encoding.TextMarshaler.func (k MachinePublic) MarshalText() ([]byte, error) { return k.AppendText(nil)}
// MarshalText implements encoding.TextUnmarshaler.func (k *MachinePublic) UnmarshalText(b []byte) error { return parseHex(k.k[:], mem.B(b), mem.S(machinePublicHexPrefix))}