Skip to content

Commit db7dcd5

Browse files
authored
Revert "control/controlclient: back out HW key attestation (tailscale#17664)" (tailscale#17732)
This reverts commit a760cbe. Signed-off-by: Andrew Lytvynov <[email protected]>
1 parent 4c85607 commit db7dcd5

File tree

10 files changed

+110
-8
lines changed

10 files changed

+110
-8
lines changed

control/controlclient/direct.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"bytes"
88
"cmp"
99
"context"
10+
"crypto"
11+
"crypto/sha256"
1012
"encoding/binary"
1113
"encoding/json"
1214
"errors"
@@ -946,6 +948,26 @@ func (c *Direct) sendMapRequest(ctx context.Context, isStreaming bool, nu Netmap
946948
ConnectionHandleForTest: connectionHandleForTest,
947949
}
948950

951+
// If we have a hardware attestation key, sign the node key with it and send
952+
// the key & signature in the map request.
953+
if buildfeatures.HasTPM {
954+
if k := persist.AsStruct().AttestationKey; k != nil && !k.IsZero() {
955+
hwPub := key.HardwareAttestationPublicFromPlatformKey(k)
956+
request.HardwareAttestationKey = hwPub
957+
958+
t := c.clock.Now()
959+
msg := fmt.Sprintf("%d|%s", t.Unix(), nodeKey.String())
960+
digest := sha256.Sum256([]byte(msg))
961+
sig, err := k.Sign(nil, digest[:], crypto.SHA256)
962+
if err != nil {
963+
c.logf("failed to sign node key with hardware attestation key: %v", err)
964+
} else {
965+
request.HardwareAttestationKeySignature = sig
966+
request.HardwareAttestationKeySignatureTimestamp = t
967+
}
968+
}
969+
}
970+
949971
var extraDebugFlags []string
950972
if buildfeatures.HasAdvertiseRoutes && hi != nil && c.netMon != nil && !c.skipIPForwardingCheck &&
951973
ipForwardingBroken(hi.RoutableIPs, c.netMon.InterfaceState()) {

ipn/ipnlocal/hwattest.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) Tailscale Inc & AUTHORS
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
4+
//go:build !ts_omit_tpm
5+
6+
package ipnlocal
7+
8+
import (
9+
"errors"
10+
11+
"tailscale.com/feature"
12+
"tailscale.com/types/key"
13+
"tailscale.com/types/logger"
14+
"tailscale.com/types/persist"
15+
)
16+
17+
func init() {
18+
feature.HookGenerateAttestationKeyIfEmpty.Set(generateAttestationKeyIfEmpty)
19+
}
20+
21+
// generateAttestationKeyIfEmpty generates a new hardware attestation key if
22+
// none exists. It returns true if a new key was generated and stored in
23+
// p.AttestationKey.
24+
func generateAttestationKeyIfEmpty(p *persist.Persist, logf logger.Logf) (bool, error) {
25+
// attempt to generate a new hardware attestation key if none exists
26+
var ak key.HardwareAttestationKey
27+
if p != nil {
28+
ak = p.AttestationKey
29+
}
30+
31+
if ak == nil || ak.IsZero() {
32+
var err error
33+
ak, err = key.NewHardwareAttestationKey()
34+
if err != nil {
35+
if !errors.Is(err, key.ErrUnsupported) {
36+
logf("failed to create hardware attestation key: %v", err)
37+
}
38+
} else if ak != nil {
39+
logf("using new hardware attestation key: %v", ak.Public())
40+
if p == nil {
41+
p = &persist.Persist{}
42+
}
43+
p.AttestationKey = ak
44+
return true, nil
45+
}
46+
}
47+
return false, nil
48+
}

ipn/ipnlocal/local.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,7 @@ func stripKeysFromPrefs(p ipn.PrefsView) ipn.PrefsView {
11901190
p2.Persist.PrivateNodeKey = key.NodePrivate{}
11911191
p2.Persist.OldPrivateNodeKey = key.NodePrivate{}
11921192
p2.Persist.NetworkLockKey = key.NLPrivate{}
1193+
p2.Persist.AttestationKey = nil
11931194
return p2.View()
11941195
}
11951196

ipn/ipnlocal/profiles.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ import (
1919
"tailscale.com/ipn"
2020
"tailscale.com/ipn/ipnext"
2121
"tailscale.com/tailcfg"
22+
"tailscale.com/types/key"
2223
"tailscale.com/types/logger"
24+
"tailscale.com/types/persist"
2325
"tailscale.com/util/clientmetric"
2426
"tailscale.com/util/eventbus"
2527
)
@@ -654,6 +656,14 @@ func (pm *profileManager) loadSavedPrefs(k ipn.StateKey) (ipn.PrefsView, error)
654656
return ipn.PrefsView{}, err
655657
}
656658
savedPrefs := ipn.NewPrefs()
659+
660+
// if supported by the platform, create an empty hardware attestation key to use when deserializing
661+
// to avoid type exceptions from json.Unmarshaling into an interface{}.
662+
hw, _ := key.NewEmptyHardwareAttestationKey()
663+
savedPrefs.Persist = &persist.Persist{
664+
AttestationKey: hw,
665+
}
666+
657667
if err := ipn.PrefsFromBytes(bs, savedPrefs); err != nil {
658668
return ipn.PrefsView{}, fmt.Errorf("parsing saved prefs: %v", err)
659669
}

