Support for application signing keys (#201)

This commit is contained in:
Paweł Szałachowski 2021-03-08 12:27:00 -08:00 committed by GitHub
parent 328912c0ae
commit 440d34a877
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 799 additions and 12 deletions

103
attest/application_key.go Normal file
View File

@ -0,0 +1,103 @@
// Copyright 2021 Google Inc.
//
// 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 attest
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"fmt"
"io"
)
type key interface {
close(tpmBase) error
marshal() ([]byte, error)
certificationParameters() CertificationParameters
sign(tpmBase, []byte) ([]byte, error)
decrypt(tpmBase, []byte) ([]byte, error)
}
// Key represents a key which can be used for signing and decrypting
// outside-TPM objects.
type Key struct {
key key
pub crypto.PublicKey
tpm tpmBase
}
// signer implements crypto.Signer returned by Key.Private().
type signer struct {
key key
pub crypto.PublicKey
tpm tpmBase
}
// Sign signs digest with the TPM-stored private signing key.
func (s *signer) Sign(r io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
return s.key.sign(s.tpm, digest)
}
// Public returns the public key corresponding to the private signing key.
func (s *signer) Public() crypto.PublicKey {
return s.pub
}
// KeyConfig encapsulates parameters for minting keys. This type is defined
// now (despite being empty) for future interface compatibility.
type KeyConfig struct {
}
// Public returns the public key corresponding to the private key.
func (k *Key) Public() crypto.PublicKey {
return k.pub
}
// Private returns an object allowing to use the TPM-backed private key.
// For now it implements only crypto.Signer.
func (k *Key) Private(pub crypto.PublicKey) (crypto.PrivateKey, error) {
switch pub.(type) {
case *rsa.PublicKey:
if _, ok := k.pub.(*rsa.PublicKey); !ok {
return nil, fmt.Errorf("incompatible public key types: %T != %T", pub, k.pub)
}
case *ecdsa.PublicKey:
if _, ok := k.pub.(*ecdsa.PublicKey); !ok {
return nil, fmt.Errorf("incompatible public key types: %T != %T", pub, k.pub)
}
default:
return nil, fmt.Errorf("unsupported public key type: %T", pub)
}
return &signer{k.key, k.pub, k.tpm}, nil
}
// Close unloads the key from the system.
func (k *Key) Close() error {
return k.key.close(k.tpm)
}
// Marshal encodes the key in a format that can be loaded with tpm.LoadKey().
// This method exists to allow consumers to store the key persistently and load
// it as a later time. Users SHOULD NOT attempt to interpret or extract values
// from this blob.
func (k *Key) Marshal() ([]byte, error) {
return k.key.marshal()
}
// CertificationParameters returns information about the key required to
// verify key certification.
func (k *Key) CertificationParameters() CertificationParameters {
return k.key.certificationParameters()
}

View File

