[Device Trust] Generate challenge response

Chrome gets the challenge from the Idp, then it create the challenge
response and send it back in the proper header.

This CL includes
- Handle the HTTP response from the Idp which contains the challenge so
the client can generate the challenge response.
- Signing message method using the key pair created by client.
- Challenge response creation with the signature using the method
mentioned above.
- Use of VA protos for the handshake that were only part of Chrome OS.


This CL doesn't contain the full logic for the challenge response
creation, items that are missing:
- Encrypted key info in the challenge response.
- Verification of the challenge.

Following CLs are going to be created to implement those logic. I
decided to separated since those items need lot of code for the crypto
part.

Change-Id: Ie1d90f1007bb08ef48b0b168a200e22342530048
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2827913
Reviewed-by: Roger Tawa <rogerta@chromium.org>
Commit-Queue: Martin Rodriguez <rodmartin@google.com>
Cr-Commit-Position: refs/heads/master@{#878847}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index aaf1bf44..f0dbcef 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3557,6 +3557,8 @@
       "enterprise/connectors/connectors_prefs.h",
       "enterprise/connectors/connectors_service.cc",
       "enterprise/connectors/connectors_service.h",
+      "enterprise/connectors/device_trust/attestation_service.cc",
+      "enterprise/connectors/device_trust/attestation_service.h",
       "enterprise/connectors/device_trust/device_trust_factory.cc",
       "enterprise/connectors/device_trust/device_trust_factory.h",
       "enterprise/connectors/device_trust/device_trust_service.cc",
@@ -4177,6 +4179,10 @@
       "//chrome/app/theme:chrome_unscaled_resources_grit",
       "//chrome/app/vector_icons",
       "//chrome/browser/cart:mojo_bindings",
+      "//chrome/browser/enterprise/connectors/device_trust:attestation_ca_proto",
+      "//chrome/browser/enterprise/connectors/device_trust:google_key_proto",
+      "//chrome/browser/enterprise/connectors/device_trust:interface_proto",
+      "//chrome/browser/enterprise/connectors/device_trust:keystore_proto",
       "//chrome/browser/policy:path_parser",
       "//chrome/browser/profile_resetter:profile_reset_report_proto",
       "//chrome/browser/resource_coordinator:intervention_policy_database_proto",
diff --git a/chrome/browser/enterprise/connectors/device_trust/BUILD.gn b/chrome/browser/enterprise/connectors/device_trust/BUILD.gn
new file mode 100644
index 0000000..e717cc8
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("keystore_proto") {
+  proto_out_dir = "chrome/browser/enterprise/connectors/device_trust/"
+  sources = [ "//chrome/browser/enterprise/connectors/device_trust/attestation_protos/keystore.proto" ]
+  generate_python = false
+}
+
+proto_library("attestation_ca_proto") {
+  proto_out_dir = "chrome/browser/enterprise/connectors/device_trust/"
+  sources = [ "//chrome/browser/enterprise/connectors/device_trust/attestation_protos/attestation_ca.proto" ]
+  generate_python = false
+}
+
+proto_library("interface_proto") {
+  proto_out_dir = "chrome/browser/enterprise/connectors/device_trust/"
+  sources = [ "//chrome/browser/enterprise/connectors/device_trust/attestation_protos/interface.proto" ]
+  generate_python = false
+}
+
+proto_library("google_key_proto") {
+  proto_out_dir = "chrome/browser/enterprise/connectors/device_trust/"
+  sources = [ "//chrome/browser/enterprise/connectors/device_trust/attestation_protos/google_key.proto" ]
+  generate_python = false
+}
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation_protos/attestation_ca.proto b/chrome/browser/enterprise/connectors/device_trust/attestation_protos/attestation_ca.proto
new file mode 100644
index 0000000..6e843f3
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation_protos/attestation_ca.proto
@@ -0,0 +1,345 @@
+// Copyright 2021 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.
+
+// This file is an exact copy of
+// third_party/cros_system_api/dbus/attestation/attestation_ca.proto
+// third_party/cros_system_api is only for ChromeOS.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package attestation;
+option go_package = "chromiumos/system_api/attestation_proto";
+
+// Enumerates various certificate profiles supported by the Attestation CA.
+enum CertificateProfile {
+  // A certificate intended for enterprise-owned devices.  It has the following
+  // subjectName fields:
+  //   CN=<stable device identifier>
+  //   OU=state:[verified|developer]
+  //   O=Chrome Device Enterprise
+  ENTERPRISE_MACHINE_CERTIFICATE = 0;
+
+  // A certificate intended for enterprise-owned user accounts.  It has the
+  // following subjectName fields:
+  //   OU=state:[verified|developer]
+  //   O=Chrome Device Enterprise
+  ENTERPRISE_USER_CERTIFICATE = 1;
+
+  // A certificate intended for platform verification by providers of protected
+  // content.  It has the following subjectName fields:
+  //   O=Chrome Device Content Protection
+  CONTENT_PROTECTION_CERTIFICATE = 2;
+
+  // Like above, but it also includes a stable ID and origin.
+  //   CN=<origin-specific device identifier>
+  //   OU=<origin>
+  //   O=Chrome Device Content Protection
+  CONTENT_PROTECTION_CERTIFICATE_WITH_STABLE_ID = 3;
+
+  // A certificate intended for cast devices.
+  CAST_CERTIFICATE = 4;
+
+  GFSC_CERTIFICATE = 5;
+
+  JETSTREAM_CERTIFICATE = 6;
+
+  // A certificate for enterprise enrollment.
+  ENTERPRISE_ENROLLMENT_CERTIFICATE = 7;
+
+  // A certificate for signing Android Testsuite Results using CTS-in-a-box.
+  XTS_CERTIFICATE = 8;
+
+  // An EK certificate for vTPM
+  //   CN=CROS VTPM PRD EK ROOT CA
+  ENTERPRISE_VTPM_EK_CERTIFICATE = 9;
+}
+
+enum TpmVersion {
+  TPM_1_2 = 1;  // NOTE: This is the default. It must remain listed first.
+  TPM_2_0 = 2;
+}
+
+// Types of NVRAM quotes used for attestation.
+enum NVRAMQuoteType {
+  // Quote of the Cr50-backed BoardID.
+  BOARD_ID = 0;
+  // Quote of the Cr50-backed SN+RMA bits.
+  SN_BITS = 1;
+  // Quote of the Cr50-backed RSA public endorsement key certificate.
+  RSA_PUB_EK_CERT = 2;
+  // Quote of the Cr50-backed RSU device ID.
+  RSU_DEVICE_ID = 3;
+}
+
+// Holds information about a quote generated by the TPM.
+message Quote {
+  // The quote; a signature generated with the AIK.
+  optional bytes quote = 1;
+  // The serialized data that was quoted; this assists in verifying the quote.
+  optional bytes quoted_data = 2;
+  // The value of the PCR(s) at the time the quote was generated.
+  optional bytes quoted_pcr_value = 3;
+  // Source data which was originally used to extend the PCR. If this field
+  // exists it can be expected that SHA1(pcr_source_hint) was extended into the
+  // PCR.
+  optional bytes pcr_source_hint = 4;
+}
+
+// Holds encrypted data and information required to decrypt it.
+message EncryptedData {
+  // A key that has been sealed to the TPM or wrapped by another key.
+  optional bytes wrapped_key = 2;
+  // The initialization vector used during encryption.
+  optional bytes iv = 3;
+  // MAC of (iv + encrypted_data).
+  optional bytes mac = 4;
+  optional bytes encrypted_data = 5;
+  // An identifier for the wrapping key to assist in decryption.
+  optional bytes wrapping_key_id = 6;
+}
+
+// The wrapper message of any data and its signature.
+message SignedData {
+  // The data to be signed.
+  optional bytes data = 1;
+  // The signature of the data field.
+  optional bytes signature = 2;
+}
+
+// The first two fields are suitable for passing to Tspi_TPM_ActivateIdentity()
+// directly when using TPM 1.2. For TPM 2.0 the first two fields are not used.
+message EncryptedIdentityCredential {
+  // TPM_ASYM_CA_CONTENTS, encrypted with EK public key.
+  optional bytes asym_ca_contents = 1;
+  // TPM_SYM_CA_ATTESTATION, encrypted with the key in aysm_ca_contents.
+  optional bytes sym_ca_attestation = 2;
+
+  optional TpmVersion tpm_version = 3;
+
+  // The following fields are used only for TPM 2.0. For details see the TPM 2.0
+  // specification Part 1 Rev 1.16:
+  // - Section 9.5.3.3: General description of the scheme.
+  // - Section 24: More details including how to use the seed to compute the
+  //               values for 'credential_mac' and 'wrapped_certificate->
+  //               wrapped_key'
+  // - Section B.10.4: Encrypting the seed with a RSA EK.
+  // - Section C.7.4: Encrypting the seed with an EC EK.
+
+  // A seed encrypted with the EK public key. The TPM will use this seed to
+  // derive both an HMAC key to verify the 'credential_mac' field and an AES key
+  // to unwrap the 'wrapped_certificate->wrapped_key' field.
+  optional bytes encrypted_seed = 4;
+
+  // An integrity value computed using HMAC-SHA256 over the
+  // 'wrapped_certificate.wrapped_key' field and the 'Name' of the identity key.
+  optional bytes credential_mac = 5;
+
+  // A certificate encrypted with a 'credential' that is decrypted by the TPM.
+  // The 'wrapped_key' field contains the encrypted credential which is
+  // encrypted using AES-256-CFB with a zero IV. The encryption of the
+  // certificate itself uses AES-256-CBC with PKCS #5 padding and a random IV.
+  // The encryption key is derived from the 'credential' using:
+  //   SHA256('ENCRYPT' + credential)
+  // The mac uses HMAC-SHA256 with a key derived using:
+  //   SHA256('MAC' + credential)
+  optional EncryptedData wrapped_certificate = 6;
+}
+
+// This message holds all information to be sent to the attestation server in
+// order to complete enrollment.
+message AttestationEnrollmentRequest {
+  // The EK cert, in X.509 form, encrypted using the server's public key with
+  // the following parameters:
+  //   Key encryption: RSA-OAEP with no custom parameters.
+  //   Data encryption: 256-bit key, AES-CBC with PKCS5 padding.
+  //   MAC: HMAC-SHA-512 using the AES key.
+  optional EncryptedData encrypted_endorsement_credential = 1;
+  // The AIK public key, the raw TPM format. (TPM_PUBKEY for TPM 1.2,
+  // TPMT_PUBLIC for TPM 2.0).
+  optional bytes identity_public_key = 2;
+  // PCR0 quoted by AIK.
+  optional Quote pcr0_quote = 3;
+  // PCR1 quoted by AIK.
+  optional Quote pcr1_quote = 4;
+  // DEN for enterprise zero-touch enrollment (crbug/624187).
+  optional bytes enterprise_enrollment_nonce = 5;
+  // The device TPM version.
+  optional TpmVersion tpm_version = 6;
+  // An encrypted quote of the RSA EK cert, in X.509 form, if the endorsement
+  // credential is not RSA.
+  optional EncryptedData encrypted_rsa_endorsement_quote = 7;
+}
+
+enum ResponseStatus {
+  OK = 0;
+  // Internal server error.
+  SERVER_ERROR = 1;
+  // The server cannot parse the request.
+  BAD_REQUEST = 2;
+  // The server rejects the request.
+  REJECT = 3;
+  // Only appears in enrollment response. The server returns the same generated
+  // id and reports the quota limit exceeded status when the number of reset
+  // action in a specified time window is more than self reset limitation.
+  QUOTA_LIMIT_EXCEEDED = 4;
+}
+
+// The response from the attestation server for the enrollment request.
+message AttestationEnrollmentResponse {
+  optional ResponseStatus status = 1;
+  // Short detail response message. Included when the result is not OK.
+  optional string detail = 2;
+  optional EncryptedIdentityCredential encrypted_identity_credential = 3;
+  // Extra details included when the result is not OK.
+  optional string extra_details = 4;
+}
+
+// The certificate request to be sent to the attestation server.
+message AttestationCertificateRequest {
+  // The AIK cert in X.509 format.
+  optional bytes identity_credential = 1;
+  // A certified public key in TPM_PUBKEY (TPMT_PUBLIC for TPM 2.0).
+  optional bytes certified_public_key = 3;
+  // The serialized TPM_CERTIFY_INFO (TPMS_ATTEST for TPM 2.0) for the
+  // certified key.
+  optional bytes certified_key_info = 4;
+  // The signature of the TPM_CERTIFY_INFO (TPMS_ATTEST for TPM 2.0) by the AIK.
+  optional bytes certified_key_proof = 5;
+  // A message identifier to be included in the response.
+  optional bytes message_id = 10;
+  // The certificate profile defines the type of certificate to issue.
+  optional CertificateProfile profile = 11;
+  // Information about the origin of the request which may be used depending on
+  // the certificate profile.
+  optional string origin = 12;
+  // The index of a temporal value.  This may be used or ignored depending on
+  // the certificate profile.
+  optional int32 temporal_index = 13;
+  // The device TPM version.
+  optional TpmVersion tpm_version = 14;
+  // NVRAM quoted by AIK. Keys are values of the NVRAMQuoteType. This is only
+  // used for enrollment certificates.
+  map<int32, Quote> nvram_quotes = 15;
+}
+
+// The response from the attestation server for the certificate request.
+message AttestationCertificateResponse {
+  optional ResponseStatus status = 1;
+  // Short detail response message. Included when the result is not OK.
+  optional string detail = 2;
+  // The credential of the certified key in X.509 format.
+  optional bytes certified_key_credential = 3;
+  // The issuer intermediate CA certificate in X.509 format.
+  optional bytes intermediate_ca_cert = 5;
+  // A message identifier from the request this message is responding to.
+  optional bytes message_id = 6;
+  // Additional intermediate CA certificates that can help in validation.
+  // Certificate chaining order is from the leaf to the root. That is,
+  // |certified_key_credential| is signed by
+  // |intermediate_ca_cert|, which is signed by
+  // |additional_intermediate_ca_cert(0)|, which is signed by
+  // |additional_intermediate_ca_cert(1)|, ... and so on.
+  repeated bytes additional_intermediate_ca_cert = 7;
+  // Extra details included when the result is not OK.
+  optional string extra_details = 8;
+}
+
+// The reset request to be sent to the attestation server.
+message AttestationResetRequest {
+  // The AIK cert, in X.509 form, encrypted using the server's public key with
+  // the following parameters:
+  //   Key encryption: RSA-OAEP with no custom parameters.
+  //   Data encryption: 256-bit key, AES-CBC with PKCS5 padding.
+  //   MAC: HMAC-SHA-512 using the AES key.
+  optional EncryptedData encrypted_identity_credential = 1;
+
+  // The one time token to make sure the reset process can be triggered only
+  // once.
+  optional bytes token = 2;
+
+  // The EK cert, in X.509 form, encrypted using the server's public key with
+  // the following parameters:
+  //   Key encryption: RSA-OAEP with no custom parameters.
+  //   Data encryption: 256-bit key, AES-CBC with PKCS5 padding.
+  //   MAC: HMAC-SHA-512 using the AES key.
+  optional EncryptedData encrypted_endorsement_credential = 3;
+}
+
+// The response from the attestation server for the reset request.
+message AttestationResetResponse {
+  // The response status.
+  optional ResponseStatus status = 1;
+  // Short detail response message. Included when the result is not OK.
+  optional string detail = 2;
+  // Extra details included when the result is not OK.
+  optional string extra_details = 3;
+}
+
+// The challenge data (as in challenge-response) generated by the server.
+// Before transmitted to the client, this message will be wrapped as a
+// SignedData message, in which the data field is the serialized Challenge
+// message, and the signature field is the signature of the data field signed
+// by the enterprise server using a hard-coded key. The signature algorithm is
+// RSASSA-PKCS1-v1_5-SHA256.
+message Challenge {
+  // A string for the client to sanity check a legitimate challenge.
+  optional string prefix = 1;
+  // A 256-bit random value generated by the server.
+  optional bytes nonce = 2;
+  // A timestamp for a stateless server to limit the timeframe during which the
+  // challenge may be replayed.
+  optional int64 timestamp = 3;
+}
+
+// The response data (as in challenge-response) generated by the client.
+// Before transmitted to the server, this message will be wrapped as a
+// SignedData message, in which the data field is the serialized
+// ChallengeResponse message, and the signature field is the signature of the
+// data field signed by the client using the key being challenged. The
+// signature algorithm is RSASSA-PKCS1-v1_5-SHA256.
+message ChallengeResponse {
+  // The original challenge data.
+  optional SignedData challenge = 1;
+  // A 256-bit random value generated by the client. Mixing in this nonce
+  // prevents a caller from using a challenge to sign arbitrary data.
+  optional bytes nonce = 2;
+  // The KeyInfo message encrypted using a public encryption key, pushed via
+  // policy with the following parameters:
+  //   Key encryption: RSA-OAEP with no custom parameters.
+  //   Data encryption: 256-bit key, AES-CBC with PKCS5 padding.
+  //   MAC: HMAC-SHA-512 using the AES key.
+  optional EncryptedData encrypted_key_info = 3;
+}
+
+// The data type of the message decrypted from
+// ChallengeResponse.encrypted_key_info.encrypted_data field. This message holds
+// information required by enterprise server to complete the verification.
+message KeyInfo {
+  // Indicates whether the key is an EMK or EUK.
+  optional KeyProfile key_type = 1;
+  // Domain information about the user associated with the key. For an
+  // EMK, this value is empty - customer_id is used instead.
+  // For an EUK, this value is the user's email address.
+  optional string domain = 2;
+  // The virtual device ID associated with the device or user.
+  optional bytes device_id = 3;
+  // If the key is an EUK, this value is the PCA-issued certificate for the key.
+  optional bytes certificate = 4;
+  // If the key is an EUK, this value may hold a SignedPublicKeyAndChallenge
+  // with a random challenge.  The SignedPublicKeyAndChallenge specification is
+  // here: https://developer.mozilla.org/en-US/docs/HTML/Element/keygen.
+  optional bytes signed_public_key_and_challenge = 5;
+  // The identifier of the customer, as defined by the Google Admin SDK at
+  // https://developers.google.com/admin-sdk/directory/v1/guides/manage-customers
+  optional string customer_id = 6;
+}
+
+enum KeyProfile {
+  // Enterprise machine key.
+  EMK = 0;
+  // Enterprise user key.
+  EUK = 1;
+}
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation_protos/google_key.proto b/chrome/browser/enterprise/connectors/device_trust/attestation_protos/google_key.proto
new file mode 100644
index 0000000..8fd316e
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation_protos/google_key.proto
@@ -0,0 +1,29 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is an exact copy of
+// third_party/cros_system_api/dbus/attestation/google_key.proto
+// third_party/cros_system_api is only for ChromeOS.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package attestation;
+
+// The RSA public key of Google key used by attestation service. Only used
+// internally for attestation service, this message is specialized to contain a
+// RSA key modulus in hex with well known exponent 65537.
+message GoogleRsaPublicKey {
+  optional string modulus_in_hex = 1;
+  // The key id for the servers to look up the keys for decryption.
+  optional bytes key_id = 2;
+}
+
+// A key set used with |DEAULT_ACA| and |DEFAULT_VA|.
+message DefaultGoogleRsaPublicKeySet {
+  optional GoogleRsaPublicKey default_ca_encryption_key = 1;
+  optional GoogleRsaPublicKey default_va_signing_key = 2;
+  optional GoogleRsaPublicKey default_va_encryption_key = 3;
+}
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation_protos/interface.proto b/chrome/browser/enterprise/connectors/device_trust/attestation_protos/interface.proto
new file mode 100644
index 0000000..150fa5b0
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation_protos/interface.proto
@@ -0,0 +1,500 @@
+// Copyright 2021 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.
+
+// This file is an exact copy of
+// third_party/cros_system_api/dbus/attestation/interface.proto
+// third_party/cros_system_api is only for ChromeOS.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+import "attestation_ca.proto";
+import "keystore.proto";
+
+package attestation;
+option go_package = "chromiumos/system_api/attestation_proto";
+
+enum AttestationStatus {
+  STATUS_SUCCESS = 0;
+  STATUS_UNEXPECTED_DEVICE_ERROR = 1;
+  STATUS_NOT_AVAILABLE = 2;
+  STATUS_NOT_READY = 3;
+  STATUS_NOT_ALLOWED = 4;
+  STATUS_INVALID_PARAMETER = 5;
+  STATUS_REQUEST_DENIED_BY_CA = 6;
+  STATUS_CA_NOT_AVAILABLE = 7;
+  STATUS_NOT_SUPPORTED = 8;
+  // The error that is translated into by the client to indicate any kind of
+  // D-Bus error.
+  STATUS_DBUS_ERROR = 9;
+}
+
+enum ACAType {
+  DEFAULT_ACA = 0;
+  TEST_ACA = 1;
+}
+
+enum VAType {
+  DEFAULT_VA = 0;
+  TEST_VA = 1;
+}
+
+message GetKeyInfoRequest {
+  optional string key_label = 1;
+  optional string username = 2;
+}
+
+message GetKeyInfoReply {
+  optional AttestationStatus status = 1;
+  optional KeyType key_type = 2;
+  optional KeyUsage key_usage = 3;
+  // The public key (X.509/DER SubjectPublicKeyInfo).
+  optional bytes public_key = 4;
+  // The serialized TPM_CERTIFY_INFO or TPM2B_ATTEST for the new key.
+  optional bytes certify_info = 5;
+  // The signature of certify_info by the Attestation Key.
+  optional bytes certify_info_signature = 6;
+  // The certificate data associated with the key (if any).
+  optional bytes certificate = 7;
+  // The payload associated with the key.
+  optional bytes payload = 8;
+}
+
+message GetEndorsementInfoRequest {
+  reserved 1;
+}
+
+message GetEndorsementInfoReply {
+  optional AttestationStatus status = 1;
+  // The endorsement public key (X.509/DER SubjectPublicKeyInfo).
+  optional bytes ek_public_key = 2;
+  // The endorsement certificate (X.509/DER).
+  optional bytes ek_certificate = 3;
+  // Human-readable string with EK information. Contains EK certificate in PEM
+  // format and SHA256 hash of the raw DER encoded certificate.
+  optional string ek_info = 4;
+}
+
+message GetAttestationKeyInfoRequest {
+  reserved 1;
+  // What kind of ACA to use.
+  optional ACAType aca_type = 2;
+}
+
+message GetAttestationKeyInfoReply {
+  optional AttestationStatus status = 1;
+  // The attestation public key (X.509/DER SubjectPublicKeyInfo).
+  optional bytes public_key = 2;
+  // The attestation public key in TPM_PUBKEY form.
+  optional bytes public_key_tpm_format = 3;
+  // The attestation key certificate.
+  optional bytes certificate = 4;
+  // A quote of PCR0 at the time of attestation key creation.
+  optional Quote pcr0_quote = 5;
+  // A quote of PCR1 at the time of attestation key creation.
+  optional Quote pcr1_quote = 6;
+}
+
+message ActivateAttestationKeyRequest {
+  reserved 1;
+  optional EncryptedIdentityCredential encrypted_certificate = 2;
+  // Whether, on success, the decrypted certificate should be retained in
+  // association with the attestation key. When using an ACA, this is normally
+  // set to true. Other protocols may use a nonce as the 'certificate' and in
+  // these cases this field is normally set to false.
+  optional bool save_certificate = 3;
+  // What kind of ACA to use.
+  optional ACAType aca_type = 4;
+}
+
+message ActivateAttestationKeyReply {
+  optional AttestationStatus status = 1;
+  // The decrypted attestation key certificate.
+  optional bytes certificate = 2;
+}
+
+message CreateCertifiableKeyRequest {
+  // An arbitrary label which can be used to reference the key later.
+  optional string key_label = 1;
+  // Provided if the new key should be accessible only by a
+  // particular user. If this field is not set or is the empty
+  // string, the key will be accessible system-wide.
+  optional string username = 2;
+  optional KeyType key_type = 3;
+  optional KeyUsage key_usage = 4;
+}
+
+message CreateCertifiableKeyReply {
+  optional AttestationStatus status = 1;
+  // The new public key (X.509/DER SubjectPublicKeyInfo).
+  optional bytes public_key = 2;
+  // The serialized TPM_CERTIFY_INFO or TPM2B_ATTEST for the new key.
+  optional bytes certify_info = 3;
+  // The signature of certify_info by the Attestation Key.
+  optional bytes certify_info_signature = 4;
+}
+
+message DecryptRequest {
+  optional string key_label = 1;
+  optional string username = 2;
+  optional bytes encrypted_data = 3;
+}
+
+message DecryptReply {
+  optional AttestationStatus status = 1;
+  optional bytes decrypted_data = 2;
+}
+
+message SignRequest {
+  optional string key_label = 1;
+  optional string username = 2;
+  optional bytes data_to_sign = 3;
+}
+
+message SignReply {
+  optional AttestationStatus status = 1;
+  optional bytes signature = 2;
+}
+
+message RegisterKeyWithChapsTokenRequest {
+  optional string key_label = 1;
+  optional string username = 2;
+  optional bool include_certificates = 3;
+}
+
+message RegisterKeyWithChapsTokenReply {
+  optional AttestationStatus status = 1;
+}
+
+// Message to check whether attestation is prepared for enrollment or not.
+message GetEnrollmentPreparationsRequest {
+  optional ACAType aca_type = 1;
+}
+
+// Reply to a check of whether attestation is prepared for enrollment or not.
+message GetEnrollmentPreparationsReply {
+  optional AttestationStatus status = 1;
+  map<int32, bool> enrollment_preparations = 2;
+}
+
+message GetStatusRequest {
+  // Get extended status (see GetStatusReply below).
+  optional bool extended_status = 1;
+}
+
+message GetStatusReply {
+  message Identity {
+    // The identity features.
+    optional int32 features = 1;
+  }
+
+  message IdentityCertificate {
+    // The identity that is enrolled.
+    optional int32 identity = 1;
+    // The Privacy CA that the identity is enrolled with.
+    optional int32 aca = 2;
+  }
+
+  optional AttestationStatus status = 1;
+  // Is prepared for enrollment? True if prepared for *any* CA.
+  optional bool prepared_for_enrollment = 2;
+  // Is enrolled (AIK certificate created)? True if enrolled with *any* CA.
+  optional bool enrolled = 3;
+  // Is in verified boot mode (according to PCR0 quote)?
+  // [Only included in reply if extended_status is set]
+  optional bool verified_boot = 4;
+  // List of identities and their identity features.
+  repeated Identity identities = 5;
+  // List of identity certificates.
+  map<int32, IdentityCertificate> identity_certificates = 6;
+  // Map of CA types to whether we are prepared for enrollment with that CA.
+  map<int32, bool> enrollment_preparations = 7;
+}
+
+// Verify attestation data.
+message VerifyRequest {
+  // Use CrosCore CA to verify that the EK is endorsed.
+  optional bool cros_core = 1;
+  // Verify EK only.
+  // Otherwise, in addition to EK, verify all attestation data as a CA would.
+  optional bool ek_only = 2;
+}
+
+message VerifyReply {
+  optional AttestationStatus status = 1;
+  optional bool verified = 2;
+}
+
+// Create an enrollment request to be sent to the Privacy CA. This request
+// is a serialized AttestationEnrollmentRequest protobuf. Attestation
+// enrollment is a process by which the Privacy CA verifies the EK certificate
+// of a device and issues a certificate for an AIK. The enrollment process can
+// be finished by sending FinishEnrollRequest with the response from the CA.
+message CreateEnrollRequestRequest {
+  // What kind of ACA to use.
+  optional ACAType aca_type = 1;
+}
+
+message CreateEnrollRequestReply {
+  optional AttestationStatus status = 1;
+  // AttestationEnrollmentRequest to be sent to CA server.
+  optional bytes pca_request = 2;
+}
+
+// Finish the enrollment process.
+message FinishEnrollRequest {
+  // AttestationEnrollmentResponse received from CA server.
+  optional bytes pca_response = 1;
+  // What kind of ACA to use.
+  optional ACAType aca_type = 2;
+}
+
+message FinishEnrollReply {
+  optional AttestationStatus status = 1;
+}
+
+// Goes through the entire enrollment process, including creating enroll
+// request, sending the request to the corresponding PCA server, and storing the
+// response from PCA server if success. The message field is identical to
+// |CreateEnrollRequestRequest|.
+message EnrollRequest {
+  // What kind of ACA to use.
+  optional ACAType aca_type = 1;
+  // No matter is the device is enrolled, (re-)enroll the device.
+  optional bool forced = 2;
+}
+
+message EnrollReply {
+  optional AttestationStatus status = 1;
+}
+
+// Create an attestation certificate request to be sent to the Privacy CA.
+// The request is a serialized AttestationCertificateRequest protobuf. The
+// certificate request process generates and certifies a key in the TPM and
+// sends the AIK certificate along with information about the certified key to
+// the Privacy CA. The PCA verifies the information and issues a certificate
+// for the certified key. The certificate request process can be finished by
+// sending FinishCertificateRequestRequest with the response from the CA.
+message CreateCertificateRequestRequest {
+  // Type of certificate to be requested.
+  optional CertificateProfile certificate_profile = 1;
+  // The canonical username of the active user profile. Leave blank
+  // if not associated with the current user.
+  optional string username = 2;
+  // Some certificate requests require information about the origin
+  // of the request.  If no origin is needed, this can be empty.
+  optional string request_origin = 3;
+  // What kind of ACA to use.
+  optional ACAType aca_type = 4;
+  // The key algorithm of certified key.
+  optional KeyType key_type = 5;
+}
+
+message CreateCertificateRequestReply {
+  optional AttestationStatus status = 1;
+  // The request to be sent to the Privacy CA.
+  optional bytes pca_request = 2;
+}
+
+// Finish the certificate request process.  The |pca_response| from the PCA
+// is a serialized AttestationCertificateResponse protobuf. This final step
+// verifies the PCA operation succeeded and extracts the certificate for the
+// certified key.  The certificate is stored with the key.
+message FinishCertificateRequestRequest {
+  // The Privacy CA's response to a certificate request.
+  optional bytes pca_response = 1;
+  // A name for the key.  If a key already exists with this name it
+  // will be destroyed and replaced with this one.
+  optional string key_label = 2;
+  // This should match |username| in CreateCertificateRequestRequest.
+  optional string username = 3;
+}
+
+message FinishCertificateRequestReply {
+  optional AttestationStatus status = 1;
+  // The PCA issued certificate chain in PEM format.  By
+  // convention the certified key certificate is listed
+  // first followed by intermediate CA certificate(s).
+  // The PCA root certificate is not included.
+  optional bytes certificate = 2;
+  // The public key (X.509/DER SubjectPublicKeyInfo).
+  optional bytes public_key = 3;
+}
+
+// Goes through the entire cert process, including creating cert, sending the
+// request to the corresponding PCA server, and storing the response from PCA
+// server if success. The message fields are basically the union of
+// |CreateCertificateRequestRequest| and |FinishCertificateRequestRequest|.
+message GetCertificateRequest {
+  // Type of certificate to be requested.
+  optional CertificateProfile certificate_profile = 1;
+  // The canonical username of the active user profile. Leave blank
+  // if not associated with the current user.
+  optional string username = 2;
+  // Some certificate requests require information about the origin
+  // of the request.  If no origin is needed, this can be empty.
+  optional string request_origin = 3;
+  // What kind of ACA to use.
+  optional ACAType aca_type = 4;
+  // The key algorithm of certified key.
+  optional KeyType key_type = 5;
+  // A name for the key.  If a key already exists with this name it
+  // will be destroyed and replaced with this one.
+  optional string key_label = 6;
+  // Ignores the current certificate if any and gets the new certificate.
+  optional bool forced = 7;
+  // If set to |true|, this request also triggers enrollment process if the
+  // device is not enrolled yet.
+  optional bool shall_trigger_enrollment = 8;
+}
+
+message GetCertificateReply {
+  optional AttestationStatus status = 1;
+  // The PCA issued certificate chain in PEM format. By
+  // convention the certified key certificate is listed
+  // first followed by intermediate CA certificate(s).
+  // The PCA root certificate is not included.
+  optional bytes certificate = 2;
+  // The public key (X.509/DER SubjectPublicKeyInfo).
+  optional bytes public_key = 3;
+}
+
+// Sign a challenge for an enterprise device / user.  This challenge is
+// typically generated by and the response verified by the Enterprise Device
+// Verification Server (DVServer).
+message SignEnterpriseChallengeRequest {
+  // The key name. This is the same name previously passed to
+  // FinishCertificateRequestRequest.
+  optional string key_label = 1;
+  // The canonical username of the active user profile.
+  // When this is set, an EUK challenge will be performed.
+  // When this is unset or empty, an EMK challenge will be performed.
+  optional string username = 2;
+  // A domain value to be included in the challenge response.
+  // This is supposed to be the user's canonical e-mail address for an EUK
+  // challenge. Ignored for EMK challenges.
+  optional string domain = 3;
+  // A device identifier to be included in the challenge response.
+  optional bytes device_id = 4;
+  // Whether the challenge response should include
+  // a SignedPublicKeyAndChallenge.
+  optional bool include_signed_public_key = 5;
+  // The challenge to be signed.
+  optional bytes challenge = 6;
+  // The VA server that will handle the challenge.
+  optional VAType va_type = 7;
+  // The name of the key used for SignedPublicKeyAndChallenge. If left empty,
+  // the same key used to sign the challenge response (EMK or EUK) will be used
+  // for SignedPublicKeyAndChallenge.
+  optional string key_name_for_spkac = 8;
+}
+
+message SignEnterpriseChallengeReply {
+  optional AttestationStatus status = 1;
+  // The challenge response.
+  optional bytes challenge_response = 2;
+}
+
+// Sign a challenge outside of an enterprise context: generate a random nonce
+// and append it to challenge, then sign and return the signature in the
+// |challenge_response|.
+// This challenge is typically generated by some module running within Chrome.
+message SignSimpleChallengeRequest {
+  // The key name. This is the same name previously passed to
+  // FinishCertificateRequestRequest.
+  optional string key_label = 1;
+  // The canonical username of the active user profile. Leave blank
+  // if not associated with the current user.
+  optional string username = 2;
+  // The challenge to be signed.
+  optional bytes challenge = 3;
+}
+
+message SignSimpleChallengeReply {
+  optional AttestationStatus status = 1;
+  // The challenge response.
+  optional bytes challenge_response = 2;
+}
+
+// Set a payload for a key; any previous payload will be overwritten.
+message SetKeyPayloadRequest {
+  // The key name. This is the same name previously passed to
+  // FinishCertificateRequestRequest.
+  optional string key_label = 1;
+  // The canonical username of the active user profile. Leave blank
+  // if not associated with the current user.
+  optional string username = 2;
+  optional bytes payload = 3;
+}
+
+message SetKeyPayloadReply {
+  optional AttestationStatus status = 1;
+}
+
+// Delete keys either by key label prefix or by exact key label.
+message DeleteKeysRequest {
+  enum MatchBehavior {
+    // Match type not specified. Behavior defaults to |MATCH_TYPE_PREFIX|.
+    MATCH_BEHAVIOR_UNSPECIFIED = 0;
+
+    // Will delete all keys that start with |key_label_match|.
+    // If no such key exists, the operation still succeeds.
+    MATCH_BEHAVIOR_PREFIX = 1;
+
+    // Will delete the key which has a key_label exactly matching
+    // |key_label_match|.
+    // If no such key exists, the operation still succeeds.
+    MATCH_BEHAVIOR_EXACT = 2;
+  }
+  // The key label prefix or the exact key label (depends on |match_behavior|).
+  optional string key_label_match = 1;
+  // The canonical username of the active user profile. Leave blank
+  // if not associated with the current user.
+  optional string username = 2;
+  // The matching behavior - see comments on the enum values. If omitted,
+  // defaults to MATCH_BEHAVIOR_PREFIX for backwards compatibility.
+  optional MatchBehavior match_behavior = 3;
+}
+
+message DeleteKeysReply {
+  optional AttestationStatus status = 1;
+}
+
+// Create a request to be sent to the PCA which will reset the identity for
+// this device on future AIK enrollments.  The |reset_token| is put in the
+// request protobuf verbatim.
+message ResetIdentityRequest {
+  optional string reset_token = 1;
+}
+
+message ResetIdentityReply {
+  optional AttestationStatus status = 1;
+  // Request to be sent to the CA.
+  optional bytes reset_request = 2;
+}
+
+message GetEnrollmentIdRequest {
+  optional bool ignore_cache = 1;
+}
+
+message GetEnrollmentIdReply {
+  optional AttestationStatus status = 1;
+  optional string enrollment_id = 2;
+}
+
+// Gets a copy of the specific NV data, signed using the key with the specified
+// label, eg "attest-ent-machine".
+message GetCertifiedNvIndexRequest {
+  optional int32 nv_index = 1;
+  optional int32 nv_size = 2;
+  optional string key_label = 3;
+}
+
+message GetCertifiedNvIndexReply {
+  optional AttestationStatus status = 1;
+  optional bytes certified_data = 2;
+  optional bytes signature = 3;
+  optional bytes key_certificate = 4;
+}
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation_protos/keystore.proto b/chrome/browser/enterprise/connectors/device_trust/attestation_protos/keystore.proto
new file mode 100644
index 0000000..2caaedbf
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation_protos/keystore.proto
@@ -0,0 +1,26 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is an exact copy of
+// third_party/cros_system_api/dbus/attestation/keystore.proto
+// third_party/cros_system_api is only for ChromeOS.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package attestation;
+option go_package = "chromiumos/system_api/attestation_proto";
+
+// Describes key type.
+enum KeyType {
+  KEY_TYPE_RSA = 1;
+  KEY_TYPE_ECC = 2;
+}
+
+// Describes allowed key usage.
+enum KeyUsage {
+  KEY_USAGE_SIGN = 1;
+  KEY_USAGE_DECRYPT = 2;
+}
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation_service.cc b/chrome/browser/enterprise/connectors/device_trust/attestation_service.cc
new file mode 100644
index 0000000..854bb941
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation_service.cc
@@ -0,0 +1,140 @@
+// Copyright 2021 The Chromium 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 "chrome/browser/enterprise/connectors/device_trust/attestation_service.h"
+
+#include "base/base64.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair.h"
+
+namespace attestation {
+
+AttestationService::AttestationService() {
+#if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+  key_pair_ = std::make_unique<enterprise_connectors::DeviceTrustKeyPair>();
+  key_pair_->Init();
+#endif  // defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+}
+
+AttestationService::~AttestationService() = default;
+
+std::string AttestationService::JsonChallengeToProtobufChallenge(
+    const std::string& challenge) {
+  attestation::SignedData signed_challenge;
+  // Get challenge and decode it.
+  base::Optional<base::Value> data = base::JSONReader::Read(
+      challenge, base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS);
+
+  // If json is malformed or it doesn't include the needed fields return
+  // an empty string.
+  if (!data || !data.value().FindPath("challenge.data") ||
+      !data.value().FindPath("challenge.signature"))
+    return std::string();
+
+  base::Base64Decode(data.value().FindPath("challenge.data")->GetString(),
+                     signed_challenge.mutable_data());
+  base::Base64Decode(data.value().FindPath("challenge.signature")->GetString(),
+                     signed_challenge.mutable_signature());
+
+  std::string serialized_signed_challenge;
+  if (!signed_challenge.SerializeToString(&serialized_signed_challenge)) {
+    LOG(ERROR) << __func__ << ": Failed to serialize signed data.";
+    return std::string();
+  }
+  return serialized_signed_challenge;
+}
+
+std::string AttestationService::ProtobufChallengeToJsonChallenge(
+    const std::string& challenge_response) {
+  base::Value signed_data(base::Value::Type::DICTIONARY);
+
+  std::string encoded;
+  base::Base64Encode(challenge_response, &encoded);
+  signed_data.SetKey("data", base::Value(encoded));
+
+#if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+  std::string signature;
+  key_pair_->GetSignatureInBase64(challenge_response, &signature);
+  signed_data.SetKey("signature", base::Value(signature));
+#endif  // defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("challengeResponse", std::move(signed_data));
+
+  std::string json;
+  base::JSONWriter::Write(dict, &json);
+  return json;
+}
+
+#if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+std::string AttestationService::ExportPEMPublicKey() {
+  return key_pair_->ExportPEMPublicKey();
+}
+#endif  // defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+
+void AttestationService::SignEnterpriseChallenge(
+    const SignEnterpriseChallengeRequest& request,
+    SignEnterpriseChallengeReply* result) {
+  SignEnterpriseChallengeTask(request, result);
+}
+
+void AttestationService::SignEnterpriseChallengeTask(
+    const SignEnterpriseChallengeRequest& request,
+    SignEnterpriseChallengeReply* result) {
+  // Validate that the challenge is coming from the expected source.
+  SignedData signed_challenge;
+  if (!signed_challenge.ParseFromString(request.challenge())) {
+    LOG(ERROR) << __func__ << ": Failed to parse signed challenge.";
+    result->set_status(STATUS_INVALID_PARAMETER);
+    return;
+  }
+
+  KeyInfo key_info;
+  // Set the public key so VA can verify the client.
+#if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+  key_info.set_signed_public_key_and_challenge(ExportPEMPublicKey());
+#endif  // defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+
+  ChallengeResponse response_pb;
+  *response_pb.mutable_challenge() = signed_challenge;
+  // TODO(b/185459013): Encrypt `key_info` and add it to `response_pb`.
+
+  // Serialize and sign the response protobuf.
+  std::string serialized;
+  if (!response_pb.SerializeToString(&serialized)) {
+    result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
+    return;
+  }
+  // Sign data using the client generated key pair.
+  if (!SignChallengeData(serialized, result->mutable_challenge_response())) {
+    result->clear_challenge_response();
+    result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
+    return;
+  }
+}
+
+bool AttestationService::SignChallengeData(const std::string& data,
+                                           std::string* response) {
+  std::string signature;
+#if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+  if (!key_pair_->GetSignatureInBase64(data, &signature)) {
+    LOG(ERROR) << __func__ << ": Failed to sign data.";
+    return false;
+  }
+#endif  // defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+  SignedData signed_data;
+  signed_data.set_data(data);
+  signed_data.set_signature(signature);
+  if (!signed_data.SerializeToString(response)) {
+    LOG(ERROR) << __func__ << ": Failed to serialize signed data.";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace attestation
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation_service.h b/chrome/browser/enterprise/connectors/device_trust/attestation_service.h
new file mode 100644
index 0000000..206a5b9
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation_service.h
@@ -0,0 +1,84 @@
+// Copyright 2021 The Chromium 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 CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_ATTESTATION_SERVICE_H_
+#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_ATTESTATION_SERVICE_H_
+
+#include "build/build_config.h"
+#include "chrome/browser/enterprise/connectors/device_trust/attestation_ca.pb.h"
+#include "chrome/browser/enterprise/connectors/device_trust/interface.pb.h"
+
+namespace enterprise_connectors {
+
+class DeviceTrustKeyPair;
+
+}
+
+namespace attestation {
+
+// This class is in charge of handling the key pair used for attestation. Also
+// provides the methods needed in the handshake between Chrome, an IdP and
+// Verified Access.
+// The main method is `SignEnterpriseChallenge` which take a challenge request
+// and builds a challenge response and wrap it into a
+// `SignEnterpriseChallengeReply`.
+class AttestationService {
+ public:
+  AttestationService();
+  ~AttestationService();
+
+  // Export the public key of `key_pair_`.
+  std::string ExportPEMPublicKey();
+
+  void SignEnterpriseChallenge(const SignEnterpriseChallengeRequest& request,
+                               SignEnterpriseChallengeReply* result);
+
+  // Take the challenge that comes from the Idp in json format and generate a
+  // SignedData proto.
+  // The expected format of the challenge is the following:
+  // {
+  //    "challenge": {
+  //        object (SignedData)
+  //    }
+  // }
+  // SignedData has the following scheme:
+  // {
+  //    "data": string (base64 encoded string),
+  //    "signature": string (base64 encoded string),
+  // }
+  std::string JsonChallengeToProtobufChallenge(const std::string& challenge);
+
+  // Take a challenge_response proto and return the json version of it.
+  // The format is the following:
+  // {
+  //    "challengeResponse": {
+  //        object (SignedData)
+  //    }
+  // }
+  // SignedData has the following scheme:
+  // {
+  //    "data": string (base64 encoded string),
+  //    "signature": string (base64 encoded string),
+  // }
+  std::string ProtobufChallengeToJsonChallenge(
+      const std::string& challenge_response);
+
+ private:
+  // Sign the challenge and return the challenge response in
+  // `result.challenge_response`.
+  void SignEnterpriseChallengeTask(
+      const SignEnterpriseChallengeRequest& request,
+      SignEnterpriseChallengeReply* result);
+
+  // Sign `data` using `key_pair_` and store that value in `signature`.
+  bool SignChallengeData(const std::string& data, std::string* response);
+
+#if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+  std::unique_ptr<enterprise_connectors::DeviceTrustKeyPair> key_pair_;
+#endif  // defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+};
+
+}  // namespace attestation
+
+#endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_ATTESTATION_SERVICE_H_
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation_service_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/attestation_service_unittest.cc
new file mode 100644
index 0000000..3ffb9bd
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation_service_unittest.cc
@@ -0,0 +1,87 @@
+// Copyright 2021 The Chromium 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 "chrome/browser/enterprise/connectors/device_trust/attestation_service.h"
+
+#include "base/base64.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/os_crypt/os_crypt_mocker.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+constexpr char challenge[] =
+    "{"
+    "\"challenge\": {"
+    " \"data\": "
+    "\"ChZFbnRlcnByaXNlS2V5Q2hhbGxlbmdlEiAXyp394cl5TtKo+yhlQPa+CQMPbFyIoY//"
+    "CXHxvDqVqhiBktSOgi8=\","
+    "  \"signature\": "
+    "\"BRRaR9cKJe6NRBAUtPLjRujd0BawaYaPpHXzaWxSqCbFZcB2ZnDwFskh4qPeO0EganhwrPBj"
+    "bD1yLXY1uiPM38TjYQgj2LFXyq0RjGehZ7qLDv2zebiIn6TIDvRi4rhEoXDg3bpKczBTDgp9im"
+    "BQ6QjJS7Pbj0kxPwHzkoVq5UnF9mUUecOAHgKV6ONs4rVjNSpZAPSD/"
+    "jC39wDlIXR5YDKSPCs46u66koDyjM7DNVig+S8nTdr14sXEGFSiHyeFaZC5kXQo103bB9j+"
+    "tcSpwa0MfuZJ5QFJlB1HipjpaGSImZbJPfkjtoK3F1rn9AiHz+nIjLWPrg3KnQt2eaTNSw==\""
+    "}"
+    "}";
+
+}  // namespace
+
+namespace attestation {
+
+class AttestationServiceTest : public testing::Test {
+ public:
+  AttestationServiceTest() : local_state_(TestingBrowserProcess::GetGlobal()) {}
+  void SetUp() override {
+    testing::Test::SetUp();
+    OSCryptMocker::SetUp();
+  }
+
+  void TearDown() override {
+    testing::Test::TearDown();
+    OSCryptMocker::TearDown();
+  }
+
+ private:
+  ScopedTestingLocalState local_state_;
+};
+
+TEST_F(AttestationServiceTest, BuildChallengeResponse) {
+  AttestationService attestation_service_;
+  SignEnterpriseChallengeRequest request;
+  SignEnterpriseChallengeReply result;
+  // Get the challenge from the SignedData json and create request.
+  request.set_challenge(
+      attestation_service_.JsonChallengeToProtobufChallenge(challenge));
+  // If challenge is equal to empty string, then
+  // `JsonChallengeToProtobufChallenge()` failed.
+  EXPECT_NE(request.challenge(), std::string());
+
+  attestation_service_.SignEnterpriseChallenge(request, &result);
+  // If challenge is equal to empty string, then
+  // `JsonChallengeToProtobufChallenge()` failed.
+  EXPECT_NE(result.challenge_response(), std::string());
+
+  base::Optional<base::Value> challenge_response = base::JSONReader::Read(
+      attestation_service_.ProtobufChallengeToJsonChallenge(
+          result.challenge_response()),
+      base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS);
+
+  EXPECT_NE(challenge_response.value()
+                .FindPath("challengeResponse.data")
+                ->GetString(),
+            std::string());
+  EXPECT_NE(challenge_response.value()
+                .FindPath("challengeResponse.signature")
+                ->GetString(),
+            std::string());
+}
+
+}  // namespace attestation
diff --git a/chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair.cc b/chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair.cc
index e0b8bca..634e8bf 100644
--- a/chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair.cc
@@ -96,7 +96,7 @@
   if (!base::Base64Decode(base64_encrypted_private_key_info, &decoded)) {
     // TODO(crbug/1176175): Handle error for when it can't get the private
     // key.
-    LOG(ERROR) << "[DeviceTrust - Init] error while decoding the private key";
+    LOG(ERROR) << "[Init] error while decoding the private key";
     return false;
   }
 
@@ -105,7 +105,7 @@
   if (!success) {
     // TODO(crbug/1176175): Handle error for when it can't get the private
     // key.
-    LOG(ERROR) << "[DeviceTrust - Init] error while decrypting the private key";
+    LOG(ERROR) << "[Init] error while decrypting the private key";
     return false;
   }
 
@@ -117,7 +117,7 @@
 
 bool DeviceTrustKeyPair::StoreKeyPair() {
   if (!key_pair_) {
-    LOG(ERROR) << "[DeviceTrust - StoreKeyPair] no `key_pair_` member";
+    LOG(ERROR) << "[StoreKeyPair] no `key_pair_` member";
     return false;
   }
 
@@ -131,8 +131,7 @@
   std::string encrypted_key;
   bool encrypted = OSCrypt::EncryptString(encoded_private_key, &encrypted_key);
   if (!encrypted) {
-    LOG(ERROR) << "[DeviceTrust - StoreKeyPair] error while encrypting the "
-                  "private key.";
+    LOG(ERROR) << "[StoreKeyPair] error while encrypting the private key.";
     return false;
   }
   // The string must be encoded as base64 for storage in local state.
@@ -182,4 +181,20 @@
   return public_key;
 }
 
+bool DeviceTrustKeyPair::SignMessage(const std::string& message,
+                                     std::vector<uint8_t>& signature) {
+  std::unique_ptr<crypto::ECSignatureCreator> signer(
+      crypto::ECSignatureCreator::Create(key_pair_.get()));
+  return signer->Sign(reinterpret_cast<const uint8_t*>(message.c_str()),
+                      message.size(), &signature);
+}
+bool DeviceTrustKeyPair::GetSignatureInBase64(const std::string& message,
+                                              std::string* signature) {
+  std::vector<uint8_t> signature_vector;
+  if (!SignMessage(message, signature_vector))
+    return false;
+  *signature = BytesToEncodedString(signature_vector);
+  return true;
+}
+
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair.h b/chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair.h
index 82fd0d5..667ad7e 100644
--- a/chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair.h
+++ b/chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair.h
@@ -50,6 +50,13 @@
   // Return strue on success.
   bool Init();
 
+  // Sign `message` using elliptic curve (EC) private key.
+  bool SignMessage(const std::string& message, std::vector<uint8_t>& signature);
+
+  // Sign `message` using `SignMessage` method and return the signature as a
+  // base64 encode string.
+  bool GetSignatureInBase64(const std::string& message, std::string* signature);
+
  private:
   std::unique_ptr<crypto::ECPrivateKey> key_pair_;
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/device_trust_service.cc b/chrome/browser/enterprise/connectors/device_trust/device_trust_service.cc
index 4441370e..d2c28d1 100644
--- a/chrome/browser/enterprise/connectors/device_trust/device_trust_service.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/device_trust_service.cc
@@ -4,9 +4,15 @@
 
 #include "chrome/browser/enterprise/connectors/device_trust/device_trust_service.h"
 
+#include "base/base64.h"
+#include "base/base64url.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/enterprise/connectors/connectors_prefs.h"
+#include "chrome/browser/enterprise/connectors/device_trust/attestation_ca.pb.h"
+#include "chrome/browser/enterprise/connectors/device_trust/interface.pb.h"
 #include "chrome/browser/enterprise/connectors/device_trust/signal_reporter.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
@@ -25,6 +31,7 @@
   key_pair_ = std::make_unique<DeviceTrustKeyPair>();
 #endif  // defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
 
+  attestation_service_ = std::make_unique<attestation::AttestationService>();
   pref_observer_.Init(prefs_);
   pref_observer_.Add(kContextAwareAccessSignalsAllowlistPref,
                      base::BindRepeating(&DeviceTrustService::OnPolicyUpdated,
@@ -125,4 +132,17 @@
 }
 #endif  // defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
 
+std::string DeviceTrustService::BuildChallengeResponse(
+    const std::string& challenge) {
+  ::attestation::SignEnterpriseChallengeRequest request;
+  ::attestation::SignEnterpriseChallengeReply result;
+  // Get the challenge from the SignedData json and create request.
+  request.set_challenge(
+      attestation_service_->JsonChallengeToProtobufChallenge(challenge));
+  attestation_service_->SignEnterpriseChallenge(request, &result);
+
+  return attestation_service_->ProtobufChallengeToJsonChallenge(
+      result.challenge_response());
+}
+
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/device_trust_service.h b/chrome/browser/enterprise/connectors/device_trust/device_trust_service.h
index 2afb6b37..40a0698 100644
--- a/chrome/browser/enterprise/connectors/device_trust/device_trust_service.h
+++ b/chrome/browser/enterprise/connectors/device_trust/device_trust_service.h
@@ -6,8 +6,7 @@
 #define CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_DEVICE_TRUST_SERVICE_H_
 
 #include "build/build_config.h"
-#include "build/buildflag.h"
-#include "build/chromeos_buildflags.h"
+#include "chrome/browser/enterprise/connectors/device_trust/attestation_service.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/policy/core/browser/configuration_policy_handler.h"
 #include "components/prefs/pref_change_registrar.h"
@@ -47,6 +46,10 @@
   std::string GetAttestationCredentialForTesting() const;
 #endif  // defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
 
+  // Starts flow that actually builds a response. This method is called
+  // from a non_UI thread.
+  std::string BuildChallengeResponse(const std::string& challenge);
+
  private:
   friend class DeviceTrustFactory;
 
@@ -73,6 +76,7 @@
 
   std::unique_ptr<enterprise_connectors::DeviceTrustSignalReporter> reporter_;
   SignalReportCallback signal_report_callback_;
+  std::unique_ptr<attestation::AttestationService> attestation_service_;
   base::WeakPtrFactory<DeviceTrustService> weak_factory_{this};
 };
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/navigation_throttle.cc b/chrome/browser/enterprise/connectors/device_trust/navigation_throttle.cc
index 834bb65..09e4182 100644
--- a/chrome/browser/enterprise/connectors/device_trust/navigation_throttle.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/navigation_throttle.cc
@@ -5,13 +5,18 @@
 #include "chrome/browser/enterprise/connectors/device_trust/navigation_throttle.h"
 
 #include "base/memory/ptr_util.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/enterprise/connectors/connectors_prefs.h"
 #include "chrome/browser/enterprise/connectors/device_trust/device_trust_factory.h"
-#include "chrome/browser/enterprise/util/managed_browser_utils.h"
-#include "chrome/browser/policy/chrome_browser_policy_connector.h"
+#include "chrome/browser/enterprise/connectors/device_trust/device_trust_service.h"
+#include "chrome/browser/enterprise/connectors/device_trust/interface.pb.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/policy/core/browser/url_util.h"
 #include "components/prefs/pref_service.h"
+#include "components/url_matcher/url_matcher.h"
 #include "components/user_prefs/user_prefs.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_handle.h"
@@ -20,6 +25,13 @@
 
 namespace enterprise_connectors {
 
+// Const headers used in the handshake flow.
+constexpr char kDeviceTrustHeader[] = "X-Device-Trust";
+constexpr char kDeviceTrustHeaderValue[] = "VerifiedAccess";
+constexpr char kVerifiedAccessChallengeHeader[] = "X-Verified-Access-Challenge";
+constexpr char kVerifiedAccessResponseHeader[] =
+    "X-Verified-Access-Challenge-Response";
+
 // static
 std::unique_ptr<DeviceTrustNavigationThrottle>
 DeviceTrustNavigationThrottle::MaybeCreateThrottleFor(
@@ -106,18 +118,57 @@
 
   // If we are starting an attestation flow.
   if (navigation_handle()->GetResponseHeaders() == nullptr) {
-    navigation_handle()->SetRequestHeader("X-Device-Trust", "VerifiedAccess");
+    navigation_handle()->SetRequestHeader(kDeviceTrustHeader,
+                                          kDeviceTrustHeaderValue);
     return PROCEED;
   }
 
   // If a challenge is coming from the Idp.
-  if (!navigation_handle()->GetRequestHeaders().HasHeader(
-          "x-verified-access-challenge")) {
-    navigation_handle()->RemoveRequestHeader("X-Device-Trust");
-    return PROCEED;
-  }
+  if (navigation_handle()->GetResponseHeaders()->HasHeader(
+          kVerifiedAccessChallengeHeader)) {
+    // Remove request header since is not needed for challenge response.
+    navigation_handle()->RemoveRequestHeader(kDeviceTrustHeader);
 
+    // Get challenge.
+    const net::HttpResponseHeaders* headers =
+        navigation_handle()->GetResponseHeaders();
+    std::string challenge;
+    if (headers->GetNormalizedHeader(kVerifiedAccessChallengeHeader,
+                                     &challenge)) {
+      // Post a task to get the challenge response. It will defer the navigation
+      // and it will be resumed after it's built.
+      // `StartSignChallengeAndReplyWithResponse` won't run in the main thread,
+      // and then reply to `ReplyChallengeResponseAndResume` which makes use of
+      // `weak_ptr_factory_.GetWeakPtr()` so it won't run in case the object is
+      // destroyed.
+      AttestationCallback reply = base::BindOnce(
+          &DeviceTrustNavigationThrottle::ReplyChallengeResponseAndResume,
+          weak_ptr_factory_.GetWeakPtr());
+
+      base::ThreadPool::PostTaskAndReplyWithResult(
+          FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
+          base::BindOnce(&DeviceTrustNavigationThrottle::
+                             StartSignChallengeAndReplyWithResponse,
+                         base::Unretained(this), challenge),
+          std::move(reply));
+
+      return DEFER;
+    }
+  }
   return PROCEED;
 }
 
+std::string
+DeviceTrustNavigationThrottle::StartSignChallengeAndReplyWithResponse(
+    const std::string& challenge) {
+  return device_trust_service_->BuildChallengeResponse(challenge);
+}
+
+void DeviceTrustNavigationThrottle::ReplyChallengeResponseAndResume(
+    std::string challenge_response) {
+  navigation_handle()->SetRequestHeader(kVerifiedAccessResponseHeader,
+                                        challenge_response);
+  Resume();
+}
+
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/navigation_throttle.h b/chrome/browser/enterprise/connectors/device_trust/navigation_throttle.h
index 5d17aa2..ddb946a 100644
--- a/chrome/browser/enterprise/connectors/device_trust/navigation_throttle.h
+++ b/chrome/browser/enterprise/connectors/device_trust/navigation_throttle.h
@@ -5,22 +5,37 @@
 #ifndef CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_NAVIGATION_THROTTLE_H_
 #define CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_NAVIGATION_THROTTLE_H_
 
-#include "chrome/browser/enterprise/connectors/device_trust/device_trust_service.h"
 #include "components/prefs/pref_change_registrar.h"
-#include "components/url_matcher/url_matcher.h"
 #include "content/public/browser/navigation_throttle.h"
 
 class GURL;
 
+namespace url_matcher {
+
+class URLMatcher;
+
+}
+
 namespace enterprise_connectors {
 
+class DeviceTrustService;
+
 // DeviceTrustNavigationThrottle provides a simple way to start a handshake
 // base on a list of allowed URLs.
+// Handshake is an exchange between Chrome and an IdP. This start when Chrome
+// visit one of the allowed list of URLs set in the policy
+// `ContextAwareAccessSignalsAllowlist`. Chrome will add a HTTP header
+// (X-Device-Trust: VerifiedAccess), when the IdP detect that header it should
+// reply with a challenge from Verified Access. Chrome will take this challenge
+// and build a challenge response that is sent back to the IdP using another
+// HTTP header (X-Verified-Access-Challenge-Response).
 class DeviceTrustNavigationThrottle : public content::NavigationThrottle {
  public:
   static std::unique_ptr<DeviceTrustNavigationThrottle> MaybeCreateThrottleFor(
       content::NavigationHandle* navigation_handle);
 
+  using AttestationCallback = base::OnceCallback<void(std::string)>;
+
   explicit DeviceTrustNavigationThrottle(
       content::NavigationHandle* navigation_handle);
   DeviceTrustNavigationThrottle(const DeviceTrustNavigationThrottle&) = delete;
@@ -40,11 +55,26 @@
 
   content::NavigationThrottle::ThrottleCheckResult AddHeadersIfNeeded();
 
+  // Not owned.
   DeviceTrustService* device_trust_service_;
+
+  // Set `challege_response` into the header
+  // `X-Verified-Access-Challenge-Response` of the redirection request to the
+  // IdP and resume the navigation.
+  void ReplyChallengeResponseAndResume(std::string challenge_response);
+
+  // Call `DeviceTrustService::BuildChallengeResponse` which is the method that
+  // actually builds the challenge response, and return it as a string with the
+  // format described in `AttestationService::ProtobufChallengeToJsonChallenge`.
+  std::string StartSignChallengeAndReplyWithResponse(
+      const std::string& challenge);
+
   // The URL matcher created from the ContextAwareAccessSignalsAllowlist policy.
   std::unique_ptr<url_matcher::URLMatcher> matcher_;
 
   PrefChangeRegistrar pref_change_registrar_;
+
+  base::WeakPtrFactory<DeviceTrustNavigationThrottle> weak_ptr_factory_{this};
 };
 
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/navigation_throttle_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/navigation_throttle_unittest.cc
index d73f0b1..2546dfd8 100644
--- a/chrome/browser/enterprise/connectors/device_trust/navigation_throttle_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/navigation_throttle_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -20,8 +20,8 @@
 
 namespace {
 
-const base::Value origins[]{base::Value("https://www.example.com"),
-                            base::Value("example2.example.com")};
+const base::Value kOrigins[]{base::Value("https://www.example.com"),
+                             base::Value("example2.example.com")};
 
 }  // namespace
 
@@ -47,7 +47,7 @@
     Profile::FromBrowserContext(browser_context())
         ->GetPrefs()
         ->Set(kContextAwareAccessSignalsAllowlistPref,
-              std::move(base::ListValue(origins)));
+              base::ListValue(kOrigins));
   }
 
  private:
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 509b098..4951cc34 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -5896,6 +5896,7 @@
 
   if (is_linux || is_mac || is_win) {
     sources += [
+      "../browser/enterprise/connectors/device_trust/attestation_service_unittest.cc",
       "../browser/enterprise/connectors/device_trust/device_trust_key_pair_unittest.cc",
       "../browser/enterprise/connectors/device_trust/navigation_throttle_unittest.cc",
     ]