types/key: Update HardwareAttestationPublic representation (#17233)

Sidestep cmd/viewer incompatibility hiccups with
HardwareAttestationPublic type due to its *ecdsa.PublicKey inner member
by serializing the key to a byte slice instead.

Updates tailscale/corp#31269

Signed-off-by: Patrick O'Doherty <patrick@tailscale.com>
bradfitz/rm_usermetrics_66KB
Patrick O'Doherty 3 months ago committed by GitHub
parent 21dc5f4e21
commit db02a46645
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -7,6 +7,7 @@ import (
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/subtle"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@ -18,11 +19,13 @@ var ErrUnsupported = fmt.Errorf("key type not supported on this platform")
const hardwareAttestPublicHexPrefix = "hwattestpub:" const hardwareAttestPublicHexPrefix = "hwattestpub:"
const pubkeyLength = 65 // uncompressed P-256
// HardwareAttestationKey describes a hardware-backed key that is used to // HardwareAttestationKey describes a hardware-backed key that is used to
// identify a node. Implementation details will // identify a node. Implementation details will
// vary based on the platform in use (SecureEnclave for Apple, TPM for // vary based on the platform in use (SecureEnclave for Apple, TPM for
// Windows/Linux, Android Hardware-backed Keystore). // Windows/Linux, Android Hardware-backed Keystore).
// This key can only be marshalled and unmarshalled on the same machine. // This key can only be marshalled and unmarshaled on the same machine.
type HardwareAttestationKey interface { type HardwareAttestationKey interface {
crypto.Signer crypto.Signer
json.Marshaler json.Marshaler
@ -43,25 +46,41 @@ func HardwareAttestationPublicFromPlatformKey(k HardwareAttestationKey) Hardware
if !ok { if !ok {
panic("hardware attestation key is not ECDSA") panic("hardware attestation key is not ECDSA")
} }
return HardwareAttestationPublic{k: ecdsaPub} bytes, err := ecdsaPub.Bytes()
if err != nil {
panic(err)
}
if len(bytes) != pubkeyLength {
panic("hardware attestation key is not uncompressed ECDSA P-256")
}
var ecdsaPubArr [pubkeyLength]byte
copy(ecdsaPubArr[:], bytes)
return HardwareAttestationPublic{k: ecdsaPubArr}
} }
// HardwareAttestationPublic is the public key counterpart to // HardwareAttestationPublic is the public key counterpart to
// HardwareAttestationKey. // HardwareAttestationKey.
type HardwareAttestationPublic struct { type HardwareAttestationPublic struct {
k *ecdsa.PublicKey k [pubkeyLength]byte
} }
func (k HardwareAttestationPublic) Equal(o HardwareAttestationPublic) bool { func (k *HardwareAttestationPublic) Clone() *HardwareAttestationPublic {
if k.k == nil || o.k == nil { if k == nil {
return k.k == o.k return nil
} }
return k.k.X.Cmp(o.k.X) == 0 && k.k.Y.Cmp(o.k.Y) == 0 && k.k.Curve == o.k.Curve var out HardwareAttestationPublic
copy(out.k[:], k.k[:])
return &out
}
func (k HardwareAttestationPublic) Equal(o HardwareAttestationPublic) bool {
return subtle.ConstantTimeCompare(k.k[:], o.k[:]) == 1
} }
// IsZero reports whether k is the zero value. // IsZero reports whether k is the zero value.
func (k HardwareAttestationPublic) IsZero() bool { func (k HardwareAttestationPublic) IsZero() bool {
return k.k == nil var zero [pubkeyLength]byte
return k.k == zero
} }
// String returns the hex-encoded public key with a type prefix. // String returns the hex-encoded public key with a type prefix.
@ -75,7 +94,7 @@ func (k HardwareAttestationPublic) String() string {
// MarshalText implements encoding.TextMarshaler. // MarshalText implements encoding.TextMarshaler.
func (k HardwareAttestationPublic) MarshalText() ([]byte, error) { func (k HardwareAttestationPublic) MarshalText() ([]byte, error) {
if k.k == nil { if k.IsZero() {
return nil, nil return nil, nil
} }
return k.AppendText(nil) return k.AppendText(nil)
@ -89,30 +108,30 @@ func (k *HardwareAttestationPublic) UnmarshalText(b []byte) error {
return nil return nil
} }
kb := make([]byte, 65) kb := make([]byte, pubkeyLength)
if err := parseHex(kb, mem.B(b), mem.S(hardwareAttestPublicHexPrefix)); err != nil { if err := parseHex(kb, mem.B(b), mem.S(hardwareAttestPublicHexPrefix)); err != nil {
return err return err
} }
pk, err := ecdsa.ParseUncompressedPublicKey(elliptic.P256(), kb) _, err := ecdsa.ParseUncompressedPublicKey(elliptic.P256(), kb)
if err != nil { if err != nil {
return err return err
} }
k.k = pk copy(k.k[:], kb)
return nil return nil
} }
func (k HardwareAttestationPublic) AppendText(dst []byte) ([]byte, error) { func (k HardwareAttestationPublic) AppendText(dst []byte) ([]byte, error) {
b, err := k.k.Bytes() return appendHexKey(dst, hardwareAttestPublicHexPrefix, k.k[:]), nil
if err != nil {
return nil, err
}
return appendHexKey(dst, hardwareAttestPublicHexPrefix, b), nil
} }
// Verifier returns the ECDSA public key for verifying signatures made by k. // Verifier returns the ECDSA public key for verifying signatures made by k.
func (k HardwareAttestationPublic) Verifier() *ecdsa.PublicKey { func (k HardwareAttestationPublic) Verifier() *ecdsa.PublicKey {
return k.k pk, err := ecdsa.ParseUncompressedPublicKey(elliptic.P256(), k.k[:])
if err != nil {
panic(err)
}
return pk
} }
// emptyHardwareAttestationKey is a function that returns an empty // emptyHardwareAttestationKey is a function that returns an empty

Loading…
Cancel
Save