blob: 0c9b78483f325caa20687c5f3ac7c3a88347dafb [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"
"path/filepath"
"time"
"chromiumos/tast/common/hwsec"
"chromiumos/tast/common/perf"
"chromiumos/tast/common/pkcs11"
"chromiumos/tast/common/pkcs11/pkcs11test"
"chromiumos/tast/errors"
"chromiumos/tast/local/bundles/cros/hwsec/util"
hwseclocal "chromiumos/tast/local/hwsec"
"chromiumos/tast/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: ChapsECPerf,
Desc: "Chaps performance test that includes key import, key sign operation performance measure",
Contacts: []string{
"zuan@chromium.org",
"cros-hwsec@chromium.org",
},
Attr: []string{"group:crosbolt", "crosbolt_perbuild"},
SoftwareDeps: []string{"chrome", "tpm2"},
Timeout: 4 * time.Minute,
})
}
// prepareECKeyWithOpenSSL generate an EC key pair in DER format and store in on the disk with OpenSSL.
func prepareECKeyWithOpenSSL(ctx context.Context, scratchpadPath string, runner hwsec.CmdRunner) (privKeyPath string, retErr error) {
// Note that we are using openssl command here for reproducibility during debugging.
privKeyPemPath := filepath.Join(scratchpadPath, "testkey2-priv.pem")
privKeyPath = filepath.Join(scratchpadPath, "testkey2-priv.der")
if _, err := runner.Run(ctx, "openssl", "ecparam", "-name", "prime256v1", "-genkey", "-noout", "-out", privKeyPemPath); err != nil {
return "", errors.Wrap(err, "failed to create key with openssl")
}
// Convert the private key to DER format.
if _, err := runner.Run(ctx, "openssl", "ec", "-inform", "pem", "-outform", "der", "-in", privKeyPemPath, "-out", privKeyPath); err != nil {
return "", errors.Wrap(err, "failed to convert private key to DER format with OpenSSL")
}
return privKeyPath, nil
}
func ChapsECPerf(ctx context.Context, s *testing.State) {
r := hwseclocal.NewCmdRunner()
helper, err := hwseclocal.NewHelper(r)
if err != nil {
s.Fatal("Failed to create hwsec helper: ", err)
}
utility := helper.CryptohomeClient()
pkcs11Util, err := pkcs11.NewChaps(ctx, r, utility)
if err != nil {
s.Fatal("Failed to create PKCS#11 Utility: ", err)
}
const scratchpadPath = "/tmp/ChapsECPerf"
// Remove all keys/certs before the test as well.
if err := pkcs11test.CleanupScratchpad(ctx, r, scratchpadPath); err != nil {
s.Fatal("Failed to clean scratchpad before the start of test: ", err)
}
// Remove the user vault, if any is remaining from another test.
util.CleanupUserMount(ctx, utility)
// Prepare the scratchpad.
f1, f2, err := pkcs11test.PrepareScratchpadAndTestFiles(ctx, r, scratchpadPath)
if err != nil {
s.Fatal("Failed to initialize the scratchpad space: ", err)
}
// Remove all keys/certs, if any at the end. i.e. Cleanup after ourselves.
defer pkcs11test.CleanupScratchpad(ctx, r, scratchpadPath)
// Create a user vault for the slot/token.
// Mount the vault of the user, so that we can test user keys as well.
if err := utility.MountVault(ctx, util.FirstUsername, util.FirstPassword, util.PasswordLabel, true, hwsec.NewVaultConfig()); err != nil {
s.Fatal("Failed to mount user vault: ", err)
}
defer func() {
if err := util.CleanupUserMount(ctx, utility); err != nil {
s.Error("Cleanup failed: ", err)
}
}()
// Generate one single EC private key.
privECKeyPath, err := prepareECKeyWithOpenSSL(ctx, scratchpadPath, r)
if err != nil {
s.Fatal("Failed to create private key: ", err)
}
if err := utility.WaitForUserToken(ctx, util.FirstUsername); err != nil {
s.Fatal("Failed to wait for user token: ", err)
}
// Get the slot ID for the user vault.
slot, err := utility.GetTokenForUser(ctx, util.FirstUsername)
if err != nil {
s.Fatal("Failed to get user token slot ID: ", err)
}
// Time the import operation for hw-backed keys.
importedHwKeys, importHwElapsed, err := util.ImportKeysAndMeasure(ctx, pkcs11Util, privECKeyPath, slot, "11", util.ImportHWTimes, false)
if err != nil {
s.Fatal("Failed to import hardware backed keys: ", err)
}
// Note: We don't need to cleanup the imported keys here because it goes with the vault cleanup.
// Time the import operation for sw-backed keys.
importedSwKeys, importSwElapsed, err := util.ImportKeysAndMeasure(ctx, pkcs11Util, privECKeyPath, slot, "22", util.ImportSWTimes, true)
if err != nil {
s.Fatal("Failed to import software backed keys: ", err)
}
// Time the signing operation for hw-backed keys.
signHwElapsed, err := util.SignAndMeasure(ctx, pkcs11Util, importedHwKeys[0], &pkcs11.ECDSASHA1, util.SignHWTimes, f1, f2)
if err != nil {
s.Fatal("Failed to sign with hardware backed keys: ", err)
}
// Time the signing operation for sw-backed keys.
signSwElapsed, err := util.SignAndMeasure(ctx, pkcs11Util, importedSwKeys[0], &pkcs11.ECDSASHA1, util.SignSWTimes, f1, f2)
if err != nil {
s.Fatal("Failed to sign with software backed keys: ", err)
}
// Record the perf measurements.
value := perf.NewValues()
value.Set(perf.Metric{
Name: "chaps_import_time",
Variant: "hwbacked_ec",
Unit: "s",
Direction: perf.SmallerIsBetter,
Multiple: false,
}, importHwElapsed.Seconds()/float64(util.ImportHWTimes))
value.Set(perf.Metric{
Name: "chaps_import_time",
Variant: "swbacked_ec",
Unit: "s",
Direction: perf.SmallerIsBetter,
Multiple: false,
}, importSwElapsed.Seconds()/float64(util.ImportSWTimes))
value.Set(perf.Metric{
Name: "chaps_sign_time",
Variant: "hwbacked_ec_sha1",
Unit: "s",
Direction: perf.SmallerIsBetter,
Multiple: false,
}, signHwElapsed.Seconds()/float64(util.SignHWTimes))
value.Set(perf.Metric{
Name: "chaps_sign_time",
Variant: "swbacked_ec_sha1",
Unit: "s",
Direction: perf.SmallerIsBetter,
Multiple: false,
}, signSwElapsed.Seconds()/float64(util.SignSWTimes))
value.Save(s.OutDir())
}