@ -0,0 +1,171 @@
// Copyright 2021 Google Inc.
//
// 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.
// +build !localtest !tpm12
// +build cgo
// NOTE: simulator requires cgo, hence the build tag.
package attest
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/x509"
"encoding/asn1"
"math/big"
"testing"
)
func TestSimTPM20KeyCreateAndLoad(t *testing.T) {
sim, tpm := setupSimulatedTPM(t)
defer sim.Close()
testKeyCreateAndLoad(t, tpm)
}
func TestTPM20KeyCreateAndLoad(t *testing.T) {
if !*testLocal {
t.SkipNow()
}
tpm, err := OpenTPM(nil)
if err != nil {
t.Fatalf("OpenTPM() failed: %v", err)
}
defer tpm.Close()
testKeyCreateAndLoad(t, tpm)
}
func testKeyCreateAndLoad(t *testing.T, tpm *TPM) {
ak, err := tpm.NewAK(nil)
if err != nil {
t.Fatalf("NewAK() failed: %v", err)
}
sk, err := tpm.NewKey(ak, nil)
if err != nil {
t.Fatalf("NewKey() failed: %v", err)
}
defer sk.Close()
enc, err := sk.Marshal()
if err != nil {
t.Fatalf("sk.Marshal() failed: %v", err)
}
if err := sk.Close(); err != nil {
t.Fatalf("sk.Close() failed: %v", err)
}
loaded, err := tpm.LoadKey(enc)
if err != nil {
t.Fatalf("LoadKey() failed: %v", err)
}
defer loaded.Close()
k1, k2 := sk.key.(*wrappedKey20), loaded.key.(*wrappedKey20)
if !bytes.Equal(k1.public, k2.public) {
t.Error("Original & loaded Key public blobs did not match.")
t.Logf("Original = %v", k1.public)
t.Logf("Loaded = %v", k2.public)
}
priv1, err := sk.Private(sk.Public())
if err != nil {
t.Fatalf("sk.Private() failed: %v", err)
}
signer1, ok := priv1.(crypto.Signer)
if !ok {
t.Fatalf("want crypto.Signer, got %T", priv1)
}
pk1, err := x509.MarshalPKIXPublicKey(signer1.Public())
if err != nil {
t.Fatalf("cannot marshal public key: %v", err)
}
priv2, err := loaded.Private(loaded.Public())
if err != nil {
t.Fatalf("loaded.Private() failed: %v", err)
}
signer2, ok := priv2.(crypto.Signer)
if !ok {
t.Fatalf("want crypto.Signer, got %T", priv2)
}
pk2, err := x509.MarshalPKIXPublicKey(signer2.Public())
if err != nil {
t.Fatalf("cannot marshal public key: %v", err)
}
if !bytes.Equal(pk1, pk2) {
t.Error("public keys do not match")
}
}
func TestSimTPM20KeySign(t *testing.T) {
sim, tpm := setupSimulatedTPM(t)
defer sim.Close()
testKeySign(t, tpm)
}
func TestTPM20KeySign(t *testing.T) {
if !*testLocal {
t.SkipNow()
}
tpm, err := OpenTPM(nil)
if err != nil {
t.Fatalf("OpenTPM() failed: %v", err)
}
defer tpm.Close()
testKeySign(t, tpm)
}
func testKeySign(t *testing.T, tpm *TPM) {
ak, err := tpm.NewAK(nil)
if err != nil {
t.Fatalf("NewAK() failed: %v", err)
}
sk, err := tpm.NewKey(ak, nil)
if err != nil {
t.Fatalf("NewKey() failed: %v", err)
}
defer sk.Close()
pub := sk.Public()
priv, err := sk.Private(pub)
if err != nil {
t.Fatalf("sk.Private() failed: %v", err)
}
signer, ok := priv.(crypto.Signer)
if !ok {
t.Fatalf("want crypto.Signer, got %T", priv)
}
digest := []byte("12345678901234567890123456789012")
sig, err := signer.Sign(rand.Reader, digest, nil)
if !ok {
t.Fatalf("signer.Sign() failed: %v", err)
}
parsed := struct{ R, S *big.Int }{}
_, err = asn1.Unmarshal(sig, &parsed)
if err != nil {
t.Fatalf("signature parsing failed: %v", err)
}
pubECDSA, ok := pub.(*ecdsa.PublicKey)
if !ok {
t.Fatalf("want *ecdsa.PublicKey, got %T", pub)
}
if !ecdsa.Verify(pubECDSA, digest[:], parsed.R, parsed.S) {
t.Fatalf("ecdsa.Verify() failed")
}
}

View File

@ -24,6 +24,7 @@ import (
"github.com/google/certificate-transparency-go/x509"
"github.com/google/go-tpm/tpm"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpmutil"
)
// TPMVersion is used to configure a preference in
@ -103,6 +104,7 @@ type ak interface {
activateCredential(tpm tpmBase, in EncryptedCredential) ([]byte, error)
quote(t tpmBase, nonce []byte, alg HashAlg) (*Quote, error)
attestationParameters() AttestationParameters
handle() (tpmutil.Handle, error)
}
// AK represents a key which can be used for attestation.

170
attest/certification.go Normal file
View File

