tpm2: introduce TPM_CCE_PolicyFidoSigned command

This patch implements TPM_CCE_PolicyFidoSigned command support as in
the design document, http://go/h1-for-fido.
Policy Digest is extended by

SHA256(TPM_CCE_PolicyFidoSigned || authenticatorDataDescr ||
  authenticatorData[authenticatorDataDescr] || signing key name),
  where
  TPM_CCE_PolicyFidoSigned is 0x2008001,
  authenticatorDataDescr is an array of (offset, size) tuples,
  authenticatorData is a signature generated by FIDO security key,
  and signing key name is an object name of the signing key.

The auth parameter shall be the signature for authenticatorData and
nonce only, that is,
  auth = sign(AuthenticatorData || hash(session nonce)).

This patch increases the flash usage by 1020 bytes.

BUG=b:140527213
TEST=ran 'trunks_client --regression_test' with trunks, built from
crrev.com/c/1907759, which adds PolicyFidoSigned test case.

Change-Id: I94ba184d206db6c5301bbe930f47a7486ab0ab80
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/tpm2/+/1892419
Tested-by: Namyoon Woo <namyoon@chromium.org>
Reviewed-by: Andrey Pronin <apronin@chromium.org>
Commit-Queue: Namyoon Woo <namyoon@chromium.org>
diff --git a/CommandAttributeData.c b/CommandAttributeData.c
index fb5a665..9226f5f 100644
--- a/CommandAttributeData.c
+++ b/CommandAttributeData.c
@@ -118,7 +118,10 @@
          {0x018c,   0,   0,   0,   0,   1,   0,   0,   0},     //   TPM_CC_PolicyPassword
          {0x018d,   0,   0,   0,   0,   1,   0,   0,   0},     //   TPM_CC_ZGen_2Phase
          {0x018e,   0,   0,   0,   0,   0,   0,   0,   0},     //   TPM_CC_EC_Ephemeral
-         {0x018f,   0,   0,   0,   0,   1,   0,   0,   0}      //   TPM_CC_PolicyNvWritten
+         {0x018f,   0,   0,   0,   0,   1,   0,   0,   0},     //   TPM_CC_PolicyNvWritten
+
+         // Attr for Extended Commands
+         {0x8001,   0,   0,   0,   0,   2,   0,   0,   0},  // TPM_CCE_PolicyFidoSigned
 };
 typedef    UINT16                    _ATTR_;
 #define    NOT_IMPLEMENTED           (_ATTR_)(0)
@@ -372,5 +375,5 @@
 
 // CommandAttributes for extended commands
 static const COMMAND_ATTRIBUTES    s_commandAttributesExt [] = {
- (_ATTR_)(0),
+  (_ATTR_)(CCE_PolicyFidoSigned         * (IS_IMPLEMENTED+DECRYPT_2+ENCRYPT_2)),
 };
diff --git a/CommandDispatcher.c b/CommandDispatcher.c
index 430a470..ba9f03b 100644
--- a/CommandDispatcher.c
+++ b/CommandDispatcher.c
@@ -77,6 +77,7 @@
 #include "PolicyCounterTimer_fp.h"
 #include "PolicyCpHash_fp.h"
 #include "PolicyDuplicationSelect_fp.h"
+#include "PolicyFidoSigned_fp.h"
 #include "PolicyGetDigest_fp.h"
 #include "PolicyLocality_fp.h"
 #include "PolicyNV_fp.h"
