// Copyright 2014 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef TRUNKS_HMAC_AUTHORIZATION_DELEGATE_H_
#define TRUNKS_HMAC_AUTHORIZATION_DELEGATE_H_

#include <string>

#include "authorization_delegate.h"
#include "tpm_generated.h"
#include "trunks_export.h"

namespace trunks {

const size_t kAesKeySize = 16;      // 128 bits is minimum AES key size.
const size_t kHashDigestSize = 32;  // 256 bits is SHA256 digest size.

// HmacAuthorizationDelegate is an implementation of the AuthorizationDelegate
// interface. It provides the necessary Auth data for HMAC sessions.
// This delegate also does parameter encryption on sessions that support it.

// Usage:
// 1) After running the StartAuthSession command on the TPM2.0, we declare this
// delegate using the constructor. We can specify if we want parameter
// obfuscation enabled or not.
// 2) We initialize the session using |InitSession|. We feed in the handle and
// tpm_nonce returned by StartAuthSession. Additionally we inject the
// caller_nonce, salt and auth_value of the bound entity we fed into
// StartAuthSession.
// 3) Pass a pointer to this delegate to any TPM command that needs
// authorization using this delegate.

// Sample control flow:
//  TrunksProxy proxy;
//  proxy.Init();
//  Tpm tpm(&proxy);
//  tpm.StartAuthSession(...);
//  HmacAuthorizationDelegate hmac();
//  hmac.InitSession(...);
//  tpm.Create(..., &hmac);
//  hmac.set_entity_authorization_value(...);
//  tpm.Load(..., &hmac);
class TRUNKS_EXPORT HmacAuthorizationDelegate : public AuthorizationDelegate {
 public:
  HmacAuthorizationDelegate();
  HmacAuthorizationDelegate(const HmacAuthorizationDelegate&) = delete;
  HmacAuthorizationDelegate& operator=(const HmacAuthorizationDelegate&) =
      delete;

  ~HmacAuthorizationDelegate() override;

  // AuthorizationDelegate methods.
  bool GetCommandAuthorization(const std::string& command_hash,
                               bool is_command_parameter_encryption_possible,
                               bool is_response_parameter_encryption_possible,
                               std::string* authorization) override;
  bool CheckResponseAuthorization(const std::string& response_hash,
                                  const std::string& authorization) override;
  bool EncryptCommandParameter(std::string* parameter) override;
  bool DecryptResponseParameter(std::string* parameter) override;
  bool GetTpmNonce(std::string* nonce) override;

  // This function is called with the return data of |StartAuthSession|. It
  // will initialize the session to start providing auth information. It can
  // only be called once per delegate, and must be called before the delegate
  // is used for any operation. The boolean arg |enable_parameter_encryption|
  // specifies if parameter encryption should be enabled for this delegate.
  // |salt| and |bind_auth_value| specify the injected auth values into this
  // delegate.
  bool InitSession(TPM_HANDLE session_handle, const TPM2B_NONCE& tpm_nonce,
                   const TPM2B_NONCE& caller_nonce, const std::string& salt,
                   const std::string& bind_auth_value,
                   bool enable_parameter_encryption);

  // This method sets the FutureAuthorizationValue. This value is used in
  // computing the HMAC response of TPM2_HierarchyChangeAuth.
  void set_future_authorization_value(const std::string& auth_value);

  std::string future_authorization_value() {
    return future_authorization_value_;
  }

  // This method is used to inject an auth_value associated with an entity.
  // This auth_value is then used when generating HMACs and encryption keys.
  // Note: This value will be used for all commands until explicitly reset.
  void set_entity_authorization_value(const std::string& auth_value) {
    entity_authorization_value_ = auth_value;
  }

  std::string entity_authorization_value() const {
    return entity_authorization_value_;
  }

  TPM_HANDLE session_handle() const { return session_handle_; }

  void set_use_entity_authorization_for_encryption_only(bool value) {
    use_entity_authorization_for_encryption_only_ = value;
  }

 private:
  // This method implements the key derivation function used in the TPM.
  // NOTE: It only returns 32 byte keys.
  std::string CreateKey(const std::string& hmac_key, const std::string& label,
                        const TPM2B_NONCE& nonce_newer,
                        const TPM2B_NONCE& nonce_older);
  // This method performs a FIPS198 HMAC operation on |data| using |key|
  std::string HmacSha256(const std::string& key, const std::string& data);
  // This method performs an AES operation using a 128 bit key.
  // |operation_type| can be either AES_ENCRYPT or AES_DECRYPT and it
  // determines if the operation is an encryption or decryption.
  void AesOperation(std::string* parameter, const TPM2B_NONCE& nonce_newer,
                    const TPM2B_NONCE& nonce_older, int operation_type);
  // This method regenerates the caller nonce. The new nonce is the same
  // length as the previous nonce. The buffer is filled with random data using
  // openssl's |RAND_bytes| function.
  // NOTE: This operation is DESTRUCTIVE, and rewrites the caller_nonce_ field.
  void RegenerateCallerNonce();

  TPM_HANDLE session_handle_;
  TPM2B_NONCE caller_nonce_;
  TPM2B_NONCE tpm_nonce_;
  bool is_parameter_encryption_enabled_;
  bool nonce_generated_;
  std::string session_key_;
  std::string entity_authorization_value_;
  bool future_authorization_value_set_;
  std::string future_authorization_value_;
  // This boolean flag determines if the entity_authorization_value_ is needed
  // when computing the hmac_key to create the authorization hmac. Defaults
  // to false, but policy sessions may set this flag to true.
  bool use_entity_authorization_for_encryption_only_;
};

}  // namespace trunks

#endif  // TRUNKS_HMAC_AUTHORIZATION_DELEGATE_H_