@ -0,0 +1,170 @@
// Copyright 2021 Google Inc.
//
// 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 attest
import (
"bytes"
"crypto"
"crypto/rsa"
"errors"
"fmt"
"github.com/google/go-tpm/tpm2"
)
// secureCurves represents a set of secure elliptic curves. For now,
// the selection is based on the key size only.
var secureCurves = map[tpm2.EllipticCurve]bool{
tpm2.CurveNISTP256: true,
tpm2.CurveNISTP384: true,
tpm2.CurveNISTP521: true,
tpm2.CurveBNP256: true,
tpm2.CurveBNP638: true,
tpm2.CurveSM2P256: true,
}
// CertificationParameters encapsulates the inputs for certifying an application key.
// Only TPM 2.0 is supported at this point.
type CertificationParameters struct {
// Public represents the key's canonical encoding (a TPMT_PUBLIC structure).
// It includes the public key and signing parameters.
Public []byte
// CreateData represents the properties of a TPM 2.0 key. It is encoded
// as a TPMS_CREATION_DATA structure.
CreateData []byte
// CreateAttestation represents an assertion as to the details of the key.
// It is encoded as a TPMS_ATTEST structure.
CreateAttestation []byte
// CreateSignature represents a signature of the CreateAttestation structure.
// It is encoded as a TPMT_SIGNATURE structure.
CreateSignature []byte
}
// VerifyOpts specifies options for the key certification's verification.
type VerifyOpts struct {
// Public is the public key used to verify key ceritification.
Public crypto.PublicKey
// Hash is the hash function used for signature verification. It can be
// extracted from the properties of the certifying key.
Hash crypto.Hash
}
// Verify verifies the TPM2-produced certification parameters checking whether:
// - the key length is secure
// - the attestation parameters matched the attested key
// - the key was TPM-generated and resides within TPM
// - the key can sign/decrypt outside-TPM objects
// - the signature is successfuly verified against the passed public key
// For now, it accepts only RSA verification keys.
func (p *CertificationParameters) Verify(opts VerifyOpts) error {
pub, err := tpm2.DecodePublic(p.Public)
if err != nil {
return fmt.Errorf("DecodePublic() failed: %v", err)
}
_, err = tpm2.DecodeCreationData(p.CreateData)
if err != nil {
return fmt.Errorf("DecodeCreationData() failed: %v", err)
}
att, err := tpm2.DecodeAttestationData(p.CreateAttestation)
if err != nil {
return fmt.Errorf("DecodeAttestationData() failed: %v", err)
}
if att.Type != tpm2.TagAttestCreation {
return fmt.Errorf("attestation does not apply to creation data, got tag %x", att.Type)
}
switch pub.Type {
case tpm2.AlgRSA:
if pub.RSAParameters.KeyBits < minRSABits {
return fmt.Errorf("attested key too small: must be at least %d bits but was %d bits", minRSABits, pub.RSAParameters.KeyBits)
}
case tpm2.AlgECC:
if !secureCurves[pub.ECCParameters.CurveID] {
return fmt.Errorf("attested key uses insecure curve")
}
default:
return fmt.Errorf("public key of alg 0x%x not supported", pub.Type)
}
// Compute & verify that the creation data matches the digest in the
// attestation structure.
nameHash, err := pub.NameAlg.Hash()
if err != nil {
return fmt.Errorf("HashConstructor() failed: %v", err)
}
h := nameHash.New()
h.Write(p.CreateData)
if !bytes.Equal(att.AttestedCreationInfo.OpaqueDigest, h.Sum(nil)) {
return errors.New("attestation refers to different public key")
}
// Make sure the key has sane parameters (e.g., attestation can be faked if an AK
// can be used for arbitrary signatures).
// We verify the following:
// - Key is TPM backed.
// - Key is TPM generated.
// - Key is not restricted (means it can do arbitrary signing/decrypt ops).
// - Key cannot be duplicated.
// - Key was generated by a call to TPM_Create*.
if att.Magic != tpm20GeneratedMagic {
return errors.New("creation attestation was not produced by a TPM")
}
if (pub.Attributes & tpm2.FlagFixedTPM) == 0 {
return errors.New("provided key is exportable")
}
if (pub.Attributes & tpm2.FlagRestricted) != 0 {
return errors.New("provided key is restricted")
}
if (pub.Attributes & tpm2.FlagFixedParent) == 0 {
return errors.New("provided key can be duplicated to a different parent")
}
if (pub.Attributes & tpm2.FlagSensitiveDataOrigin) == 0 {
return errors.New("provided key is not created by TPM")
}
// Verify the attested creation name matches what is computed from
// the public key.
match, err := att.AttestedCreationInfo.Name.MatchesPublic(pub)
if err != nil {
return err
}
if !match {
return errors.New("creation attestation refers to a different key")
}
// Check the signature over the attestation data verifies correctly.
// TODO: Support ECC certifying keys
pk, ok := opts.Public.(*rsa.PublicKey)
if !ok {
return fmt.Errorf("Only RSA verification keys are supported")
}
hsh := opts.Hash.New()
hsh.Write(p.CreateAttestation)
if len(p.CreateSignature) < 8 {
return fmt.Errorf("signature invalid: length of %d is shorter than 8", len(p.CreateSignature))
}
sig, err := tpm2.DecodeSignature(bytes.NewBuffer(p.CreateSignature))
if err != nil {
return fmt.Errorf("DecodeSignature() failed: %v", err)
}
if err := rsa.VerifyPKCS1v15(pk, opts.Hash, hsh.Sum(nil), sig.RSA.Signature); err != nil {
return fmt.Errorf("could not verify attestation: %v", err)
}
return nil
}