@@ -123,6 +124,22 @@
                          UINT32* response_handle_buffer_size,
                          UINT32* response_parameter_buffer_size) {
   BYTE* request_parameter_buffer = request_parameter_buffer_start;
+
+  if (command_code & TPM_CCE_BIT_MASK) {
+    switch (command_code) {
+#if IS_CCE_ENABLED(PolicyFidoSigned)
+    case TPM_CCE_PolicyFidoSigned:
+      return Exec_PolicyFidoSigned(tag, &request_parameter_buffer,
+                              request_parameter_buffer_size, request_handles,
+                              response_handle_buffer_size,
+                              response_parameter_buffer_size);
+#endif
+    default:
+      break;
+    }
+    return TPM_RC_COMMAND_CODE;
+  }
+
   switch (command_code) {
 #if IS_CC_ENABLED(ActivateCredential)
     case TPM_CC_ActivateCredential:
diff --git a/GetCommandCodeString.c b/GetCommandCodeString.c
index 5026ab1..a1b2702 100644
--- a/GetCommandCodeString.c
+++ b/GetCommandCodeString.c
@@ -7,6 +7,18 @@
 #include "GetCommandCodeString_fp.h"
 
 const char* GetCommandCodeString(TPM_CC command_code) {
+  if (command_code & TPM_CCE_BIT_MASK) {
+    switch (command_code) {
+#if IS_CCE_ENABLED(PolicyFidoSigned)
+    case TPM_CCE_PolicyFidoSigned:
+      return "PolicyFidoSigned";
+#endif
+    default:
+      break;
+    }
+    return "Unknown ext command";
+  }
+
   switch (command_code) {
 #if IS_CC_ENABLED(ActivateCredential)
     case TPM_CC_ActivateCredential:
diff --git a/HandleProcess.c b/HandleProcess.c
index 2d07d22..c22cd56 100644
--- a/HandleProcess.c
+++ b/HandleProcess.c
@@ -16,6 +16,33 @@
                          UINT32* num_request_handles) {
   TPM_RC result = TPM_RC_SUCCESS;
   *num_request_handles = 0;
+
+  if (command_code & TPM_CCE_BIT_MASK) {
+    switch (command_code) {
+#if IS_CCE_ENABLED(PolicyFidoSigned)
+    case TPM_CCE_PolicyFidoSigned:
+      result = TPMI_DH_OBJECT_Unmarshal(
+          (TPMI_DH_OBJECT*)&request_handles[*num_request_handles],
+          request_handle_buffer_start, request_buffer_remaining_size, FALSE);
+      if (result != TPM_RC_SUCCESS)
+        return result;
+
+      ++(*num_request_handles);
+      result = TPMI_SH_POLICY_Unmarshal(
+          (TPMI_SH_POLICY*)&request_handles[*num_request_handles],
+          request_handle_buffer_start, request_buffer_remaining_size);
+      if (result != TPM_RC_SUCCESS)
+        return result;
+
+      ++(*num_request_handles);
+      return TPM_RC_SUCCESS;
+#endif
+    default:
+      break;
+    }
+    return TPM_RC_COMMAND_CODE;
+  }
+
   switch (command_code) {
 #if IS_CC_ENABLED(ActivateCredential)
     case TPM_CC_ActivateCredential:
diff --git a/Implementation.h b/Implementation.h
index 4fe49b3..829f1af 100644
--- a/Implementation.h
+++ b/Implementation.h
@@ -276,6 +276,8 @@
 #define    CC_ZGen_2Phase                    CC_SET((CC_S_OPTIONAL * ALG_ECC))
 
 #define IS_CCE_ENABLED(cmd) ((CCE_##cmd) == CC_YES)
+#define    CCE_PolicyFidoSigned              CC_SET(CC_S_CROS_TCG)
+
 //
 //      From Vendor-Specific: Table 7 - Defines for Implementation Values
 //
@@ -707,6 +709,7 @@
  */
 #define TPM_CCE_BIT_MASK                      (TPM_CC)(0x20000000)
 #define TPM_CCE_FIRST                         (TPM_CC)(TPM_CCE_BIT_MASK|0x8001)
+#define TPM_CCE_PolicyFidoSigned              (TPM_CC)(TPM_CCE_FIRST)
 #define TPM_CCE_LAST                          (TPM_CC)(TPM_CCE_BIT_MASK|0x8001)
 
 #ifndef MAX
diff --git a/Makefile b/Makefile
index 9b3d864..c3a8302 100644
--- a/Makefile
+++ b/Makefile
@@ -159,6 +159,7 @@
 SOURCES += Marshal_PolicyCounterTimer.c
 SOURCES += Marshal_PolicyCpHash.c
 SOURCES += Marshal_PolicyDuplicationSelect.c
+SOURCES += Marshal_PolicyFidoSigned.c
 SOURCES += Marshal_PolicyGetDigest.c
 SOURCES += Marshal_PolicyLocality.c
 SOURCES += Marshal_PolicyNV.c
@@ -234,6 +235,7 @@
 SOURCES += PolicyCounterTimer.c
 SOURCES += PolicyCpHash.c
 SOURCES += PolicyDuplicationSelect.c
+SOURCES += PolicyFidoSigned.c
 SOURCES += PolicyGetDigest.c
 SOURCES += PolicyLocality.c
 SOURCES += PolicyNV.c
diff --git a/Marshal_PolicyFidoSigned.c b/Marshal_PolicyFidoSigned.c
new file mode 100644
index 0000000..9057edb
--- /dev/null
+++ b/Marshal_PolicyFidoSigned.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "MemoryLib_fp.h"
+#include "PolicyFidoSigned_fp.h"
+
+#if IS_CCE_ENABLED(PolicyFidoSigned)
+/*
+ * Marshal output data of PolicyFidoSigned command.
+ *
+ * @param source  input buffer of data to marshal
+ * @param tag     Request tag
+ * @param buffer  output buffer to write marshalled data
+ * @param size    size of marshalled data
+ * @return actual size of response excluding the size of the parameter field.
+ */
+static UINT16
+PolicyFidoSigned_Out_Marshal(PolicyFidoSigned_Out* source,
+                             TPMI_ST_COMMAND_TAG tag,
+                             BYTE** buffer,
+                             INT32* size)
+{
+  if (tag == TPM_ST_SESSIONS) {
+    UINT32 param_size = 0;
+
+    /* Add param_size=0 to indicate size of the parameter area. */
+    UINT32_Marshal(&param_size, buffer, size);
+  }
+
+  return 0;
+}
+
+/*
+ * Unmarshal input data of PolicyFidoSigned command.
+ *
+ * @param target       input buffer to unmarshal
+ * @param req_handles
+ * @param buffer       output buffer to write unmarshalled data
+ * @param size         size of unmarshalled data.
+ * @return TPM_RC_SUCCESS if processed successfully, or
+ *         TPM_RC_SIZE if input data size is not correct, or
+ *         TPM_RC_SCHEME if Authenticator algorithm is not specified, or
+ *         TPM_RC_HASH if hash is missing or wrong.
+ *
+ *
+ */
+static TPM_RC
+PolicyFidoSigned_In_Unmarshal(PolicyFidoSigned_In* target,
+                              TPM_HANDLE req_handles[],
+                              BYTE** buffer,
+                              INT32* size)
+{
+  TPM_RC result = TPM_RC_SUCCESS;
+  int i;
+
+  /* Get request handles from req_handles array. */
+  target->authObject = req_handles[0];
+  target->policySession = req_handles[1];
+
+  result = UINT16_Unmarshal(&target->authData.t.size, buffer, size);
+  if (result != TPM_RC_SUCCESS)
+    return result;
+
+  if (target->authData.t.size > MAX_AUTH_DATA_SIZE ||
+      target->authData.t.size == 0)
+    return TPM_RC_SIZE;
+
+  /* Unmarshall authData. */
+  for (i = 0; i < target->authData.t.size; ++i) {
+    result = BYTE_Unmarshal(&(target->authData.t.buffer[i]), buffer, size);
+    if (result != TPM_RC_SUCCESS)
+      return result;
+  }
+
+  /* Unmarshal request parameters. */
+  result = UINT16_Unmarshal(&target->authDataDescrCount, buffer, size);
+  if (result != TPM_RC_SUCCESS)
+    return result;
+
+  if (target->authDataDescrCount > sizeof(target->authDataDescr))
+    return TPM_RC_SIZE;
+
+  /* Unmarshall authDataDescr. */
+  for (i = 0; i < target->authDataDescrCount; i++) {
+    result = UINT16_Unmarshal(&(target->authDataDescr[i].offset), buffer, size);
+    if (result != TPM_RC_SUCCESS)
+      return result;
+
+    result = UINT16_Unmarshal(&(target->authDataDescr[i].size), buffer, size);
+    if (result != TPM_RC_SUCCESS)
+      return result;
+
+    /* The range size should be non-zero. */
+    if (target->authDataDescr[i].size == 0)
+      return TPM_RC_VALUE;
+
+    /* The range is beyond the actual authData string */
+    if (target->authDataDescr[i].offset + target->authDataDescr[i].size >
+        target->authData.t.size)
+      return TPM_RC_VALUE;
+  }
+
+  result = TPMT_SIGNATURE_Unmarshal(&target->auth, buffer, size);
+  if (result != TPM_RC_SUCCESS)
+    return result;
+
+  /* Size should be zero. */
+  if (*size)
+    return TPM_RC_SIZE;
+
+  return result;
+}
+
+TPM_RC Exec_PolicyFidoSigned(TPMI_ST_COMMAND_TAG tag,
+                             BYTE** req_param_buffer,
+                             INT32* req_param_buffer_size,
+                             TPM_HANDLE req_handles[],
+                             UINT32* resp_handle_buf_size,
+                             UINT32* resp_param_buf_size)
+{
+  TPM_RC result = TPM_RC_SUCCESS;
+  PolicyFidoSigned_In in;
+  PolicyFidoSigned_Out out;
+  BYTE* resp_buf;
+  INT32 resp_buf_size;
+  const INT32 resp_header_size = sizeof(TPM_ST) + sizeof(UINT32) +
+                                 sizeof(TPM_RC);
+
+  *resp_handle_buf_size = 0;
+  *resp_param_buf_size = 0;
+
+  /* Unmarshal request parameters to input structure. */
+  result = PolicyFidoSigned_In_Unmarshal(&in,
+                                         req_handles,
+                                         req_param_buffer,
+                                         req_param_buffer_size);
+  if (result != TPM_RC_SUCCESS)
+    return result;
+
+  /* Execute command. */
+  result = TPM2_PolicyFidoSigned(&in, &out);
+  if (result != TPM_RC_SUCCESS)
+    return result;
+
+  /* Marshal output structure to global response buffer. */
+  resp_buf = MemoryGetResponseBuffer(TPM_CCE_PolicyFidoSigned)
+             + resp_header_size;
+  resp_buf_size = MAX_RESPONSE_SIZE - resp_header_size;
+
+  *resp_param_buf_size = PolicyFidoSigned_Out_Marshal(&out,
+                                                      tag,
+                                                      &resp_buf,
+                                                      &resp_buf_size);
+  return TPM_RC_SUCCESS;
+}
+#endif    /* IS_CCE_ENABLED(PolicyFidoSigned) */
diff --git a/PolicyFidoSigned.c b/PolicyFidoSigned.c
new file mode 100644
index 0000000..d969d25
--- /dev/null
+++ b/PolicyFidoSigned.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "InternalRoutines.h"
+#include "PolicyFidoSigned_fp.h"
+
+/*
+ * PolicyFidoContextUpdate
+ *
+ * @param commandCode      IN: TPM Ext command code
+ * @param name             IN: name of entity
+ * @param in               IN: input buffer of the command
+ * @param policyTimeout    IN: policy timeout
+ * @param session          IN/OUT: policy session to be updated
+ * @return none
+ */
+static void PolicyFidoContextUpdate(TPM_CC commandCode,
+                                    TPM2B_NAME *entityName,
+                                    PolicyFidoSigned_In *in,
+                                    SESSION *session)
+{
+  HASH_STATE hashState;
+  UINT16 policyDigestSize;
+  int i;
+
+  /* Start hash */
+  policyDigestSize = CryptStartHash(session->authHashAlg, &hashState);
+
+  /*
+   * policyDigest size should always be the digest size of session hash
+   * algorithm.
+   */
+  pAssert(session->u2.policyDigest.t.size == policyDigestSize);
+
+  /* add old digest */
+  CryptUpdateDigest2B(&hashState, &session->u2.policyDigest.b);
+
+  /* add commandCode */
+  CryptUpdateDigestInt(&hashState, sizeof(TPM_CC), &commandCode);
+
+  /* add name if applicable */
+  if (entityName != NULL)
+    CryptUpdateDigest2B(&hashState, &entityName->b);
+
+  /* Complete the digest and get the results */
+  CryptCompleteHash2B(&hashState, &session->u2.policyDigest.b);
+
+  /* Start second hash computation */
+  CryptStartHash(session->authHashAlg, &hashState);
+
+  /* add policy digest */
+  CryptUpdateDigest2B(&hashState, &session->u2.policyDigest.b);
+
+  /* add authDataDescr */
+  CryptUpdateDigest(&hashState,
+                    in->authDataDescrCount * sizeof(DATA_OFFSET),
+                    (BYTE *)in->authDataDescr);
+
+  /* add authData[authDataDescr] */
+  for (i = 0; i < in->authDataDescrCount; i++) {
+    CryptUpdateDigest(&hashState,
+                      in->authDataDescr[i].size,
+                      &(in->authData.t.buffer[in->authDataDescr[i].offset]));
+  }
+
+  /* Complete second digest */
+  CryptCompleteHash2B(&hashState, &session->u2.policyDigest.b);
+
+  return;
+}
+
+TPM_RC TPM2_PolicyFidoSigned(PolicyFidoSigned_In *in,
+                             PolicyFidoSigned_Out *out)
+{
+  TPM_RC result = TPM_RC_SUCCESS;
+  SESSION *session;
+  TPM2B_NAME entityName;
+
+  session = SessionGet(in->policySession);
+
+  /* Only do input validation if this is not a trial policy session */
+  if (session->attributes.isTrialPolicy == CLEAR) {
+    TPM2B_DIGEST authHash;
+    HASH_STATE hashState;
+
+    /*
+     * Re-compute the digest being signed:
+     *
+     * aHash := hash (authenticatorData[] || nonceTPM)
+     *
+     * Start hash
+     */
+    authHash.t.size = CryptStartHash(CryptGetSignHashAlg(&in->auth),
+                                     &hashState);
+
+    /* add authData */
+    CryptUpdateDigest(&hashState,
+                      in->authData.t.size,
+                      (BYTE *)in->authData.t.buffer);
+
+    /* Add the sesson nonce */
+    CryptUpdateDigest2B(&hashState, &session->nonceTPM.b);
+
+    /* Complete digest */
+    CryptCompleteHash2B(&hashState, &authHash.b);
+
+    /*
+     * Validate Signature. A TPM_RC_SCHEME, TPM_RC_HANDLE or
+     * TPM_RC_SIGNATURE error may be returned at this point.
+     */
+    result = CryptVerifySignature(in->authObject, &authHash, &in->auth);
+    if (result != TPM_RC_SUCCESS)
+      return RcSafeAddToResult(result, RC_PolicySigned_auth);
+  }
+
+  /* Internal Data Update */
+
+  /* Need the Name of the signing entity */
+  entityName.t.size = EntityGetName(in->authObject, &entityName.t.name);
+
+  /*
+   * Update policy with input policyRef and name of auth key
+   * These values are updated even if the session is a trial session
+   */
+  PolicyFidoContextUpdate(TPM_CCE_PolicyFidoSigned,
+                          &entityName,
+                          in,
+                          session);
+
+  /*
+   * Command Output
+   *
+   *   No output to generate.
+   */
+
+  return TPM_RC_SUCCESS;
+}
diff --git a/PolicyFidoSigned_fp.h b/PolicyFidoSigned_fp.h
new file mode 100644
index 0000000..7da52bc
--- /dev/null
+++ b/PolicyFidoSigned_fp.h
@@ -0,0 +1,83 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TPM2_POLICYFIDOSIGNED_FP_H_
+#define TPM2_POLICYFIDOSIGNED_FP_H_
+
+#include "tpm_generated.h"
+
+/*
+ * Data range expression to select the segments in authenticatorData
+ * for policy digest extension
+ */
+typedef struct {
+  UINT16 offset;
+  UINT16 size;
+} DATA_OFFSET;
+
+/*
+ * Input data structure of PolicyFidoSigned command
+ */
+typedef struct {
+  TPMI_DH_OBJECT authObject;
+  TPMI_SH_POLICY policySession;
+
+  TPM2B_AUTHDATA authData;
+  UINT16 authDataDescrCount;
+  DATA_OFFSET authDataDescr[MAX_AUTH_DATA_DESCR_COUNT];
+
+  TPMT_SIGNATURE auth;
+} PolicyFidoSigned_In;
+
+/*
+ * Output data structure of PolicyFidoSigned command
+ */
+typedef struct {
+
+  /* No response parameter */
+
+} PolicyFidoSigned_Out;
+
+/*
+ * Process PolicyFidoSigned command
+ *
+ * @param in   input buffer of request
+ * @param out  output buffer of response
+ * @return TPM_RC_SUCCESS or
+ *         TPM_RC_SIGNATURE if the signature is not genuine, or
+ *         TPM_RC_SCHEME if the scheme is not supported, or
+ *         TPM_RC_HANDLE if an HMAC key was selected but the private part of
+ *                          the key is not.
+ */
+TPM_RC TPM2_PolicyFidoSigned(PolicyFidoSigned_In* in,
+                             PolicyFidoSigned_Out* out);
+
+/*
+ * Execute PolicyFidoSigned command, including unmarshaling/marshaling and
+ * processing
+ *
+ * Unmarshals any request parameters starting at |request_parameter_buffer|.
+ * Executes command. Marshals any response handles and parameters to the
+ * global response buffer and computes |*response_handle_buffer_size| and
+ * |*response_parameter_buffer_size|. If |tag| == TPM_ST_SESSIONS, marshals
+ * parameter_size indicating the size of the parameter area. parameter_size
+ * field is located between the handle area and parameter area.
+ *
+ * @param tag                     command tag
+ * @param req_param_buffer        Buffer of request parameters
+ * @param req_param_buffer_size   Buffer size of request parameters
+ * @param req_handles             Array of request handles
+ * @param resp_handle_buf_size    Buffer size of handles
+ * @param resp_param_buf_size     Buffer size of response parameters
+ * @return TPM_RC_SUCCESS if processed successfully, or
+ *         non-zero error code otherwise.
+ */
+TPM_RC Exec_PolicyFidoSigned(TPMI_ST_COMMAND_TAG tag,
+                             BYTE** req_param_buffer,
+                             INT32* req_param_buffer_size,
+                             TPM_HANDLE req_handles[],
+                             UINT32* resp_handle_buf_size,
+                             UINT32* resp_param_buf_size);
+
+#endif  // TPM2_POLICYFIDOSIGNED_FP_H_
\ No newline at end of file
diff --git a/tpm_generated.c b/tpm_generated.c
index a2f9132..36959ba 100644
--- a/tpm_generated.c
+++ b/tpm_generated.c
@@ -5218,6 +5218,11 @@
     return TPM_RC_SUCCESS;
   }
 #endif
+#if IS_CCE_ENABLED(PolicyFidoSigned)
+  if (*target == TPM_CCE_PolicyFidoSigned) {
+    return TPM_RC_SUCCESS;
+  }
+#endif
   return TPM_RC_COMMAND_CODE;
 }
 
diff --git a/tpm_types.h b/tpm_types.h
index a17cf50..cb9a0a7 100644
--- a/tpm_types.h
+++ b/tpm_types.h
@@ -1657,5 +1657,16 @@
   TPM2B b;
 } TPM2B_CREATION_DATA;
 
+// Definitions of TPM2B_AUTHDATA, a parameter for PolicyFidoSigned command
+#define MAX_AUTH_DATA_DESCR_COUNT       16
+#define MAX_AUTH_DATA_SIZE              512
+
+typedef union {
+  struct {
+    UINT16  size;
+    BYTE    buffer[MAX_AUTH_DATA_SIZE];
+  } t;
+  TPM2B b;
+} TPM2B_AUTHDATA;
 
 #endif  // TPM2_TPM_TYPES_H_