mirror of
https://github.com/google/go-attestation.git
synced 2025-02-21 01:11:21 +00:00
Support for application signing keys (#201)
This commit is contained in:
parent
328912c0ae
commit
440d34a877
103
attest/application_key.go
Normal file
103
attest/application_key.go
Normal 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()
|
||||
}
|
171
attest/application_key_test.go
Normal file
171
attest/application_key_test.go
Normal 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")
|
||||
}
|
||||
}
|
@ -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
170
attest/certification.go
Normal 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
|
||||
}
|
155
attest/certification_test.go
Normal file
155
attest/certification_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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.
|
||||
//
|
||||
|
@ -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 {
|
||||
|
@ -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{}
|
||||
|
@ -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")
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user