m/n/core: only warn if no TPM 2.0 has been found

Currently the TPM is basically unused. The only user is the generator of
node and cluster unlock keys, which get fed with both TPM and local entropy
which marginally increases security.
This converts a missing TPM 2.0 into a warning and falls back to generating
both of those keys purely with Linux entropy, allowing Metropolis to boot
on hardware without a TPM 2.0.

Change-Id: I910f9768ede554e5ec2c3a35079a6799d1ee9c8c
Reviewed-on: https://review.monogon.dev/c/monogon/+/514
Reviewed-by: Sergiusz Bazanski <serge@monogon.tech>
diff --git a/metropolis/node/core/localstorage/directory_data.go b/metropolis/node/core/localstorage/directory_data.go
index a98a915..5992bf0 100644
--- a/metropolis/node/core/localstorage/directory_data.go
+++ b/metropolis/node/core/localstorage/directory_data.go
@@ -17,6 +17,7 @@
 package localstorage
 
 import (
+	"crypto/rand"
 	"fmt"
 	"os/exec"
 
@@ -72,11 +73,23 @@
 	}
 	d.mounted = true
 
-	nodeUnlockKey, err := tpm.GenerateSafeKey(keySize)
+	var nodeUnlockKey, globalUnlockKey []byte
+	var err error
+	if tpm.IsInitialized() {
+		nodeUnlockKey, err = tpm.GenerateSafeKey(keySize)
+	} else {
+		nodeUnlockKey = make([]byte, keySize)
+		_, err = rand.Read(nodeUnlockKey)
+	}
 	if err != nil {
 		return nil, fmt.Errorf("generating local unlock key: %w", err)
 	}
-	globalUnlockKey, err := tpm.GenerateSafeKey(keySize)
+	if tpm.IsInitialized() {
+		globalUnlockKey, err = tpm.GenerateSafeKey(keySize)
+	} else {
+		globalUnlockKey = make([]byte, keySize)
+		_, err = rand.Read(globalUnlockKey)
+	}
 	if err != nil {
 		return nil, fmt.Errorf("generating global unlock key: %w", err)
 	}
diff --git a/metropolis/node/core/main.go b/metropolis/node/core/main.go
index afe1d7b..fef5f81 100644
--- a/metropolis/node/core/main.go
+++ b/metropolis/node/core/main.go
@@ -90,7 +90,7 @@
 	logger.Info("Starting Metropolis node init")
 
 	if err := tpm.Initialize(logger); err != nil {
-		logger.Fatalf("Failed to initialize TPM 2.0: %v", err)
+		logger.Warningf("Failed to initialize TPM 2.0, attempting fallback to untrusted: %v", err)
 	}
 
 	networkSvc := network.New()
diff --git a/metropolis/pkg/tpm/tpm.go b/metropolis/pkg/tpm/tpm.go
index f84aac6..de9b0d8 100644
--- a/metropolis/pkg/tpm/tpm.go
+++ b/metropolis/pkg/tpm/tpm.go
@@ -180,6 +180,14 @@
 	return nil
 }
 
+// IsInitialized returns true if Initialize was called an at least one
+// TPM 2.0 was found and initialized. Otherwise it returns false.
+func IsInitialized() bool {
+	lock.Lock()
+	defer lock.Unlock()
+	return !(tpm == nil)
+}
+
 // GenerateSafeKey uses two sources of randomness (Kernel & TPM) to generate
 // the key
 func GenerateSafeKey(size uint16) ([]byte, error) {