github.com/jlmucb/cloudproxy@v0.0.0-20170830161738-b5aa0b619bc4/go/tao/tpm_tao.go (about)

     1  // Copyright (c) 2014, Google Inc. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package tao
    16  
    17  import (
    18  	"crypto/rsa"
    19  	"crypto/x509"
    20  	"encoding/hex"
    21  	"errors"
    22  	"io"
    23  	"os"
    24  	"runtime"
    25  	"strconv"
    26  	"strings"
    27  	"time"
    28  
    29  	"github.com/golang/protobuf/proto"
    30  	"github.com/google/go-tpm/tpm"
    31  	"github.com/jlmucb/cloudproxy/go/tao/auth"
    32  )
    33  
    34  // A TPMTao implements the Tao using a hardware TPM device.
    35  type TPMTao struct {
    36  	// tpmfile is the file through which TPMTao communicates with the TPM. E.g.,
    37  	// on Linux, this is usually /dev/tpm0.
    38  	tpmfile *os.File
    39  
    40  	// srkAuth is the authenticator for the SRK. In most simple cases, it's 20
    41  	// bytes of zeros. That value is called the "well-known authentictor"
    42  	srkAuth [20]byte
    43  
    44  	// aikHandle is an integer handle for an AIK held by the TPM. This key is
    45  	// used for creating Quote values from the TPM.
    46  	aikHandle tpm.Handle
    47  
    48  	// verifier is a representation of the AIK that can be used to verify Quote
    49  	// and Quote2 attestations.
    50  	verifier *rsa.PublicKey
    51  
    52  	// DER encoded endorsement certificate for the AIK
    53  	aikCert []byte
    54  
    55  	// pcrCount is the number of PCRs in the TPM. The current go-tpm
    56  	// implementation fixes this at 24.
    57  	pcrCount uint32
    58  	pcrNums  []int
    59  	pcrVals  [][]byte
    60  
    61  	// The name of the TPMTao is tpm(...K...) with extensions that represent the
    62  	// PCR values (and maybe someday the locality).
    63  	name auth.Prin
    64  
    65  	// The current TPMTao code uses only locality 0, so this value is never set.
    66  	locality byte
    67  }
    68  
    69  // NewTPMTao creates a new TPMTao and returns it under the Tao interface.
    70  func NewTPMTao(tpmPath string, aikblob []byte, pcrNums []int, aikCert []byte) (Tao, error) {
    71  	var err error
    72  	tt := &TPMTao{pcrCount: 24}
    73  	tt.tpmfile, err = os.OpenFile(tpmPath, os.O_RDWR, 0)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	// Make sure the TPMTao releases all its resources
    79  	runtime.SetFinalizer(tt, FinalizeTPMTao)
    80  
    81  	// For now, the SRK Auth value is all zero, which is the well-known value.
    82  	// So, we don't set it here.
    83  	// TODO(tmroeder): add support for general SRK auth values.
    84  
    85  	// TODO(tmroeder): the current tpm implementation in go-tpm assumes 24 PCRs.
    86  	// This is not true in general, and it should be generalized there then
    87  	// changed here.
    88  	tt.aikHandle, err = tpm.LoadKey2(tt.tpmfile, aikblob, tt.srkAuth[:])
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	tt.verifier, err = tpm.UnmarshalRSAPublicKey(aikblob)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	tt.aikCert = aikCert
    99  
   100  	// Get the pcr values for the PCR nums.
   101  	tt.pcrNums = make([]int, len(pcrNums))
   102  	for i, v := range pcrNums {
   103  		tt.pcrNums[i] = v
   104  	}
   105  
   106  	tt.pcrVals, err = ReadPCRs(tt.tpmfile, pcrNums)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	// Create principal.
   112  	tt.name, err = MakeTPMPrin(tt.verifier, tt.pcrNums, tt.pcrVals)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	return tt, nil
   118  }
   119  
   120  // FinalizeTPMTao releases the resources for the TPMTao.
   121  func FinalizeTPMTao(tt *TPMTao) {
   122  	// Flush the AIK.
   123  	tt.aikHandle.CloseKey(tt.tpmfile)
   124  
   125  	// Release the file handle.
   126  	tt.tpmfile.Close()
   127  }
   128  
   129  // GetTaoName returns the Tao principal name assigned to the caller.
   130  func (tt *TPMTao) GetTaoName() (name auth.Prin, err error) {
   131  	return tt.name, nil
   132  }
   133  
   134  // ExtendTaoName irreversibly extends the Tao principal name of the caller.
   135  func (tt *TPMTao) ExtendTaoName(subprin auth.SubPrin) error {
   136  	tt.name = tt.name.MakeSubprincipal(subprin)
   137  	return nil
   138  }
   139  
   140  // GetRandomBytes returns a slice of n random bytes.
   141  func (tt *TPMTao) GetRandomBytes(n int) ([]byte, error) {
   142  	if n <= 0 {
   143  		return nil, errors.New("invalid number of requested random bytes")
   144  	}
   145  	return tpm.GetRandom(tt.tpmfile, uint32(n))
   146  }
   147  
   148  // Read implements io.Reader to read random bytes from the TPMTao.
   149  func (tt *TPMTao) Read(p []byte) (int, error) {
   150  	bytes, err := tt.GetRandomBytes(len(p))
   151  	if err != nil {
   152  		return 0, err
   153  	}
   154  
   155  	copy(p, bytes)
   156  	return len(p), nil
   157  }
   158  
   159  // Rand produces an io.Reader for random bytes from this Tao.
   160  func (tt *TPMTao) Rand() io.Reader {
   161  	return tt
   162  }
   163  
   164  // GetSharedSecret returns a slice of n secret bytes.
   165  func (tt *TPMTao) GetSharedSecret(n int, policy string) (bytes []byte, err error) {
   166  	return nil, errors.New("the TPMTao does not implement GetSharedSecret")
   167  }
   168  
   169  // Attest requests the Tao host sign a statement on behalf of the caller. The
   170  // optional issuer, time and expiration will be given default values if nil.
   171  func (tt *TPMTao) Attest(issuer *auth.Prin, start, expiration *int64, message auth.Form) (*Attestation, error) {
   172  	if issuer == nil {
   173  		issuer = &tt.name
   174  	} else if !auth.SubprinOrIdentical(*issuer, tt.name) {
   175  		return nil, errors.New("invalid issuer in statement")
   176  	}
   177  
   178  	// TODO(tmroeder): we assume here that the PCRs haven't changed (e.g., been
   179  	// extended) since this TPMTao was created. If they have, then the PCRs will
   180  	// be wrong when we extend the principal here with them as the first
   181  	// component. This doesn't matter at the moment, since we don't currently
   182  	// support extending the PCRs or clearing them, but it will need to be
   183  	// changed when we do.
   184  	stmt := auth.Says{
   185  		Speaker:    *issuer,
   186  		Time:       start,
   187  		Expiration: expiration,
   188  		Message:    message,
   189  	}
   190  
   191  	// This is done in GenerateAttestation, but the TPM attestation is signed
   192  	// differently, so we do the time calculations here.
   193  	t := time.Now()
   194  	if stmt.Time == nil {
   195  		i := t.UnixNano()
   196  		stmt.Time = &i
   197  	}
   198  
   199  	if stmt.Expiration == nil {
   200  		i := t.Add(365 * 24 * time.Hour).UnixNano()
   201  		stmt.Expiration = &i
   202  	}
   203  
   204  	ser := auth.Marshal(stmt)
   205  	// TODO(tmroeder): check the pcrVals for sanity once we support extending or
   206  	// clearing the PCRs.
   207  	sig, _, err := tpm.Quote(tt.tpmfile, tt.aikHandle, ser, tt.pcrNums, tt.srkAuth[:])
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  
   212  	aik, err := x509.MarshalPKIXPublicKey(tt.verifier)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  	a := &Attestation{
   217  		SerializedStatement: ser,
   218  		Signature:           sig,
   219  		SignerType:          proto.String("tpm"),
   220  		SignerKey:           aik,
   221  		RootEndorsement:     tt.aikCert,
   222  	}
   223  	return a, nil
   224  }
   225  
   226  // Seal encrypts data so only certain hosted programs can unseal it. Note that
   227  // at least some TPMs can only seal up to 149 bytes of data. So, we employ a
   228  // hybrid encryption scheme that seals a key and uses the key to encrypt the
   229  // data separately. We use the keys infrastructure to perform secure and
   230  // flexible encryption.
   231  func (tt *TPMTao) Seal(data []byte, policy string) (sealed []byte, err error) {
   232  	if policy != SealPolicyDefault {
   233  		return nil, errors.New("tpm-specific policies are not yet implemented")
   234  	}
   235  
   236  	keyName := "SealingKey"
   237  	keyEpoch := int32(1)
   238  	keyPurpose := "crypting"
   239  	keyStatus := "active"
   240  	keyType := CrypterTypeFromSuiteName(TaoCryptoSuite)
   241  	if keyType == nil {
   242  		return nil, errors.New("unsupported sealer crypter")
   243  	}
   244  	ck := GenerateCryptoKey(*keyType, &keyName, &keyEpoch, &keyPurpose, &keyStatus)
   245  	if ck == nil {
   246  		return nil, errors.New("Can't generate sealing key")
   247  	}
   248  	crypter := CrypterFromCryptoKey(*ck)
   249  	if crypter  == nil {
   250  		return nil, errors.New("Can't get crypter from sealing key")
   251  	}
   252  	defer crypter.Clear()
   253  
   254  	c, err := crypter.Encrypt(data)
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	ckb, err := proto.Marshal(ck)
   260  	if err != nil {
   261  		return nil, err
   262  	}
   263  	defer ZeroBytes(ckb)
   264  
   265  	s, err := tpm.Seal(tt.tpmfile, tt.locality, tt.pcrNums, ckb, tt.srkAuth[:])
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	h := &HybridSealedData{
   271  		SealedKey:     s,
   272  		EncryptedData: c,
   273  	}
   274  
   275  	return proto.Marshal(h)
   276  }
   277  
   278  // Unseal decrypts data that has been sealed by the Seal() operation, but only
   279  // if the policy specified during the Seal() operation is satisfied.
   280  func (tt *TPMTao) Unseal(sealed []byte) (data []byte, policy string, err error) {
   281  	// The sealed data is a HybridSealedData.
   282  	var h HybridSealedData
   283  	if err := proto.Unmarshal(sealed, &h); err != nil {
   284  		return nil, "", err
   285  	}
   286  
   287  	unsealed, err := tpm.Unseal(tt.tpmfile, h.SealedKey, tt.srkAuth[:])
   288  	if err != nil {
   289  		return nil, "", err
   290  	}
   291  	defer ZeroBytes(unsealed)
   292  
   293  	var ck CryptoKey
   294  	if err := proto.Unmarshal(unsealed, &ck); err != nil {
   295  		return nil, "", err
   296  	}
   297  	defer ck.Clear()
   298  
   299  	crypter := CrypterFromCryptoKey(ck)
   300  	if err == nil {
   301  		return nil, "", errors.New("Cant get crypter from crypto key")
   302  	}
   303  
   304  	m, err := crypter.Decrypt(h.EncryptedData)
   305  	if err != nil {
   306  		return nil, "", err
   307  	}
   308  
   309  	return m, SealPolicyDefault, nil
   310  }
   311  
   312  func (s *TPMTao) InitCounter(label string, c int64) (error) {
   313  	return errors.New("InitCounter for tpm not implemented")
   314  }
   315  
   316  func (s *TPMTao) GetCounter(label string) (int64, error) {
   317  	return int64(0), errors.New("GetCounter for tpm not implemented")
   318  }
   319  
   320  func (s *TPMTao) RollbackProtectedSeal(label string, data []byte, policy string) ([]byte, error) {
   321  	return nil, errors.New("RollbackProtectedSeal for tpm not implemented")
   322  }
   323  
   324  func (s *TPMTao) RollbackProtectedUnseal(sealed []byte) ([]byte, string,  error) {
   325  	return nil, "", errors.New("RollbackProtectedUnseal for tpm not implemented")
   326  }
   327  
   328  // extractPCRs gets the PCRs from a tpm principal.
   329  func extractPCRs(p auth.Prin) ([]int, []byte, error) {
   330  	if p.Type != "tpm" {
   331  		return nil, nil, errors.New("can only extract PCRs from a TPM principal")
   332  	}
   333  
   334  	// The PCRs are stored as the first subprincipal value, with name "PCRs".
   335  	if len(p.Ext) == 0 {
   336  		return nil, nil, errors.New("no subprincipals available for PCR extraction")
   337  	}
   338  
   339  	if p.Ext[0].Name != "PCRs" {
   340  		return nil, nil, errors.New("the first subprincipal must have Name 'PCRs' for PCR extraction to work")
   341  	}
   342  
   343  	sp := p.Ext[0]
   344  	if len(sp.Arg) != 2 {
   345  		return nil, nil, errors.New("the PCRs subprincipal must have exactly two arguments")
   346  	}
   347  
   348  	// auth.Str is exactly a string.
   349  	arg0, ok0 := sp.Arg[0].(auth.Str)
   350  	arg1, ok1 := sp.Arg[1].(auth.Str)
   351  	if !ok0 || !ok1 {
   352  		return nil, nil, errors.New("both Terms in the PCRs subprincipal must be strings")
   353  	}
   354  
   355  	nums := strings.Split(string(arg0), ",")
   356  	vals := strings.Split(string(arg1), ",")
   357  	if len(nums) != len(vals) {
   358  		return nil, nil, errors.New("mismatched count between PCR nums and vals")
   359  	}
   360  
   361  	pcrNums := make([]int, len(nums))
   362  	var pcrVals []byte
   363  	for i, v := range nums {
   364  		n, err := strconv.ParseInt(v, 10, 16)
   365  		if err != nil {
   366  			return nil, nil, err
   367  		}
   368  		pcrNums[i] = int(n)
   369  
   370  		b, err := hex.DecodeString(vals[i])
   371  		if err != nil {
   372  			return nil, nil, err
   373  		}
   374  		pcrVals = append(pcrVals, b...)
   375  	}
   376  	return pcrNums, pcrVals, nil
   377  }
   378  
   379  // extractTPMKey gets an RSA public key from the TPM key material.
   380  func extractTPMKey(material []byte) (*rsa.PublicKey, error) {
   381  	// The principal's Key should be a binary SubjectPublicKeyInfo.
   382  	pk, err := x509.ParsePKIXPublicKey(material)
   383  	if err != nil {
   384  		return nil, err
   385  	}
   386  
   387  	aik, ok := pk.(*rsa.PublicKey)
   388  	if !ok {
   389  		return nil, errors.New("wrong type of public key: only RSA is supported for AIKs")
   390  	}
   391  
   392  	return aik, nil
   393  }
   394  
   395  func ReadPCRs(tpmfile *os.File, pcrNums []int) ([][]byte, error) {
   396  	pcrVals := make([][]byte, len(pcrNums))
   397  	for i, v := range pcrNums {
   398  		pv, err := tpm.ReadPCR(tpmfile, uint32(v))
   399  		if err != nil {
   400  			return nil, err
   401  		}
   402  		pcrVals[i] = pv
   403  	}
   404  	return pcrVals, nil
   405  }
   406  
   407  func MakeTPMPrin(verifier *rsa.PublicKey, pcrNums []int, pcrVals [][]byte) (auth.Prin, error) {
   408  	aik, err := x509.MarshalPKIXPublicKey(verifier)
   409  	if err != nil {
   410  		return auth.Prin{}, err
   411  	}
   412  
   413  	name := auth.NewTPMPrin(aik)
   414  
   415  	asp := auth.PrinExt{
   416  		Name: "PCRs",
   417  		Arg:  make([]auth.Term, 2),
   418  	}
   419  	var pcrNumStrs []string
   420  	for _, v := range pcrNums {
   421  		pcrNumStrs = append(pcrNumStrs, strconv.Itoa(v))
   422  	}
   423  	asp.Arg[0] = auth.Str(strings.Join(pcrNumStrs, ","))
   424  
   425  	var pcrValStrs []string
   426  	for _, p := range pcrVals {
   427  		pcrValStrs = append(pcrValStrs, hex.EncodeToString(p))
   428  	}
   429  	asp.Arg[1] = auth.Str(strings.Join(pcrValStrs, ","))
   430  
   431  	// The PCRs are the first extension of the name.
   432  	name.Ext = []auth.PrinExt{asp}
   433  
   434  	return name, nil
   435  }
   436  
   437  // CleanUpTPMTao runs the finalizer for TPMTao early then unsets it so it
   438  // doesn't run later. Normal code will only create one instance of TPMTao, so
   439  // the finalizer will work correctly. But this test code creates multiple such
   440  // instances, so it needs to call the finalizer early.
   441  func CleanUpTPMTao(tt *TPMTao) {
   442  	FinalizeTPMTao(tt)
   443  	runtime.SetFinalizer(tt, nil)
   444  }