ipn/ipnlocal/profiles_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ func TestProfileDupe(t *testing.T) {
151151
ID: tailcfg.UserID(user),
152152
LoginName: fmt.Sprintf("user%[email protected]", user),
153153
},
154+
AttestationKey: nil,
154155
}
155156
}
156157
user1Node1 := newPersist(1, 1)

ipn/prefs_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ func TestPrefsPretty(t *testing.T) {
501501
},
502502
},
503503
"linux",
504-
`Prefs{ra=false dns=false want=false routes=[] nf=off update=off Persist{o=, n=[B1VKl] u=""}}`,
504+
`Prefs{ra=false dns=false want=false routes=[] nf=off update=off Persist{o=, n=[B1VKl] u="" ak=-}}`,
505505
},
506506
{
507507
Prefs{

types/persist/persist.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type Persist struct {
2626
UserProfile tailcfg.UserProfile
2727
NetworkLockKey key.NLPrivate
2828
NodeID tailcfg.StableNodeID
29+
AttestationKey key.HardwareAttestationKey `json:",omitempty"`
2930

3031
// DisallowedTKAStateIDs stores the tka.State.StateID values which
3132
// this node will not operate network lock on. This is used to
@@ -84,24 +85,37 @@ func (p *Persist) Equals(p2 *Persist) bool {
8485
return false
8586
}
8687

88+
var pub, p2Pub key.HardwareAttestationPublic
89+
if p.AttestationKey != nil && !p.AttestationKey.IsZero() {
90+
pub = key.HardwareAttestationPublicFromPlatformKey(p.AttestationKey)
91+
}
92+
if p2.AttestationKey != nil && !p2.AttestationKey.IsZero() {
93+
p2Pub = key.HardwareAttestationPublicFromPlatformKey(p2.AttestationKey)
94+
}
95+
8796
return p.PrivateNodeKey.Equal(p2.PrivateNodeKey) &&
8897
p.OldPrivateNodeKey.Equal(p2.OldPrivateNodeKey) &&
8998
p.UserProfile.Equal(&p2.UserProfile) &&
9099
p.NetworkLockKey.Equal(p2.NetworkLockKey) &&
91100
p.NodeID == p2.NodeID &&
101+
pub.Equal(p2Pub) &&
92102
reflect.DeepEqual(nilIfEmpty(p.DisallowedTKAStateIDs), nilIfEmpty(p2.DisallowedTKAStateIDs))
93103
}
94104

95105
func (p *Persist) Pretty() string {
96106
var (
97107
ok, nk key.NodePublic
98108
)
109+
akString := "-"
99110
if !p.OldPrivateNodeKey.IsZero() {
100111
ok = p.OldPrivateNodeKey.Public()
101112
}
102113
if !p.PrivateNodeKey.IsZero() {
103114
nk = p.PublicNodeKey()
104115
}
105-
return fmt.Sprintf("Persist{o=%v, n=%v u=%#v}",
106-
ok.ShortString(), nk.ShortString(), p.UserProfile.LoginName)
116+
if p.AttestationKey != nil && !p.AttestationKey.IsZero() {
117+
akString = fmt.Sprintf("%v", p.AttestationKey.Public())
118+
}
119+
return fmt.Sprintf("Persist{o=%v, n=%v u=%#v ak=%s}",
120+
ok.ShortString(), nk.ShortString(), p.UserProfile.LoginName, akString)
107121
}

types/persist/persist_clone.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

types/persist/persist_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func fieldsOf(t reflect.Type) (fields []string) {
2121
}
2222

2323
func TestPersistEqual(t *testing.T) {
24-
persistHandles := []string{"PrivateNodeKey", "OldPrivateNodeKey", "UserProfile", "NetworkLockKey", "NodeID", "DisallowedTKAStateIDs"}
24+
persistHandles := []string{"PrivateNodeKey", "OldPrivateNodeKey", "UserProfile", "NetworkLockKey", "NodeID", "AttestationKey", "DisallowedTKAStateIDs"}
2525
if have := fieldsOf(reflect.TypeFor[Persist]()); !reflect.DeepEqual(have, persistHandles) {
2626
t.Errorf("Persist.Equal check might be out of sync\nfields: %q\nhandled: %q\n",
2727
have, persistHandles)

types/persist/persist_view.go

Lines changed: 6 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)