View File

@ -0,0 +1,155 @@
// Copyright 2021 Google Inc.
//
// 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 attest
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/go-tpm/tpm2"
)
func TestCertificationParametersTPM20(t *testing.T) {
s, tpm := setupSimulatedTPM(t)
defer s.Close()
ak, err := tpm.NewAK(nil)
if err != nil {
t.Fatal(err)
}
akAttestParams := ak.AttestationParameters()
pub, err := tpm2.DecodePublic(akAttestParams.Public)
if err != nil {
t.Fatal(err)
}
if pub.Type != tpm2.AlgRSA {
t.Fatal("non-RSA verifying key")
}
pk := &rsa.PublicKey{E: int(pub.RSAParameters.Exponent()), N: pub.RSAParameters.Modulus()}
hash, err := pub.RSAParameters.Sign.Hash.Hash()
if err != nil {
t.Fatal(err)
}
correctOpts := VerifyOpts{
Public: pk,
Hash: hash,
}
wrongKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatal(err)
}
wrongHash := crypto.SHA512_256
if wrongHash == correctOpts.Hash {
wrongHash = crypto.SHA256
}
sk, err := tpm.NewKey(ak, nil)
if err != nil {
t.Fatal(err)
}
skCertParams := sk.CertificationParameters()
for _, test := range []struct {
name string
p *CertificationParameters
opts VerifyOpts
err error
}{
{
name: "OK",
p: &skCertParams,
opts: correctOpts,
err: nil,
},
{
name: "wrong public key",
p: &skCertParams,
opts: VerifyOpts{
Public: wrongKey.Public,
Hash: correctOpts.Hash,
},
err: cmpopts.AnyError,
},
{
name: "wrong hash function",
p: &skCertParams,
opts: VerifyOpts{
Public: correctOpts.Public,
Hash: wrongHash,
},
err: cmpopts.AnyError,
},
{
name: "modified Public",
p: &CertificationParameters{
Public: akAttestParams.Public,
CreateData: skCertParams.CreateData,
CreateAttestation: skCertParams.CreateAttestation,
CreateSignature: skCertParams.CreateSignature,
},
opts: correctOpts,
err: cmpopts.AnyError,
},
{
name: "modified CreateData",
p: &CertificationParameters{
Public: skCertParams.Public,
CreateData: []byte("unparsable"),
CreateAttestation: skCertParams.CreateAttestation,
CreateSignature: skCertParams.CreateSignature,
},
opts: correctOpts,
err: cmpopts.AnyError,
},
{
name: "modified CreateAttestation",
p: &CertificationParameters{
Public: skCertParams.Public,
CreateData: skCertParams.CreateData,
CreateAttestation: akAttestParams.CreateAttestation,
CreateSignature: skCertParams.CreateSignature,
},
opts: correctOpts,
err: cmpopts.AnyError,
},
{
name: "modified CreateSignature",
p: &CertificationParameters{
Public: skCertParams.Public,
CreateData: skCertParams.CreateData,
CreateAttestation: skCertParams.CreateAttestation,
CreateSignature: akAttestParams.CreateSignature,
},
opts: correctOpts,
err: cmpopts.AnyError,
},
} {
t.Run(test.name, func(t *testing.T) {
err := test.p.Verify(test.opts)
if test.err == nil && err == nil {
return
}
if got, want := err, test.err; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
t.Errorf("p.Verify() err = %v, want = %v", got, want)
}
})
}
}

