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

     1  // Copyright (c) 2014, Kevin Walsh.  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  	"time"
    19  
    20  	"github.com/golang/protobuf/proto"
    21  	"github.com/google/go-tpm/tpm"
    22  	"github.com/jlmucb/cloudproxy/go/tao/auth"
    23  	"github.com/jlmucb/cloudproxy/go/tpm2"
    24  )
    25  
    26  // ValidSigner checks the signature on an attestation and, if so, returns the
    27  // principal name for the signer.
    28  func (a *Attestation) ValidSigner() (auth.Prin, error) {
    29  	signer := auth.NewPrin(*a.SignerType, a.SignerKey)
    30  	switch *a.SignerType {
    31  	case "tpm":
    32  		// The PCRs are contained in the Speaker of an auth.Says statement that
    33  		// makes up the a.SerializedStatement.
    34  		f, err := auth.UnmarshalForm(a.SerializedStatement)
    35  		if err != nil {
    36  			return auth.Prin{}, newError("tao: couldn't unmarshal the statement: %s", err)
    37  		}
    38  
    39  		// A TPM attestation must be an auth.Says.
    40  		says, ok := f.(auth.Says)
    41  		if !ok {
    42  			return auth.Prin{}, newError("tao: the attestation statement was not an auth.Says statement")
    43  		}
    44  
    45  		// Signer is tpm; use tpm-specific signature verification. Extract the
    46  		// PCRs from the issuer name, unmarshal the key as an RSA key, and call
    47  		// tpm.VerifyQuote().
    48  		speaker, ok := says.Speaker.(auth.Prin)
    49  		if !ok {
    50  			return auth.Prin{}, newError("tao: the speaker of an attestation must be an auth.Prin")
    51  		}
    52  		pcrNums, pcrVals, err := extractPCRs(speaker)
    53  		if err != nil {
    54  			return auth.Prin{}, newError("tao: couldn't extract TPM PCRs from attestation: %s", err)
    55  		}
    56  
    57  		pk, err := extractTPMKey(a.SignerKey)
    58  		if err != nil {
    59  			return auth.Prin{}, newError("tao: couldn't extract TPM key from attestation: %s", err)
    60  		}
    61  		if err := tpm.VerifyQuote(pk, a.SerializedStatement, a.Signature, pcrNums, pcrVals); err != nil {
    62  			return auth.Prin{}, newError("tao: TPM quote failed verification: %s", err)
    63  		}
    64  
    65  		return signer, nil
    66  	case "tpm2":
    67  		// TODO -- tpm2
    68  		// The PCRs are contained in the Speaker of an auth.Says statement that
    69  		// makes up the a.SerializedStatement.
    70  		f, err := auth.UnmarshalForm(a.SerializedStatement)
    71  		if err != nil {
    72  			return auth.Prin{}, newError("tao: couldn't unmarshal the statement: %s", err)
    73  		}
    74  
    75  		// put this back in
    76  		// A TPM attestation must be an auth.Says.
    77  		says, ok := f.(auth.Says)
    78  		if !ok {
    79  			return auth.Prin{}, newError("tao: the attestation statement was not an auth.Says statement")
    80  		}
    81  
    82  		// Signer is tpm; use tpm-specific signature verification. Extract the
    83  		// PCRs from the issuer name, unmarshal the key as an RSA key, and call
    84  		// tpm2.VerifyQuote().
    85  		speaker, ok := says.Speaker.(auth.Prin)
    86  		if !ok {
    87  			return auth.Prin{}, newError("tao: the speaker of an attestation must be an auth.Prin")
    88  		}
    89  
    90  		key, err := extractTPM2Key(a.SignerKey)
    91  		if err != nil {
    92  			return auth.Prin{}, newError("tao: couldn't extract TPM key from attestation: %s", err)
    93  		}
    94  		pcrNums, pcrVal, err := extractTpm2PCRs(speaker)
    95  		if err != nil {
    96  			return auth.Prin{}, newError("tao: couldn't extract TPM PCRs from attestation: %s", err)
    97  		}
    98  		// Note: Hash algorithm used below must match hash algorithm in the signing scheme
    99  		// used for the quote key.
   100  		expectedPcrDigest, err := tpm2.ComputePcrDigest(tpm2.AlgTPM_ALG_SHA1, pcrVal)
   101  		if err != nil {
   102  			return auth.Prin{}, newError("tao: Error computing PCR digest: %s", err)
   103  		}
   104  		ok, err = tpm2.VerifyTpm2Quote(a.SerializedStatement,
   105  			pcrNums, expectedPcrDigest, a.Tpm2QuoteStructure, a.Signature,
   106  			key)
   107  		if err != nil {
   108  			return auth.Prin{}, newError("tao: TPM quote verification error: %s", err)
   109  		}
   110  		if !ok {
   111  			return auth.Prin{}, newError("tao: TPM quote failed verification")
   112  		}
   113  		return signer, nil
   114  	case "key":
   115  		// Signer is ECDSA key, use Tao signature verification.
   116  		// TODO v, err := UnmarshalKey(a.SignerKey)
   117  		v, err := VerifierKeyFromCanonicalKeyBytes(a.SignerKey)
   118  		if err != nil {
   119  			return auth.Prin{}, err
   120  		}
   121  		ok, err := v.Verify(a.SerializedStatement, AttestationSigningContext, a.Signature)
   122  		if err != nil {
   123  			return auth.Prin{}, err
   124  		}
   125  		if !ok {
   126  			return auth.Prin{}, newError("tao: attestation signature invalid")
   127  		}
   128  		return signer, nil
   129  	default:
   130  		return auth.Prin{}, newError("tao: attestation signer principal unrecognized: %s", signer.String())
   131  	}
   132  }
   133  
   134  // Validate checks whether an attestation is valid and, if so, it returns the
   135  // statement conveyed by the attestation.
   136  func (a *Attestation) Validate() (auth.Says, error) {
   137  	signer, err := a.ValidSigner()
   138  	if err != nil {
   139  		return auth.Says{}, err
   140  	}
   141  	f, err := auth.UnmarshalForm(a.SerializedStatement)
   142  	if err != nil {
   143  		return auth.Says{}, err
   144  	}
   145  	var stmt *auth.Says
   146  	if ptr, ok := f.(*auth.Says); ok {
   147  		stmt = ptr
   148  	} else if val, ok := f.(auth.Says); ok {
   149  		stmt = &val
   150  	} else {
   151  		return auth.Says{}, newError("tao: attestation statement has wrong type: %T", f)
   152  	}
   153  
   154  	if a.SerializedDelegation == nil {
   155  		// Case (1), no delegation present.
   156  		// Require that stmt.Speaker be a subprincipal of (or identical to) a.signer.
   157  		if !auth.SubprinOrIdentical(stmt.Speaker, signer) {
   158  			return auth.Says{}, newError("tao: attestation statement signer (%v) does not evidently speak for issuer (%v)", signer, stmt.Speaker)
   159  		}
   160  	} else {
   161  		// Case (2), delegation present.
   162  		// Require that:
   163  		// - delegation conveys delegator says delegate speaksfor delegator,
   164  		// - a.signer speaks for delegate
   165  		// - and delegator speaks for s.Speaker
   166  		var da Attestation
   167  		if err := proto.Unmarshal(a.SerializedDelegation, &da); err != nil {
   168  			return auth.Says{}, err
   169  		}
   170  		delegationStatement, err := da.Validate()
   171  		if err != nil {
   172  			return auth.Says{}, err
   173  		}
   174  		var delegation *auth.Speaksfor
   175  		if ptr, ok := delegationStatement.Message.(*auth.Speaksfor); ok {
   176  			delegation = ptr
   177  		} else if val, ok := delegationStatement.Message.(auth.Speaksfor); ok {
   178  			delegation = &val
   179  		} else {
   180  			return auth.Says{}, newError("tao: attestation delegation is wrong type")
   181  		}
   182  		if !delegationStatement.Speaker.Identical(delegation.Delegator) {
   183  			return auth.Says{}, newError("tao: attestation delegation is invalid")
   184  		}
   185  		if !auth.SubprinOrIdentical(delegation.Delegate, signer) {
   186  			return auth.Says{}, newError("tao: attestation delegation irrelevant to signer")
   187  		}
   188  		if !auth.SubprinOrIdentical(stmt.Speaker, delegation.Delegator) {
   189  			return auth.Says{}, newError("tao: attestation delegation irrelevant to issuer")
   190  		}
   191  		if stmt.Time == nil {
   192  			stmt.Time = delegationStatement.Time
   193  		} else if delegationStatement.Time != nil && *stmt.Time < *delegationStatement.Time {
   194  			stmt.Time = delegationStatement.Time
   195  		}
   196  		if stmt.Expiration == nil {
   197  			stmt.Expiration = delegationStatement.Expiration
   198  		} else if delegationStatement.Expiration != nil && *stmt.Expiration > *delegationStatement.Expiration {
   199  			stmt.Expiration = delegationStatement.Expiration
   200  		}
   201  	}
   202  	return *stmt, nil
   203  }
   204  
   205  // GenerateAttestation uses the signing key to generate an attestation for this
   206  // statement.
   207  func GenerateAttestation(s *Signer, delegation []byte, stmt auth.Says) (*Attestation, error) {
   208  	t := time.Now()
   209  	if stmt.Time == nil {
   210  		i := t.UnixNano()
   211  		stmt.Time = &i
   212  	}
   213  
   214  	if stmt.Expiration == nil {
   215  		i := t.Add(365 * 24 * time.Hour).UnixNano()
   216  		stmt.Expiration = &i
   217  	}
   218  
   219  	ser := auth.Marshal(stmt)
   220  	sig, err := s.Sign(ser, AttestationSigningContext)
   221  	if err != nil {
   222  		return nil, err
   223  	}
   224  
   225  	sk, err := s.GetVerifierFromSigner().CanonicalKeyBytesFromVerifier()
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  	a := &Attestation{
   230  		SerializedStatement: ser,
   231  		Signature:           sig,
   232  		SignerType:          proto.String("key"),
   233  		SignerKey:           sk,
   234  	}
   235  
   236  	if len(delegation) > 0 {
   237  		a.SerializedDelegation = delegation
   238  	}
   239  
   240  	return a, nil
   241  }