Attestation & Identity & Global Unlock & Enrolment

This changes the node startup sequence significantly. Now the following three startup procedures replace the old setup/join mechanic:
* If no enrolment config is present, automatically bootstrap a new cluster and become master for it.
* If an enrolment config with an enrolment token is present, register with the NodeManagementService.
* If an enrolment config without an enrolment token is present, attempt a normal cluster unlock.

It also completely revamps the GRPC management services:
* NodeManagementService is a master-only service that deals with other nodes and has a cluster-wide identity
* NodeService is only available in unlocked state and keyed with the node identity
* ClusterManagement is now a master-only service that's been spun out of the main NMS since they have very different authentication models and also deals with EnrolmentConfigs

The TPM support library has also been extended by:
* Lots of integrity attestation and verification functions
* Built-in AK management
* Some advanced policy-based authentication stuff

Also contains various enhancements to the network service to make everything work in a proper multi-node environment.

Lots of old code has been thrown out.

Test Plan: Passed a full manual test of all three startup modes (bootstrap, enrolment and normal unlock) including automated EnrolmentConfig generation and consumption in a dual-node configuration on swtpm / OVMF.

Bug: T499

X-Origin-Diff: phab/D291
GitOrigin-RevId: d53755c828218b1df83a1d7ad252c7b3231abca8
diff --git a/core/pkg/tpm/credactivation_compat.go b/core/pkg/tpm/credactivation_compat.go
new file mode 100644
index 0000000..0a848d2
--- /dev/null
+++ b/core/pkg/tpm/credactivation_compat.go
@@ -0,0 +1,121 @@
+// Copyright 2020 The Monogon Project Authors.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tpm
+
+// This file is adapted from github.com/google/go-tpm/tpm2/credactivation which outputs broken
+// challenges for unknown reasons. They use u16 length-delimited outputs for the challenge blobs
+// which is incorrect.
+// TODO(lorenz): I'll eventually deal with this upstream, but for now just fix it here (it's not that)
+// much code after all.
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/hmac"
+	"crypto/rsa"
+	"fmt"
+	"io"
+
+	"github.com/google/go-tpm/tpm2"
+	"github.com/google/go-tpm/tpmutil"
+)
+
+const (
+	labelIdentity  = "IDENTITY"
+	labelStorage   = "STORAGE"
+	labelIntegrity = "INTEGRITY"
+)
+
+func generateRSA(aik *tpm2.HashValue, pub *rsa.PublicKey, symBlockSize int, secret []byte, rnd io.Reader) ([]byte, []byte, error) {
+	newAIKHash, err := aik.Alg.HashConstructor()
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// The seed length should match the keysize used by the EKs symmetric cipher.
+	// For typical RSA EKs, this will be 128 bits (16 bytes).
+	// Spec: TCG 2.0 EK Credential Profile revision 14, section 2.1.5.1.
+	seed := make([]byte, symBlockSize)
+	if _, err := io.ReadFull(rnd, seed); err != nil {
+		return nil, nil, fmt.Errorf("generating seed: %v", err)
+	}
+
+	// Encrypt the seed value using the provided public key.
+	// See annex B, section 10.4 of the TPM specification revision 2 part 1.
+	label := append([]byte(labelIdentity), 0)
+	encSecret, err := rsa.EncryptOAEP(newAIKHash(), rnd, pub, seed, label)
+	if err != nil {
+		return nil, nil, fmt.Errorf("generating encrypted seed: %v", err)
+	}
+
+	// Generate the encrypted credential by convolving the seed with the digest of
+	// the AIK, and using the result as the key to encrypt the secret.
+	// See section 24.4 of TPM 2.0 specification, part 1.
+	aikNameEncoded, err := aik.Encode()
+	if err != nil {
+		return nil, nil, fmt.Errorf("encoding aikName: %v", err)
+	}
+	symmetricKey, err := tpm2.KDFa(aik.Alg, seed, labelStorage, aikNameEncoded, nil, len(seed)*8)
+	if err != nil {
+		return nil, nil, fmt.Errorf("generating symmetric key: %v", err)
+	}
+	c, err := aes.NewCipher(symmetricKey)
+	if err != nil {
+		return nil, nil, fmt.Errorf("symmetric cipher setup: %v", err)
+	}
+	cv, err := tpmutil.Pack(tpmutil.U16Bytes(secret))
+	if err != nil {
+		return nil, nil, fmt.Errorf("generating cv (TPM2B_Digest): %v", err)
+	}
+
+	// IV is all null bytes. encIdentity represents the encrypted credential.
+	encIdentity := make([]byte, len(cv))
+	cipher.NewCFBEncrypter(c, make([]byte, len(symmetricKey))).XORKeyStream(encIdentity, cv)
+
+	// Generate the integrity HMAC, which is used to protect the integrity of the
+	// encrypted structure.
+	// See section 24.5 of the TPM specification revision 2 part 1.
+	macKey, err := tpm2.KDFa(aik.Alg, seed, labelIntegrity, nil, nil, newAIKHash().Size()*8)
+	if err != nil {
+		return nil, nil, fmt.Errorf("generating HMAC key: %v", err)
+	}
+
+	mac := hmac.New(newAIKHash, macKey)
+	mac.Write(encIdentity)
+	mac.Write(aikNameEncoded)
+	integrityHMAC := mac.Sum(nil)
+
+	idObject := &tpm2.IDObject{
+		IntegrityHMAC: integrityHMAC,
+		EncIdentity:   encIdentity,
+	}
+	id, err := tpmutil.Pack(idObject)
+	if err != nil {
+		return nil, nil, fmt.Errorf("encoding IDObject: %v", err)
+	}
+
+	packedID, err := tpmutil.Pack(id)
+	if err != nil {
+		return nil, nil, fmt.Errorf("packing id: %v", err)
+	}
+	packedEncSecret, err := tpmutil.Pack(encSecret)
+	if err != nil {
+		return nil, nil, fmt.Errorf("packing encSecret: %v", err)
+	}
+
+	return packedID, packedEncSecret, nil
+}