View File

@ -19,6 +19,7 @@ package attest
import (
"fmt"
"github.com/google/go-tpm/tpmutil"
"github.com/google/go-tspi/attestation"
)
@ -91,3 +92,7 @@ func (k *trousersKey12) attestationParameters() AttestationParameters {
UseTCSDActivationFormat: true,
}
}
func (k *trousersKey12) handle() (tpmutil.Handle, error) {
return 0, fmt.Errorf("not implemented")
}

View File

@ -20,6 +20,7 @@ import (
"fmt"
tpm1 "github.com/google/go-tpm/tpm"
"github.com/google/go-tpm/tpmutil"
)
// windowsKey12 represents a Windows-managed key on a TPM1.2 TPM.
@ -111,6 +112,10 @@ func (k *windowsKey12) attestationParameters() AttestationParameters {
}
}
func (k *windowsKey12) handle() (tpmutil.Handle, error) {
return 0, fmt.Errorf("not implemented")
}
// windowsKey20 represents a key bound to a TPM 2.0.
type windowsKey20 struct {
hnd uintptr
@ -184,3 +189,7 @@ func (k *windowsKey20) attestationParameters() AttestationParameters {
CreateSignature: k.createSignature,
}
}
func (k *windowsKey20) handle() (tpmutil.Handle, error) {
return 0, fmt.Errorf("not implemented")
}

View File

