m/n/core: save NUK and Node Credentials

This makes the node save its Node Unlock Key and Node Credentials after
registering.

Change-Id: Ie16e8fd149745e22a2c02e56ccf3c2d87d052079
Reviewed-on: https://review.monogon.dev/c/monogon/+/537
Reviewed-by: Sergiusz Bazanski <serge@monogon.tech>
diff --git a/metropolis/node/core/cluster/cluster_register.go b/metropolis/node/core/cluster/cluster_register.go
index 76458b8..7376b84 100644
--- a/metropolis/node/core/cluster/cluster_register.go
+++ b/metropolis/node/core/cluster/cluster_register.go
@@ -78,8 +78,10 @@
 		supervisor.Logger(ctx).Infof("    Node ID: %s, Addresses: %s", id, strings.Join(addresses, ","))
 	}
 
-	// Mount new storage with generated CUK, and save LUK into sealed config proto.
-	state.configuration = &ppb.SealedConfiguration{}
+	// Mount new storage with generated CUK, MountNew will save NUK into sc, to be
+	// saved into the ESP after successful registration.
+	var sc ppb.SealedConfiguration
+	state.configuration = &sc
 	supervisor.Logger(ctx).Infof("Registering: mounting new storage...")
 	cuk, err := m.storageRoot.Data.MountNew(state.configuration)
 	if err != nil {
@@ -139,9 +141,16 @@
 	if err != nil {
 		return fmt.Errorf("NewNodeCredentials failed after receiving certificate from cluster: %w", err)
 	}
-
 	m.roleServer.ProvideRegisterData(*creds, register.ClusterDirectory)
-	// TODO(q3k): save keypair to localstorage
+
+	// Save NUK
+	if err = m.storageRoot.ESP.Metropolis.SealedConfiguration.SealSecureBoot(&sc); err != nil {
+		return fmt.Errorf("failed to seal and write configuration: %w", err)
+	}
+	// Save Node Credentials
+	if err = m.storageRoot.Data.Node.Credentials.WriteAll(certBytes, priv, caCertBytes); err != nil {
+		return fmt.Errorf("while writing node credentials: %w", err)
+	}
 
 	supervisor.Signal(ctx, supervisor.SignalHealthy)
 	supervisor.Signal(ctx, supervisor.SignalDone)
diff --git a/metropolis/node/core/localstorage/storage_esp.go b/metropolis/node/core/localstorage/storage_esp.go
index 329b357..e3d0d2a 100644
--- a/metropolis/node/core/localstorage/storage_esp.go
+++ b/metropolis/node/core/localstorage/storage_esp.go
@@ -86,6 +86,28 @@
 	return &config, nil
 }
 
+func (e *ESPSealedConfiguration) SealSecureBoot(c *ppb.SealedConfiguration) error {
+	bytes, err := proto.Marshal(c)
+	if err != nil {
+		return fmt.Errorf("while marshaling: %w", err)
+	}
+
+	// Use Secure Boot PCRs to seal the configuration.
+	// See: TCG PC Client Platform Firmware Profile Specification v1.05,
+	//      table 3.3.4.1
+	// See: https://trustedcomputinggroup.org/wp-content/uploads/
+	//      TCG_PCClient_PFP_r1p05_v22_02dec2020.pdf
+	bytes, err = tpm.Seal(bytes, tpm.SecureBootPCRs)
+	if err != nil {
+		return fmt.Errorf("while using tpm: %w", err)
+	}
+
+	if err := e.Write(bytes, 0644); err != nil {
+		return fmt.Errorf("while writing: %w", err)
+	}
+	return nil
+}
+
 func (e *ESPSealedConfiguration) Unseal() (*ppb.SealedConfiguration, error) {
 	bytes, err := e.Read()
 	if err != nil {