in client-library/src/Attestation/AttestationClient/lib/AttestationClientImpl.cpp [74:202]
AttestationResult AttestationClientImpl::Attest(const ClientParameters& client_params,
unsigned char** jwt_token_out) noexcept {
AttestationResult result(AttestationResult::ErrorCode::SUCCESS);
// Validate the token to make sure that the input parameter is not empty.
// Check the Version of the structure.
if (client_params.version != CLIENT_PARAMS_VERSION ||
client_params.attestation_endpoint_url == nullptr ||
jwt_token_out == nullptr) {
CLIENT_LOG_ERROR("Invalid input parameter");
result.code_ = AttestationResult::ErrorCode::ERROR_INVALID_INPUT_PARAMETER;
result.description_ = std::string("Invalid input parameter");
return result;
}
TpmCertOperations tpm_cert_ops;
bool is_ak_cert_renewal_required = false;
if ((result = tpm_cert_ops.IsAkCertRenewalRequired(is_ak_cert_renewal_required)).code_ != AttestationResult::ErrorCode::SUCCESS) {
CLIENT_LOG_ERROR("Failure while checking AkCert Renewal state %s", result.description_.c_str());
if (result.tpm_error_code_ != 0) {
CLIENT_LOG_ERROR("Internal TPM Error occurred, Tpm Error Code: %d", result.tpm_error_code_);
return result;
} else if (result.code_ == attest::AttestationResult::ErrorCode::ERROR_AK_CERT_PROVISIONING_FAILED) {
CLIENT_LOG_ERROR("Attestation Key cert provisioning delayed. Please try attestation after some time.");
result.description_ = std::string("AK cert provisioning delayed. Please try attestation after some time.");
return result;
}
}
result = AttestationResult::ErrorCode::SUCCESS;
if (is_ak_cert_renewal_required) {
if ((result = tpm_cert_ops.RenewAndReplaceAkCert()).code_ != AttestationResult::ErrorCode::SUCCESS) {
CLIENT_LOG_ERROR("Failed to renew AkCert, description: %s with error code: %d", result.description_, static_cast<int>(result.code_));
if (telemetry_reporting.get() != nullptr) {
telemetry_reporting->UpdateEvent("AkRenew",
"Failed to renew AkCert, error description: " + result.description_,
TelemetryReportingBase::EventLevel::AK_RENEW_UNEXPECTED_ERROR);
}
}
}
result = AttestationResult::ErrorCode::SUCCESS;
std::string url = std::string(const_cast<char*>(reinterpret_cast<const char*>(client_params.attestation_endpoint_url)));
// parse the url and extract the dns
std::string dns;
if ((result = url::ParseURL(url, dns)).code_ != AttestationResult::ErrorCode::SUCCESS) {
return result;
}
// Copy attestation endpoint and access token to member variables.
attestation_url_ = std::string(std::string(azure_guest_protocol))
.append(dns)
.append(std::string(azure_guest_url));
CLIENT_LOG_INFO("Attestation URL - %s", attestation_url_.c_str());
AttestationParameters params = {};
std::unordered_map<std::string, std::string> client_payload_map;
if (client_params.client_payload != nullptr) {
if ((result = ParseClientPayload(client_params.client_payload, client_payload_map)).code_ !=
AttestationResult::ErrorCode::SUCCESS) {
return result;
}
}
if((result = getAttestationParameters(client_payload_map,
params)).code_ !=
AttestationResult::ErrorCode::SUCCESS) {
CLIENT_LOG_ERROR("Failed to get attestation parameters with error:%s",
result.description_.c_str());
return result;
}
if(!params.Validate()) {
// One or more parameters are invalid. Log error indicating validation
// failed along with function name and error string.
CLIENT_LOG_ERROR("Failed to validate attestation parameters");
result = AttestationResult::ErrorCode::ERROR_ATTESTATION_PARAMETERS_VALIDATION_FAILED;
result.description_ = std::string("Failed to validate parameters for attestation request.");
return result;
}
std::string maa_response;
std::string token_encrypted;
std::string token_decrypted;
uint8_t attestation_retries = 0;
while(true) {
if((result = sendAttestationRequest(params, maa_response)).code_ !=
AttestationResult::ErrorCode::SUCCESS) {
CLIENT_LOG_ERROR("Failed to send attestation request with error:%s",
result.description_.c_str());
return result;
}
if((result = ParseMaaResponse(maa_response, token_encrypted)).code_ !=
AttestationResult::ErrorCode::SUCCESS){
CLIENT_LOG_ERROR("Failed to parse the MAA response: %s",
result.description_.c_str());
return result;
}
if((result = DecryptMaaToken(token_encrypted, token_decrypted)).code_ != AttestationResult::ErrorCode::SUCCESS) {
CLIENT_LOG_ERROR("Failed to Decrypt with error:%d description:%s\n",
static_cast<int>(result.code_),
result.description_.c_str());
// If decryption of the jwt fails, retrying attestation to make sure this is not
// a transient failure. This will prevent false positive reporting the VM health.
if(attestation_retries < MAX_ATTESTATION_RETRIES) {
CLIENT_LOG_INFO("Retyring Attestation");
std::this_thread::sleep_for(
std::chrono::seconds(
static_cast<long long>(5 * pow(2.0, static_cast<double>(attestation_retries++)))));
continue;
}
CLIENT_LOG_ERROR("Maximum attestation retries exceeded");
return result;
}
CLIENT_LOG_INFO("Successfully attested and decrypted response.");
break;
}
unsigned char *jwt_token = (unsigned char*) malloc((sizeof(unsigned char) * token_decrypted.size()) + 1); // allocating an extra byte for the null char at the end
std::memcpy(jwt_token, token_decrypted.data(), token_decrypted.size());
jwt_token[token_decrypted.size()] = '\0';
*jwt_token_out = jwt_token;
return result;
}