blob: 193cca32da47965f3727e2c2caf2c12bb94dc50a [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package hwsec
import (
"context"
"time"
"github.com/golang/protobuf/proto"
apb "chromiumos/system_api/attestation_proto"
"chromiumos/tast/common/hwsec"
"chromiumos/tast/errors"
hwseclocal "chromiumos/tast/local/hwsec"
"chromiumos/tast/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: Attestation,
Desc: "Verifies attestation-related functionality",
Attr: []string{"group:mainline", "informational"},
Contacts: []string{"cylai@chromium.org", "cros-hwsec@google.com"},
SoftwareDeps: []string{"tpm", "endorsement"},
Timeout: 4 * time.Minute,
})
}
// Attestation runs through the attestation flow, including enrollment, cert, sign challenge.
// Also, it verifies the the key access functionality.
func Attestation(ctx context.Context, s *testing.State) {
r := hwseclocal.NewCmdRunner()
helper, err := hwseclocal.NewFullHelper(ctx, r)
if err != nil {
s.Fatal("Helper creation error: ", err)
}
attestation := helper.AttestationClient()
cryptohome := helper.CryptohomeClient()
mountInfo := hwsec.NewCryptohomeMountInfo(r, cryptohome)
if err := helper.EnsureTPMIsReady(ctx, hwsec.DefaultTakingOwnershipTimeout); err != nil {
s.Fatal("Failed to ensure tpm readiness: ", err)
}
s.Log("TPM is ensured to be ready")
if err := helper.EnsureIsPreparedForEnrollment(ctx, hwsec.DefaultPreparationForEnrolmentTimeout); err != nil {
s.Fatal("Failed to prepare for enrollment: ", err)
}
at := hwsec.NewAttestationTest(attestation, hwsec.DefaultPCA)
ac, err := hwseclocal.NewAttestationDBus(ctx)
if err != nil {
s.Fatal("Failed to create attestation client: ", err)
}
enrollReply, err := ac.Enroll(ctx, &apb.EnrollRequest{Forced: proto.Bool(true)})
if err != nil {
s.Fatal("Failed to call Enroll D-Bus API: ", err)
}
if *enrollReply.Status != apb.AttestationStatus_STATUS_SUCCESS {
s.Fatal("Faild to enroll: ", enrollReply.Status.String())
}
const username = "test@crashwsec.bigr.name"
s.Log("Resetting vault in case the cryptohome status is contaminated")
// Okay to call it even if the vault doesn't exist.
if err := mountInfo.CleanUpMount(ctx, username); err != nil {
s.Fatal("Failed to cleanup: ", err)
}
if err := cryptohome.MountVault(ctx, username, "testpass", "fake_label", true /* create */, hwsec.NewVaultConfig()); err != nil {
s.Fatal("Failed to create user vault: ", err)
}
defer func(ctx context.Context) {
s.Log("Resetting vault after use")
if err := mountInfo.CleanUpMount(ctx, username); err != nil {
s.Error("Failed to cleanup: ", err)
}
}(ctx)
for _, param := range []struct {
name string
username string
}{
{
name: "system_cert",
username: "",
},
{
name: "user_cert",
username: username,
},
} {
s.Run(ctx, param.name, func(ctx context.Context, s *testing.State) {
username := param.username
certReply, err := ac.GetCertificate(ctx, &apb.GetCertificateRequest{Username: proto.String(username), KeyLabel: proto.String(hwsec.DefaultCertLabel)})
if err != nil {
s.Fatal("Failed to call D-Bus API to get certificate: ", err)
}
if *certReply.Status != apb.AttestationStatus_STATUS_SUCCESS {
s.Fatal("Faild to get certificate: ", certReply.Status.String())
}
// TODO(b/165426637): Enable it after we inject the fake device policy with customer ID.
if username != "" {
if err := at.SignEnterpriseChallenge(ctx, username, hwsec.DefaultCertLabel); err != nil {
s.Fatal("Failed to sign enterprise challenge: ", err)
}
}
if err := at.SignSimpleChallenge(ctx, username, hwsec.DefaultCertLabel); err != nil {
s.Fatal("Failed to sign simple challenge: ", err)
}
s.Log("Start key payload closed-loop testing")
s.Log("Setting key payload")
expectedPayload := hwsec.DefaultKeyPayload
_, err = attestation.SetKeyPayload(ctx, username, hwsec.DefaultCertLabel, expectedPayload)
if err != nil {
s.Fatal("Failed to set key payload: ", err)
}
s.Log("Getting key payload")
resultPayload, err := attestation.GetKeyPayload(ctx, username, hwsec.DefaultCertLabel)
if err != nil {
s.Fatal("Failed to get key payload: ", err)
}
if resultPayload != expectedPayload {
s.Fatalf("Inconsistent paylaod -- result: %s / expected: %s", resultPayload, expectedPayload)
}
s.Log("Start key payload closed-loop done")
s.Log("Start verifying key registration")
isSuccessful, err := attestation.RegisterKeyWithChapsToken(ctx, username, hwsec.DefaultCertLabel)
if err != nil {
s.Fatal("Failed to register key with chaps token due to error: ", err)
}
if !isSuccessful {
s.Fatal("Failed to register key with chaps token")
}
// Now the key has been registered and remove from the key store
_, err = attestation.GetPublicKey(ctx, username, hwsec.DefaultCertLabel)
if err == nil {
s.Fatal("unsidered successful operation -- key should be removed after registration")
}
// Well, actually we need more on system key so the key registration is validated.
s.Log("Key registration verified")
s.Log("Verifying deletion of keys by prefix")
for _, label := range []string{"label1", "label2", "label3"} {
certReply, err := ac.GetCertificate(ctx, &apb.GetCertificateRequest{Username: proto.String(username), KeyLabel: proto.String(label)})
if err != nil {
s.Fatalf("Failed to create certificate request for label %q: %v", label, err)
}
if *certReply.Status != apb.AttestationStatus_STATUS_SUCCESS {
s.Fatalf("Faild to get certificate for label %q: %v", label, certReply.Status.String())
}
_, err = attestation.GetPublicKey(ctx, username, label)
if err != nil {
s.Fatalf("Failed to get public key for label %q: %v", label, err)
}
}
s.Log("Deleting keys just created")
if err := attestation.DeleteKeys(ctx, username, "label"); err != nil {
s.Fatal("Failed to remove the key group: ", err)
}
for _, label := range []string{"label1", "label2", "label3"} {
_, err := attestation.GetPublicKey(ctx, username, label)
var ae *hwsec.AttestationError
if err == nil {
s.Fatalf("Key with label %q still found", label)
}
if !errors.As(err, &ae) {
s.Fatalf("Failed to get public key with label %q: %v", label, err)
}
if ae.AttestationStatus != apb.AttestationStatus_STATUS_INVALID_PARAMETER {
s.Fatalf("Unexpected error status: got %s; want STATUS_INVALID_PARAMETER", ae.AttestationStatus.String())
}
}
s.Log("Deletion of keys by prefix verified")
})
}
}