AttestationResult AttestationClientImpl::Attest()

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;
}