tailscale/types/key/chal.go
Brad Fitzpatrick 910db02652 client/tailscale, tsnet, ipn/ipnlocal: prove nodekey ownership over noise
Fixes #5972

Change-Id: Ic33a93d3613ac5dbf172d6a8a459ca06a7f9e547
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-11-02 09:22:26 -07:00

88 lines
2.6 KiB
Go

// Copyright (c) 2022 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 key
import (
"errors"
"go4.org/mem"
"tailscale.com/types/structs"
)
const (
// chalPublicHexPrefix is the prefix used to identify a
// hex-encoded challenge public key.
//
// This prefix is used in the control protocol, so cannot be
// changed.
chalPublicHexPrefix = "chalpub:"
)
// ChallengePrivate is a challenge key, used to test whether clients control a
// key they want to prove ownership of.
//
// A ChallengePrivate is ephemeral and not serialized to the disk or network.
type ChallengePrivate struct {
_ structs.Incomparable // because == isn't constant-time
k [32]byte
}
// NewChallenge creates and returns a new node private key.
func NewChallenge() ChallengePrivate {
return ChallengePrivate(NewNode())
}
// Public returns the ChallengePublic for k.
// Panics if ChallengePublic is zero.
func (k ChallengePrivate) Public() ChallengePublic {
pub := NodePrivate(k).Public()
return ChallengePublic(pub)
}
// MarshalText implements encoding.TextMarshaler, but by returning an error.
// It shouldn't need to be marshalled anywhere.
func (k ChallengePrivate) MarshalText() ([]byte, error) {
return nil, errors.New("refusing to marshal")
}
// SealToChallenge is like SealTo, but for a ChallengePublic.
func (k NodePrivate) SealToChallenge(p ChallengePublic, cleartext []byte) (ciphertext []byte) {
return k.SealTo(NodePublic(p), cleartext)
}
// OpenFrom opens the NaCl box ciphertext, which must be a value
// created by NodePrivate.SealToChallenge, and returns the inner cleartext if
// ciphertext is a valid box from p to k.
func (k ChallengePrivate) OpenFrom(p NodePublic, ciphertext []byte) (cleartext []byte, ok bool) {
return NodePrivate(k).OpenFrom(p, ciphertext)
}
// ChallengePublic is the public portion of a ChallengePrivate.
type ChallengePublic struct {
k [32]byte
}
// String returns the output of MarshalText as a string.
func (k ChallengePublic) String() string {
bs, err := k.MarshalText()
if err != nil {
panic(err)
}
return string(bs)
}
// MarshalText implements encoding.TextMarshaler.
func (k ChallengePublic) MarshalText() ([]byte, error) {
return toHex(k.k[:], chalPublicHexPrefix), nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (k *ChallengePublic) UnmarshalText(b []byte) error {
return parseHex(k.k[:], mem.B(b), mem.S(chalPublicHexPrefix))
}
// IsZero reports whether k is the zero value.
func (k ChallengePublic) IsZero() bool { return k == ChallengePublic{} }