@ -96,6 +96,23 @@ var (
ModulusRaw: make([]byte, 256),
},
}
// Template for an ECC key for signing outside-TPM objects.
eccKeyTemplate = tpm2.Public{
Type: tpm2.AlgECC,
NameAlg: tpm2.AlgSHA256,
Attributes: tpm2.FlagSignerDefault ^ tpm2.FlagRestricted,
ECCParameters: &tpm2.ECCParams{
Sign: &tpm2.SigScheme{
Alg: tpm2.AlgECDSA,
Hash: tpm2.AlgSHA256,
},
CurveID: tpm2.CurveNISTP256,
Point: tpm2.ECPoint{
XRaw: make([]byte, 32),
YRaw: make([]byte, 32),
},
},
}
)
type tpm20Info struct {
@ -278,6 +295,8 @@ type tpmBase interface {
loadAK(opaqueBlob []byte) (*AK, error)
newAK(opts *AKConfig) (*AK, error)
loadKey(opaqueBlob []byte) (*Key, error)
newKey(ak *AK, opts *KeyConfig) (*Key, error)
pcrs(alg HashAlg) ([]PCR, error)
measurementLog() ([]byte, error)
}
@ -335,6 +354,19 @@ func (t *TPM) NewAK(opts *AKConfig) (*AK, error) {
return t.tpm.newAK(opts)
}
// NewKey creates an application key certified by the attestation key.
func (t *TPM) NewKey(ak *AK, opts *KeyConfig) (*Key, error) {
return t.tpm.newKey(ak, opts)
}
// LoadKey loads a previously-created application key into the TPM for use.
// A key loaded via this function needs to be closed with .Close().
// Only blobs generated by calling Key.Serialize() are valid parameters
// to this function.
func (t *TPM) LoadKey(opaqueBlob []byte) (*Key, error) {
return t.tpm.loadKey(opaqueBlob)
}
// PCRs returns the present value of Platform Configuration Registers with
// the given digest algorithm.
//

View File

@ -103,6 +103,14 @@ func (t *trousersTPM) eks() ([]EK, error) {
}, nil
}
func (t *trousersTPM) newKey(*AK, *KeyConfig) (*Key, error) {
return nil, fmt.Errorf("not implemented")
}
func (t *trousersTPM) loadKey(opaqueBlob []byte) (*Key, error) {
return nil, fmt.Errorf("not implemented")
}
func (t *trousersTPM) newAK(opts *AKConfig) (*AK, error) {
pub, blob, err := attestation.CreateAIK(t.ctx)
if err != nil {

View File

@ -48,7 +48,7 @@ func probeSystemTPMs() ([]probedTPM, error) {
tbs = windows.MustLoadDLL("Tbs.dll")
tbsGetDeviceInfo = tbs.MustFindProc("Tbsi_GetDeviceInfo")
}
// Windows systems appear to only support a single abstracted TPM.
// If we fail to initialize the Platform Crypto Provider, we assume
// a TPM is not present.
@ -324,6 +324,14 @@ func (t *windowsTPM) loadAK(opaqueBlob []byte) (*AK, error) {
}
}
func (t *windowsTPM) newKey(*AK, *KeyConfig) (*Key, error) {
return nil, fmt.Errorf("not implemented")
}
func (t *windowsTPM) loadKey(opaqueBlob []byte) (*Key, error) {
return nil, fmt.Errorf("not implemented")
}
func allPCRs12(tpm io.ReadWriter) (map[uint32][]byte, error) {
numPCRs := 24
out := map[uint32][]byte{}

View File

@ -19,7 +19,9 @@ import (
"crypto/rsa"
"errors"
"fmt"
"math/big"
"github.com/google/certificate-transparency-go/asn1"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpmutil"
)
@ -152,27 +154,100 @@ func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) {
if err != nil {
return nil, fmt.Errorf("failed to pack TPMT_SIGNATURE: %v", err)
}
return &AK{ak: newWrappedKey20(keyHandle, blob, pub, creationData, attestation, signature)}, nil
return &AK{ak: newWrappedAK20(keyHandle, blob, pub, creationData, attestation, signature)}, nil
}
func (t *wrappedTPM20) loadAK(opaqueBlob []byte) (*AK, error) {
sKey, err := deserializeKey(opaqueBlob, TPMVersion20)
func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
// TODO(szp): TODO(jsonp): Abstract choice of hierarchy & parent.
certifierHandle, err := ak.ak.handle()
if err != nil {
return nil, fmt.Errorf("deserializeKey() failed: %v", err)
}
if sKey.Encoding != keyEncodingEncrypted {
return nil, fmt.Errorf("unsupported key encoding: %x", sKey.Encoding)
return nil, fmt.Errorf("cannot get AK's handle: %v", err)
}
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
if err != nil {
return nil, fmt.Errorf("failed to get SRK handle: %v", err)
}
var hnd tpmutil.Handle
if hnd, _, err = tpm2.Load(t.rwc, srk, "", sKey.Public, sKey.Blob); err != nil {
blob, pub, creationData, creationHash, tix, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", eccKeyTemplate)
if err != nil {
return nil, fmt.Errorf("CreateKey() failed: %v", err)
}
keyHandle, _, err := tpm2.Load(t.rwc, srk, "", pub, blob)
if err != nil {
return nil, fmt.Errorf("Load() failed: %v", err)
}
return &AK{ak: newWrappedKey20(hnd, sKey.Blob, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, nil
// If any errors occur, free the handle.
defer func() {
if err != nil {
tpm2.FlushContext(t.rwc, keyHandle)
}
}()
// Certify application key by AK
attestation, sig, err := tpm2.CertifyCreation(t.rwc, "", keyHandle, certifierHandle, nil, creationHash, tpm2.SigScheme{tpm2.AlgRSASSA, tpm2.AlgSHA256, 0}, tix)
if err != nil {
return nil, fmt.Errorf("CertifyCreation failed: %v", err)
}
// Pack the raw structure into a TPMU_SIGNATURE.
signature, err := tpmutil.Pack(tpm2.AlgRSASSA, tpm2.AlgSHA256, tpmutil.U16Bytes(sig))
if err != nil {
return nil, fmt.Errorf("failed to pack TPMT_SIGNATURE: %v", err)
}
tpmPub, _, _, err := tpm2.ReadPublic(t.rwc, keyHandle)
if err != nil {
return nil, fmt.Errorf("read public blob: %v", err)
}
pubKey, err := tpmPub.Key()
if err != nil {
return nil, fmt.Errorf("decode public key: %v", err)
}
return &Key{key: newWrappedKey20(keyHandle, blob, pub, creationData, attestation, signature), pub: pubKey, tpm: t}, nil
}
func (t *wrappedTPM20) deserializeAndLoad(opaqueBlob []byte) (tpmutil.Handle, *serializedKey, error) {
sKey, err := deserializeKey(opaqueBlob, TPMVersion20)
if err != nil {
return 0, nil, fmt.Errorf("deserializeKey() failed: %v", err)
}
if sKey.Encoding != keyEncodingEncrypted {
return 0, nil, fmt.Errorf("unsupported key encoding: %x", sKey.Encoding)
}
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
if err != nil {
return 0, nil, fmt.Errorf("failed to get SRK handle: %v", err)
}
var hnd tpmutil.Handle
if hnd, _, err = tpm2.Load(t.rwc, srk, "", sKey.Public, sKey.Blob); err != nil {
return 0, nil, fmt.Errorf("Load() failed: %v", err)
}
return hnd, sKey, nil
}
func (t *wrappedTPM20) loadAK(opaqueBlob []byte) (*AK, error) {
hnd, sKey, err := t.deserializeAndLoad(opaqueBlob)
if err != nil {
return nil, fmt.Errorf("cannot load attestation key: %v", err)
}
return &AK{ak: newWrappedAK20(hnd, sKey.Blob, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, nil
}
func (t *wrappedTPM20) loadKey(opaqueBlob []byte) (*Key, error) {
hnd, sKey, err := t.deserializeAndLoad(opaqueBlob)
if err != nil {
return nil, fmt.Errorf("cannot load signing key: %v", err)
}
tpmPub, _, _, err := tpm2.ReadPublic(t.rwc, hnd)
if err != nil {
return nil, fmt.Errorf("read public blob: %v", err)
}
pub, err := tpmPub.Key()
if err != nil {
return nil, fmt.Errorf("decode public key: %v", err)
}
return &Key{key: newWrappedKey20(hnd, sKey.Blob, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature), pub: pub, tpm: t}, nil
}
func (t *wrappedTPM20) pcrs(alg HashAlg) ([]PCR, error) {
@ -208,7 +283,18 @@ type wrappedKey20 struct {
createSignature []byte
}
func newWrappedKey20(hnd tpmutil.Handle, blob, public, createData, createAttestation, createSig []byte) ak {
func newWrappedAK20(hnd tpmutil.Handle, blob, public, createData, createAttestation, createSig []byte) ak {
return &wrappedKey20{
hnd: hnd,
blob: blob,
public: public,
createData: createData,
createAttestation: createAttestation,
createSignature: createSig,
}
}
func newWrappedKey20(hnd tpmutil.Handle, blob, public, createData, createAttestation, createSig []byte) key {
return &wrappedKey20{
hnd: hnd,
blob: blob,
@ -300,3 +386,41 @@ func (k *wrappedKey20) attestationParameters() AttestationParameters {
CreateSignature: k.createSignature,
}
}
func (k *wrappedKey20) certificationParameters() CertificationParameters {
return CertificationParameters{
Public: k.public,
CreateData: k.createData,
CreateAttestation: k.createAttestation,
CreateSignature: k.createSignature,
}
}
func (k *wrappedKey20) handle() (tpmutil.Handle, error) {
return k.hnd, nil
}
func (k *wrappedKey20) sign(tb tpmBase, digest []byte) ([]byte, error) {
t, ok := tb.(*wrappedTPM20)
if !ok {
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
}
sig, err := tpm2.Sign(t.rwc, k.hnd, "", digest, nil, nil)
if err != nil {
return nil, fmt.Errorf("signing data: %v", err)
}
if sig.RSA != nil {
return sig.RSA.Signature, nil
}
if sig.ECC != nil {
return asn1.Marshal(struct {
R *big.Int
S *big.Int
}{sig.ECC.R, sig.ECC.S})
}
return nil, fmt.Errorf("unsupported signature type: %v", sig.Alg)
}
func (k *wrappedKey20) decrypt(tb tpmBase, ctxt []byte) ([]byte, error) {
return nil, fmt.Errorf("not implemented")
}