tpm/tpm.go (116 lines of code) (raw):
// Copyright 2019 Google, LLC.
//
// 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 implements TPM2 API to seal and Unseal data.
package tpm
import (
"fmt"
"io"
"github.com/golang/glog"
"github.com/google/go-tpm/legacy/tpm2"
"github.com/google/go-tpm/tpmutil"
)
var (
// Default EK template defined in:
// https://trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf
// Shared SRK template based off of EK template and specified in:
// https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf
srkTemplate = tpm2.Public{
Type: tpm2.AlgRSA,
NameAlg: tpm2.AlgSHA256,
Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | tpm2.FlagUserWithAuth | tpm2.FlagRestricted | tpm2.FlagDecrypt | tpm2.FlagNoDA,
AuthPolicy: nil,
RSAParameters: &tpm2.RSAParams{
Symmetric: &tpm2.SymScheme{
Alg: tpm2.AlgAES,
KeyBits: 128,
Mode: tpm2.AlgCFB,
},
KeyBits: 2048,
ExponentRaw: 0,
ModulusRaw: make([]byte, 256),
},
}
)
// Seal seals supplied data to TPM using PCRs and password.
func Seal(tpmPath string, pcr int, srkPassword, objectPassword string, dataToSeal []byte) ([]byte, []byte, error) {
rwc, err := tpm2.OpenTPM(tpmPath)
if err != nil {
return nil, nil, fmt.Errorf("can't open TPM %q: %v", tpmPath, err)
}
defer rwc.Close()
// Create the parent key against which to seal the data
srkHandle, _, err := tpm2.CreatePrimary(rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", srkPassword, srkTemplate)
if err != nil {
return nil, nil, fmt.Errorf("can't create primary key: %v", err)
}
defer tpm2.FlushContext(rwc, srkHandle)
glog.Infof("Created parent key with handle: 0x%x\n", srkHandle)
// Note the value of the pcr against which we will seal the data
pcrVal, err := tpm2.ReadPCR(rwc, pcr, tpm2.AlgSHA256)
if err != nil {
return nil, nil, fmt.Errorf("unable to read PCR: %v", err)
}
glog.Infof("PCR %v value: 0x%x\n", pcr, pcrVal)
// Get the authorization policy that will protect the data to be sealed
sessHandle, policy, err := policyPCRPasswordSession(rwc, pcr, objectPassword)
if err != nil {
return nil, nil, fmt.Errorf("unable to get policy: %v", err)
}
if err := tpm2.FlushContext(rwc, sessHandle); err != nil {
return nil, nil, fmt.Errorf("unable to flush session: %v", err)
}
glog.Infof("Created authorization policy: 0x%x\n", policy)
// Seal the data to the parent key and the policy
privateArea, publicArea, err := tpm2.Seal(rwc, srkHandle, srkPassword, objectPassword, policy, dataToSeal)
if err != nil {
return nil, nil, fmt.Errorf("unable to seal data: %v", err)
}
glog.Infof("Sealed data: 0x%x\n", privateArea)
return privateArea, publicArea, nil
}
// Unseal unseals data from TPM based on PCRs and password defined by the polic attached to the protected object.
func Unseal(tpmPath string, pcr int, srkPassword, objectPassword string, privateArea, publicArea []byte) ([]byte, error) {
rwc, err := tpm2.OpenTPM(tpmPath)
if err != nil {
return nil, fmt.Errorf("can't open TPM %q: %v", tpmPath, err)
}
defer rwc.Close()
// Create the parent key against which to seal the data
srkHandle, _, err := tpm2.CreatePrimary(rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", srkPassword, srkTemplate)
if err != nil {
return nil, fmt.Errorf("can't create primary key: %v", err)
}
defer tpm2.FlushContext(rwc, srkHandle)
glog.Infof("Created parent key with handle: 0x%x\n", srkHandle)
// Load the sealed data into the TPM.
objectHandle, _, err := tpm2.Load(rwc, srkHandle, srkPassword, publicArea, privateArea)
if err != nil {
return nil, fmt.Errorf("unable to load data: %v", err)
}
defer tpm2.FlushContext(rwc, objectHandle)
glog.Infof("Loaded sealed data with handle: 0x%x\n", objectHandle)
// Create the authorization session
sessHandle, _, err := policyPCRPasswordSession(rwc, pcr, objectPassword)
if err != nil {
return nil, fmt.Errorf("unable to get auth session: %v", err)
}
defer tpm2.FlushContext(rwc, sessHandle)
// Unseal the data
unsealedData, err := tpm2.UnsealWithSession(rwc, sessHandle, objectHandle, objectPassword)
if err != nil {
return nil, fmt.Errorf("unable to Unseal data: %v", err)
}
return unsealedData, nil
}
// Returns session handle and policy digest.
func policyPCRPasswordSession(rwc io.ReadWriteCloser, pcr int, password string) (tpmutil.Handle, []byte, error) {
// FYI, this is not a very secure session.
sessHandle, _, err := tpm2.StartAuthSession(
rwc,
tpm2.HandleNull, /*tpmKey*/
tpm2.HandleNull, /*bindKey*/
make([]byte, 16), /*nonceCaller*/
nil, /*secret*/
tpm2.SessionPolicy,
tpm2.AlgNull,
tpm2.AlgSHA256)
if err != nil {
return tpm2.HandleNull, nil, fmt.Errorf("unable to start session: %v", err)
}
pcrSelection := tpm2.PCRSelection{
Hash: tpm2.AlgSHA256,
PCRs: []int{pcr},
}
// An empty expected digest means that digest verification is skipped.
if err := tpm2.PolicyPCR(rwc, sessHandle, nil /*expectedDigest*/, pcrSelection); err != nil {
return sessHandle, nil, fmt.Errorf("unable to bind PCRs to auth policy: %v", err)
}
if err := tpm2.PolicyPassword(rwc, sessHandle); err != nil {
return sessHandle, nil, fmt.Errorf("unable to require password for auth policy: %v", err)
}
policy, err := tpm2.PolicyGetDigest(rwc, sessHandle)
if err != nil {
return sessHandle, nil, fmt.Errorf("unable to get policy digest: %v", err)
}
return sessHandle, policy, nil
}